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/>.
26 #include "config/feature.h"
27 #include "build/version.h"
29 #include "config/parameter_group.h"
30 #include "config/parameter_group_ids.h"
32 #include "common/maths.h"
33 #include "common/streambuf.h"
34 #include "common/utils.h"
36 #include "sensors/battery.h"
39 #include "io/serial.h"
41 #include "fc/rc_controls.h"
42 #include "fc/runtime_config.h"
46 #include "flight/imu.h"
51 #include "telemetry/telemetry.h"
52 #include "telemetry/crsf.h"
54 #include "fc/config.h"
56 #define CRSF_CYCLETIME_US 100000 // 100ms, 10 Hz
58 static bool crsfTelemetryEnabled
;
59 static uint8_t crsfFrame
[CRSF_FRAME_SIZE_MAX
];
61 static void crsfInitializeFrame(sbuf_t
*dst
)
64 dst
->end
= ARRAYEND(crsfFrame
);
66 sbufWriteU8(dst
, CRSF_ADDRESS_BROADCAST
);
69 static void crsfWriteCrc(sbuf_t
*dst
, uint8_t *start
)
72 uint8_t *end
= sbufPtr(dst
);
73 for (uint8_t *ptr
= start
; ptr
< end
; ++ptr
) {
74 crc
= crc8_dvb_s2(crc
, *ptr
);
76 sbufWriteU8(dst
, crc
);
79 static void crsfFinalize(sbuf_t
*dst
)
81 crsfWriteCrc(dst
, &crsfFrame
[2]); // start at byte 2, since CRC does not include device address and frame length
82 sbufSwitchToReader(dst
, crsfFrame
);
83 // write the telemetry frame to the receiver.
84 crsfRxWriteTelemetryData(sbufPtr(dst
), sbufBytesRemaining(dst
));
87 static int crsfFinalizeBuf(sbuf_t
*dst
, uint8_t *frame
)
89 crsfWriteCrc(dst
, &crsfFrame
[2]); // start at byte 2, since CRC does not include device address and frame length
90 sbufSwitchToReader(dst
, crsfFrame
);
91 const int frameSize
= sbufBytesRemaining(dst
);
92 for (int ii
= 0; sbufBytesRemaining(dst
); ++ii
) {
93 frame
[ii
] = sbufReadU8(dst
);
99 CRSF frame has the structure:
100 <Device address> <Frame length> <Type> <Payload> <CRC>
101 Device address: (uint8_t)
102 Frame length: length in bytes including Type (uint8_t)
104 CRC: (uint8_t), crc of <Type> and <Payload>
110 int32_t Latitude ( degree / 10`000`000 )
111 int32_t Longitude (degree / 10`000`000 )
112 uint16_t Groundspeed ( km/h / 10 )
113 uint16_t GPS heading ( degree / 100 )
114 uint16 Altitude ( meter 1000m offset )
115 uint8_t Satellites in use ( counter )
117 void crsfFrameGps(sbuf_t
*dst
)
119 // use sbufWrite since CRC does not include frame length
120 sbufWriteU8(dst
, CRSF_FRAME_GPS_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
121 sbufWriteU8(dst
, CRSF_FRAMETYPE_GPS
);
122 sbufWriteU32BigEndian(dst
, GPS_coord
[LAT
]); // CRSF and betaflight use same units for degrees
123 sbufWriteU32BigEndian(dst
, GPS_coord
[LON
]);
124 sbufWriteU16BigEndian(dst
, (GPS_speed
* 36 + 5) / 10); // GPS_speed is in 0.1m/s
125 sbufWriteU16BigEndian(dst
, GPS_ground_course
* 10); // GPS_ground_course is degrees * 10
126 //Send real GPS altitude only if it's reliable (there's a GPS fix)
127 const uint16_t altitude
= (STATE(GPS_FIX
) ? GPS_altitude
: 0) + 1000;
128 sbufWriteU16BigEndian(dst
, altitude
);
129 sbufWriteU8(dst
, GPS_numSat
);
135 uint16_t Voltage ( mV * 100 )
136 uint16_t Current ( mA * 100 )
137 uint24_t Capacity ( mAh )
138 uint8_t Battery remaining ( percent )
140 void crsfFrameBatterySensor(sbuf_t
*dst
)
142 // use sbufWrite since CRC does not include frame length
143 sbufWriteU8(dst
, CRSF_FRAME_BATTERY_SENSOR_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
144 sbufWriteU8(dst
, CRSF_FRAMETYPE_BATTERY_SENSOR
);
145 sbufWriteU16BigEndian(dst
, getBatteryVoltage()); // vbat is in units of 0.1V
147 const amperageMeter_t
*amperageMeter
= getAmperageMeter(batteryConfig()->amperageMeterSource
);
148 const int16_t amperage
= constrain(amperageMeter
->amperage
, -0x8000, 0x7FFF) / 10; // send amperage in 0.01 A steps, range is -320A to 320A
149 sbufWriteU16BigEndian(dst
, amperage
); // amperage is in units of 0.1A
150 const uint32_t batteryCapacity
= batteryConfig()->batteryCapacity
;
151 const uint8_t batteryRemainingPercentage
= batteryCapacityRemainingPercentage();
153 sbufWriteU16BigEndian(dst
, getAmperage() / 10);
154 const uint32_t batteryCapacity
= batteryConfig()->batteryCapacity
;
155 const uint8_t batteryRemainingPercentage
= calculateBatteryPercentageRemaining();
157 sbufWriteU8(dst
, (batteryCapacity
>> 16));
158 sbufWriteU8(dst
, (batteryCapacity
>> 8));
159 sbufWriteU8(dst
, (uint8_t)batteryCapacity
);
161 sbufWriteU8(dst
, batteryRemainingPercentage
);
165 CRSF_ACTIVE_ANTENNA1
= 0,
166 CRSF_ACTIVE_ANTENNA2
= 1
167 } crsfActiveAntenna_e
;
170 CRSF_RF_MODE_4_HZ
= 0,
171 CRSF_RF_MODE_50_HZ
= 1,
172 CRSF_RF_MODE_150_HZ
= 2
176 CRSF_RF_POWER_0_mW
= 0,
177 CRSF_RF_POWER_10_mW
= 1,
178 CRSF_RF_POWER_25_mW
= 2,
179 CRSF_RF_POWER_100_mW
= 3,
180 CRSF_RF_POWER_500_mW
= 4,
181 CRSF_RF_POWER_1000_mW
= 5,
182 CRSF_RF_POWER_2000_mW
= 6
188 int16_t Pitch angle ( rad / 10000 )
189 int16_t Roll angle ( rad / 10000 )
190 int16_t Yaw angle ( rad / 10000 )
193 #define DECIDEGREES_TO_RADIANS10000(angle) ((int16_t)(1000.0f * (angle) * RAD))
195 void crsfFrameAttitude(sbuf_t
*dst
)
197 sbufWriteU8(dst
, CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE
+ CRSF_FRAME_LENGTH_TYPE_CRC
);
198 sbufWriteU8(dst
, CRSF_FRAMETYPE_ATTITUDE
);
199 sbufWriteU16BigEndian(dst
, DECIDEGREES_TO_RADIANS10000(attitude
.values
.pitch
));
200 sbufWriteU16BigEndian(dst
, DECIDEGREES_TO_RADIANS10000(attitude
.values
.roll
));
201 sbufWriteU16BigEndian(dst
, DECIDEGREES_TO_RADIANS10000(attitude
.values
.yaw
));
205 0x21 Flight mode text based
207 char[] Flight mode ( Nullterminated string )
209 void crsfFrameFlightMode(sbuf_t
*dst
)
211 // write zero for frame length, since we don't know it yet
212 uint8_t *lengthPtr
= sbufPtr(dst
);
214 sbufWriteU8(dst
, CRSF_FRAMETYPE_FLIGHT_MODE
);
216 // use same logic as OSD, so telemetry displays same flight text as OSD
217 const char *flightMode
= "ACRO";
218 if (isAirmodeActive()) {
221 if (FLIGHT_MODE(FAILSAFE_MODE
)) {
223 } else if (FLIGHT_MODE(ANGLE_MODE
)) {
225 } else if (FLIGHT_MODE(HORIZON_MODE
)) {
228 sbufWriteString(dst
, flightMode
);
229 // write in the frame length
230 *lengthPtr
= sbufPtr(dst
) - lengthPtr
;
233 #define BV(x) (1 << (x)) // bit value
235 // schedule array to decide how often each type of frame is sent
236 #define CRSF_SCHEDULE_COUNT_MAX 5
237 static uint8_t crsfScheduleCount
;
238 static uint8_t crsfSchedule
[CRSF_SCHEDULE_COUNT_MAX
];
241 static void processCrsf(void)
243 static uint8_t crsfScheduleIndex
= 0;
244 const uint8_t currentSchedule
= crsfSchedule
[crsfScheduleIndex
];
246 sbuf_t crsfPayloadBuf
;
247 sbuf_t
*dst
= &crsfPayloadBuf
;
249 if (currentSchedule
& BV(CRSF_FRAME_ATTITUDE
)) {
250 crsfInitializeFrame(dst
);
251 crsfFrameAttitude(dst
);
254 if (currentSchedule
& BV(CRSF_FRAME_BATTERY_SENSOR
)) {
255 crsfInitializeFrame(dst
);
256 crsfFrameBatterySensor(dst
);
259 if (currentSchedule
& BV(CRSF_FRAME_FLIGHT_MODE
)) {
260 crsfInitializeFrame(dst
);
261 crsfFrameFlightMode(dst
);
265 if (currentSchedule
& BV(CRSF_FRAME_GPS
)) {
266 crsfInitializeFrame(dst
);
271 crsfScheduleIndex
= (crsfScheduleIndex
+ 1) % crsfScheduleCount
;
274 void initCrsfTelemetry(void)
276 // check if there is a serial port open for CRSF telemetry (ie opened by the CRSF RX)
277 // and feature is enabled, if so, set CRSF telemetry enabled
278 crsfTelemetryEnabled
= crsfRxIsActive();
280 crsfSchedule
[index
++] = BV(CRSF_FRAME_ATTITUDE
);
281 crsfSchedule
[index
++] = BV(CRSF_FRAME_BATTERY_SENSOR
);
282 crsfSchedule
[index
++] = BV(CRSF_FRAME_FLIGHT_MODE
);
283 if (feature(FEATURE_GPS
)) {
284 crsfSchedule
[index
++] = BV(CRSF_FRAME_GPS
);
286 crsfScheduleCount
= (uint8_t)index
;
290 bool checkCrsfTelemetryState(void)
292 return crsfTelemetryEnabled
;
296 * Called periodically by the scheduler
298 void handleCrsfTelemetry(timeUs_t currentTimeUs
)
300 static uint32_t crsfLastCycleTime
;
302 if (!crsfTelemetryEnabled
) {
305 // Give the receiver a chance to send any outstanding telemetry data.
306 // This needs to be done at high frequency, to enable the RX to send the telemetry frame
307 // in between the RX frames.
308 crsfRxSendTelemetryData();
310 // Actual telemetry data only needs to be sent at a low frequency, ie 10Hz
311 if (currentTimeUs
>= crsfLastCycleTime
+ CRSF_CYCLETIME_US
) {
312 crsfLastCycleTime
= currentTimeUs
;
317 int getCrsfFrame(uint8_t *frame
, crsfFrameType_e frameType
)
320 sbuf_t
*sbuf
= &crsfFrameBuf
;
322 crsfInitializeFrame(sbuf
);
325 case CRSF_FRAME_ATTITUDE
:
326 crsfFrameAttitude(sbuf
);
328 case CRSF_FRAME_BATTERY_SENSOR
:
329 crsfFrameBatterySensor(sbuf
);
331 case CRSF_FRAME_FLIGHT_MODE
:
332 crsfFrameFlightMode(sbuf
);
340 const int frameSize
= crsfFinalizeBuf(sbuf
, frame
);