2 * This file is part of ExpressLRS
3 * See https://github.com/AlessandroAU/ExpressLRS
5 * This file provides utilities for packing and unpacking the data to
6 * be sent over the radio link.
13 static_assert(sizeof(OTA_Packet4_s
) == OTA4_PACKET_SIZE
, "OTA4 packet stuct is invalid!");
14 static_assert(sizeof(OTA_Packet8_s
) == OTA8_PACKET_SIZE
, "OTA8 packet stuct is invalid!");
17 volatile uint8_t OtaNonce
;
18 uint16_t OtaCrcInitializer
;
19 OtaSwitchMode_e OtaSwitchModeCurrent
;
22 static Crc2Byte ota_crc
;
23 ValidatePacketCrc_t OtaValidatePacketCrc
;
24 GeneratePacketCrc_t OtaGeneratePacketCrc
;
26 void OtaUpdateCrcInitFromUid()
28 OtaCrcInitializer
= (UID
[4] << 8) | UID
[5];
29 OtaCrcInitializer
^= OTA_VERSION_ID
;
32 static inline uint8_t ICACHE_RAM_ATTR
HybridWideNonceToSwitchIndex(uint8_t const nonce
)
34 // Returns the sequence (0 to 7, then 0 to 7 rotated left by 1):
35 // 0, 1, 2, 3, 4, 5, 6, 7,
36 // 1, 2, 3, 4, 5, 6, 7, 0
37 // Because telemetry can occur on every 2, 4, 8, 16, 32, 64, 128th packet
38 // this makes sure each of the 8 values is sent at least once every 16 packets
39 // regardless of the TLM ratio
40 // Index 7 also can never fall on a telemetry slot
41 return ((nonce
& 0b111) + ((nonce
>> 3) & 0b1)) % 8;
44 #if TARGET_TX || defined(UNIT_TEST)
46 // Current ChannelData generator function being used by TX
47 PackChannelData_t OtaPackChannelData
;
48 #if defined(DEBUG_RCVR_LINKSTATS)
49 static uint32_t packetCnt
;
52 /******** Decimate 11bit to 10bit functions ********/
53 typedef uint32_t (*Decimate11to10_fn
)(uint32_t ch11bit
);
55 static uint32_t ICACHE_RAM_ATTR
Decimate11to10_Limit(uint32_t ch11bit
)
57 // Limit 10-bit result to the range CRSF_CHANNEL_VALUE_MIN/MAX
58 return CRSF_to_UINT10(constrain(ch11bit
, CRSF_CHANNEL_VALUE_MIN
, CRSF_CHANNEL_VALUE_MAX
));
61 static uint32_t ICACHE_RAM_ATTR
Decimate11to10_Div2(uint32_t ch11bit
)
63 // Simple divide-by-2 to discard the bit
68 * @brief: Pack 4x 11-bit channel array into 4x 10 bit channel struct
69 * @desc: Values are packed little-endianish such that bits A987654321 -> 87654321, 000000A9
70 * which is compatible with the 10-bit CRSF subset RC frame structure (0x17) in
71 * Betaflight, but depends on which decimate function is used if it is legacy or CRSFv3 10-bit
72 * destChannels4x10 must be zeroed before this call, the channels are ORed into it
74 static void ICACHE_RAM_ATTR
PackUInt11ToChannels4x10(uint32_t const * const src
, OTA_Channels_4x10
* const destChannels4x10
, Decimate11to10_fn decimate
)
76 const unsigned DEST_PRECISION
= 10; // number of bits for each dest, must be <SRC
77 uint8_t *dest
= (uint8_t *)destChannels4x10
;
79 unsigned destShift
= 0;
80 for (unsigned ch
=0; ch
<4; ++ch
)
82 // Convert to DEST_PRECISION value
83 unsigned chVal
= decimate(src
[ch
]);
85 // Put the low bits in any remaining dest capacity
86 *dest
++ |= chVal
<< destShift
;
88 // Shift the high bits down and place them into the next dest byte
89 unsigned srcBitsLeft
= DEST_PRECISION
- 8 + destShift
;
90 *dest
= chVal
>> (DEST_PRECISION
- srcBitsLeft
);
91 // Next dest should be shifted up by the bits consumed
92 // if destShift == 8 then everything should reset for another set
93 // but this code only expects to do the 4 channels -> 5 bytes evenly
94 destShift
= srcBitsLeft
;
98 static void ICACHE_RAM_ATTR
PackChannelDataHybridCommon(OTA_Packet4_s
* const ota4
, const uint32_t *channelData
)
100 ota4
->type
= PACKET_TYPE_RCDATA
;
101 #if defined(DEBUG_RCVR_LINKSTATS)
102 // Incremental packet counter for verification on the RX side, 32 bits shoved into CH1-CH4
103 ota4
->dbg_linkstats
.packetNum
= packetCnt
++;
105 // CRSF input is 11bit and OTA will carry only 10bit. Discard the Extended Limits (E.Limits)
106 // range and use the full 10bits to carry only 998us - 2012us
107 PackUInt11ToChannels4x10(&channelData
[0], &ota4
->rc
.ch
, &Decimate11to10_Limit
);
108 ota4
->rc
.ch4
= CRSF_to_BIT(channelData
[4]);
109 #endif /* !DEBUG_RCVR_LINKSTATS */
113 * Hybrid switches packet encoding for sending over the air
115 * Analog channels are reduced to 10 bits to allow for switch encoding
116 * Switch[0] is sent on every packet.
117 * A 3 bit switch index and 3-4 bit value is used to send the remaining switches
118 * in a round-robin fashion.
120 * Inputs: channelData, TelemetryStatus
121 * Outputs: OTA_Packet4_s, side-effects the sentSwitch value
123 // The next switch index to send, where 0=AUX2 and 6=AUX8
124 static uint8_t Hybrid8NextSwitchIndex
;
125 #if defined(UNIT_TEST)
126 void OtaSetHybrid8NextSwitchIndex(uint8_t idx
) { Hybrid8NextSwitchIndex
= idx
; }
128 void ICACHE_RAM_ATTR
GenerateChannelDataHybrid8(OTA_Packet_s
* const otaPktPtr
, const uint32_t *channelData
,
129 bool const TelemetryStatus
, uint8_t const tlmDenom
)
133 OTA_Packet4_s
* const ota4
= &otaPktPtr
->std
;
134 PackChannelDataHybridCommon(ota4
, channelData
);
136 // Actually send switchIndex - 1 in the packet, to shift down 1-7 (0b111) to 0-6 (0b110)
137 // If the two high bits are 0b11, the receiver knows it is the last switch and can use
138 // that bit to store data
139 uint8_t bitclearedSwitchIndex
= Hybrid8NextSwitchIndex
;
141 // AUX8 is High Resolution 16-pos (4-bit)
142 if (bitclearedSwitchIndex
== 6)
143 value
= CRSF_to_N(channelData
[6 + 1 + 4], 16);
146 // AUX2-7 are Low Resolution, "7pos" 6+center (3-bit)
147 // The output is mapped evenly across 6 output values (0-5)
148 // with a special value 7 indicating the middle so it works
149 // with switches with a middle position as well as 6-position
150 const uint16_t CHANNEL_BIN_COUNT
= 6;
151 const uint16_t CHANNEL_BIN_SIZE
= (CRSF_CHANNEL_VALUE_MAX
- CRSF_CHANNEL_VALUE_MIN
) / CHANNEL_BIN_COUNT
;
152 uint16_t ch
= channelData
[bitclearedSwitchIndex
+ 1 + 4];
153 // If channel is within 1/4 a BIN of being in the middle use special value 7
154 if (ch
< (CRSF_CHANNEL_VALUE_MID
-CHANNEL_BIN_SIZE
/4)
155 || ch
> (CRSF_CHANNEL_VALUE_MID
+CHANNEL_BIN_SIZE
/4))
156 value
= CRSF_to_N(ch
, CHANNEL_BIN_COUNT
);
162 TelemetryStatus
<< 6 |
163 // tell the receiver which switch index this is
164 bitclearedSwitchIndex
<< 3 |
165 // include the switch value
168 // update the sent value
169 Hybrid8NextSwitchIndex
= (bitclearedSwitchIndex
+ 1) % 7;
173 * Return the OTA value respresentation of the switch contained in ChannelData
174 * Switches 1-6 (AUX2-AUX7) are 6 or 7 bit depending on the lowRes parameter
176 static uint8_t ICACHE_RAM_ATTR
HybridWideSwitchToOta(const uint32_t *channelData
, uint8_t const switchIdx
, bool const lowRes
)
178 uint16_t ch
= channelData
[switchIdx
+ 4];
179 uint8_t binCount
= (lowRes
) ? 64 : 128;
180 ch
= CRSF_to_N(ch
, binCount
);
182 return ch
& 0b111111; // 6-bit
184 return ch
& 0b1111111; // 7-bit
188 * HybridWide switches packet encoding for sending over the air
190 * Analog channels are reduced to 10 bits to allow for switch encoding
191 * Switch[0] is sent on every packet.
192 * A 6 or 7 bit switch value is used to send the remaining switches
193 * in a round-robin fashion.
195 * Inputs: cchannelData, TelemetryStatus
196 * Outputs: OTA_Packet4_s
198 void ICACHE_RAM_ATTR
GenerateChannelDataHybridWide(OTA_Packet_s
* const otaPktPtr
, const uint32_t *channelData
,
199 bool const TelemetryStatus
, uint8_t const tlmDenom
)
201 OTA_Packet4_s
* const ota4
= &otaPktPtr
->std
;
202 PackChannelDataHybridCommon(ota4
, channelData
);
204 uint8_t telemBit
= TelemetryStatus
<< 6;
205 uint8_t nextSwitchIndex
= HybridWideNonceToSwitchIndex(OtaNonce
);
207 // Using index 7 means the telemetry bit will always be sent in the packet
208 // preceding the RX's telemetry slot for all tlmDenom >= 8
209 // For more frequent telemetry rates, include the bit in every
210 // packet and degrade the value to 6-bit
211 // (technically we could squeeze 7-bits in for 2 channels with tlmDenom=4)
212 if (nextSwitchIndex
== 7)
214 value
= telemBit
| CRSF::LinkStatistics
.uplink_TX_Power
;
218 bool telemInEveryPacket
= (tlmDenom
< 8);
219 value
= HybridWideSwitchToOta(channelData
, nextSwitchIndex
+ 1, telemInEveryPacket
);
220 if (telemInEveryPacket
)
224 ota4
->rc
.switches
= value
;
227 static void ICACHE_RAM_ATTR
GenerateChannelData8ch12ch(OTA_Packet8_s
* const ota8
, const uint32_t *channelData
, bool const TelemetryStatus
, bool const isHighAux
)
229 // All channel data is 10 bit apart from AUX1 which is 1 bit
230 ota8
->rc
.packetType
= PACKET_TYPE_RCDATA
;
231 ota8
->rc
.telemetryStatus
= TelemetryStatus
;
232 // uplinkPower has 8 items but only 3 bits, but 0 is 0 power which we never use, shift 1-8 -> 0-7
233 ota8
->rc
.uplinkPower
= constrain(CRSF::LinkStatistics
.uplink_TX_Power
, 1, 8) - 1;
234 ota8
->rc
.isHighAux
= isHighAux
;
235 ota8
->rc
.ch4
= CRSF_to_BIT(channelData
[4]);
236 #if defined(DEBUG_RCVR_LINKSTATS)
237 // Incremental packet counter for verification on the RX side, 32 bits shoved into CH1-CH4
238 ota8
->dbg_linkstats
.packetNum
= packetCnt
++;
241 // 8ch always: low=0 high=5
242 // 12ch isHighAux=false: low=0 high=5
243 // 12ch isHighAux=true: low=0 high=9
244 // 16ch isHighAux=false: low=0 high=4
245 // 16ch isHighAux=true: low=8 high=12
248 if (OtaSwitchModeCurrent
== smHybridOr16ch
)
265 chSrcHigh
= isHighAux
? 9 : 5;
267 PackUInt11ToChannels4x10(&channelData
[chSrcLow
], &ota8
->rc
.chLow
, &Decimate11to10_Div2
);
268 PackUInt11ToChannels4x10(&channelData
[chSrcHigh
], &ota8
->rc
.chHigh
, &Decimate11to10_Div2
);
272 static void ICACHE_RAM_ATTR
GenerateChannelData8ch(OTA_Packet_s
* const otaPktPtr
, const uint32_t *channelData
, bool const TelemetryStatus
, uint8_t const tlmDenom
)
276 GenerateChannelData8ch12ch((OTA_Packet8_s
* const)otaPktPtr
, channelData
, TelemetryStatus
, false);
279 static bool FullResIsHighAux
;
280 #if defined(UNIT_TEST)
281 void OtaSetFullResNextChannelSet(bool next
) { FullResIsHighAux
= next
; }
283 static void ICACHE_RAM_ATTR
GenerateChannelData12ch(OTA_Packet_s
* const otaPktPtr
, const uint32_t *channelData
, bool const TelemetryStatus
, uint8_t const tlmDenom
)
287 // Every time this function is called, the opposite high Aux channels are sent
288 // This tries to ensure a fair split of high and low aux channels packets even
289 // at 1:2 ratio and around sync packets
290 GenerateChannelData8ch12ch((OTA_Packet8_s
* const)otaPktPtr
, channelData
, TelemetryStatus
, FullResIsHighAux
);
291 FullResIsHighAux
= !FullResIsHighAux
;
296 #if TARGET_RX || defined(UNIT_TEST)
298 // Current ChannelData unpacker function being used by RX
299 UnpackChannelData_t OtaUnpackChannelData
;
301 #if defined(DEBUG_RCVR_LINKSTATS)
302 // Sequential PacketID from the TX
303 uint32_t debugRcvrLinkstatsPacketId
;
306 static void UnpackChannels4x10ToUInt11(OTA_Channels_4x10
const * const srcChannels4x10
, uint32_t * const dest
)
308 uint8_t const * const payload
= (uint8_t const * const)srcChannels4x10
;
309 constexpr unsigned numOfChannels
= 4;
310 constexpr unsigned srcBits
= 10;
311 constexpr unsigned dstBits
= 11;
312 constexpr unsigned inputChannelMask
= (1 << srcBits
) - 1;
313 constexpr unsigned precisionShift
= dstBits
- srcBits
;
315 // code from BetaFlight rx/crsf.cpp / bitpacker_unpack
316 uint8_t bitsMerged
= 0;
317 uint32_t readValue
= 0;
318 unsigned readByteIndex
= 0;
319 for (uint8_t n
= 0; n
< numOfChannels
; n
++)
321 while (bitsMerged
< srcBits
)
323 uint8_t readByte
= payload
[readByteIndex
++];
324 readValue
|= ((uint32_t) readByte
) << bitsMerged
;
327 //printf("rv=%x(%x) bm=%u\n", readValue, (readValue & channelMask), bitsMerged);
328 dest
[n
] = (readValue
& inputChannelMask
) << precisionShift
;
329 readValue
>>= srcBits
;
330 bitsMerged
-= srcBits
;
333 #endif /* !DEBUG_RCVR_LINKSTATS */
335 static void ICACHE_RAM_ATTR
UnpackChannelDataHybridCommon(OTA_Packet4_s
const * const ota4
, uint32_t *channelData
)
337 #if defined(DEBUG_RCVR_LINKSTATS)
338 debugRcvrLinkstatsPacketId
= ota4
->dbg_linkstats
.packetNum
;
340 // The analog channels, encoded as 10bit where 0 = 998us and 1023 = 2012us
341 UnpackChannels4x10ToUInt11(&ota4
->rc
.ch
, &channelData
[0]);
342 // The unpacker simply does a << 1 to convert 10 to 11bit, but Hybrid/Wide modes
343 // only pack a subset of the full range CRSF data, so properly expand it
344 // This is ~80 bytes less code than passing an 10-to-11 expander fn to the unpacker
345 for (unsigned ch
=0; ch
<4; ++ch
)
347 channelData
[ch
] = UINT10_to_CRSF(channelData
[ch
] >> 1);
349 channelData
[4] = BIT_to_CRSF(ota4
->rc
.ch4
);
354 * Hybrid switches decoding of over the air data
356 * Hybrid switches uses 10 bits for each analog channel,
357 * 2 bits for the low latency switch[0]
358 * 3 bits for the round-robin switch index and 2 bits for the value
361 * Output: channelData
362 * Returns: TelemetryStatus bit
364 bool ICACHE_RAM_ATTR
UnpackChannelDataHybridSwitch8(OTA_Packet_s
const * const otaPktPtr
, uint32_t *channelData
,
365 uint8_t const tlmDenom
)
369 OTA_Packet4_s
const * const ota4
= (OTA_Packet4_s
const * const)otaPktPtr
;
370 UnpackChannelDataHybridCommon(ota4
, channelData
);
372 // The round-robin switch, switchIndex is actually index-1
373 // to leave the low bit open for switch 7 (sent as 0b11x)
374 // where x is the high bit of switch 7
375 const uint8_t switchByte
= ota4
->rc
.switches
;
376 uint8_t switchIndex
= (switchByte
& 0b111000) >> 3;
377 if (switchIndex
>= 6)
379 // Because AUX1 (index 0) is the low latency switch, the low bit
380 // of the switchIndex can be used as data, and arrives as index "6"
381 channelData
[11] = N_to_CRSF(switchByte
& 0b1111, 15);
385 channelData
[5+switchIndex
] = SWITCH3b_to_CRSF(switchByte
& 0b111);
388 // TelemetryStatus bit
389 return switchByte
& (1 << 6);
393 * HybridWide switches decoding of over the air data
395 * Hybrid switches uses 10 bits for each analog channel,
396 * 1 bits for the low latency switch[0]
397 * 6 or 7 bits for the round-robin switch
398 * 1 bit for the TelemetryStatus, which may be in every packet or just idx 7
399 * depending on TelemetryRatio
401 * Output: channelData
402 * Returns: TelemetryStatus bit
404 bool ICACHE_RAM_ATTR
UnpackChannelDataHybridWide(OTA_Packet_s
const * const otaPktPtr
, uint32_t *channelData
,
405 uint8_t const tlmDenom
)
407 static bool TelemetryStatus
= false;
409 OTA_Packet4_s
const * const ota4
= (OTA_Packet4_s
const * const)otaPktPtr
;
410 UnpackChannelDataHybridCommon(ota4
, channelData
);
412 // The round-robin switch, 6-7 bits with the switch index implied by the nonce
413 const uint8_t switchByte
= ota4
->rc
.switches
;
414 bool telemInEveryPacket
= (tlmDenom
< 8);
415 uint8_t switchIndex
= HybridWideNonceToSwitchIndex(OtaNonce
);
416 if (telemInEveryPacket
|| switchIndex
== 7)
417 TelemetryStatus
= (switchByte
& 0b01000000) >> 6;
418 if (switchIndex
== 7)
420 CRSF::LinkStatistics
.uplink_TX_Power
= switchByte
& 0b111111;
425 uint16_t switchValue
;
426 if (telemInEveryPacket
)
429 switchValue
= switchByte
& 0b111111; // 6-bit
434 switchValue
= switchByte
& 0b1111111; // 7-bit
437 channelData
[5 + switchIndex
] = N_to_CRSF(switchValue
, bins
);
440 return TelemetryStatus
;
443 bool ICACHE_RAM_ATTR
UnpackChannelData8ch(OTA_Packet_s
const * const otaPktPtr
, uint32_t *channelData
, uint8_t const tlmDenom
)
447 OTA_Packet8_s
const * const ota8
= (OTA_Packet8_s
const * const)otaPktPtr
;
449 #if defined(DEBUG_RCVR_LINKSTATS)
450 debugRcvrLinkstatsPacketId
= ota8
->dbg_linkstats
.packetNum
;
454 if (OtaSwitchModeCurrent
== smHybridOr16ch
)
456 if (ota8
->rc
.isHighAux
)
469 channelData
[4] = BIT_to_CRSF(ota8
->rc
.ch4
);
471 chDstHigh
= (ota8
->rc
.isHighAux
) ? 9 : 5;
474 // Analog channels packed 10bit covering the entire CRSF extended range (i.e. not just 988-2012)
475 // ** Different than the 10bit encoding in Hybrid/Wide mode **
476 UnpackChannels4x10ToUInt11(&ota8
->rc
.chLow
, &channelData
[chDstLow
]);
477 UnpackChannels4x10ToUInt11(&ota8
->rc
.chHigh
, &channelData
[chDstHigh
]);
479 // Restore the uplink_TX_Power range 0-7 -> 1-8
480 CRSF::LinkStatistics
.uplink_TX_Power
= ota8
->rc
.uplinkPower
+ 1;
481 return ota8
->rc
.telemetryStatus
;
485 bool ICACHE_RAM_ATTR
ValidatePacketCrcFull(OTA_Packet_s
* const otaPktPtr
)
487 uint16_t const calculatedCRC
=
488 ota_crc
.calc((uint8_t*)otaPktPtr
, OTA8_CRC_CALC_LEN
, OtaCrcInitializer
);
489 return otaPktPtr
->full
.crc
== calculatedCRC
;
492 bool ICACHE_RAM_ATTR
ValidatePacketCrcStd(OTA_Packet_s
* const otaPktPtr
)
494 uint16_t const inCRC
= ((uint16_t)otaPktPtr
->std
.crcHigh
<< 8) + otaPktPtr
->std
.crcLow
;
495 // For smHybrid the CRC only has the packet type in byte 0
496 // For smWide the FHSS slot is added to the CRC in byte 0 on PACKET_TYPE_RCDATAs
497 #if defined(TARGET_RX)
498 if (otaPktPtr
->std
.type
== PACKET_TYPE_RCDATA
&& OtaSwitchModeCurrent
== smWideOr8ch
)
500 otaPktPtr
->std
.crcHigh
= (OtaNonce
% ExpressLRS_currAirRate_Modparams
->FHSShopInterval
) + 1;
505 otaPktPtr
->std
.crcHigh
= 0;
507 uint16_t const calculatedCRC
=
508 ota_crc
.calc((uint8_t*)otaPktPtr
, OTA4_CRC_CALC_LEN
, OtaCrcInitializer
);
509 return inCRC
== calculatedCRC
;
512 void ICACHE_RAM_ATTR
GeneratePacketCrcFull(OTA_Packet_s
* const otaPktPtr
)
514 otaPktPtr
->full
.crc
= ota_crc
.calc((uint8_t*)otaPktPtr
, OTA8_CRC_CALC_LEN
, OtaCrcInitializer
);
517 void ICACHE_RAM_ATTR
GeneratePacketCrcStd(OTA_Packet_s
* const otaPktPtr
)
519 #if defined(TARGET_TX)
520 // artificially inject the low bits of the nonce on data packets, this will be overwritten with the CRC after it's calculated
521 if (otaPktPtr
->std
.type
== PACKET_TYPE_RCDATA
&& OtaSwitchModeCurrent
== smWideOr8ch
)
523 otaPktPtr
->std
.crcHigh
= (OtaNonce
% ExpressLRS_currAirRate_Modparams
->FHSShopInterval
) + 1;
526 uint16_t crc
= ota_crc
.calc((uint8_t*)otaPktPtr
, OTA4_CRC_CALC_LEN
, OtaCrcInitializer
);
527 otaPktPtr
->std
.crcHigh
= (crc
>> 8);
528 otaPktPtr
->std
.crcLow
= crc
;
531 void OtaUpdateSerializers(OtaSwitchMode_e
const switchMode
, uint8_t packetSize
)
533 OtaIsFullRes
= (packetSize
== OTA8_PACKET_SIZE
);
537 OtaValidatePacketCrc
= &ValidatePacketCrcFull
;
538 OtaGeneratePacketCrc
= &GeneratePacketCrcFull
;
539 ota_crc
.init(16, ELRS_CRC16_POLY
);
541 #if defined(TARGET_TX) || defined(UNIT_TEST)
542 if (switchMode
== smWideOr8ch
)
543 OtaPackChannelData
= &GenerateChannelData8ch
;
545 OtaPackChannelData
= &GenerateChannelData12ch
;
547 #if defined(TARGET_RX) || defined(UNIT_TEST)
548 OtaUnpackChannelData
= &UnpackChannelData8ch
;
554 OtaValidatePacketCrc
= &ValidatePacketCrcStd
;
555 OtaGeneratePacketCrc
= &GeneratePacketCrcStd
;
556 ota_crc
.init(14, ELRS_CRC14_POLY
);
558 if (switchMode
== smWideOr8ch
)
560 #if defined(TARGET_TX) || defined(UNIT_TEST)
561 OtaPackChannelData
= &GenerateChannelDataHybridWide
;
563 #if defined(TARGET_RX) || defined(UNIT_TEST)
564 OtaUnpackChannelData
= &UnpackChannelDataHybridWide
;
566 } // !is8ch and smWideOr8ch
570 #if defined(TARGET_TX) || defined(UNIT_TEST)
571 OtaPackChannelData
= &GenerateChannelDataHybrid8
;
573 #if defined(TARGET_RX) || defined(UNIT_TEST)
574 OtaUnpackChannelData
= &UnpackChannelDataHybridSwitch8
;
576 } // !is8ch and smHybridOr16ch
579 OtaSwitchModeCurrent
= switchMode
;
582 void OtaPackAirportData(OTA_Packet_s
* const otaPktPtr
, FIFO_GENERIC
<AP_MAX_BUF_LEN
> * inputBuffer
)
584 uint8_t count
= inputBuffer
->size();
587 count
= std::min(count
, (uint8_t)ELRS8_TELEMETRY_BYTES_PER_CALL
);
588 otaPktPtr
->full
.airport
.count
= count
;
589 inputBuffer
->popBytes(otaPktPtr
->full
.airport
.payload
, count
);
593 count
= std::min(count
, (uint8_t)ELRS4_TELEMETRY_BYTES_PER_CALL
);
594 otaPktPtr
->std
.airport
.count
= count
;
595 inputBuffer
->popBytes(otaPktPtr
->std
.airport
.payload
, count
);
596 otaPktPtr
->std
.airport
.type
= ELRS_TELEMETRY_TYPE_DATA
;
600 void OtaUnpackAirportData(OTA_Packet_s
const * const otaPktPtr
, FIFO_GENERIC
<AP_MAX_BUF_LEN
> * outputBuffer
)
604 uint8_t count
= otaPktPtr
->full
.airport
.count
;
605 outputBuffer
->pushBytes(otaPktPtr
->full
.airport
.payload
, count
);
609 uint8_t count
= otaPktPtr
->std
.airport
.count
;
610 outputBuffer
->pushBytes(otaPktPtr
->std
.airport
.payload
, count
);