fix(power): use XOSC standby to keep TCXO on through CAD→RX; add burst window

- cadWake() now sets standbyXOSC=true so RadioLib's internal standby() calls
  keep the TCXO powered continuously through the CAD scan and into startReceive,
  eliminating the freq-drift window that dropped ~50% of long packets
- PS_RX_TIMEOUT_MS 1500→3000 to cover 256 B packets at SF8/BW62.5/CR5 (~1925 ms)
- 500 ms burst window after each successful receive: skip inter-scan sleep so
  back-to-back messages are caught without waiting out the 65 ms cadence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-06-10 18:01:39 +02:00
parent 25d154d6d0
commit 25341e8b8f
3 changed files with 18 additions and 6 deletions

View File

@@ -41,14 +41,15 @@ public:
void doResetAGC() override { sx126xResetAGC((SX126x *)_radio); }
// Warm sleep between CAD scans: ~1.5 µA vs ~600 µA in standby-RC.
// cadWake() delays 5 ms so the TCXO fully re-stabilises before the next scan
// (previous attempt without the delay corrupted long packets via freq drift).
// cadWake() sets standbyXOSC=true so every subsequent internal standby() call
// keeps the TCXO powered — no gap between CAD and startReceive, no freq drift.
// The chip's programmed TCXO timeout (set by begin()) handles stabilisation.
void cadSleep() override {
((CustomSX1262 *)_radio)->sleep(true); // warm sleep — config retained
}
void cadWake() override {
_radio->standby();
delayMicroseconds(5000); // TCXO stabilisation after warm sleep
((SX126x *)_radio)->standbyXOSC = true; // all subsequent standby() → XOSC
_radio->standby(); // wake into standby-XOSC, TCXO on
}
void setRxBoostedGainMode(bool en) override {

View File

@@ -24,8 +24,13 @@
// → 65 ms gives 2 guaranteed windows with margin. Revisit if SF/BW change.
#define PS_CAD_INTERVAL_MS 65
// If CAD detects activity but no packet actually lands, fall back to scanning
// instead of sitting in continuous RX forever.
#define PS_RX_TIMEOUT_MS 1500
// instead of sitting in continuous RX forever. Must exceed the longest possible
// packet air-time: 256 B at SF8/BW62.5/CR5 ≈ 1925 ms from preamble start, so
// 3000 ms gives ~1 s margin for a false-detect case.
#define PS_RX_TIMEOUT_MS 3000
// After a successful receive, skip inter-scan sleep for this long so back-to-back
// messages are caught without waiting out the 65 ms window each time.
#define PS_BURST_WINDOW_MS 500
static volatile uint8_t state = STATE_IDLE;
@@ -160,6 +165,10 @@ void RadioLibWrapper::armRecv() {
}
void RadioLibWrapper::enterCadSleep() {
if ((int32_t)(millis() - _ps_burst_until) < 0) {
armRecv(); // burst window: skip sleep, scan again immediately
return;
}
cadSleep(); // warm sleep (SX126x) or standby fallback between scans
_ps_phase = PS_SLEEPING;
_ps_timer = millis() + PS_CAD_INTERVAL_MS;
@@ -228,6 +237,7 @@ int RadioLibWrapper::recvRaw(uint8_t* bytes, int sz) {
} else {
// Serial.print(" readData() -> "); Serial.println(len);
n_recv++;
_ps_burst_until = millis() + PS_BURST_WINDOW_MS;
}
}
state = STATE_IDLE; // need another startReceive()

View File

@@ -27,6 +27,7 @@ protected:
bool _power_save = false;
uint8_t _ps_phase = 0; // PS_* (see .cpp): scanning / sleeping / rxing
uint32_t _ps_timer = 0; // next-scan time, or RX false-detect deadline
uint32_t _ps_burst_until = 0; // scan continuously until this time after RX
void armRecv(); // arm RX honouring power-save (CAD) or continuous
void powerSaveLoop();
void enterCadSleep();