From 25f65735dfb45ac97feedf538a5ded4908ab527c Mon Sep 17 00:00:00 2001 From: jirogit Date: Fri, 17 Jul 2026 21:35:29 -0700 Subject: [PATCH] JP LBT: make busy-channel backoff non-blocking isChannelActive() previously spin-waited (with YIELD_TASK()) for up to 16s after detecting RSSI busy, blocking Dispatcher::loop() from servicing incoming packets for the duration of the backoff. Replace the blocking wait with a state machine (_lbt_backoff_active / _lbt_deadline): on busy detection, arm a deadline and return true (busy) immediately. Dispatcher::checkSend()'s existing retry mechanism (getCADFailRetryDelay()) re-polls isChannelActive() on the next loop() pass; once the deadline has elapsed, re-sense (bounded, single-level recursion) instead of assuming still-busy. The initial 5ms RSSI sense and the post-clear jitter wait remain blocking, as both are short (<=5ms and <=~1s respectively) relative to the up-to-16s backoff this change targets. Hardware-tested (LilyGo T3S3 sx1262 / Wio Tracker L1 Pro) in both quiet and electrically noisy environments; confirmed both bidirectional and unidirectional message delivery under legitimate busy-channel conditions. --- src/helpers/radiolib/RadioLibWrappers.cpp | 18 ++++++++++++++---- src/helpers/radiolib/RadioLibWrappers.h | 5 ++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index f6afbaaf..7e5e1acf 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -194,6 +194,18 @@ int16_t RadioLibWrapper::performChannelScan() { bool RadioLibWrapper::isChannelActive() { if (isAS923_1_JP()) { + // Non-blocking backoff: if a prior busy detection armed a backoff wait, + // report busy and let Dispatcher::checkSend() re-poll on the next loop() + // pass instead of spin-waiting here (which used to block the Dispatcher + // loop for up to 16s). + if (_lbt_backoff_active) { + if ((int32_t)(millis() - _lbt_deadline) < 0) { + return true; // still waiting; re-checked next call + } + _lbt_backoff_active = false; + return isChannelActive(); // backoff elapsed -- re-sense (bounded, single-level recursion) + } + // ARIB STD-T108: 5ms continuous RSSI sensing, -80dBm absolute threshold uint32_t sense_start = millis(); while (millis() - sense_start < 5) { @@ -201,10 +213,8 @@ bool RadioLibWrapper::isChannelActive() { _busy_count++; uint32_t base_ms = 2000; uint32_t max_backoff = min(base_ms * (1u << _busy_count), (uint32_t)16000); - uint32_t backoff_until = millis() + random(max_backoff / 2, max_backoff); - while (millis() < backoff_until) { - YIELD_TASK(); - } + _lbt_deadline = millis() + random(max_backoff / 2, max_backoff); + _lbt_backoff_active = true; return true; } YIELD_TASK(); diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 699b3122..b58ac8ab 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -20,6 +20,9 @@ protected: int32_t _floor_sample_sum; uint8_t _preamble_sf; + bool _lbt_backoff_active; // non-blocking wait after RSSI busy detection (up to 16s) + uint32_t _lbt_deadline; + void idle(); void startRecv(); float packetScoreInt(float snr, int sf, int packet_len); @@ -27,7 +30,7 @@ protected: virtual void doResetAGC(); public: - RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _busy_count(0), _preamble_sf(0) { n_recv = n_sent = 0; } + RadioLibWrapper(PhysicalLayer& radio, mesh::MainBoard& board) : _radio(&radio), _board(&board), _busy_count(0), _preamble_sf(0), _lbt_backoff_active(false), _lbt_deadline(0) { n_recv = n_sent = 0; } void begin() override; virtual void powerOff() { _radio->sleep(); }