2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
24 #if defined(USE_TELEMETRY) && defined(USE_SERIALRX_CRSF) && defined(USE_TELEMETRY_CRSF)
26 #include "build/atomic.h"
27 #include "build/build_config.h"
28 #include "build/version.h"
30 #include "common/axis.h"
31 #include "common/crc.h"
32 #include "common/streambuf.h"
33 #include "common/time.h"
34 #include "common/utils.h"
35 #include "common/printf.h"
37 #include "config/feature.h"
39 #include "drivers/serial.h"
40 #include "drivers/time.h"
41 #include "drivers/nvic.h"
43 #include "fc/config.h"
44 #include "fc/rc_controls.h"
45 #include "fc/rc_modes.h"
46 #include "fc/runtime_config.h"
48 #include "flight/imu.h"
51 #include "io/serial.h"
53 #include "navigation/navigation.h"
58 #include "sensors/battery.h"
59 #include "sensors/sensors.h"
61 #include "telemetry/crsf.h"
62 #include "telemetry/telemetry.h"
63 #include "telemetry/msp_shared.h"
66 #define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz
67 #define CRSF_DEVICEINFO_VERSION 0x01
68 // According to TBS: "CRSF over serial should always use a sync byte at the beginning of each frame.
69 // To get better performance it's recommended to use the sync byte 0xC8 to get better performance"
71 // Digitalentity: Using frame address byte as a sync field looks somewhat hacky to me, but seems it's needed to get CRSF working properly
72 #define CRSF_DEVICEINFO_PARAMETER_COUNT 0
74 #define CRSF_MSP_BUFFER_SIZE 96
75 #define CRSF_MSP_LENGTH_OFFSET 1
77 static uint8_t crsfCrc
;
78 static bool crsfTelemetryEnabled
;
79 static bool deviceInfoReplyPending
;
80 static uint8_t crsfFrame
[CRSF_FRAME_SIZE_MAX
];
82 #if defined(USE_MSP_OVER_TELEMETRY)
83 typedef struct mspBuffer_s
{
84 uint8_t bytes
[CRSF_MSP_BUFFER_SIZE
];
88 static mspBuffer_t mspRxBuffer
;
90 void initCrsfMspBuffer(void)
95 bool bufferCrsfMspFrame(uint8_t *frameStart
, int frameLength
)
97 if (mspRxBuffer
.len
+ CRSF_MSP_LENGTH_OFFSET
+ frameLength
> CRSF_MSP_BUFFER_SIZE
) {
100 uint8_t *p
= mspRxBuffer
.bytes
+ mspRxBuffer
.len
;
102 memcpy(p
, frameStart
, frameLength
);
103 mspRxBuffer
.len
+= CRSF_MSP_LENGTH_OFFSET
+ frameLength
;
108 bool handleCrsfMspFrameBuffer(uint8_t payloadSize
, mspResponseFnPtr responseFn
)
110 bool requestHandled
= false;
111 if (!mspRxBuffer
.len
) {
116 const int mspFrameLength
= mspRxBuffer
.bytes
[pos
];
117 if (handleMspFrame(&mspRxBuffer
.bytes
[CRSF_MSP_LENGTH_OFFSET
+ pos
], mspFrameLength
)) {
118 requestHandled
|= sendMspReply(payloadSize
, responseFn
);
120 pos
+= CRSF_MSP_LENGTH_OFFSET
+ mspFrameLength
;
121 ATOMIC_BLOCK(NVIC_PRIO_SERIALUART
) {
122 if (pos
>= mspRxBuffer
.len
) {
124 return requestHandled
;
128 return requestHandled
;
132 static void crsfInitializeFrame(sbuf_t
*dst
)
135 dst
->ptr
= crsfFrame
;
136 dst
->end
= ARRAYEND(crsfFrame
);
138 sbufWriteU8(dst
, CRSF_TELEMETRY_SYNC_BYTE
);
141 static void crsfSerialize8(sbuf_t
*dst
, uint8_t v
)
144 crsfCrc
= crc8_dvb_s2(crsfCrc
, v
);
147 static void crsfSerialize16(sbuf_t
*dst
, uint16_t v
)
149 // Use BigEndian format
150 crsfSerialize8(dst
, (v
>> 8));
151 crsfSerialize8(dst
, (uint8_t)v
);
154 static void crsfSerialize32(sbuf_t
*dst
, uint32_t v
)
156 // Use BigEndian format
157 crsfSerialize8(dst
, (v
>> 24));
158 crsfSerialize8(dst
, (v
>> 16));
159 crsfSerialize8(dst
, (v
>> 8));
160 crsfSerialize8(dst
, (uint8_t)v
);
163 static void crsfSerializeData(sbuf_t
*dst
, const uint8_t *data
, int len
)
165 for (int ii
= 0; ii
< len
; ++ii
) {
166 crsfSerialize8(dst
, data
[ii
]);
170 static void crsfFinalize(sbuf_t
*dst
)
172 sbufWriteU8(dst
, crsfCrc
);
173 sbufSwitchToReader(dst
, crsfFrame
);
174 // write the telemetry frame to the receiver.
175 crsfRxWriteTelemetryData(sbufPtr(dst
), sbufBytesRemaining(dst
));
178 static int crsfFinalizeBuf(sbuf_t
*dst
, uint8_t *frame
)
180 sbufWriteU8(dst
, crsfCrc
);
181 sbufSwitchToReader(dst
, crsfFrame
);
182 const int frameSize
= sbufBytesRemaining(dst
);
183 for (int ii
= 0; sbufBytesRemaining(dst
); ++ii
) {
184 frame
[ii
] = sbufReadU8(dst
);
190 CRSF frame has the structure:
191 <Device address> <Frame length> <Type> <Payload> <CRC>
192 Device address: (uint8_t)
193 Frame length: length in bytes including Type (uint8_t)
195 CRC: (uint8_t), crc of <Type> and <Payload>
201 int32_t Latitude ( degree / 10`000`000 )
202 int32_t Longitude (degree / 10`000`000 )
203 uint16_t Groundspeed ( km/h / 10 )
204 uint16_t GPS heading ( degree / 100 )
205 uint16 Altitude ( meter 1000m offset )
206 uint8_t Satellites in use ( counter )
208 static void crsfFrameGps(sbuf_t
*dst
)
210 // use sbufWrite since CRC does not include frame length
211 sbufWriteU8(dst
, CRSF_FRAME_GPS_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
212 crsfSerialize8(dst
, CRSF_FRAMETYPE_GPS
);
213 crsfSerialize32(dst
, gpsSol
.llh
.lat
); // CRSF and betaflight use same units for degrees
214 crsfSerialize32(dst
, gpsSol
.llh
.lon
);
215 crsfSerialize16(dst
, (gpsSol
.groundSpeed
* 36 + 50) / 100); // gpsSol.groundSpeed is in cm/s
216 crsfSerialize16(dst
, DECIDEGREES_TO_CENTIDEGREES(gpsSol
.groundCourse
)); // gpsSol.groundCourse is 0.1 degrees, need 0.01 deg
217 const uint16_t altitude
= (getEstimatedActualPosition(Z
) / 100) + 1000;
218 crsfSerialize16(dst
, altitude
);
219 crsfSerialize8(dst
, gpsSol
.numSat
);
225 int16 Vertical speed ( cm/s )
227 static void crsfFrameVarioSensor(sbuf_t
*dst
)
229 // use sbufWrite since CRC does not include frame length
230 sbufWriteU8(dst
, CRSF_FRAME_VARIO_SENSOR_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
231 crsfSerialize8(dst
, CRSF_FRAMETYPE_VARIO_SENSOR
);
232 crsfSerialize16(dst
, lrintf(getEstimatedActualVelocity(Z
)));
238 uint16_t Voltage ( mV * 100 )
239 uint16_t Current ( mA * 100 )
240 uint24_t Capacity ( mAh )
241 uint8_t Battery remaining ( percent )
243 static void crsfFrameBatterySensor(sbuf_t
*dst
)
245 // use sbufWrite since CRC does not include frame length
246 sbufWriteU8(dst
, CRSF_FRAME_BATTERY_SENSOR_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
247 crsfSerialize8(dst
, CRSF_FRAMETYPE_BATTERY_SENSOR
);
248 if (telemetryConfig()->report_cell_voltage
) {
249 crsfSerialize16(dst
, getBatteryAverageCellVoltage() / 10);
251 crsfSerialize16(dst
, getBatteryVoltage() / 10); // vbat is in units of 0.01V
253 crsfSerialize16(dst
, getAmperage() / 10);
254 const uint8_t batteryRemainingPercentage
= calculateBatteryPercentage();
255 crsfSerialize8(dst
, (getMAhDrawn() >> 16));
256 crsfSerialize8(dst
, (getMAhDrawn() >> 8));
257 crsfSerialize8(dst
, (uint8_t)getMAhDrawn());
258 crsfSerialize8(dst
, batteryRemainingPercentage
);
262 CRSF_ACTIVE_ANTENNA1
= 0,
263 CRSF_ACTIVE_ANTENNA2
= 1
264 } crsfActiveAntenna_e
;
267 CRSF_RF_MODE_4_HZ
= 0,
268 CRSF_RF_MODE_50_HZ
= 1,
269 CRSF_RF_MODE_150_HZ
= 2
273 CRSF_RF_POWER_0_mW
= 0,
274 CRSF_RF_POWER_10_mW
= 1,
275 CRSF_RF_POWER_25_mW
= 2,
276 CRSF_RF_POWER_100_mW
= 3,
277 CRSF_RF_POWER_500_mW
= 4,
278 CRSF_RF_POWER_1000_mW
= 5,
279 CRSF_RF_POWER_2000_mW
= 6,
280 CRSF_RF_POWER_250_mW
= 7
286 int16_t Pitch angle ( rad / 10000 )
287 int16_t Roll angle ( rad / 10000 )
288 int16_t Yaw angle ( rad / 10000 )
291 // convert andgle in decidegree to radians/10000 with reducing angle to +/-180 degree range
292 static int16_t decidegrees2Radians10000(int16_t angle_decidegree
)
294 while (angle_decidegree
> 1800) {
295 angle_decidegree
-= 3600;
297 while (angle_decidegree
< -1800) {
298 angle_decidegree
+= 3600;
300 return (int16_t)(RAD
* 1000.0f
* angle_decidegree
);
303 static void crsfFrameAttitude(sbuf_t
*dst
)
305 sbufWriteU8(dst
, CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
306 crsfSerialize8(dst
, CRSF_FRAMETYPE_ATTITUDE
);
307 crsfSerialize16(dst
, decidegrees2Radians10000(attitude
.values
.pitch
));
308 crsfSerialize16(dst
, decidegrees2Radians10000(attitude
.values
.roll
));
309 crsfSerialize16(dst
, decidegrees2Radians10000(attitude
.values
.yaw
));
313 0x21 Flight mode text based
315 char[] Flight mode ( Nullterminated string )
317 static void crsfFrameFlightMode(sbuf_t
*dst
)
319 // just do "OK" for the moment as a placeholder
320 // write zero for frame length, since we don't know it yet
321 uint8_t *lengthPtr
= sbufPtr(dst
);
323 crsfSerialize8(dst
, CRSF_FRAMETYPE_FLIGHT_MODE
);
325 // use same logic as OSD, so telemetry displays same flight text as OSD when armed
326 const char *flightMode
= "OK";
327 if (ARMING_FLAG(ARMED
)) {
328 if (STATE(AIRMODE_ACTIVE
)) {
333 if (FLIGHT_MODE(FAILSAFE_MODE
)) {
335 } else if (IS_RC_MODE_ACTIVE(BOXHOMERESET
) && !FLIGHT_MODE(NAV_RTH_MODE
) && !FLIGHT_MODE(NAV_WP_MODE
)) {
337 } else if (FLIGHT_MODE(MANUAL_MODE
)) {
339 } else if (FLIGHT_MODE(NAV_RTH_MODE
)) {
341 } else if (FLIGHT_MODE(NAV_POSHOLD_MODE
)) {
343 } else if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
) && FLIGHT_MODE(NAV_ALTHOLD_MODE
)) {
345 } else if (FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) {
347 } else if (FLIGHT_MODE(NAV_WP_MODE
)) {
349 } else if (FLIGHT_MODE(NAV_ALTHOLD_MODE
)) {
351 } else if (FLIGHT_MODE(ANGLE_MODE
)) {
353 } else if (FLIGHT_MODE(HORIZON_MODE
)) {
355 } else if (FLIGHT_MODE(ANGLEHOLD_MODE
)) {
357 #ifdef USE_FW_AUTOLAND
358 } else if (FLIGHT_MODE(NAV_FW_AUTOLAND
)) {
363 } else if (feature(FEATURE_GPS
) && navConfig()->general
.flags
.extra_arming_safety
&& (!STATE(GPS_FIX
) || !STATE(GPS_FIX_HOME
))) {
364 flightMode
= "WAIT"; // Waiting for GPS lock
366 } else if (isArmingDisabled()) {
370 crsfSerializeData(dst
, (const uint8_t*)flightMode
, strlen(flightMode
));
371 crsfSerialize8(dst
, 0); // zero terminator for string
372 // write in the length
373 *lengthPtr
= sbufPtr(dst
) - lengthPtr
;
381 char[] Device Name ( Null terminated string )
385 uint8_t 255 (Max MSP Parameter)
386 uint8_t 0x01 (Parameter version 1)
388 static void crsfFrameDeviceInfo(sbuf_t
*dst
)
391 tfp_sprintf(buff
, "%s %s: %s", FC_FIRMWARE_NAME
, FC_VERSION_STRING
, TARGET_BOARD_IDENTIFIER
);
393 uint8_t *lengthPtr
= sbufPtr(dst
);
395 crsfSerialize8(dst
, CRSF_FRAMETYPE_DEVICE_INFO
);
396 crsfSerialize8(dst
, CRSF_ADDRESS_RADIO_TRANSMITTER
);
397 crsfSerialize8(dst
, CRSF_ADDRESS_FLIGHT_CONTROLLER
);
398 crsfSerializeData(dst
, (const uint8_t*)buff
, strlen(buff
));
399 crsfSerialize8(dst
, 0); // zero terminator for string
400 for (unsigned int ii
=0; ii
<12; ii
++) {
401 crsfSerialize8(dst
, 0x00);
403 crsfSerialize8(dst
, CRSF_DEVICEINFO_PARAMETER_COUNT
);
404 crsfSerialize8(dst
, CRSF_DEVICEINFO_VERSION
);
405 *lengthPtr
= sbufPtr(dst
) - lengthPtr
;
408 #define BV(x) (1 << (x)) // bit value
410 // schedule array to decide how often each type of frame is sent
412 CRSF_FRAME_START_INDEX
= 0,
413 CRSF_FRAME_ATTITUDE_INDEX
= CRSF_FRAME_START_INDEX
,
414 CRSF_FRAME_BATTERY_SENSOR_INDEX
,
415 CRSF_FRAME_FLIGHT_MODE_INDEX
,
416 CRSF_FRAME_GPS_INDEX
,
417 CRSF_FRAME_VARIO_SENSOR_INDEX
,
418 CRSF_SCHEDULE_COUNT_MAX
419 } crsfFrameTypeIndex_e
;
421 static uint8_t crsfScheduleCount
;
422 static uint8_t crsfSchedule
[CRSF_SCHEDULE_COUNT_MAX
];
424 #if defined(USE_MSP_OVER_TELEMETRY)
426 static bool mspReplyPending
;
428 void crsfScheduleMspResponse(void)
430 mspReplyPending
= true;
433 void crsfSendMspResponse(uint8_t *payload
)
435 sbuf_t crsfPayloadBuf
;
436 sbuf_t
*dst
= &crsfPayloadBuf
;
438 crsfInitializeFrame(dst
);
439 sbufWriteU8(dst
, CRSF_FRAME_TX_MSP_FRAME_SIZE
+ CRSF_FRAME_LENGTH_EXT_TYPE_CRC
);
440 crsfSerialize8(dst
, CRSF_FRAMETYPE_MSP_RESP
);
441 crsfSerialize8(dst
, CRSF_ADDRESS_RADIO_TRANSMITTER
);
442 crsfSerialize8(dst
, CRSF_ADDRESS_FLIGHT_CONTROLLER
);
443 crsfSerializeData(dst
, (const uint8_t*)payload
, CRSF_FRAME_TX_MSP_FRAME_SIZE
);
448 static void processCrsf(void)
450 static uint8_t crsfScheduleIndex
= 0;
451 const uint8_t currentSchedule
= crsfSchedule
[crsfScheduleIndex
];
453 sbuf_t crsfPayloadBuf
;
454 sbuf_t
*dst
= &crsfPayloadBuf
;
456 if (currentSchedule
& BV(CRSF_FRAME_ATTITUDE_INDEX
)) {
457 crsfInitializeFrame(dst
);
458 crsfFrameAttitude(dst
);
461 if (currentSchedule
& BV(CRSF_FRAME_BATTERY_SENSOR_INDEX
)) {
462 crsfInitializeFrame(dst
);
463 crsfFrameBatterySensor(dst
);
466 if (currentSchedule
& BV(CRSF_FRAME_FLIGHT_MODE_INDEX
)) {
467 crsfInitializeFrame(dst
);
468 crsfFrameFlightMode(dst
);
472 if (currentSchedule
& BV(CRSF_FRAME_GPS_INDEX
)) {
473 crsfInitializeFrame(dst
);
478 #if defined(USE_BARO) || defined(USE_GPS)
479 if (currentSchedule
& BV(CRSF_FRAME_VARIO_SENSOR_INDEX
)) {
480 crsfInitializeFrame(dst
);
481 crsfFrameVarioSensor(dst
);
485 crsfScheduleIndex
= (crsfScheduleIndex
+ 1) % crsfScheduleCount
;
488 void crsfScheduleDeviceInfoResponse(void)
490 deviceInfoReplyPending
= true;
493 void initCrsfTelemetry(void)
495 // check if there is a serial port open for CRSF telemetry (ie opened by the CRSF RX)
496 // and feature is enabled, if so, set CRSF telemetry enabled
497 crsfTelemetryEnabled
= crsfRxIsActive();
499 deviceInfoReplyPending
= false;
500 #if defined(USE_MSP_OVER_TELEMETRY)
501 mspReplyPending
= false;
505 crsfSchedule
[index
++] = BV(CRSF_FRAME_ATTITUDE_INDEX
);
506 crsfSchedule
[index
++] = BV(CRSF_FRAME_BATTERY_SENSOR_INDEX
);
507 crsfSchedule
[index
++] = BV(CRSF_FRAME_FLIGHT_MODE_INDEX
);
509 if (feature(FEATURE_GPS
)) {
510 crsfSchedule
[index
++] = BV(CRSF_FRAME_GPS_INDEX
);
513 #if defined(USE_BARO) || defined(USE_GPS)
514 if (sensors(SENSOR_BARO
) || (STATE(FIXED_WING_LEGACY
) && feature(FEATURE_GPS
))) {
515 crsfSchedule
[index
++] = BV(CRSF_FRAME_VARIO_SENSOR_INDEX
);
518 crsfScheduleCount
= (uint8_t)index
;
521 bool checkCrsfTelemetryState(void)
523 return crsfTelemetryEnabled
;
527 * Called periodically by the scheduler
529 void handleCrsfTelemetry(timeUs_t currentTimeUs
)
531 static uint32_t crsfLastCycleTime
;
533 if (!crsfTelemetryEnabled
) {
536 // Give the receiver a chance to send any outstanding telemetry data.
537 // This needs to be done at high frequency, to enable the RX to send the telemetry frame
538 // in between the RX frames.
539 crsfRxSendTelemetryData();
541 // Send ad-hoc response frames as soon as possible
542 #if defined(USE_MSP_OVER_TELEMETRY)
543 if (mspReplyPending
) {
544 mspReplyPending
= handleCrsfMspFrameBuffer(CRSF_FRAME_TX_MSP_FRAME_SIZE
, &crsfSendMspResponse
);
545 crsfLastCycleTime
= currentTimeUs
; // reset telemetry timing due to ad-hoc request
550 if (deviceInfoReplyPending
) {
551 sbuf_t crsfPayloadBuf
;
552 sbuf_t
*dst
= &crsfPayloadBuf
;
553 crsfInitializeFrame(dst
);
554 crsfFrameDeviceInfo(dst
);
556 deviceInfoReplyPending
= false;
557 crsfLastCycleTime
= currentTimeUs
; // reset telemetry timing due to ad-hoc request
561 // Actual telemetry data only needs to be sent at a low frequency, ie 10Hz
562 // Spread out scheduled frames evenly so each frame is sent at the same frequency.
563 if (currentTimeUs
>= crsfLastCycleTime
+ (CRSF_CYCLETIME_US
/ crsfScheduleCount
)) {
564 crsfLastCycleTime
= currentTimeUs
;
569 int getCrsfFrame(uint8_t *frame
, crsfFrameType_e frameType
)
572 sbuf_t
*sbuf
= &crsfFrameBuf
;
574 crsfInitializeFrame(sbuf
);
577 case CRSF_FRAMETYPE_ATTITUDE
:
578 crsfFrameAttitude(sbuf
);
580 case CRSF_FRAMETYPE_BATTERY_SENSOR
:
581 crsfFrameBatterySensor(sbuf
);
583 case CRSF_FRAMETYPE_FLIGHT_MODE
:
584 crsfFrameFlightMode(sbuf
);
587 case CRSF_FRAMETYPE_GPS
:
591 case CRSF_FRAMETYPE_VARIO_SENSOR
:
592 crsfFrameVarioSensor(sbuf
);
595 const int frameSize
= crsfFinalizeBuf(sbuf
, frame
);