Update actions to ubuntu-latest (#14114)
[betaflight.git] / src / main / rx / expresslrs_telemetry.c
blob76ea8a31405190ad85e64676d8b216f4aacfb122
1 /*
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)
8 * any later version.
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/>.
22 * Based on https://github.com/ExpressLRS/ExpressLRS
23 * Thanks to AlessandroAU, original creator of the ExpressLRS project.
25 * Authors:
26 * Phobos- - Original port.
29 #include <string.h>
30 #include "platform.h"
32 #ifdef USE_RX_EXPRESSLRS
34 #include "common/maths.h"
35 #include "config/feature.h"
36 #include "fc/runtime_config.h"
38 #include "msp/msp_protocol.h"
40 #include "rx/crsf_protocol.h"
41 #include "rx/expresslrs_telemetry.h"
43 #include "telemetry/crsf.h"
44 #include "telemetry/telemetry.h"
46 #include "sensors/battery.h"
47 #include "sensors/sensors.h"
49 static uint8_t tlmBuffer[CRSF_FRAME_SIZE_MAX];
51 typedef enum {
52 CRSF_FRAME_GPS_INDEX = 0,
53 CRSF_FRAME_BATTERY_SENSOR_INDEX,
54 CRSF_FRAME_ATTITUDE_INDEX,
55 CRSF_FRAME_FLIGHT_MODE_INDEX,
56 CRSF_FRAME_PAYLOAD_TYPES_COUNT //should be last
57 } frameTypeIndex_e;
59 static crsfFrameType_e payloadTypes[] = {
60 CRSF_FRAMETYPE_GPS,
61 CRSF_FRAMETYPE_BATTERY_SENSOR,
62 CRSF_FRAMETYPE_ATTITUDE,
63 CRSF_FRAMETYPE_FLIGHT_MODE
66 STATIC_UNIT_TESTED uint8_t tlmSensors = 0;
67 STATIC_UNIT_TESTED uint8_t currentPayloadIndex;
69 static uint8_t *data = NULL;
70 static uint8_t length = 0;
71 static uint8_t currentOffset;
72 static uint8_t bytesLastPayload;
73 static uint8_t currentPackage;
74 static bool waitUntilTelemetryConfirm;
75 static uint16_t waitCount;
76 static uint16_t maxWaitCount;
77 static volatile stubbornSenderState_e senderState;
79 static void telemetrySenderResetState(void)
81 bytesLastPayload = 0;
82 currentOffset = 0;
83 currentPackage = 1;
84 waitUntilTelemetryConfirm = true;
85 waitCount = 0;
86 // 80 corresponds to UpdateTelemetryRate(ANY, 2, 1), which is what the TX uses in boost mode
87 maxWaitCount = 80;
88 senderState = ELRS_SENDER_IDLE;
91 /***
92 * Queues a message to send, will abort the current message if one is currently being transmitted
93 ***/
94 void setTelemetryDataToTransmit(const uint8_t lengthToTransmit, uint8_t* dataToTransmit)
96 length = lengthToTransmit;
97 data = dataToTransmit;
98 currentOffset = 0;
99 currentPackage = 1;
100 waitCount = 0;
101 senderState = (senderState == ELRS_SENDER_IDLE) ? ELRS_SENDING : ELRS_RESYNC_THEN_SEND;
104 bool isTelemetrySenderActive(void)
106 return senderState != ELRS_SENDER_IDLE;
109 /***
110 * Copy up to maxLen bytes from the current package to outData
111 * packageIndex
112 ***/
113 uint8_t getCurrentTelemetryPayload(uint8_t *outData)
115 uint8_t packageIndex;
117 bytesLastPayload = 0;
118 switch (senderState) {
119 case ELRS_RESYNC:
120 case ELRS_RESYNC_THEN_SEND:
121 packageIndex = ELRS_TELEMETRY_MAX_PACKAGES;
122 break;
123 case ELRS_SENDING:
124 bytesLastPayload = MIN((uint8_t)(length - currentOffset), ELRS_TELEMETRY_BYTES_PER_CALL);
125 // If this is the last data chunk, and there has been at least one other packet
126 // skip the blank packet needed for WAIT_UNTIL_NEXT_CONFIRM
127 if (currentPackage > 1 && (currentOffset + bytesLastPayload) >= length) {
128 packageIndex = 0;
129 } else {
130 packageIndex = currentPackage;
132 memcpy(outData, &data[currentOffset], bytesLastPayload);
134 break;
135 default:
136 packageIndex = 0;
139 return packageIndex;
142 void confirmCurrentTelemetryPayload(const bool telemetryConfirmValue)
144 stubbornSenderState_e nextSenderState = senderState;
146 switch (senderState) {
147 case ELRS_SENDING:
148 if (telemetryConfirmValue != waitUntilTelemetryConfirm) {
149 waitCount++;
150 if (waitCount > maxWaitCount) {
151 waitUntilTelemetryConfirm = !telemetryConfirmValue;
152 nextSenderState = ELRS_RESYNC;
154 break;
157 currentOffset += bytesLastPayload;
158 if (currentOffset >= length) {
159 // A 0th packet is always requred so the reciver can
160 // differentiate a new send from a resend, if this is
161 // the first packet acked, send another, else IDLE
162 if (currentPackage == 1) {
163 nextSenderState = ELRS_WAIT_UNTIL_NEXT_CONFIRM;
164 } else {
165 nextSenderState = ELRS_SENDER_IDLE;
169 currentPackage++;
170 waitUntilTelemetryConfirm = !waitUntilTelemetryConfirm;
171 waitCount = 0;
172 break;
174 case ELRS_RESYNC:
175 case ELRS_RESYNC_THEN_SEND:
176 case ELRS_WAIT_UNTIL_NEXT_CONFIRM:
177 if (telemetryConfirmValue == waitUntilTelemetryConfirm) {
178 nextSenderState = (senderState == ELRS_RESYNC_THEN_SEND) ? ELRS_SENDING : ELRS_SENDER_IDLE;
179 waitUntilTelemetryConfirm = !telemetryConfirmValue;
180 } else if (senderState == ELRS_WAIT_UNTIL_NEXT_CONFIRM) { // switch to resync if tx does not confirm value fast enough
181 waitCount++;
182 if (waitCount > maxWaitCount) {
183 waitUntilTelemetryConfirm = !telemetryConfirmValue;
184 nextSenderState = ELRS_RESYNC;
188 break;
190 case ELRS_SENDER_IDLE:
191 break;
194 senderState = nextSenderState;
197 #ifdef USE_MSP_OVER_TELEMETRY
198 static uint8_t *mspData = NULL;
199 static volatile bool finishedData;
200 static volatile uint8_t mspLength = 0;
201 static volatile uint8_t mspCurrentOffset;
202 static volatile uint8_t mspCurrentPackage;
203 static volatile bool mspConfirm;
205 STATIC_UNIT_TESTED volatile bool mspReplyPending;
206 STATIC_UNIT_TESTED volatile bool deviceInfoReplyPending;
208 void mspReceiverResetState(void)
210 mspCurrentOffset = 0;
211 mspCurrentPackage = 1;
212 mspConfirm = false;
213 mspReplyPending = false;
214 deviceInfoReplyPending = false;
217 bool getCurrentMspConfirm(void)
219 return mspConfirm;
222 void setMspDataToReceive(const uint8_t maxLength, uint8_t* dataToReceive)
224 mspLength = maxLength;
225 mspData = dataToReceive;
226 mspCurrentPackage = 1;
227 mspCurrentOffset = 0;
228 finishedData = false;
231 void receiveMspData(const uint8_t packageIndex, const volatile uint8_t* const receiveData)
233 // Resync
234 if (packageIndex == ELRS_MSP_MAX_PACKAGES) {
235 mspConfirm = !mspConfirm;
236 mspCurrentPackage = 1;
237 mspCurrentOffset = 0;
238 finishedData = false;
239 return;
242 if (finishedData) {
243 return;
246 bool acceptData = false;
247 if (packageIndex == 0 && mspCurrentPackage > 1) {
248 // PackageIndex 0 (the final packet) can also contain data
249 acceptData = true;
250 finishedData = true;
251 } else if (packageIndex == mspCurrentPackage) {
252 acceptData = true;
253 mspCurrentPackage++;
256 if (acceptData && (receiveData != NULL)) {
257 uint8_t len = MIN((uint8_t)(mspLength - mspCurrentOffset), ELRS_MSP_BYTES_PER_CALL);
258 memcpy(&mspData[mspCurrentOffset], (const uint8_t*) receiveData, len);
259 mspCurrentOffset += len;
260 mspConfirm = !mspConfirm;
264 bool hasFinishedMspData(void)
266 return finishedData;
269 void mspReceiverUnlock(void)
271 if (finishedData) {
272 mspCurrentPackage = 1;
273 mspCurrentOffset = 0;
274 finishedData = false;
278 static uint8_t mspFrameSize = 0;
280 static void bufferMspResponse(uint8_t *payload, const uint8_t payloadSize)
282 mspFrameSize = getCrsfMspFrame(tlmBuffer, payload, payloadSize);
285 void processMspPacket(uint8_t *packet)
287 switch (packet[2]) {
288 case CRSF_FRAMETYPE_DEVICE_PING:
289 deviceInfoReplyPending = true;
290 break;
291 case CRSF_FRAMETYPE_MSP_REQ:
292 case CRSF_FRAMETYPE_MSP_WRITE:
293 if (bufferCrsfMspFrame(&packet[ELRS_MSP_PACKET_OFFSET], CRSF_FRAME_RX_MSP_FRAME_SIZE)) {
294 handleCrsfMspFrameBuffer(&bufferMspResponse);
295 mspReplyPending = true;
297 break;
298 default:
299 break;
302 #endif
305 * Called when the telemetry ratio or air rate changes, calculate
306 * the new threshold for how many times the telemetryConfirmValue
307 * can be wrong in a row before giving up and going to RESYNC
309 void updateTelemetryRate(const uint16_t airRate, const uint8_t tlmRatio, const uint8_t tlmBurst)
311 // consipicuously unused airRate parameter, the wait count is strictly based on number
312 // of packets, not time between the telemetry packets, or a wall clock timeout
313 UNUSED(airRate);
314 // The expected number of packet periods between telemetry packets
315 uint32_t packsBetween = tlmRatio * (1 + tlmBurst) / tlmBurst;
316 maxWaitCount = packsBetween * ELRS_TELEMETRY_MAX_MISSED_PACKETS;
319 void initTelemetry(void)
321 if (!featureIsEnabled(FEATURE_TELEMETRY)) {
322 return;
325 if (sensors(SENSOR_ACC) && telemetryIsSensorEnabled(SENSOR_PITCH | SENSOR_ROLL | SENSOR_HEADING)) {
326 tlmSensors |= BIT(CRSF_FRAME_ATTITUDE_INDEX);
328 if ((isBatteryVoltageConfigured() && telemetryIsSensorEnabled(SENSOR_VOLTAGE))
329 || (isAmperageConfigured() && telemetryIsSensorEnabled(SENSOR_CURRENT | SENSOR_FUEL))) {
330 tlmSensors |= BIT(CRSF_FRAME_BATTERY_SENSOR_INDEX);
332 if (telemetryIsSensorEnabled(SENSOR_MODE)) {
333 tlmSensors |= BIT(CRSF_FRAME_FLIGHT_MODE_INDEX);
335 #ifdef USE_GPS
336 if (featureIsEnabled(FEATURE_GPS)
337 && telemetryIsSensorEnabled(SENSOR_ALTITUDE | SENSOR_LAT_LONG | SENSOR_GROUND_SPEED | SENSOR_HEADING)) {
338 tlmSensors |= BIT(CRSF_FRAME_GPS_INDEX);
340 #endif
342 telemetrySenderResetState();
343 #ifdef USE_MSP_OVER_TELEMETRY
344 mspReceiverResetState();
345 #endif
348 bool getNextTelemetryPayload(uint8_t *nextPayloadSize, uint8_t **payloadData)
350 #ifdef USE_MSP_OVER_TELEMETRY
351 if (deviceInfoReplyPending) {
352 *nextPayloadSize = getCrsfFrame(tlmBuffer, CRSF_FRAMETYPE_DEVICE_INFO);
353 *payloadData = tlmBuffer;
354 deviceInfoReplyPending = false;
355 return true;
356 } else if (mspReplyPending) {
357 *nextPayloadSize = mspFrameSize;
358 *payloadData = tlmBuffer;
359 mspReplyPending = false;
360 return true;
361 } else
362 #endif
363 if (tlmSensors & BIT(currentPayloadIndex)) {
364 *nextPayloadSize = getCrsfFrame(tlmBuffer, payloadTypes[currentPayloadIndex]);
365 *payloadData = tlmBuffer;
366 currentPayloadIndex = (currentPayloadIndex + 1) % CRSF_FRAME_PAYLOAD_TYPES_COUNT;
367 return true;
368 } else {
369 currentPayloadIndex = (currentPayloadIndex + 1) % CRSF_FRAME_PAYLOAD_TYPES_COUNT;
370 *nextPayloadSize = 0;
371 *payloadData = 0;
372 return false;
376 #endif