1 #include "rxtx_common.h"
2 #include "LowPassFilter.h"
5 #include "telemetry_protocol.h"
7 #include "stubborn_sender.h"
8 #include "stubborn_receiver.h"
16 #include "MeanAccumulator.h"
17 #include "freqTable.h"
19 #include "rx-serial/SerialIO.h"
20 #include "rx-serial/SerialNOOP.h"
21 #include "rx-serial/SerialCRSF.h"
22 #include "rx-serial/SerialSBUS.h"
23 #include "rx-serial/SerialSUMD.h"
24 #include "rx-serial/SerialAirPort.h"
25 #include "rx-serial/SerialHoTT_TLM.h"
26 #include "rx-serial/SerialMavlink.h"
27 #include "rx-serial/SerialTramp.h"
28 #include "rx-serial/SerialSmartAudio.h"
30 #include "rx-serial/devSerialIO.h"
34 #include "devButton.h"
35 #include "devServoOutput.h"
36 #include "devVTXSPI.h"
37 #include "devAnalogVbat.h"
38 #include "devSerialUpdate.h"
40 #include "devMSPVTX.h"
41 #include "devThermal.h"
43 #if defined(PLATFORM_ESP8266)
44 #include <user_interface.h>
46 #elif defined(PLATFORM_ESP32)
48 #include "esp_task_wdt.h"
52 // Code encapsulated by the ARDUINO_CORE_INVERT_FIX #ifdef temporarily fixes EpressLRS issue #2609 which is caused
53 // by the Arduino core (see https://github.com/espressif/arduino-esp32/issues/9896) and fixed
54 // by Espressif with Arduino core release 3.0.3 (see https://github.com/espressif/arduino-esp32/pull/9950)
56 // With availability of Arduino core 3.0.3 and upgrading ExpressLRS to Arduino core 3.0.3 the temporary fix
57 // should be deleted again
59 // ARDUINO_CORE_INVERT_FIX PT1
60 #define ARDUINO_CORE_INVERT_FIX
62 #if defined(PLATFORM_ESP32) && defined(ARDUINO_CORE_INVERT_FIX)
63 #include "driver/uart.h"
65 // ARDUINO_CORE_INVERT_FIX PT1 end
68 #define LUA_MAX_PARAMS 32
72 #define SEND_LINK_STATS_TO_FC_INTERVAL 100
73 #define DIVERSITY_ANTENNA_INTERVAL 5
74 #define DIVERSITY_ANTENNA_RSSI_TRIGGER 5
75 #define PACKET_TO_TOCK_SLACK 200 // Desired buffer time between Packet ISR and Tock ISR
78 device_affinity_t ui_devices
[] = {
80 #if defined(PLATFORM_ESP32)
83 #if defined(PLATFORM_ESP32)
84 {&SerialUpdate_device
, 1},
102 #ifdef USE_ANALOG_VBAT
103 {&AnalogVbat_device
, 0},
105 #ifdef HAS_SERVO_OUTPUT
106 {&ServoOut_device
, 1},
109 {&Baro_device
, 0}, // must come after AnalogVbat_device to slow updates
112 {&MSPVTx_device
, 0}, // dependency on VTxSPI_device
114 #if defined(HAS_THERMAL) || defined(HAS_FAN)
115 {&Thermal_device
, 0},
119 uint8_t antenna
= 0; // which antenna is currently in use
120 uint8_t geminiMode
= 0;
127 Stream
*SerialLogger
;
128 bool hardwareConfigured
= true;
130 #if defined(USE_MSP_WIFI)
131 #include "crsf2msp.h"
132 #include "msp2crsf.h"
134 CROSSFIRE2MSP crsf2msp
;
135 MSP2CROSSFIRE msp2crsf
;
138 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
139 unsigned long rebootTime
= 0;
140 extern bool webserverPreventAutoStart
;
141 bool pwmSerialDefined
= false;
145 /* SERIAL_PROTOCOL_TX is used by CRSF output */
146 #if defined(TARGET_RX_FM30_MINI)
147 HardwareSerial
SERIAL_PROTOCOL_TX(USART2
);
148 #elif defined(TARGET_DIY_900_RX_STM32)
149 HardwareSerial
SERIAL_PROTOCOL_TX(USART1
);
151 #define SERIAL_PROTOCOL_TX Serial
153 #if defined(PLATFORM_ESP32)
154 #define SERIAL1_PROTOCOL_TX Serial1
156 // SBUS driver needs to distinguish stream for SBUS/DJI protocol
157 const Stream
*serial_protocol_tx
= &(SERIAL_PROTOCOL_TX
);
158 const Stream
*serial1_protocol_tx
= &(SERIAL1_PROTOCOL_TX
);
160 SerialIO
*serial1IO
= nullptr;
164 SerialIO
*serialIO
= nullptr;
166 /* SERIAL_PROTOCOL_RX is used by telemetry receiver and can be on a different peripheral */
167 #if defined(TARGET_RX_GHOST_ATTO_V1) /* !TARGET_RX_GHOST_ATTO_V1 */
168 #define SERIAL_PROTOCOL_RX CrsfRxSerial
169 HardwareSerial
CrsfRxSerial(USART1
, HALF_DUPLEX_ENABLED
);
170 #elif defined(TARGET_R9SLIMPLUS_RX) /* !TARGET_R9SLIMPLUS_RX */
171 #define SERIAL_PROTOCOL_RX CrsfRxSerial
172 HardwareSerial
CrsfRxSerial(USART3
);
173 #elif defined(TARGET_RX_FM30_MINI)
174 #define SERIAL_PROTOCOL_RX SERIAL_PROTOCOL_TX
176 #define SERIAL_PROTOCOL_RX Serial
177 #define SERIAL1_PROTOCOL_RX Serial1
180 StubbornSender TelemetrySender
;
181 static uint8_t telemetryBurstCount
;
182 static uint8_t telemetryBurstMax
;
184 StubbornReceiver MspReceiver
;
185 uint8_t MspData
[ELRS_MSP_BUFFER
];
187 uint8_t mavlinkSSBuffer
[CRSF_MAX_PACKET_LEN
]; // Buffer for current stubbon sender packet (mavlink only)
189 static bool tlmSent
= false;
190 static uint8_t NextTelemetryType
= ELRS_TELEMETRY_TYPE_LINK
;
191 static bool telemBurstValid
;
192 /// PFD Filters ////////////////
196 /// LQ/RSSI/SNR Calculation //////////
198 LQCALC
<100> LQCalcDVDA
;
200 LPF
LPF_UplinkRSSI0(5); // track rssi per antenna
201 LPF
LPF_UplinkRSSI1(5);
202 MeanAccumulator
<int32_t, int8_t, -16> SnrMean
;
204 static uint8_t scanIndex
;
205 uint8_t ExpressLRS_nextAirRateIndex
;
206 int8_t SwitchModePending
;
208 int32_t PfdPrevRawOffset
;
209 RXtimerState_e RXtimerState
;
210 uint32_t GotConnectionMillis
= 0;
211 const uint32_t ConsiderConnGoodMillis
= 1000; // minimum time before we can consider a connection to be 'good'
212 bool doStartTimer
= false;
214 ///////////////////////////////////////////////
216 bool didFHSS
= false;
217 bool alreadyFHSS
= false;
218 bool alreadyTLMresp
= false;
220 //////////////////////////////////////////////////////////////
222 ///////Variables for Telemetry and Link Quality///////////////
223 uint32_t LastValidPacket
= 0; //Time the last valid packet was recv
224 uint32_t LastSyncPacket
= 0; //Time the last valid packet was recv
226 static uint32_t SendLinkStatstoFCintervalLastSent
;
227 static uint8_t SendLinkStatstoFCForcedSends
;
229 int16_t RFnoiseFloor
; //measurement of the current RF noise floor
230 #if defined(DEBUG_RX_SCOREBOARD)
231 static bool lastPacketCrcError
;
233 ///////////////////////////////////////////////////////////////
235 /// Variables for Sync Behaviour ////
236 uint32_t cycleInterval
; // in ms
237 uint32_t RFmodeLastCycled
= 0;
238 #define RFmodeCycleMultiplierSlow 10
239 uint8_t RFmodeCycleMultiplier
;
240 bool LockRFmode
= false;
241 ///////////////////////////////////////
243 #if defined(DEBUG_BF_LINK_STATS)
249 ///////////////////////////////////////
252 #if defined(DEBUG_RCVR_LINKSTATS)
253 static bool debugRcvrLinkstatsPending
;
254 static uint8_t debugRcvrLinkstatsFhssIdx
;
257 bool BindingModeRequest
= false;
258 static uint32_t BindingRateChangeTime
;
259 #define BindingRateChangeCyclePeriod 125
261 extern void setWifiUpdateMode();
262 void reconfigureSerial();
266 return LQCalc
.getLQ();
269 static inline void checkGeminiMode()
273 geminiMode
= config
.GetAntennaMode() || FHSSuseDualBand
; // Force Gemini when in DualBand mode. Whats the point of using a single frequency!
277 static uint8_t minLqForChaos()
279 // Determine the most number of CRC-passing packets we could receive on
280 // a single channel out of 100 packets that fill the LQcalc span.
281 // The LQ must be GREATER THAN this value, not >=
282 // The amount of time we coexist on the same channel is
283 // 100 divided by the total number of packets in a FHSS loop (rounded up)
284 // and there would be 4x packets received each time it passes by so
285 // FHSShopInterval * ceil(100 / FHSShopInterval * numfhss) or
286 // FHSShopInterval * trunc((100 + (FHSShopInterval * numfhss) - 1) / (FHSShopInterval * numfhss))
287 // With a interval of 4 this works out to: 2.4=4, FCC915=4, AU915=8, EU868=8, EU/AU433=36
288 const uint32_t numfhss
= FHSSgetChannelCount();
289 const uint8_t interval
= ExpressLRS_currAirRate_Modparams
->FHSShopInterval
;
290 return interval
* ((interval
* numfhss
+ 99) / (interval
* numfhss
));
293 void ICACHE_RAM_ATTR
getRFlinkInfo()
295 int32_t rssiDBM
= Radio
.LastPacketRSSI
;
297 if (GPIO_PIN_NSS_2
!= UNDEF_PIN
)
299 int32_t rssiDBM2
= Radio
.LastPacketRSSI2
;
301 #if !defined(DEBUG_RCVR_LINKSTATS)
302 rssiDBM
= LPF_UplinkRSSI0
.update(rssiDBM
);
303 rssiDBM2
= LPF_UplinkRSSI1
.update(rssiDBM2
);
305 rssiDBM
= (rssiDBM
> 0) ? 0 : rssiDBM
;
306 rssiDBM2
= (rssiDBM2
> 0) ? 0 : rssiDBM2
;
308 // BetaFlight/iNav expect positive values for -dBm (e.g. -80dBm -> sent as 80)
309 CRSF::LinkStatistics
.uplink_RSSI_1
= -rssiDBM
;
310 CRSF::LinkStatistics
.uplink_RSSI_2
= -rssiDBM2
;
311 antenna
= (Radio
.GetProcessingPacketRadio() == SX12XX_Radio_1
) ? 0 : 1;
313 else if (antenna
== 0)
315 #if !defined(DEBUG_RCVR_LINKSTATS)
316 rssiDBM
= LPF_UplinkRSSI0
.update(rssiDBM
);
318 if (rssiDBM
> 0) rssiDBM
= 0;
319 // BetaFlight/iNav expect positive values for -dBm (e.g. -80dBm -> sent as 80)
320 CRSF::LinkStatistics
.uplink_RSSI_1
= -rssiDBM
;
324 #if !defined(DEBUG_RCVR_LINKSTATS)
325 rssiDBM
= LPF_UplinkRSSI1
.update(rssiDBM
);
327 if (rssiDBM
> 0) rssiDBM
= 0;
328 // BetaFlight/iNav expect positive values for -dBm (e.g. -80dBm -> sent as 80)
329 // May be overwritten below if DEBUG_BF_LINK_STATS is set
330 CRSF::LinkStatistics
.uplink_RSSI_2
= -rssiDBM
;
333 SnrMean
.add(Radio
.LastPacketSNRRaw
);
335 CRSF::LinkStatistics
.active_antenna
= antenna
;
336 CRSF::LinkStatistics
.uplink_SNR
= SNR_DESCALE(Radio
.LastPacketSNRRaw
); // possibly overriden below
337 //CRSF::LinkStatistics.uplink_Link_quality = uplinkLQ; // handled in Tick
338 CRSF::LinkStatistics
.rf_Mode
= ExpressLRS_currAirRate_Modparams
->enum_rate
;
339 //DBGLN(CRSF::LinkStatistics.uplink_RSSI_1);
340 #if defined(DEBUG_BF_LINK_STATS)
341 CRSF::LinkStatistics
.downlink_RSSI_1
= debug1
;
342 CRSF::LinkStatistics
.downlink_Link_quality
= debug2
;
343 CRSF::LinkStatistics
.downlink_SNR
= debug3
;
344 CRSF::LinkStatistics
.uplink_RSSI_2
= debug4
;
347 #if defined(DEBUG_RCVR_LINKSTATS)
348 // DEBUG_RCVR_LINKSTATS gets full precision SNR, override the value
349 CRSF::LinkStatistics
.uplink_SNR
= Radio
.LastPacketSNRRaw
;
350 debugRcvrLinkstatsFhssIdx
= FHSSsequence
[FHSSptr
];
354 void SetRFLinkRate(uint8_t index
, bool bindMode
) // Set speed of RF link
356 expresslrs_mod_settings_s
*const ModParams
= get_elrs_airRateConfig(index
);
357 expresslrs_rf_pref_params_s
*const RFperf
= get_elrs_RFperfParams(index
);
358 // Binding always uses invertIQ
359 bool invertIQ
= bindMode
|| (UID
[5] & 0x01);
361 uint32_t interval
= ModParams
->interval
;
362 #if defined(DEBUG_FREQ_CORRECTION) && defined(RADIO_SX128X)
363 interval
= interval
* 12 / 10; // increase the packet interval by 20% to allow adding packet header
366 hwTimer::updateInterval(interval
);
368 FHSSusePrimaryFreqBand
= !(ModParams
->radio_type
== RADIO_TYPE_LR1121_LORA_2G4
) && !(ModParams
->radio_type
== RADIO_TYPE_LR1121_GFSK_2G4
);
369 FHSSuseDualBand
= ModParams
->radio_type
== RADIO_TYPE_LR1121_LORA_DUAL
;
371 Radio
.Config(ModParams
->bw
, ModParams
->sf
, ModParams
->cr
, FHSSgetInitialFreq(),
372 ModParams
->PreambleLen
, invertIQ
, ModParams
->PayloadLength
, 0
373 #if defined(RADIO_SX128X)
374 , uidMacSeedGet(), OtaCrcInitializer
, (ModParams
->radio_type
== RADIO_TYPE_SX128x_FLRC
)
376 #if defined(RADIO_LR1121)
377 , ModParams
->radio_type
== RADIO_TYPE_LR1121_GFSK_900
|| ModParams
->radio_type
== RADIO_TYPE_LR1121_GFSK_2G4
, (uint8_t)UID
[5], (uint8_t)UID
[4]
381 #if defined(RADIO_LR1121)
384 Radio
.Config(ModParams
->bw2
, ModParams
->sf2
, ModParams
->cr2
, FHSSgetInitialGeminiFreq(),
385 ModParams
->PreambleLen2
, invertIQ
, ModParams
->PayloadLength
, 0,
386 ModParams
->radio_type
== RADIO_TYPE_LR1121_GFSK_900
|| ModParams
->radio_type
== RADIO_TYPE_LR1121_GFSK_2G4
,
387 (uint8_t)UID
[5], (uint8_t)UID
[4], SX12XX_Radio_2
);
391 Radio
.FuzzySNRThreshold
= (RFperf
->DynpowerSnrThreshUp
== DYNPOWER_SNR_THRESH_NONE
) ? 0 : (RFperf
->DynpowerSnrThreshDn
- RFperf
->DynpowerSnrThreshUp
);
396 Radio
.SetFrequencyReg(FHSSgetInitialGeminiFreq(), SX12XX_Radio_2
);
399 OtaUpdateSerializers(smWideOr8ch
, ModParams
->PayloadLength
);
400 MspReceiver
.setMaxPackageIndex(ELRS_MSP_MAX_PACKAGES
);
401 TelemetrySender
.setMaxPackageIndex(OtaIsFullRes
? ELRS8_TELEMETRY_MAX_PACKAGES
: ELRS4_TELEMETRY_MAX_PACKAGES
);
403 // Wait for (11/10) 110% of time it takes to cycle through all freqs in FHSS table (in ms)
404 cycleInterval
= ((uint32_t)11U * FHSSgetChannelCount() * ModParams
->FHSShopInterval
* interval
) / (10U * 1000U);
406 ExpressLRS_currAirRate_Modparams
= ModParams
;
407 ExpressLRS_currAirRate_RFperfParams
= RFperf
;
408 ExpressLRS_nextAirRateIndex
= index
; // presumably we just handled this
409 telemBurstValid
= false;
412 bool ICACHE_RAM_ATTR
HandleFHSS()
414 uint8_t modresultFHSS
= (OtaNonce
+ 1) % ExpressLRS_currAirRate_Modparams
->FHSShopInterval
;
416 if ((ExpressLRS_currAirRate_Modparams
->FHSShopInterval
== 0) || alreadyFHSS
== true || InBindingMode
|| (modresultFHSS
!= 0) || (connectionState
== disconnected
))
425 if ((((OtaNonce
+ 1)/ExpressLRS_currAirRate_Modparams
->FHSShopInterval
) % 2 == 0) || FHSSuseDualBand
) // When in DualBand do not switch between radios. The OTA modulation paramters and HighFreq/LowFreq Tx amps are set during Config.
427 Radio
.SetFrequencyReg(FHSSgetNextFreq(), SX12XX_Radio_1
);
428 Radio
.SetFrequencyReg(FHSSgetGeminiFreq(), SX12XX_Radio_2
);
432 // Write radio1 first. This optimises the SPI traffic order.
433 uint32_t freqRadio2
= FHSSgetNextFreq();
434 Radio
.SetFrequencyReg(FHSSgetGeminiFreq(), SX12XX_Radio_1
);
435 Radio
.SetFrequencyReg(freqRadio2
, SX12XX_Radio_2
);
440 Radio
.SetFrequencyReg(FHSSgetNextFreq());
443 #if defined(RADIO_SX127X)
444 // SX127x radio has to reset receive mode after hopping
445 uint8_t modresultTLM
= (OtaNonce
+ 1) % ExpressLRS_currTlmDenom
;
446 if (modresultTLM
!= 0 || ExpressLRS_currTlmDenom
== 1) // if we are about to send a tlm response don't bother going back to rx
451 #if defined(Regulatory_Domain_EU_CE_2400)
452 SetClearChannelAssessmentTime();
457 void ICACHE_RAM_ATTR
LinkStatsToOta(OTA_LinkStats_s
* const ls
)
459 // The value in linkstatistics is "positivized" (inverted polarity)
460 // and must be inverted on the TX side. Positive values are used
461 // so save a bit to encode which antenna is in use
462 ls
->uplink_RSSI_1
= CRSF::LinkStatistics
.uplink_RSSI_1
;
463 ls
->uplink_RSSI_2
= CRSF::LinkStatistics
.uplink_RSSI_2
;
464 ls
->antenna
= antenna
;
465 ls
->modelMatch
= connectionHasModelMatch
;
466 ls
->lq
= CRSF::LinkStatistics
.uplink_Link_quality
;
467 ls
->mspConfirm
= MspReceiver
.GetCurrentConfirm() ? 1 : 0;
468 #if defined(DEBUG_FREQ_CORRECTION)
469 ls
->SNR
= FreqCorrection
* 127 / FreqCorrectionMax
;
471 if (SnrMean
.getCount())
473 ls
->SNR
= SnrMean
.mean();
477 ls
->SNR
= SnrMean
.previousMean();
482 bool ICACHE_RAM_ATTR
HandleSendTelemetryResponse()
484 uint8_t modresult
= (OtaNonce
+ 1) % ExpressLRS_currTlmDenom
;
486 if ((connectionState
== disconnected
) || (ExpressLRS_currTlmDenom
== 1) || (alreadyTLMresp
== true) || (modresult
!= 0) || !teamraceHasModelMatch
)
488 return false; // don't bother sending tlm if disconnected or TLM is off
491 // ESP requires word aligned buffer
492 WORD_ALIGNED_ATTR OTA_Packet_s otaPkt
= {0};
493 alreadyTLMresp
= true;
494 otaPkt
.std
.type
= PACKET_TYPE_TLM
;
496 bool noAirportDataQueued
= firmwareOptions
.is_airport
&& apOutputBuffer
.size() == 0;
497 bool noTlmQueued
= !TelemetrySender
.IsActive() && noAirportDataQueued
;
499 if (NextTelemetryType
== ELRS_TELEMETRY_TYPE_LINK
|| noTlmQueued
)
501 OTA_LinkStats_s
* ls
;
504 otaPkt
.full
.tlm_dl
.containsLinkStats
= 1;
505 ls
= &otaPkt
.full
.tlm_dl
.ul_link_stats
.stats
;
506 // Include some advanced telemetry in the extra space
507 // Note the use of `ul_link_stats.payload` vs just `payload`
508 otaPkt
.full
.tlm_dl
.packageIndex
= TelemetrySender
.GetCurrentPayload(
509 otaPkt
.full
.tlm_dl
.ul_link_stats
.payload
,
510 sizeof(otaPkt
.full
.tlm_dl
.ul_link_stats
.payload
));
514 otaPkt
.std
.tlm_dl
.type
= ELRS_TELEMETRY_TYPE_LINK
;
515 ls
= &otaPkt
.std
.tlm_dl
.ul_link_stats
.stats
;
519 NextTelemetryType
= ELRS_TELEMETRY_TYPE_DATA
;
520 // Start the count at 1 because the next will be DATA and doing +1 before checking
521 // against Max below is for some reason 10 bytes more code
522 telemetryBurstCount
= 1;
526 if (telemetryBurstCount
< telemetryBurstMax
)
528 telemetryBurstCount
++;
532 NextTelemetryType
= ELRS_TELEMETRY_TYPE_LINK
;
535 if (TelemetrySender
.IsActive())
539 otaPkt
.full
.tlm_dl
.packageIndex
= TelemetrySender
.GetCurrentPayload(
540 otaPkt
.full
.tlm_dl
.payload
,
541 sizeof(otaPkt
.full
.tlm_dl
.payload
));
545 otaPkt
.std
.tlm_dl
.type
= ELRS_TELEMETRY_TYPE_DATA
;
546 otaPkt
.std
.tlm_dl
.packageIndex
= TelemetrySender
.GetCurrentPayload(
547 otaPkt
.std
.tlm_dl
.payload
,
548 sizeof(otaPkt
.std
.tlm_dl
.payload
));
551 else if (firmwareOptions
.is_airport
)
553 OtaPackAirportData(&otaPkt
, &apInputBuffer
);
557 OtaGeneratePacketCrc(&otaPkt
);
559 SX12XX_Radio_Number_t transmittingRadio
;
560 if (config
.GetForceTlmOff())
562 transmittingRadio
= SX12XX_Radio_NONE
;
564 else if (isDualRadio())
566 transmittingRadio
= SX12XX_Radio_All
;
570 transmittingRadio
= Radio
.GetLastSuccessfulPacketRadio();
573 #if defined(Regulatory_Domain_EU_CE_2400)
574 transmittingRadio
&= ChannelIsClear(transmittingRadio
); // weed out the radio(s) if channel in use
577 if (!geminiMode
&& transmittingRadio
== SX12XX_Radio_All
) // If the receiver is in diversity mode, only send TLM on a single radio.
579 transmittingRadio
= Radio
.LastPacketRSSI
> Radio
.LastPacketRSSI2
? SX12XX_Radio_1
: SX12XX_Radio_2
; // Pick the radio with best rf connection to the tx.
582 Radio
.TXnb((uint8_t*)&otaPkt
, ExpressLRS_currAirRate_Modparams
->PayloadLength
, transmittingRadio
);
584 if (transmittingRadio
== SX12XX_Radio_NONE
)
586 // No packet will be sent due to LBT / Telem forced off.
587 // Defer TXdoneCallback() to prepare for TLM when the IRQ is normally triggered.
588 deferExecutionMicros(ExpressLRS_currAirRate_RFperfParams
->TOA
, Radio
.TXdoneCallback
);
594 int32_t ICACHE_RAM_ATTR
HandleFreqCorr(bool value
)
596 int32_t tempFC
= FreqCorrection
;
597 if (Radio
.GetProcessingPacketRadio() == SX12XX_Radio_2
)
599 tempFC
= FreqCorrection_2
;
604 if (tempFC
> FreqCorrectionMin
)
606 tempFC
--; // FREQ_STEP units
607 if (tempFC
== FreqCorrectionMin
)
609 DBGLN("Max -FreqCorrection reached!");
615 if (tempFC
< FreqCorrectionMax
)
617 tempFC
++; // FREQ_STEP units
618 if (tempFC
== FreqCorrectionMax
)
620 DBGLN("Max +FreqCorrection reached!");
625 if (Radio
.GetProcessingPacketRadio() == SX12XX_Radio_1
)
627 FreqCorrection
= tempFC
;
631 FreqCorrection_2
= tempFC
;
637 void ICACHE_RAM_ATTR
updatePhaseLock()
639 if (connectionState
!= disconnected
&& PFDloop
.hasResult())
641 int32_t RawOffset
= PFDloop
.calcResult();
642 int32_t Offset
= LPF_Offset
.update(RawOffset
);
643 int32_t OffsetDx
= LPF_OffsetDx
.update(RawOffset
- PfdPrevRawOffset
);
644 PfdPrevRawOffset
= RawOffset
;
646 if (RXtimerState
== tim_locked
)
648 // limit rate of freq offset adjustment, use slot 1
649 // because telemetry can fall on slot 1 and will
651 if (OtaNonce
% 8 == 1)
655 hwTimer::incFreqOffset();
659 hwTimer::decFreqOffset();
664 if (connectionState
!= connected
)
666 hwTimer::phaseShift(RawOffset
>> 1);
670 hwTimer::phaseShift(Offset
>> 2);
673 DBGVLN("%d:%d:%d:%d:%d", Offset
, RawOffset
, OffsetDx
, hwTimer::getFreqOffset(), uplinkLQ
);
674 UNUSED(OffsetDx
); // complier warning if no debug
680 void ICACHE_RAM_ATTR
HWtimerCallbackTick() // this is 180 out of phase with the other callback, occurs mid-packet reception
685 // if (!alreadyTLMresp && !alreadyFHSS && !LQCalc.currentIsSet()) // packet timeout AND didn't DIDN'T just hop or send TLM
687 // Radio.RXnb(); // put the radio cleanly back into RX in case of garbage data
691 if (ExpressLRS_currAirRate_Modparams
->numOfSends
== 1)
693 // Save the LQ value before the inc() reduces it by 1
694 uplinkLQ
= LQCalc
.getLQ();
696 if (!((OtaNonce
- 1) % ExpressLRS_currAirRate_Modparams
->numOfSends
))
698 uplinkLQ
= LQCalcDVDA
.getLQ();
702 CRSF::LinkStatistics
.uplink_Link_quality
= uplinkLQ
;
703 // Only advance the LQI period counter if we didn't send Telemetry this period
707 alreadyTLMresp
= false;
711 //////////////////////////////////////////////////////////////
712 // flip to the other antenna
713 // no-op if GPIO_PIN_ANT_CTRL not defined
714 static inline void switchAntenna()
716 if (GPIO_PIN_ANT_CTRL
!= UNDEF_PIN
&& config
.GetAntennaMode() == 2)
718 // 0 and 1 is use for gpio_antenna_select
721 (antenna
== 0) ? LPF_UplinkRSSI0
.reset() : LPF_UplinkRSSI1
.reset(); // discard the outdated value after switching
722 digitalWrite(GPIO_PIN_ANT_CTRL
, antenna
);
723 if (GPIO_PIN_ANT_CTRL_COMPL
!= UNDEF_PIN
)
725 digitalWrite(GPIO_PIN_ANT_CTRL_COMPL
, !antenna
);
730 static void ICACHE_RAM_ATTR
updateDiversity()
733 if (GPIO_PIN_ANT_CTRL
!= UNDEF_PIN
)
735 if(config
.GetAntennaMode() == 2)
737 // 0 and 1 is use for gpio_antenna_select
739 static int32_t prevRSSI
; // saved rssi so that we can compare if switching made things better or worse
740 static int32_t antennaLQDropTrigger
;
741 static int32_t antennaRSSIDropTrigger
;
742 int32_t rssi
= (antenna
== 0) ? LPF_UplinkRSSI0
.value() : LPF_UplinkRSSI1
.value();
743 int32_t otherRSSI
= (antenna
== 0) ? LPF_UplinkRSSI1
.value() : LPF_UplinkRSSI0
.value();
745 //if rssi dropped by the amount of DIVERSITY_ANTENNA_RSSI_TRIGGER
746 if ((rssi
< (prevRSSI
- DIVERSITY_ANTENNA_RSSI_TRIGGER
)) && antennaRSSIDropTrigger
>= DIVERSITY_ANTENNA_INTERVAL
)
749 antennaLQDropTrigger
= 1;
750 antennaRSSIDropTrigger
= 0;
752 else if (rssi
> prevRSSI
|| antennaRSSIDropTrigger
< DIVERSITY_ANTENNA_INTERVAL
)
755 antennaRSSIDropTrigger
++;
758 // if we didn't get a packet switch the antenna
759 if (!LQCalc
.currentIsSet() && antennaLQDropTrigger
== 0)
762 antennaLQDropTrigger
= 1;
763 antennaRSSIDropTrigger
= 0;
765 else if (antennaLQDropTrigger
>= DIVERSITY_ANTENNA_INTERVAL
)
767 // We switched antenna on the previous packet, so we now have relatively fresh rssi info for both antennas.
768 // We can compare the rssi values and see if we made things better or worse when we switched
769 if (rssi
< otherRSSI
)
771 // things got worse when we switched, so change back.
773 antennaLQDropTrigger
= 1;
774 antennaRSSIDropTrigger
= 0;
778 // all good, we can stay on the current antenna. Clear the flag.
779 antennaLQDropTrigger
= 0;
782 else if (antennaLQDropTrigger
> 0)
784 antennaLQDropTrigger
++;
789 digitalWrite(GPIO_PIN_ANT_CTRL
, config
.GetAntennaMode());
790 if (GPIO_PIN_ANT_CTRL_COMPL
!= UNDEF_PIN
)
792 digitalWrite(GPIO_PIN_ANT_CTRL_COMPL
, !config
.GetAntennaMode());
794 antenna
= config
.GetAntennaMode();
799 void ICACHE_RAM_ATTR
HWtimerCallbackTock()
801 PFDloop
.intEvent(micros()); // our internal osc just fired
803 if (ExpressLRS_currAirRate_Modparams
->numOfSends
> 1 && !(OtaNonce
% ExpressLRS_currAirRate_Modparams
->numOfSends
))
805 if (LQCalcDVDA
.currentIsSet())
807 crsfRCFrameAvailable();
808 if (teamraceHasModelMatch
)
809 servoNewChannelsAvailable();
816 else if (ExpressLRS_currAirRate_Modparams
->numOfSends
== 1)
818 if (!LQCalc
.currentIsSet())
830 Radio
.isFirstRxIrq
= true;
832 tlmSent
= HandleSendTelemetryResponse();
834 #if defined(DEBUG_RX_SCOREBOARD)
835 static bool lastPacketWasTelemetry
= false;
836 if (!LQCalc
.currentIsSet() && !lastPacketWasTelemetry
)
837 DBGW(lastPacketCrcError
? '.' : '_');
838 lastPacketCrcError
= false;
839 lastPacketWasTelemetry
= tlmSent
;
843 void LostConnection(bool resumeRx
)
845 DBGLN("lost conn fc=%d fo=%d", FreqCorrection
, hwTimer::getFreqOffset());
847 // Use this rate as the initial rate next time if we connected on it
848 if (connectionState
== connected
)
849 config
.SetRateInitialIdx(ExpressLRS_nextAirRateIndex
);
851 RFmodeCycleMultiplier
= 1;
852 connectionState
= disconnected
; //set lost connection
853 RXtimerState
= tim_disconnected
;
854 hwTimer::resetFreqOffset();
855 PfdPrevRawOffset
= 0;
856 GotConnectionMillis
= 0;
861 LPF_OffsetDx
.init(0);
862 alreadyTLMresp
= false;
867 if (hwTimer::running
)
869 while(micros() - PFDloop
.getIntEventTime() > 250); // time it just after the tock()
872 SetRFLinkRate(ExpressLRS_nextAirRateIndex
, false); // also sets to initialFreq
873 // If not resumRx, Radio will be left in SX127x_OPMODE_STANDBY / SX1280_MODE_STDBY_XOSC
881 void ICACHE_RAM_ATTR
TentativeConnection(unsigned long now
)
884 connectionState
= tentative
;
885 connectionHasModelMatch
= false;
886 RXtimerState
= tim_disconnected
;
887 DBGLN("tentative conn");
888 PfdPrevRawOffset
= 0;
891 RFmodeLastCycled
= now
; // give another 3 sec for lock to occur
893 // The caller MUST call hwTimer::resume(). It is not done here because
894 // the timer ISR will fire immediately and preempt any other code
897 void GotConnection(unsigned long now
)
899 if (connectionState
== connected
)
901 return; // Already connected
904 LockRFmode
= firmwareOptions
.lock_on_first_connection
;
906 connectionState
= connected
; //we got a packet, therefore no lost connection
907 RXtimerState
= tim_tentative
;
908 GotConnectionMillis
= now
;
909 #if defined(PLATFORM_ESP32) || defined(PLATFORM_ESP8266)
910 webserverPreventAutoStart
= true;
913 if (firmwareOptions
.is_airport
)
915 apInputBuffer
.flush();
916 apOutputBuffer
.flush();
922 static void ICACHE_RAM_ATTR
ProcessRfPacket_RC(OTA_Packet_s
const * const otaPktPtr
)
924 // Must be fully connected to process RC packets, prevents processing RC
925 // during sync, where packets can be received before connection
926 if (connectionState
!= connected
|| SwitchModePending
)
929 bool telemetryConfirmValue
= OtaUnpackChannelData(otaPktPtr
, ChannelData
, ExpressLRS_currTlmDenom
);
930 TelemetrySender
.ConfirmCurrentPayload(telemetryConfirmValue
);
932 // No channels packets to the FC or PWM pins if no model match
933 if (connectionHasModelMatch
)
935 if (ExpressLRS_currAirRate_Modparams
->numOfSends
== 1)
937 crsfRCFrameAvailable();
938 // teamrace is only checked for servos because the teamrace model select logic only runs
939 // when new frames are available, and will decide later if the frame will be forwarded
940 if (teamraceHasModelMatch
)
941 servoNewChannelsAvailable();
943 else if (!LQCalcDVDA
.currentIsSet())
947 #if defined(DEBUG_RCVR_LINKSTATS)
948 debugRcvrLinkstatsPending
= true;
953 void ICACHE_RAM_ATTR
OnELRSBindMSP(uint8_t* newUid4
)
955 // Binding over MSP only contains 4 bytes due to packet size limitations, clear out any leading bytes
958 for (unsigned i
= 0; i
< 4; i
++)
960 UID
[i
+ 2] = newUid4
[i
];
963 DBGLN("New UID = %u, %u, %u, %u, %u, %u", UID
[0], UID
[1], UID
[2], UID
[3], UID
[4], UID
[5]);
965 // Set new UID in eeprom
966 // EEPROM commit will happen on the main thread in ExitBindingMode()
970 static void ICACHE_RAM_ATTR
ProcessRfPacket_MSP(OTA_Packet_s
const * const otaPktPtr
)
972 uint8_t packageIndex
;
973 uint8_t const * payload
;
977 packageIndex
= otaPktPtr
->full
.msp_ul
.packageIndex
;
978 payload
= otaPktPtr
->full
.msp_ul
.payload
;
979 dataLen
= sizeof(otaPktPtr
->full
.msp_ul
.payload
);
980 if (config
.GetSerialProtocol() == PROTOCOL_MAVLINK
)
982 TelemetrySender
.ConfirmCurrentPayload(otaPktPtr
->full
.msp_ul
.tlmFlag
);
986 packageIndex
&= ELRS8_TELEMETRY_MAX_PACKAGES
;
991 packageIndex
= otaPktPtr
->std
.msp_ul
.packageIndex
;
992 payload
= otaPktPtr
->std
.msp_ul
.payload
;
993 dataLen
= sizeof(otaPktPtr
->std
.msp_ul
.payload
);
994 if (config
.GetSerialProtocol() == PROTOCOL_MAVLINK
)
996 TelemetrySender
.ConfirmCurrentPayload(otaPktPtr
->std
.msp_ul
.tlmFlag
);
1000 packageIndex
&= ELRS4_TELEMETRY_MAX_PACKAGES
;
1004 // Always examine MSP packets for bind information if in bind mode
1005 // [1] is the package index, first packet of the MSP
1006 if (InBindingMode
&& packageIndex
== 1 && payload
[0] == MSP_ELRS_BIND
)
1008 OnELRSBindMSP((uint8_t *)&payload
[1]);
1012 // Must be fully connected to process MSP, prevents processing MSP
1013 // during sync, where packets can be received before connection
1014 if (connectionState
!= connected
)
1017 bool currentMspConfirmValue
= MspReceiver
.GetCurrentConfirm();
1018 MspReceiver
.ReceiveData(packageIndex
, payload
, dataLen
);
1019 if (currentMspConfirmValue
!= MspReceiver
.GetCurrentConfirm())
1021 NextTelemetryType
= ELRS_TELEMETRY_TYPE_LINK
;
1025 static void ICACHE_RAM_ATTR
updateSwitchModePendingFromOta(uint8_t newSwitchMode
)
1027 if (OtaSwitchModeCurrent
== newSwitchMode
)
1029 // Cancel any switch if pending
1030 SwitchModePending
= 0;
1034 // One is added to the mode because SwitchModePending==0 means no switch pending
1035 // and that's also a valid switch mode. The 1 is removed when this is handled.
1036 // A negative SwitchModePending means not to switch yet
1037 int8_t newSwitchModePending
= -(int8_t)newSwitchMode
- 1;
1039 // Switch mode can be changed while disconnected
1040 // OR there are two sync packets with the same new switch mode,
1041 // as a "confirm". No RC packets are processed until
1042 if (connectionState
== disconnected
||
1043 SwitchModePending
== newSwitchModePending
)
1045 // Add one to the mode because SwitchModePending==0 means no switch pending
1046 // and that's also a valid switch mode. The 1 is removed when this is handled
1047 SwitchModePending
= newSwitchMode
+ 1;
1051 // Save the negative version of the new switch mode to compare
1052 // against on the next SYNC packet, but do not switch yet
1053 SwitchModePending
= newSwitchModePending
;
1057 static bool ICACHE_RAM_ATTR
ProcessRfPacket_SYNC(uint32_t const now
, OTA_Sync_s
const * const otaSync
)
1059 // Verify the first two of three bytes of the binding ID, which should always match
1060 if (otaSync
->UID3
!= UID
[3] || otaSync
->UID4
!= UID
[4])
1063 // The third byte will be XORed with inverse of the ModelId if ModelMatch is on
1064 // Only require the first 18 bits of the UID to match to establish a connection
1065 // but the last 6 bits must modelmatch before sending any data to the FC
1066 if ((otaSync
->UID5
& ~MODELMATCH_MASK
) != (UID
[5] & ~MODELMATCH_MASK
))
1069 LastSyncPacket
= now
;
1070 #if defined(DEBUG_RX_SCOREBOARD)
1074 // Will change the packet air rate in loop() if this changes
1075 ExpressLRS_nextAirRateIndex
= otaSync
->rateIndex
;
1076 updateSwitchModePendingFromOta(otaSync
->switchEncMode
);
1078 // Update TLM ratio, should never be TLM_RATIO_STD/DISARMED, the TX calculates the correct value for the RX
1079 expresslrs_tlm_ratio_e TLMrateIn
= (expresslrs_tlm_ratio_e
)(otaSync
->newTlmRatio
+ (uint8_t)TLM_RATIO_NO_TLM
);
1080 uint8_t TlmDenom
= TLMratioEnumToValue(TLMrateIn
);
1081 if (ExpressLRS_currTlmDenom
!= TlmDenom
)
1083 DBGLN("New TLMrate 1:%u", TlmDenom
);
1084 ExpressLRS_currTlmDenom
= TlmDenom
;
1085 telemBurstValid
= false;
1088 // modelId = 0xff indicates modelMatch is disabled, the XOR does nothing in that case
1089 uint8_t modelXor
= (~config
.GetModelId()) & MODELMATCH_MASK
;
1090 bool modelMatched
= otaSync
->UID5
== (UID
[5] ^ modelXor
);
1091 DBGVLN("MM %u=%u %d", otaSync
->UID5
, UID
[5], modelMatched
);
1093 if (connectionState
== disconnected
1094 || OtaNonce
!= otaSync
->nonce
1095 || FHSSgetCurrIndex() != otaSync
->fhssIndex
1096 || connectionHasModelMatch
!= modelMatched
)
1098 //DBGLN("\r\n%ux%ux%u", OtaNonce, otaPktPtr->sync.nonce, otaPktPtr->sync.fhssIndex);
1099 FHSSsetCurrIndex(otaSync
->fhssIndex
);
1100 OtaNonce
= otaSync
->nonce
;
1101 TentativeConnection(now
);
1102 // connectionHasModelMatch must come after TentativeConnection, which resets it
1103 connectionHasModelMatch
= modelMatched
;
1110 bool ICACHE_RAM_ATTR
ProcessRFPacket(SX12xxDriverCommon::rx_status
const status
)
1112 if (status
!= SX12xxDriverCommon::SX12XX_RX_OK
)
1114 DBGVLN("HW CRC error");
1115 #if defined(DEBUG_RX_SCOREBOARD)
1116 lastPacketCrcError
= true;
1120 uint32_t const beginProcessing
= micros();
1122 OTA_Packet_s
* const otaPktPtr
= (OTA_Packet_s
* const)Radio
.RXdataBuffer
;
1123 if (!OtaValidatePacketCrc(otaPktPtr
))
1125 DBGVLN("CRC error");
1126 #if defined(DEBUG_RX_SCOREBOARD)
1127 lastPacketCrcError
= true;
1132 PFDloop
.extEvent(beginProcessing
+ PACKET_TO_TOCK_SLACK
);
1134 doStartTimer
= false;
1135 unsigned long now
= millis();
1137 LastValidPacket
= now
;
1139 switch (otaPktPtr
->std
.type
)
1141 case PACKET_TYPE_RCDATA
: //Standard RC Data Packet
1142 ProcessRfPacket_RC(otaPktPtr
);
1144 case PACKET_TYPE_MSPDATA
:
1145 ProcessRfPacket_MSP(otaPktPtr
);
1147 case PACKET_TYPE_SYNC
: //sync packet from master
1148 doStartTimer
= ProcessRfPacket_SYNC(now
,
1149 OtaIsFullRes
? &otaPktPtr
->full
.sync
.sync
: &otaPktPtr
->std
.sync
)
1152 case PACKET_TYPE_TLM
:
1153 if (firmwareOptions
.is_airport
)
1155 OtaUnpackAirportData(otaPktPtr
, &apOutputBuffer
);
1162 // Store the LQ/RSSI/Antenna
1163 Radio
.GetLastPacketStats();
1166 if (Radio
.FrequencyErrorAvailable())
1168 int32_t tempFreqCorrection
= HandleFreqCorr(Radio
.GetFrequencyErrorbool()); // Adjusts FreqCorrection for RX freq offset
1169 #if defined(RADIO_SX127X)
1170 // Teamp900 also needs to adjust its demood PPM
1171 Radio
.SetPPMoffsetReg(tempFreqCorrection
);
1172 #endif /* RADIO_SX127X */
1175 // Received a packet, that's the definition of LQ
1177 // Extend sync duration since we've received a packet at this rate
1178 // but do not extend it indefinitely
1179 RFmodeCycleMultiplier
= RFmodeCycleMultiplierSlow
;
1181 #if defined(DEBUG_RX_SCOREBOARD)
1182 if (otaPktPtr
->std
.type
!= PACKET_TYPE_SYNC
) DBGW(connectionHasModelMatch
? 'R' : 'r');
1188 bool ICACHE_RAM_ATTR
RXdoneISR(SX12xxDriverCommon::rx_status
const status
)
1190 if (LQCalc
.currentIsSet() && connectionState
== connected
)
1192 return false; // Already received a packet, do not run ProcessRFPacket() again.
1195 if (ProcessRFPacket(status
))
1197 didFHSS
= HandleFHSS();
1201 doStartTimer
= false;
1202 hwTimer::resume(); // will throw an interrupt immediately
1210 void ICACHE_RAM_ATTR
TXdoneISR()
1213 #if defined(Regulatory_Domain_EU_CE_2400)
1214 SetClearChannelAssessmentTime();
1216 #if defined(DEBUG_RX_SCOREBOARD)
1221 void UpdateModelMatch(uint8_t model
)
1223 DBGLN("Set ModelId=%u", model
);
1224 config
.SetModelId(model
);
1227 void SendMSPFrameToFC(uint8_t *mspData
)
1229 serialIO
->queueMSPFrameTransmission(mspData
);
1233 * Process the assembled MSP packet in MspData[]
1235 void MspReceiveComplete()
1239 case MSP_ELRS_SET_RX_WIFI_MODE
: //0x0E
1240 #if defined(PLATFORM_ESP32) || defined(PLATFORM_ESP8266)
1241 // The MSP packet needs to be ACKed so the TX doesn't
1242 // keep sending it, so defer the switch to wifi
1243 deferExecutionMillis(500, []() {
1244 setWifiUpdateMode();
1248 case MSP_ELRS_MAVLINK_TLM
: // 0xFD
1250 mavlinkOutputBuffer
.atomicPushBytes(&MspData
[2], MspData
[1]);
1253 //handle received CRSF package
1254 crsf_ext_header_t
*receivedHeader
= (crsf_ext_header_t
*) MspData
;
1255 switch (receivedHeader
->type
)
1257 case CRSF_FRAMETYPE_MSP_WRITE
: //encapsulated MSP payload
1258 if (MspData
[7] == MSP_SET_RX_CONFIG
&& MspData
[8] == MSP_ELRS_MODEL_ID
)
1260 UpdateModelMatch(MspData
[9]);
1263 else if (MspData
[7] == MSP_SET_VTX_CONFIG
)
1265 if (OPT_HAS_VTX_SPI
) {
1266 vtxSPIFrequency
= getFreqByIdx(MspData
[8]);
1267 if (MspData
[6] >= 4) // If packet has 4 bytes it also contains power idx and pitmode.
1269 vtxSPIPowerIdx
= MspData
[10];
1270 vtxSPIPitmode
= MspData
[11];
1272 devicesTriggerEvent();
1274 #if defined(PLATFORM_ESP32)
1275 } else if (config
.GetSerial1Protocol() == PROTOCOL_SERIAL1_TRAMP
|| config
.GetSerial1Protocol() == PROTOCOL_SERIAL1_SMARTAUDIO
) {
1276 serial1IO
->queueMSPFrameTransmission(MspData
);
1283 if ((receivedHeader
->dest_addr
== CRSF_ADDRESS_BROADCAST
|| receivedHeader
->dest_addr
== CRSF_ADDRESS_CRSF_RECEIVER
))
1286 MspData
[CRSF_TELEMETRY_TYPE_INDEX
],
1287 MspData
[CRSF_TELEMETRY_FIELD_ID_INDEX
],
1288 MspData
[CRSF_TELEMETRY_FIELD_CHUNK_INDEX
]
1293 // No MSP data to the FC if no model match
1294 if (connectionHasModelMatch
&& teamraceHasModelMatch
&&
1295 (receivedHeader
->dest_addr
== CRSF_ADDRESS_BROADCAST
|| receivedHeader
->dest_addr
== CRSF_ADDRESS_FLIGHT_CONTROLLER
))
1297 serialIO
->queueMSPFrameTransmission(MspData
);
1301 MspReceiver
.Unlock();
1304 static void setupSerial()
1306 bool sbusSerialOutput
= false;
1307 bool sumdSerialOutput
= false;
1308 bool mavlinkSerialOutput
= false;
1310 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
1311 bool hottTlmSerial
= false;
1314 if (OPT_CRSF_RCVR_NO_SERIAL
)
1316 // For PWM receivers with no serial pins defined, only turn on the Serial port if logging is on
1317 #if defined(DEBUG_LOG) || defined(DEBUG_RCVR_LINKSTATS)
1318 #if defined(PLATFORM_ESP32_S3) && !defined(ESP32_S3_USB_JTAG_ENABLED)
1319 // Requires pull-down on GPIO3. If GPIO3 has a pull-up (for JTAG) this doesn't work.
1320 USBSerial
.begin(serialBaud
);
1321 SerialLogger
= &USBSerial
;
1323 Serial
.begin(serialBaud
);
1324 SerialLogger
= &Serial
;
1327 SerialLogger
= new NullStream();
1329 serialIO
= new SerialNOOP();
1332 if (config
.GetSerialProtocol() == PROTOCOL_CRSF
|| config
.GetSerialProtocol() == PROTOCOL_INVERTED_CRSF
)
1334 serialBaud
= firmwareOptions
.uart_baud
;
1336 else if (config
.GetSerialProtocol() == PROTOCOL_SBUS
|| config
.GetSerialProtocol() == PROTOCOL_INVERTED_SBUS
|| config
.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO
)
1338 sbusSerialOutput
= true;
1339 serialBaud
= 100000;
1341 else if (config
.GetSerialProtocol() == PROTOCOL_SUMD
)
1343 sumdSerialOutput
= true;
1344 serialBaud
= 115200;
1346 else if (config
.GetSerialProtocol() == PROTOCOL_MAVLINK
)
1348 mavlinkSerialOutput
= true;
1349 serialBaud
= 460800;
1351 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
1352 else if (config
.GetSerialProtocol() == PROTOCOL_HOTT_TLM
)
1354 hottTlmSerial
= true;
1358 bool invert
= config
.GetSerialProtocol() == PROTOCOL_SBUS
|| config
.GetSerialProtocol() == PROTOCOL_INVERTED_CRSF
|| config
.GetSerialProtocol() == PROTOCOL_DJI_RS_PRO
;
1360 #ifdef PLATFORM_STM32
1361 #if defined(TARGET_R9SLIMPLUS_RX)
1362 SERIAL_PROTOCOL_RX
.setRx(GPIO_PIN_RCSIGNAL_RX
);
1363 SERIAL_PROTOCOL_RX
.begin(serialBaud
);
1365 SERIAL_PROTOCOL_TX
.setTx(GPIO_PIN_RCSIGNAL_TX
);
1367 #if defined(GPIO_PIN_RCSIGNAL_RX_SBUS) && defined(GPIO_PIN_RCSIGNAL_TX_SBUS)
1370 SERIAL_PROTOCOL_TX
.setTx(GPIO_PIN_RCSIGNAL_TX_SBUS
);
1371 SERIAL_PROTOCOL_TX
.setRx(GPIO_PIN_RCSIGNAL_RX_SBUS
);
1376 SERIAL_PROTOCOL_TX
.setTx(GPIO_PIN_RCSIGNAL_TX
);
1377 SERIAL_PROTOCOL_TX
.setRx(GPIO_PIN_RCSIGNAL_RX
);
1379 #endif /* TARGET_R9SLIMPLUS_RX */
1380 #if defined(TARGET_RX_GHOST_ATTO_V1)
1381 // USART1 is used for RX (half duplex)
1382 SERIAL_PROTOCOL_RX
.setHalfDuplex();
1383 SERIAL_PROTOCOL_RX
.setTx(GPIO_PIN_RCSIGNAL_RX
);
1384 SERIAL_PROTOCOL_RX
.begin(serialBaud
);
1385 SERIAL_PROTOCOL_RX
.enableHalfDuplexRx();
1387 // USART2 is used for TX (half duplex)
1388 // Note: these must be set before begin()
1389 SERIAL_PROTOCOL_TX
.setHalfDuplex();
1390 SERIAL_PROTOCOL_TX
.setRx((PinName
)NC
);
1391 SERIAL_PROTOCOL_TX
.setTx(GPIO_PIN_RCSIGNAL_TX
);
1392 #endif /* TARGET_RX_GHOST_ATTO_V1 */
1393 SERIAL_PROTOCOL_TX
.begin(serialBaud
, sbusSerialOutput
? SERIAL_8E2
: SERIAL_8N1
);
1394 #endif /* PLATFORM_STM32 */
1395 #if defined(TARGET_RX_GHOST_ATTO_V1) || defined(TARGET_RX_FM30_MINI)
1398 LL_GPIO_SetPinPull(GPIOA
, GPIO_PIN_2
, LL_GPIO_PULL_DOWN
);
1399 USART2
->CR1
&= ~USART_CR1_UE
;
1400 USART2
->CR2
|= USART_CR2_TXINV
;
1401 USART2
->CR1
|= USART_CR1_UE
;
1405 #if defined(TARGET_RX_FM30_MINI) || defined(TARGET_DIY_900_RX_STM32)
1406 Serial
.setRx(GPIO_PIN_DEBUG_RX
);
1407 Serial
.setTx(GPIO_PIN_DEBUG_TX
);
1408 Serial
.begin(serialBaud
); // Same baud as CRSF for simplicity
1411 #if defined(PLATFORM_ESP8266)
1412 SerialConfig config
= SERIAL_8N1
;
1414 if(sbusSerialOutput
)
1416 config
= SERIAL_8E2
;
1418 else if(hottTlmSerial
)
1420 config
= SERIAL_8N2
;
1423 SerialMode mode
= (sbusSerialOutput
|| sumdSerialOutput
) ? SERIAL_TX_ONLY
: SERIAL_FULL
;
1424 Serial
.begin(serialBaud
, config
, mode
, -1, invert
);
1425 #elif defined(PLATFORM_ESP32)
1426 uint32_t config
= SERIAL_8N1
;
1428 if(sbusSerialOutput
)
1430 config
= SERIAL_8E2
;
1432 else if(hottTlmSerial
)
1434 config
= SERIAL_8N2
;
1437 // ARDUINO_CORE_INVERT_FIX PT2
1438 #if defined(ARDUINO_CORE_INVERT_FIX)
1441 uart_set_line_inverse(0, UART_SIGNAL_INV_DISABLE
);
1444 // ARDUINO_CORE_INVERT_FIX PT2 end
1446 Serial
.begin(serialBaud
, config
, GPIO_PIN_RCSIGNAL_RX
, GPIO_PIN_RCSIGNAL_TX
, invert
);
1449 if (firmwareOptions
.is_airport
)
1451 serialIO
= new SerialAirPort(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1453 else if (sbusSerialOutput
)
1455 serialIO
= new SerialSBUS(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1457 else if (sumdSerialOutput
)
1459 serialIO
= new SerialSUMD(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1461 else if (mavlinkSerialOutput
)
1463 serialIO
= new SerialMavlink(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1465 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
1466 else if (hottTlmSerial
)
1468 serialIO
= new SerialHoTT_TLM(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1473 serialIO
= new SerialCRSF(SERIAL_PROTOCOL_TX
, SERIAL_PROTOCOL_RX
);
1476 #if defined(DEBUG_ENABLED)
1477 #if defined(PLATFORM_ESP32_S3) || defined(PLATFORM_ESP32_C3)
1478 USBSerial
.begin(460800);
1479 SerialLogger
= &USBSerial
;
1481 SerialLogger
= &Serial
;
1484 SerialLogger
= new NullStream();
1488 #if defined(PLATFORM_ESP32)
1489 static void serial1Shutdown()
1491 if(serial1IO
!= nullptr)
1495 serial1IO
= nullptr;
1499 static void setupSerial1()
1502 // init secondary serial and protocol
1504 int8_t serial1RXpin
= GPIO_PIN_SERIAL1_RX
;
1506 if (serial1RXpin
== UNDEF_PIN
)
1508 for (uint8_t ch
= 0; ch
< GPIO_PIN_PWM_OUTPUTS_COUNT
; ch
++)
1510 if (config
.GetPwmChannel(ch
)->val
.mode
== somSerial1RX
)
1511 serial1RXpin
= GPIO_PIN_PWM_OUTPUTS
[ch
];
1515 int8_t serial1TXpin
= GPIO_PIN_SERIAL1_TX
;
1517 if (serial1TXpin
== UNDEF_PIN
)
1519 for (uint8_t ch
= 0; ch
< GPIO_PIN_PWM_OUTPUTS_COUNT
; ch
++)
1521 if (config
.GetPwmChannel(ch
)->val
.mode
== somSerial1TX
)
1522 serial1TXpin
= GPIO_PIN_PWM_OUTPUTS
[ch
];
1526 switch(config
.GetSerial1Protocol())
1528 case PROTOCOL_SERIAL1_OFF
:
1530 case PROTOCOL_SERIAL1_CRSF
:
1531 Serial1
.begin(firmwareOptions
.uart_baud
, SERIAL_8N1
, serial1RXpin
, serial1TXpin
, false);
1532 serial1IO
= new SerialCRSF(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
);
1534 case PROTOCOL_SERIAL1_INVERTED_CRSF
:
1535 Serial1
.begin(firmwareOptions
.uart_baud
, SERIAL_8N1
, serial1RXpin
, serial1TXpin
, true);
1536 serial1IO
= new SerialCRSF(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
);
1538 case PROTOCOL_SERIAL1_SBUS
:
1539 case PROTOCOL_SERIAL1_DJI_RS_PRO
:
1540 Serial1
.begin(100000, SERIAL_8E2
, UNDEF_PIN
, serial1TXpin
, true);
1541 serial1IO
= new SerialSBUS(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
);
1543 case PROTOCOL_SERIAL1_INVERTED_SBUS
:
1544 Serial1
.begin(100000, SERIAL_8E2
, UNDEF_PIN
, serial1TXpin
, false);
1545 serial1IO
= new SerialSBUS(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
);
1547 case PROTOCOL_SERIAL1_SUMD
:
1548 Serial1
.begin(115200, SERIAL_8N1
, UNDEF_PIN
, serial1TXpin
, false);
1549 serial1IO
= new SerialSUMD(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
);
1551 case PROTOCOL_SERIAL1_HOTT_TLM
:
1552 Serial1
.begin(19200, SERIAL_8N2
, serial1RXpin
, serial1TXpin
, false);
1553 serial1IO
= new SerialHoTT_TLM(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
, serial1TXpin
);
1555 case PROTOCOL_SERIAL1_TRAMP
:
1556 Serial1
.begin(9600, SERIAL_8N1
, UNDEF_PIN
, serial1TXpin
, false);
1557 serial1IO
= new SerialTramp(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
, serial1TXpin
);
1559 case PROTOCOL_SERIAL1_SMARTAUDIO
:
1560 Serial1
.begin(4800, SERIAL_8N2
, UNDEF_PIN
, serial1TXpin
, false);
1561 serial1IO
= new SerialSmartAudio(SERIAL1_PROTOCOL_TX
, SERIAL1_PROTOCOL_RX
, serial1TXpin
);
1566 void reconfigureSerial1()
1572 void setupSerial1() {};
1573 void reconfigureSerial1() {};
1576 static void serialShutdown()
1578 SerialLogger
= new NullStream();
1579 #ifdef PLATFORM_STM32
1580 #if defined(TARGET_R9SLIMPLUS_RX) || defined(TARGET_RX_GHOST_ATTO_V1)
1581 SERIAL_PROTOCOL_RX
.end();
1583 SERIAL_PROTOCOL_TX
.end();
1585 if(serialIO
!= nullptr)
1590 if(serialIO
!= nullptr)
1597 void reconfigureSerial()
1603 static void setupConfigAndPocCheck()
1606 config
.SetStorageProvider(&eeprom
); // Pass pointer to the Config class for access to storage
1609 // If bound, track number of plug/unplug cycles to go to binding mode in eeprom
1610 if (config
.GetIsBound() && config
.GetPowerOnCounter() < 3)
1612 config
.SetPowerOnCounter(config
.GetPowerOnCounter() + 1);
1616 // Set a deferred function to clear the power on counter if the RX has been running for more than 2s
1617 deferExecutionMillis(2000, []() {
1618 if (connectionState
!= connected
&& config
.GetPowerOnCounter() != 0)
1620 config
.SetPowerOnCounter(0);
1626 static void setupTarget()
1628 if (GPIO_PIN_ANT_CTRL
!= UNDEF_PIN
)
1630 pinMode(GPIO_PIN_ANT_CTRL
, OUTPUT
);
1631 digitalWrite(GPIO_PIN_ANT_CTRL
, LOW
);
1632 if (GPIO_PIN_ANT_CTRL_COMPL
!= UNDEF_PIN
)
1634 pinMode(GPIO_PIN_ANT_CTRL_COMPL
, OUTPUT
);
1635 digitalWrite(GPIO_PIN_ANT_CTRL_COMPL
, HIGH
);
1639 #if defined(TARGET_RX_FM30_MINI)
1640 pinMode(GPIO_PIN_UART1TX_INVERT
, OUTPUT
);
1641 digitalWrite(GPIO_PIN_UART1TX_INVERT
, LOW
);
1644 setupTargetCommon();
1647 static void setupBindingFromConfig()
1649 // VolatileBind's only function is to prevent loading the stored UID into RAM
1650 // which makes the RX boot into bind mode every time
1651 if (config
.GetIsBound())
1653 memcpy(UID
, config
.GetUID(), UID_LEN
);
1656 DBGLN("UID=(%d, %d, %d, %d, %d, %d) ModelId=%u",
1657 UID
[0], UID
[1], UID
[2], UID
[3], UID
[4], UID
[5], config
.GetModelId());
1659 OtaUpdateCrcInitFromUid();
1662 static void setupRadio()
1664 Radio
.currFreq
= FHSSgetInitialFreq();
1665 #if defined(RADIO_SX127X)
1666 //Radio.currSyncWord = UID[3];
1668 bool init_success
= Radio
.Begin(FHSSgetMinimumFreq(), FHSSgetMaximumFreq());
1672 DBGLN("Failed to detect RF chipset!!!");
1673 connectionState
= radioFailed
;
1677 DynamicPower_UpdateRx(true);
1679 #if defined(Regulatory_Domain_EU_CE_2400)
1680 LBTEnabled
= (config
.GetPower() > PWR_10mW
);
1683 Radio
.RXdoneCallback
= &RXdoneISR
;
1684 Radio
.TXdoneCallback
= &TXdoneISR
;
1686 scanIndex
= config
.GetRateInitialIdx();
1687 SetRFLinkRate(scanIndex
, false);
1688 // Start slow on the selected rate to give it the best chance
1689 // to connect before beginning rate cycling
1690 RFmodeCycleMultiplier
= RFmodeCycleMultiplierSlow
/ 2;
1693 static void updateTelemetryBurst()
1695 if (telemBurstValid
)
1697 telemBurstValid
= true;
1699 uint16_t hz
= 1000000 / ExpressLRS_currAirRate_Modparams
->interval
;
1700 telemetryBurstMax
= TLMBurstMaxForRateRatio(hz
, ExpressLRS_currTlmDenom
);
1702 // Notify the sender to adjust its expected throughput
1703 TelemetrySender
.UpdateTelemetryRate(hz
, ExpressLRS_currTlmDenom
, telemetryBurstMax
);
1706 /* If not connected will rotate through the RF modes looking for sync
1709 static void cycleRfMode(unsigned long now
)
1711 if (connectionState
== connected
|| connectionState
== wifiUpdate
|| InBindingMode
)
1714 // Actually cycle the RF mode if not LOCK_ON_FIRST_CONNECTION
1715 if (LockRFmode
== false && (now
- RFmodeLastCycled
) > (cycleInterval
* RFmodeCycleMultiplier
))
1717 RFmodeLastCycled
= now
;
1718 LastSyncPacket
= now
; // reset this variable
1719 SendLinkStatstoFCForcedSends
= 2;
1720 SetRFLinkRate(scanIndex
% RATE_MAX
, false); // switch between rates
1722 LQCalcDVDA
.reset100();
1723 // Display the current air rate to the user as an indicator something is happening
1726 DBGLN("%u", ExpressLRS_currAirRate_Modparams
->interval
);
1728 // Switch to FAST_SYNC if not already in it (won't be if was just connected)
1729 RFmodeCycleMultiplier
= 1;
1730 } // if time to switch RF mode
1733 static void EnterBindingMode()
1737 DBGLN("Already in binding mode");
1741 // Binding uses a CRCInit=0, 50Hz, and InvertIQ
1742 OtaCrcInitializer
= 0;
1743 InBindingMode
= true;
1744 // Any method of entering bind resets a loan
1745 // Model can be reloaned immediately by binding now
1746 config
.ReturnLoan();
1749 // Start attempting to bind
1750 // Lock the RF rate and freq while binding
1751 SetRFLinkRate(enumRatetoIndex(RATE_BINDING
), true);
1753 // If the Radio Params (including InvertIQ) parameter changed, need to restart RX to take effect
1756 DBGLN("Entered binding mode at freq = %d", Radio
.currFreq
);
1757 devicesTriggerEvent();
1760 static void ExitBindingMode()
1764 DBGLN("Not in binding mode");
1768 MspReceiver
.ResetState();
1770 // Prevent any new packets from coming in
1771 Radio
.SetTxIdleMode();
1772 // Write the values to eeprom
1775 OtaUpdateCrcInitFromUid();
1776 FHSSrandomiseFHSSsequence(uidMacSeedGet());
1778 #if defined(PLATFORM_ESP32) || defined(PLATFORM_ESP8266)
1779 webserverPreventAutoStart
= true;
1782 // Force RF cycling to start at the beginning immediately
1783 scanIndex
= RATE_MAX
;
1784 RFmodeLastCycled
= 0;
1786 LostConnection(false);
1788 // Do this last as LostConnection() will wait for a tock that never comes
1789 // if we're in binding mode
1790 InBindingMode
= false;
1791 DBGLN("Exiting binding mode");
1792 devicesTriggerEvent();
1795 static void updateBindingMode(unsigned long now
)
1797 // Exit binding mode if the config has been modified, indicating UID has been set
1798 if (InBindingMode
&& config
.IsModified())
1803 #if defined(RADIO_LR1121)
1804 // Change frequency domains every 500ms. This will allow single LR1121 receivers to receive bind packets from SX12XX Tx modules.
1805 else if (InBindingMode
&& (now
- BindingRateChangeTime
) > BindingRateChangeCyclePeriod
)
1807 BindingRateChangeTime
= now
;
1808 if (ExpressLRS_currAirRate_Modparams
->index
== RATE_DUALBAND_BINDING
)
1810 SetRFLinkRate(enumRatetoIndex(RATE_BINDING
), true);
1814 SetRFLinkRate(RATE_DUALBAND_BINDING
, true);
1821 // If the power on counter is >=3, enter binding, the counter will be reset after 2s
1822 else if (!InBindingMode
&& config
.GetPowerOnCounter() >= 3)
1824 #if defined(PLATFORM_ESP32) || defined(PLATFORM_ESP8266)
1825 // Never enter wifi if forced to binding mode
1826 webserverPreventAutoStart
= true;
1828 DBGLN("Power on counter >=3, enter binding mode");
1832 // If the eeprom is indicating that we're not bound, enter binding
1833 else if (!UID_IS_BOUND(UID
) && !InBindingMode
)
1835 DBGLN("RX has not been bound, enter binding mode");
1839 else if (BindingModeRequest
)
1841 DBGLN("Connected request to enter binding mode");
1842 BindingModeRequest
= false;
1843 if (connectionState
== connected
)
1845 LostConnection(false);
1846 // Skip entering bind mode if on loan. This comes from an OTA request
1847 // and the model is assumed to be inaccessible, do not want the receiver
1848 // sitting in a field ready to be bound to anyone within 10km
1849 if (config
.IsOnLoan())
1851 DBGLN("Model was on loan, becoming inert");
1852 config
.ReturnLoan();
1853 config
.Commit(); // prevents CheckConfigChangePending() re-enabling radio
1855 // Enter a completely invalid state for a receiver, to prevent wifi or radio enabling
1856 connectionState
= noCrossfire
;
1859 // if the InitRate config item was changed by LostConnection
1860 // save the config before entering bind, as the modified config
1861 // will immediately boot it out of bind mode
1868 void EnterBindingModeSafely()
1870 // Will not enter Binding mode if in the process of a passthrough update
1871 // or currently binding
1872 if (connectionState
== serialUpdate
|| InBindingMode
)
1875 #if defined(PLATFORM_ESP32) || defined(PLATFORM_ESP8266)
1876 // Never enter wifi mode after requesting to enter binding mode
1877 webserverPreventAutoStart
= true;
1879 // If the radio and everything is shut down, better to reboot and boot to binding mode
1880 if (connectionState
== wifiUpdate
|| connectionState
== bleJoystick
)
1882 // Force 3-plug binding mode
1883 config
.SetPowerOnCounter(3);
1890 // If connected, handle that in updateBindingMode()
1891 if (connectionState
== connected
)
1893 BindingModeRequest
= true;
1900 static void checkSendLinkStatsToFc(uint32_t now
)
1902 if (now
- SendLinkStatstoFCintervalLastSent
> SEND_LINK_STATS_TO_FC_INTERVAL
)
1904 if (connectionState
== disconnected
)
1909 if ((connectionState
!= disconnected
&& connectionHasModelMatch
&& teamraceHasModelMatch
) ||
1910 SendLinkStatstoFCForcedSends
)
1912 serialIO
->queueLinkStatisticsPacket();
1913 SendLinkStatstoFCintervalLastSent
= now
;
1914 if (SendLinkStatstoFCForcedSends
)
1915 --SendLinkStatstoFCForcedSends
;
1920 static void debugRcvrLinkstats()
1922 #if defined(DEBUG_RCVR_LINKSTATS)
1923 if (debugRcvrLinkstatsPending
)
1925 debugRcvrLinkstatsPending
= false;
1927 // Copy the data out of the ISR-updating bits ASAP
1928 // While YOLOing (const void *) away the volatile
1929 crsfLinkStatistics_t ls
= *(crsfLinkStatistics_t
*)((const void *)&CRSF::LinkStatistics
);
1930 uint32_t packetCounter
= debugRcvrLinkstatsPacketId
;
1931 uint8_t fhss
= debugRcvrLinkstatsFhssIdx
;
1932 // actually the previous packet's offset since the update happens in tick, and this will
1933 // fire right after packet reception (a little before tock)
1934 int32_t pfd
= PfdPrevRawOffset
;
1936 // Use serial instead of DBG() because do not necessarily want all the debug in our logs
1938 snprintf(buf
, sizeof(buf
), "%u,%u,-%u,%u,%d,%u,%u,%d\r\n",
1939 packetCounter
, ls
.active_antenna
,
1940 ls
.active_antenna
? ls
.uplink_RSSI_2
: ls
.uplink_RSSI_1
,
1941 ls
.uplink_Link_quality
, ls
.uplink_SNR
,
1942 ls
.uplink_TX_Power
, fhss
, pfd
);
1948 static void debugRcvrSignalStats(uint32_t now
)
1950 #if defined(DEBUG_RCVR_SIGNAL_STATS)
1951 static uint32_t lastReport
= 0;
1953 // log column header: cnt1, rssi1, snr1, snr1_max, telem1, fail1, cnt2, rssi2, snr2, snr2_max, telem2, fail2, or, both
1954 if(now
- lastReport
>= 1000 && connectionState
== connected
)
1956 for (int i
= 0 ; i
< (isDualRadio()?2:1) ; i
++)
1958 DBG("%d\t%f\t%f\t%f\t%d\t%d\t",
1959 Radio
.rxSignalStats
[i
].irq_count
,
1960 (Radio
.rxSignalStats
[i
].irq_count
==0) ? 0 : double(Radio
.rxSignalStats
[i
].rssi_sum
)/Radio
.rxSignalStats
[i
].irq_count
,
1961 (Radio
.rxSignalStats
[i
].irq_count
==0) ? 0 : double(Radio
.rxSignalStats
[i
].snr_sum
)/Radio
.rxSignalStats
[i
].irq_count
/RADIO_SNR_SCALE
,
1962 float(Radio
.rxSignalStats
[i
].snr_max
)/RADIO_SNR_SCALE
,
1963 Radio
.rxSignalStats
[i
].telem_count
,
1964 Radio
.rxSignalStats
[i
].fail_count
);
1966 Radio
.rxSignalStats
[i
].irq_count
= 0;
1967 Radio
.rxSignalStats
[i
].snr_sum
= 0;
1968 Radio
.rxSignalStats
[i
].rssi_sum
= 0;
1969 Radio
.rxSignalStats
[i
].snr_max
= INT8_MIN
;
1970 Radio
.rxSignalStats
[i
].telem_count
= 0;
1971 Radio
.rxSignalStats
[i
].fail_count
= 0;
1975 DBGLN("%d\t%d", Radio
.irq_count_or
, Radio
.irq_count_both
);
1981 Radio
.irq_count_or
= 0;
1982 Radio
.irq_count_both
= 0;
1989 static void updateSwitchMode()
1991 // Negative value means waiting for confirm of the new switch mode while connected
1992 if (SwitchModePending
<= 0)
1995 OtaUpdateSerializers((OtaSwitchMode_e
)(SwitchModePending
- 1), ExpressLRS_currAirRate_Modparams
->PayloadLength
);
1996 SwitchModePending
= 0;
1999 static void CheckConfigChangePending()
2001 if (config
.IsModified() && !InBindingMode
&& connectionState
< NO_CONFIG_SAVE_STATES
)
2003 LostConnection(false);
2005 devicesTriggerEvent();
2006 #if defined(Regulatory_Domain_EU_CE_2400)
2007 LBTEnabled
= (config
.GetPower() > PWR_10mW
);
2013 #if defined(PLATFORM_ESP8266)
2014 // Called from core's user_rf_pre_init() function (which is called by SDK) before setup()
2017 // Set whether the chip will do RF calibration or not when power up.
2018 // I believe the Arduino core fakes this (byte 114 of phy_init_data.bin)
2019 // to be 1, but the TX power calibration can pull over 300mA which can
2020 // lock up receivers built with a underspeced LDO (such as the EP2 "SDG")
2021 // Option 2 is just VDD33 measurement
2022 #if defined(RF_CAL_MODE)
2023 system_phy_set_powerup_option(RF_CAL_MODE
);
2025 system_phy_set_powerup_option(2);
2030 void resetConfigAndReboot()
2032 config
.SetDefaults(true);
2033 #if defined(PLATFORM_STM32)
2034 HAL_NVIC_SystemReset();
2036 // Prevent WDT from rebooting too early if
2037 // all this flash write is taking too long
2039 // Remove options.json and hardware.json
2043 options_SetTrueDefaults();
2051 #if defined(TARGET_UNIFIED_RX)
2052 hardwareConfigured
= options_init();
2053 if (!hardwareConfigured
)
2055 // In the failure case we set the logging to the null logger so nothing crashes
2056 // if it decides to log something
2057 SerialLogger
= new NullStream();
2059 // Register the WiFi with the framework
2060 static device_affinity_t wifi_device
[] = {
2063 devicesRegister(wifi_device
, ARRAY_SIZE(wifi_device
));
2066 connectionState
= hardwareUndefined
;
2069 hardwareConfigured
= options_init();
2072 if (hardwareConfigured
)
2074 // default to CRSF protocol and the compiled baud rate
2075 serialBaud
= firmwareOptions
.uart_baud
;
2077 // pre-initialise serial must be done before anything as some libs write
2078 // to the serial port and they'll block if the buffer fills
2079 #if defined(DEBUG_LOG)
2080 Serial
.begin(serialBaud
);
2081 SerialLogger
= &Serial
;
2083 SerialLogger
= new NullStream();
2086 // External EEPROM needs I2C setup so it can load config
2087 // but configurable I2C pins for PWM RX needs config loaded first
2088 #if (defined(TARGET_USE_EEPROM) && defined(USE_I2C))
2091 // Init EEPROM and load config, checking powerup count
2092 setupConfigAndPocCheck();
2093 #if !(defined(TARGET_USE_EEPROM) && defined(USE_I2C))
2097 #if defined(OPT_HAS_SERVO_OUTPUT)
2098 // If serial is not already defined, then see if there is serial pin configured in the PWM configuration
2099 if (GPIO_PIN_RCSIGNAL_RX
== UNDEF_PIN
&& GPIO_PIN_RCSIGNAL_TX
== UNDEF_PIN
)
2101 for (int i
= 0 ; i
< GPIO_PIN_PWM_OUTPUTS_COUNT
; i
++)
2103 eServoOutputMode pinMode
= (eServoOutputMode
)config
.GetPwmChannel(i
)->val
.mode
;
2104 if (pinMode
== somSerial
)
2106 pwmSerialDefined
= true;
2115 devicesRegister(ui_devices
, ARRAY_SIZE(ui_devices
));
2118 setupBindingFromConfig();
2120 FHSSrandomiseFHSSsequence(uidMacSeedGet());
2124 if (connectionState
!= radioFailed
)
2126 // RFnoiseFloor = MeasureNoiseFloor(); //TODO move MeasureNoiseFloor to driver libs
2127 // DBGLN("RF noise floor: %d dBm", RFnoiseFloor);
2129 MspReceiver
.SetDataToReceive(MspData
, ELRS_MSP_BUFFER
);
2131 hwTimer::init(HWtimerCallbackTick
, HWtimerCallbackTock
);
2135 #if defined(HAS_BUTTON)
2136 registerButtonFunction(ACTION_BIND
, EnterBindingModeSafely
);
2137 registerButtonFunction(ACTION_RESET_REBOOT
, resetConfigAndReboot
);
2142 // setup() eats up some of this time, which can cause the first mode connection to fail.
2143 // Resetting the time here give the first mode a better chance of connection.
2144 RFmodeLastCycled
= millis();
2147 #if defined(PLATFORM_ESP32_C3)
2153 unsigned long now
= millis();
2155 if (MspReceiver
.HasFinishedData())
2157 MspReceiveComplete();
2162 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
2163 // If the reboot time is set and the current time is past the reboot time then reboot.
2164 if (rebootTime
!= 0 && now
> rebootTime
) {
2169 CheckConfigChangePending();
2170 executeDeferredFunction(micros());
2172 // Clear the power-on-count
2173 if ((connectionState
== connected
|| connectionState
== tentative
) && config
.GetPowerOnCounter() != 0)
2175 config
.SetPowerOnCounter(0);
2178 if (connectionState
> MODE_STATES
)
2183 if ((connectionState
!= disconnected
) && (ExpressLRS_currAirRate_Modparams
->index
!= ExpressLRS_nextAirRateIndex
)){ // forced change
2184 DBGLN("Req air rate change %u->%u", ExpressLRS_currAirRate_Modparams
->index
, ExpressLRS_nextAirRateIndex
);
2185 LostConnection(true);
2186 LastSyncPacket
= now
; // reset this variable to stop rf mode switching and add extra time
2187 RFmodeLastCycled
= now
; // reset this variable to stop rf mode switching and add extra time
2188 SendLinkStatstoFCintervalLastSent
= 0;
2189 SendLinkStatstoFCForcedSends
= 2;
2192 if (connectionState
== tentative
&& (now
- LastSyncPacket
> ExpressLRS_currAirRate_RFperfParams
->RxLockTimeoutMs
))
2194 DBGLN("Bad sync, aborting");
2195 LostConnection(true);
2196 RFmodeLastCycled
= now
;
2197 LastSyncPacket
= now
;
2202 uint32_t localLastValidPacket
= LastValidPacket
; // Required to prevent race condition due to LastValidPacket getting updated from ISR
2203 if ((connectionState
== connected
) && ((int32_t)ExpressLRS_currAirRate_RFperfParams
->DisconnectTimeoutMs
< (int32_t)(now
- localLastValidPacket
))) // check if we lost conn.
2205 LostConnection(true);
2208 if ((connectionState
== tentative
) && (abs(LPF_OffsetDx
.value()) <= 10) && (LPF_Offset
.value() < 100) && (LQCalc
.getLQRaw() > minLqForChaos())) //detects when we are connected
2213 checkSendLinkStatsToFc(now
);
2215 if ((RXtimerState
== tim_tentative
) && ((now
- GotConnectionMillis
) > ConsiderConnGoodMillis
) && (abs(LPF_OffsetDx
.value()) <= 5))
2217 RXtimerState
= tim_locked
;
2218 DBGLN("Timer locked");
2221 uint8_t *nextPayload
= 0;
2222 uint8_t nextPlayloadSize
= 0;
2223 if (!TelemetrySender
.IsActive() && telemetry
.GetNextPayload(&nextPlayloadSize
, &nextPayload
))
2225 TelemetrySender
.SetDataToTransmit(nextPayload
, nextPlayloadSize
);
2228 uint16_t count
= mavlinkInputBuffer
.size();
2229 if (count
> 0 && !TelemetrySender
.IsActive())
2231 count
= std::min(count
, (uint16_t)CRSF_PAYLOAD_SIZE_MAX
); // Constrain to CRSF max payload size to match SS
2232 // First 2 bytes conform to crsf_header_s format
2233 mavlinkSSBuffer
[0] = CRSF_ADDRESS_USB
; // device_addr - used on TX to differentiate between std tlm and mavlink
2234 mavlinkSSBuffer
[1] = count
;
2235 // Following n bytes are just raw mavlink
2236 mavlinkInputBuffer
.popBytes(mavlinkSSBuffer
+ CRSF_FRAME_NOT_COUNTED_BYTES
, count
);
2237 nextPayload
= mavlinkSSBuffer
;
2238 nextPlayloadSize
= count
+ CRSF_FRAME_NOT_COUNTED_BYTES
;
2239 TelemetrySender
.SetDataToTransmit(nextPayload
, nextPlayloadSize
);
2242 updateTelemetryBurst();
2243 updateBindingMode(now
);
2246 DynamicPower_UpdateRx(false);
2247 debugRcvrLinkstats();
2248 debugRcvrSignalStats(now
);
2251 #if defined(PLATFORM_ESP32_C3)
2252 [[noreturn
]] void loop()
2254 // We have to do this dodgy hack because on the C3 the Arduino main loop calls
2255 // a yield function (vTaskDelay) every 2 seconds, which causes us to lose connection!
2256 extern bool loopTaskWDTEnabled
;
2258 if (loopTaskWDTEnabled
) {
2259 esp_task_wdt_reset();
2268 uint32_t reset_type
;
2271 void reset_into_bootloader(void)
2273 SERIAL_PROTOCOL_TX
.println((const char *)&target_name
[4]);
2274 SERIAL_PROTOCOL_TX
.flush();
2275 #if defined(PLATFORM_STM32)
2277 DBGLN("Jumping to Bootloader...");
2280 /** Write command for firmware update.
2282 * Bootloader checks this memory area (if newer enough) and
2283 * perpare itself for fw update. Otherwise it skips the check
2284 * and starts ELRS firmware immediately
2286 extern __IO
uint32_t _bootloader_data
;
2287 volatile struct bootloader
* blinfo
= ((struct bootloader
*)&_bootloader_data
) + 0;
2288 blinfo
->key
= 0x454c5253; // ELRS
2289 blinfo
->reset_type
= 0xACDC;
2291 HAL_NVIC_SystemReset();
2292 #elif defined(PLATFORM_ESP8266)
2294 ESP
.rebootIntoUartDownloadMode();
2295 #elif defined(PLATFORM_ESP32)
2297 connectionState
= serialUpdate
;