2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
27 #if defined(USE_TELEMETRY_JETIEXBUS)
29 #include "build/build_config.h"
30 #include "build/debug.h"
31 #include "fc/runtime_config.h"
32 #include "fc/config.h"
33 #include "config/feature.h"
35 #include "common/utils.h"
36 #include "common/bitarray.h"
38 #include "drivers/serial.h"
39 #include "drivers/serial_uart.h"
40 #include "drivers/time.h"
42 #include "flight/imu.h"
44 #include "io/serial.h"
48 #include "rx/jetiexbus.h"
50 #include "sensors/battery.h"
51 #include "sensors/sensors.h"
52 #include "sensors/acceleration.h"
54 #include "telemetry/jetiexbus.h"
55 #include "telemetry/telemetry.h"
57 #include "navigation/navigation.h"
60 #include "sensors/esc_sensor.h"
61 #include "flight/mixer.h"
64 #define EXTEL_DATA_MSG (0x40)
65 #define EXTEL_UNMASK_TYPE (0x3F)
66 #define EXTEL_SYNC_LEN 1
67 #define EXTEL_CRC_LEN 1
68 #define EXTEL_HEADER_LEN 6
69 #define EXTEL_MAX_LEN 26
70 #define EXTEL_OVERHEAD (EXTEL_SYNC_LEN + EXTEL_HEADER_LEN + EXTEL_CRC_LEN)
71 #define EXTEL_MAX_PAYLOAD (EXTEL_MAX_LEN - EXTEL_OVERHEAD)
72 #define EXBUS_MAX_REQUEST_BUFFER_SIZE (EXBUS_OVERHEAD + EXTEL_MAX_LEN)
75 EXTEL_HEADER_SYNC
= 0,
76 EXTEL_HEADER_TYPE_LEN
,
90 EXBUS_TRANS_IS_TX_COMPLETED
,
95 EX_TYPE_6b
= 0, // int6_t Data type 6b (-31 ¸31)
96 EX_TYPE_14b
= 1, // int14_t Data type 14b (-8191 ¸8191)
97 EX_TYPE_22b
= 4, // int22_t Data type 22b (-2097151 ¸2097151)
98 EX_TYPE_DT
= 5, // int22_t Special data type – time and date
99 EX_TYPE_30b
= 8, // int30_t Data type 30b (-536870911 ¸536870911)
100 EX_TYPE_GPS
= 9, // int30_t Special data type – GPS coordinates: lo/hi minute - lo/hi degree.
101 EX_TYPE_DES
= 255 // only for devicedescription
104 const uint8_t exDataTypeLen
[] = {
113 typedef struct exBusSensor_s
{
116 const uint8_t exDataType
;
117 const uint8_t decimals
;
120 #define DECIMAL_MASK(decimals) (decimals << 5)
122 // list of telemetry messages
123 // after every 15 sensors a new header has to be inserted (e.g. "BF D2")
124 const exBusSensor_t jetiExSensors
[] = {
125 {"INAV D1", "", EX_TYPE_DES
, 0 }, // device descripton
126 {"Voltage", "V", EX_TYPE_22b
, DECIMAL_MASK(2)},
127 {"Current", "A", EX_TYPE_22b
, DECIMAL_MASK(2)},
128 {"Altitude", "m", EX_TYPE_22b
, DECIMAL_MASK(2)},
129 {"Capacity", "mAh", EX_TYPE_22b
, DECIMAL_MASK(0)},
130 {"Power", "W", EX_TYPE_22b
, DECIMAL_MASK(0)},
131 {"Roll angle", "\xB0", EX_TYPE_22b
, DECIMAL_MASK(1)},
132 {"Pitch angle", "\xB0", EX_TYPE_22b
, DECIMAL_MASK(1)},
133 {"Heading", "\xB0", EX_TYPE_22b
, DECIMAL_MASK(1)},
134 {"Vario", "m/s", EX_TYPE_22b
, DECIMAL_MASK(2)},
135 {"GPS Sats", "", EX_TYPE_22b
, DECIMAL_MASK(0)},
136 {"GPS Long", "", EX_TYPE_GPS
, DECIMAL_MASK(0)},
137 {"GPS Lat", "", EX_TYPE_GPS
, DECIMAL_MASK(0)},
138 {"GPS Speed", "m/s", EX_TYPE_22b
, DECIMAL_MASK(2)},
139 {"GPS H-Distance", "m", EX_TYPE_22b
, DECIMAL_MASK(0)},
140 {"GPS H-Direction", "\xB0", EX_TYPE_22b
, DECIMAL_MASK(1)},
141 {"INAV D2", "", EX_TYPE_DES
, 0 }, // device descripton
142 {"GPS Heading", "\xB0", EX_TYPE_22b
, DECIMAL_MASK(1)},
143 {"GPS Altitude", "m", EX_TYPE_22b
, DECIMAL_MASK(2)},
144 {"G-Force X", "", EX_TYPE_22b
, DECIMAL_MASK(3)},
145 {"G-Force Y", "", EX_TYPE_22b
, DECIMAL_MASK(3)},
146 {"G-Force Z", "", EX_TYPE_22b
, DECIMAL_MASK(3)},
147 {"RPM", "", EX_TYPE_22b
, DECIMAL_MASK(0)},
148 {"Trip Distance", "m", EX_TYPE_22b
, DECIMAL_MASK(1)}
151 // after every 15 sensors increment the step by 2 (e.g. ...EX_VAL15, EX_VAL16 = 17) to skip the device description
166 EX_GPS_DISTANCE_TO_HOME
,
167 EX_GPS_DIRECTION_TO_HOME
,
184 #define JETI_EX_SENSOR_COUNT (ARRAYLEN(jetiExSensors))
186 static uint8_t jetiExBusTelemetryFrame
[40];
187 static uint8_t jetiExBusTransceiveState
= EXBUS_TRANS_RX
;
188 static uint8_t firstActiveSensor
= 0;
189 static uint32_t exSensorEnabled
= 0;
191 static uint8_t sendJetiExBusTelemetry(uint8_t packetID
, uint8_t item
);
192 static uint8_t getNextActiveSensor(uint8_t currentSensor
);
194 // Jeti Ex Telemetry CRC calculations for a frame
195 uint8_t calcCRC8(uint8_t *pt
, uint8_t msgLen
)
198 for (uint8_t mlen
= 0; mlen
< msgLen
; mlen
++) {
200 crc
= crc
^ (crc
<< 1) ^ (crc
<< 2) ^ (0x0e090700 >> ((crc
>> 3) & 0x18));
205 void enableGpsTelemetry(bool enable
)
208 bitArraySet(&exSensorEnabled
, EX_GPS_SATS
);
209 bitArraySet(&exSensorEnabled
, EX_GPS_LONG
);
210 bitArraySet(&exSensorEnabled
, EX_GPS_LAT
);
211 bitArraySet(&exSensorEnabled
, EX_GPS_SPEED
);
212 bitArraySet(&exSensorEnabled
, EX_GPS_DISTANCE_TO_HOME
);
213 bitArraySet(&exSensorEnabled
, EX_GPS_DIRECTION_TO_HOME
);
214 bitArraySet(&exSensorEnabled
, EX_GPS_HEADING
);
215 bitArraySet(&exSensorEnabled
, EX_GPS_ALTITUDE
);
216 bitArraySet(&exSensorEnabled
, EX_TRIP_DISTANCE
);
218 bitArrayClr(&exSensorEnabled
, EX_GPS_SATS
);
219 bitArrayClr(&exSensorEnabled
, EX_GPS_LONG
);
220 bitArrayClr(&exSensorEnabled
, EX_GPS_LAT
);
221 bitArrayClr(&exSensorEnabled
, EX_GPS_SPEED
);
222 bitArrayClr(&exSensorEnabled
, EX_GPS_DISTANCE_TO_HOME
);
223 bitArrayClr(&exSensorEnabled
, EX_GPS_DIRECTION_TO_HOME
);
224 bitArrayClr(&exSensorEnabled
, EX_GPS_HEADING
);
225 bitArrayClr(&exSensorEnabled
, EX_GPS_ALTITUDE
);
226 bitArrayClr(&exSensorEnabled
, EX_TRIP_DISTANCE
);
231 * -----------------------------------------------
232 * Jeti Ex Bus Telemetry
233 * -----------------------------------------------
235 void initJetiExBusTelemetry(void)
237 // Init Ex Bus Frame header
238 jetiExBusTelemetryFrame
[EXBUS_HEADER_SYNC
] = 0x3B; // Startbytes
239 jetiExBusTelemetryFrame
[EXBUS_HEADER_REQ
] = 0x01;
240 jetiExBusTelemetryFrame
[EXBUS_HEADER_DATA_ID
] = 0x3A; // Ex Telemetry
242 // Init Ex Telemetry header
243 uint8_t *jetiExTelemetryFrame
= &jetiExBusTelemetryFrame
[EXBUS_HEADER_DATA
];
245 jetiExTelemetryFrame
[EXTEL_HEADER_SYNC
] = 0x9F; // Startbyte
246 jetiExTelemetryFrame
[EXTEL_HEADER_USN_LB
] = 0x1E; // Serial Number 4 Byte
247 jetiExTelemetryFrame
[EXTEL_HEADER_USN_HB
] = 0xA4;
248 jetiExTelemetryFrame
[EXTEL_HEADER_LSN_LB
] = 0x00; // increment by telemetry count (%16) > only 15 values per device possible
249 jetiExTelemetryFrame
[EXTEL_HEADER_LSN_HB
] = 0x00;
250 jetiExTelemetryFrame
[EXTEL_HEADER_RES
] = 0x00; // reserved, by default 0x00
252 // Check which sensors are available
253 if (isBatteryVoltageConfigured()) {
254 bitArraySet(&exSensorEnabled
, EX_VOLTAGE
);
256 if (isAmperageConfigured()) {
257 bitArraySet(&exSensorEnabled
, EX_CURRENT
);
259 if (isBatteryVoltageConfigured() && isAmperageConfigured()) {
260 bitArraySet(&exSensorEnabled
, EX_POWER
);
261 bitArraySet(&exSensorEnabled
, EX_CAPACITY
);
263 if (sensors(SENSOR_BARO
)) {
264 bitArraySet(&exSensorEnabled
, EX_ALTITUDE
);
265 bitArraySet(&exSensorEnabled
, EX_VARIO
);
267 if (sensors(SENSOR_ACC
)) {
268 bitArraySet(&exSensorEnabled
, EX_ROLL_ANGLE
);
269 bitArraySet(&exSensorEnabled
, EX_PITCH_ANGLE
);
270 bitArraySet(&exSensorEnabled
, EX_GFORCE_X
);
271 bitArraySet(&exSensorEnabled
, EX_GFORCE_Y
);
272 bitArraySet(&exSensorEnabled
, EX_GFORCE_Z
);
274 if (sensors(SENSOR_MAG
)) {
275 bitArraySet(&exSensorEnabled
, EX_HEADING
);
278 enableGpsTelemetry(feature(FEATURE_GPS
));
280 #ifdef USE_ESC_SENSOR
281 if (STATE(ESC_SENSOR_ENABLED
) && getMotorCount() > 0) {
282 bitArraySet(&exSensorEnabled
, EX_RPM
);
286 firstActiveSensor
= getNextActiveSensor(0); // find the first active sensor
289 void createExTelemetryTextMessage(uint8_t *exMessage
, uint8_t messageID
, const exBusSensor_t
*sensor
)
291 uint8_t labelLength
= strlen(sensor
->label
);
292 uint8_t unitLength
= strlen(sensor
->unit
);
294 exMessage
[EXTEL_HEADER_TYPE_LEN
] = EXTEL_OVERHEAD
+ labelLength
+ unitLength
;
295 exMessage
[EXTEL_HEADER_LSN_LB
] = messageID
& 0xF0; // Device ID
296 exMessage
[EXTEL_HEADER_ID
] = messageID
& 0x0F; // Sensor ID (%16)
297 exMessage
[EXTEL_HEADER_DATA
] = (labelLength
<< 3) + unitLength
;
299 memcpy(&exMessage
[EXTEL_HEADER_DATA
+ 1], sensor
->label
, labelLength
);
300 memcpy(&exMessage
[EXTEL_HEADER_DATA
+ 1 + labelLength
], sensor
->unit
, unitLength
);
302 exMessage
[exMessage
[EXTEL_HEADER_TYPE_LEN
] + EXTEL_CRC_LEN
] = calcCRC8(&exMessage
[EXTEL_HEADER_TYPE_LEN
], exMessage
[EXTEL_HEADER_TYPE_LEN
]);
305 uint32_t calcGpsDDMMmmm(int32_t value
, bool isLong
)
307 uint32_t absValue
= ABS(value
);
308 uint16_t deg16
= absValue
/ GPS_DEGREES_DIVIDER
;
309 uint16_t min16
= (absValue
- deg16
* GPS_DEGREES_DIVIDER
) * 6 / 1000;
312 exGps
.vWord
[0] = min16
;
313 exGps
.vWord
[1] = deg16
;
314 exGps
.vWord
[1] |= isLong
? 0x2000 : 0;
315 exGps
.vWord
[1] |= (value
< 0) ? 0x4000 : 0;
321 int32_t getSensorValue(uint8_t sensor
)
324 #ifdef USE_ESC_SENSOR
325 escSensorData_t
* escSensor
;
330 return telemetryConfig()->report_cell_voltage
? getBatteryAverageCellVoltage() : getBatteryVoltage();
334 return getAmperage();
338 return getEstimatedActualPosition(Z
);
342 return getMAhDrawn();
346 return (getBatteryVoltage() * getAmperage() / 10000);
350 return attitude
.values
.roll
;
354 return attitude
.values
.pitch
;
358 return attitude
.values
.yaw
;
362 return getEstimatedActualVelocity(Z
);
367 return gpsSol
.numSat
;
371 return calcGpsDDMMmmm(gpsSol
.llh
.lon
, true);
375 return calcGpsDDMMmmm(gpsSol
.llh
.lat
, false);
379 return gpsSol
.groundSpeed
;
382 case EX_GPS_DISTANCE_TO_HOME
:
383 return GPS_distanceToHome
;
386 case EX_GPS_DIRECTION_TO_HOME
:
387 return GPS_directionToHome
;
391 return gpsSol
.groundCourse
;
394 case EX_GPS_ALTITUDE
:
395 return getEstimatedActualPosition(Z
);
400 return acc
.accADCf
[X
] * 1000;
404 return acc
.accADCf
[Y
] * 1000;
408 return acc
.accADCf
[Z
] * 1000;
411 #ifdef USE_ESC_SENSOR
413 escSensor
= escSensorGetData();
414 if (escSensor
&& escSensor
->dataAge
<= ESC_DATA_MAX_AGE
) {
415 return escSensor
->rpm
;
422 case EX_TRIP_DISTANCE
:
423 return getTotalTravelDistance() / 10;
430 uint8_t getNextActiveSensor(uint8_t currentSensor
)
432 while( ++currentSensor
< JETI_EX_SENSOR_COUNT
) {
433 if (bitArrayGet(&exSensorEnabled
, currentSensor
)) {
437 if (currentSensor
== JETI_EX_SENSOR_COUNT
) {
438 currentSensor
= firstActiveSensor
;
440 return currentSensor
;
443 uint8_t createExTelemetryValueMessage(uint8_t *exMessage
, uint8_t item
)
445 uint8_t startItem
= item
;
446 uint8_t sensorItemMaxGroup
= (item
& 0xF0) + 0x10;
449 uint32_t sensorValue
;
451 exMessage
[EXTEL_HEADER_LSN_LB
] = item
& 0xF0; // Device ID
452 uint8_t *p
= &exMessage
[EXTEL_HEADER_ID
];
454 while (item
< sensorItemMaxGroup
) {
455 *p
++ = ((item
& 0x0F) << 4) | jetiExSensors
[item
].exDataType
; // Sensor ID (%16) | EX Data Type
457 sensorValue
= getSensorValue(item
);
458 iCount
= exDataTypeLen
[jetiExSensors
[item
].exDataType
];
462 sensorValue
= sensorValue
>> 8;
465 if (jetiExSensors
[item
].exDataType
!= EX_TYPE_GPS
) {
466 *p
++ = (sensorValue
& 0x9F) | jetiExSensors
[item
].decimals
;
471 item
= getNextActiveSensor(item
);
473 if (startItem
>= item
) {
477 if ((p
- &exMessage
[EXTEL_HEADER_ID
]) + exDataTypeLen
[jetiExSensors
[item
].exDataType
] + 1 >= EXTEL_MAX_PAYLOAD
) {
481 messageSize
= (EXTEL_HEADER_LEN
+ (p
-&exMessage
[EXTEL_HEADER_ID
]));
482 exMessage
[EXTEL_HEADER_TYPE_LEN
] = EXTEL_DATA_MSG
| messageSize
;
483 exMessage
[messageSize
+ EXTEL_CRC_LEN
] = calcCRC8(&exMessage
[EXTEL_HEADER_TYPE_LEN
], messageSize
);
485 return item
; // return the next item
488 void createExBusMessage(uint8_t *exBusMessage
, uint8_t *exMessage
, uint8_t packetID
)
492 exBusMessage
[EXBUS_HEADER_PACKET_ID
] = packetID
;
493 exBusMessage
[EXBUS_HEADER_SUBLEN
] = (exMessage
[EXTEL_HEADER_TYPE_LEN
] & EXTEL_UNMASK_TYPE
) + 2; // +2: startbyte & CRC8
494 exBusMessage
[EXBUS_HEADER_MSG_LEN
] = EXBUS_OVERHEAD
+ exBusMessage
[EXBUS_HEADER_SUBLEN
];
496 crc16
= jetiExBusCalcCRC16(exBusMessage
, exBusMessage
[EXBUS_HEADER_MSG_LEN
] - EXBUS_CRC_LEN
);
497 exBusMessage
[exBusMessage
[EXBUS_HEADER_MSG_LEN
] - 2] = crc16
;
498 exBusMessage
[exBusMessage
[EXBUS_HEADER_MSG_LEN
] - 1] = crc16
>> 8;
501 void checkJetiExBusTelemetryState(void)
506 void handleJetiExBusTelemetry(void)
508 static uint16_t framesLost
= 0; // only for debug
509 static uint8_t item
= 0;
512 // Check if we shall reset frame position due to time
513 if (jetiExBusRequestState
== EXBUS_STATE_RECEIVED
) {
515 // to prevent timing issues from request to answer - max. 4ms
516 timeDiff
= micros() - jetiTimeStampRequest
;
518 if (timeDiff
> 3000) { // include reserved time
519 jetiExBusRequestState
= EXBUS_STATE_ZERO
;
524 if ((jetiExBusRequestFrame
[EXBUS_HEADER_DATA_ID
] == EXBUS_EX_REQUEST
) && (jetiExBusCalcCRC16(jetiExBusRequestFrame
, jetiExBusRequestFrame
[EXBUS_HEADER_MSG_LEN
]) == 0)) {
525 if (serialRxBytesWaiting(jetiExBusPort
) == 0) {
526 jetiExBusTransceiveState
= EXBUS_TRANS_TX
;
527 item
= sendJetiExBusTelemetry(jetiExBusRequestFrame
[EXBUS_HEADER_PACKET_ID
], item
);
528 jetiExBusRequestState
= EXBUS_STATE_PROCESSED
;
532 jetiExBusRequestState
= EXBUS_STATE_ZERO
;
537 // check the state if transmit is ready
538 if (jetiExBusTransceiveState
== EXBUS_TRANS_IS_TX_COMPLETED
) {
539 if (isSerialTransmitBufferEmpty(jetiExBusPort
)) {
540 jetiExBusTransceiveState
= EXBUS_TRANS_RX
;
541 jetiExBusRequestState
= EXBUS_STATE_ZERO
;
546 uint8_t sendJetiExBusTelemetry(uint8_t packetID
, uint8_t item
)
548 static uint8_t sensorDescriptionCounter
= 0xFF;
549 static uint8_t requestLoop
= 0xFF;
550 static bool allSensorsActive
= true;
551 uint8_t *jetiExTelemetryFrame
= &jetiExBusTelemetryFrame
[EXBUS_HEADER_DATA
];
554 while( ++sensorDescriptionCounter
< JETI_EX_SENSOR_COUNT
) {
555 if (bitArrayGet(&exSensorEnabled
, sensorDescriptionCounter
) || (jetiExSensors
[sensorDescriptionCounter
].exDataType
== EX_TYPE_DES
)) {
559 if (sensorDescriptionCounter
== JETI_EX_SENSOR_COUNT
) {
560 sensorDescriptionCounter
= 0;
563 createExTelemetryTextMessage(jetiExTelemetryFrame
, sensorDescriptionCounter
, &jetiExSensors
[sensorDescriptionCounter
]);
564 createExBusMessage(jetiExBusTelemetryFrame
, jetiExTelemetryFrame
, packetID
);
566 if (requestLoop
== 0) {
567 item
= firstActiveSensor
;
568 if (feature(FEATURE_GPS
)) {
569 enableGpsTelemetry(false);
570 allSensorsActive
= false;
574 item
= createExTelemetryValueMessage(jetiExTelemetryFrame
, item
);
575 createExBusMessage(jetiExBusTelemetryFrame
, jetiExTelemetryFrame
, packetID
);
577 if (!allSensorsActive
) {
578 if (sensors(SENSOR_GPS
)
579 #ifdef USE_GPS_FIX_ESTIMATION
580 || STATE(GPS_ESTIMATED_FIX
)
583 enableGpsTelemetry(true);
584 allSensorsActive
= true;
589 serialWriteBuf(jetiExBusPort
, jetiExBusTelemetryFrame
, jetiExBusTelemetryFrame
[EXBUS_HEADER_MSG_LEN
]);
590 jetiExBusTransceiveState
= EXBUS_TRANS_IS_TX_COMPLETED
;