Skip to content

Fix LR2021 occasionally stopping reception after a TX power change - #3218

Open
fkallay1 wants to merge 1 commit into
meshcore-dev:devfrom
fkallay1:fix/lr2021-set-tx-power-rx-stall
Open

Fix LR2021 occasionally stopping reception after a TX power change#3218
fkallay1 wants to merge 1 commit into
meshcore-dev:devfrom
fkallay1:fix/lr2021-set-tx-power-rx-stall

Conversation

@fkallay1

Copy link
Copy Markdown

Type: bug

Symptom

On an LR2021 board, changing the transmit power at runtime (set tx <dbm>)
could stop the radio from receiving. Nothing recovered it — the node kept
running and still transmitted its own adverts, but never received another
packet until it was rebooted. It happened only occasionally, a few times over
several hours of use.

Cause

RadioLibWrapper::setTxPower() calls setOutputPower() and nothing else:

void RadioLibWrapper::setTxPower(int8_t dbm) {
  _radio->setOutputPower(dbm);
}

On LR2021 that writes the PA config and TxParams, which are standby-only
commands, and the receiver is never re-armed afterwards.

This only bites on LR2021 because of the platform split in recvRaw():

#if defined(USE_LR2021)
  state = STATE_RX;     // LR2021 stays in Rx after readData
#else
  state = STATE_IDLE;   // need another startReceive()
#endif

On SX126x the state falls back to STATE_IDLE, so the next recvRaw() calls
startReceive() regardless and the write is harmless. On LR2021 the wrapper
keeps STATE_RX forever, so nothing calls startReceive() on its own. If the
PA config write knocks the receiver down, it stays down.

setTxPower() is the only setter in the wrapper that does not do this —
idle(), resetAGC() and applySideDetectorConfig() all set
state = STATE_IDLE to trigger a fresh startReceive().

The stuck-radio check in Dispatcher::loop() cannot catch this either:
isInRecvMode() reports the wrapper's own state flag rather than the chip's
actual mode, so it still believes the radio is in Rx. And even when it does
trip, it only raises ERR_EVENT_STARTRX_TIMEOUT without attempting recovery.

Fix

On LR2021, drop to standby via idle() before writing the PA config, and let
checkRecv() re-arm Rx — the same pattern the other setters already use.
Other radios are left untouched, since they re-arm on their own.

Affected boards

meshtracker_x1 and meshnology_w12 (both build with USE_LR2021).

Testing

Verified on hardware with an LR2021 repeater (Seeed XIAO nRF52840 with a NiceRF
LoRa2021F33-2G4 module, 869.618 MHz, SF7, BW 62.5):

  • Before the fix the receiver occasionally stopped after a set tx, with no
    way back other than a reboot.
  • After the fix, six consecutive power changes (15/18/21/14/19/14) left the
    radio fully functional: it kept receiving and forwarding mesh traffic
    (rawrx/rxpkts increasing, no missed IRQs). Noise-floor sampling also kept
    updating, which is an independent confirmation that Rx was re-armed, since
    that sampling only runs while state == STATE_RX.

MeshTracker_X1_repeater builds clean.

RadioLibWrapper::setTxPower() called setOutputPower() and nothing else. On
LR2021 that writes the PA config and TxParams, which are standby-only commands,
and it never re-arms the receiver.

This only affects LR2021 boards. recvRaw() has:

    #if defined(USE_LR2021)
      state = STATE_RX;    // LR2021 stays in Rx after readData
    #else
      state = STATE_IDLE;  // need another startReceive()
    #endif

so on SX126x the next recvRaw() re-arms Rx anyway and the write is harmless,
while on LR2021 startReceive() is never called again on its own. A 'set tx'
issued while the radio was listening could therefore stop reception until the
next reboot. Observed a few times on an LR2021 repeater; recovery required a
power cycle.

Dispatcher's stuck-radio check does not catch it: isInRecvMode() reports the
wrapper's own `state` flag rather than the chip's actual mode, so it still
believes the radio is in Rx, and it only raises ERR_EVENT_STARTRX_TIMEOUT
without attempting recovery.

Fix: on LR2021, drop to standby via idle() before writing the PA config and let
checkRecv() re-arm Rx - the same pattern resetAGC() and
applySideDetectorConfig() already use. Other radios are left untouched.

Affects meshtracker_x1 and meshnology_w12 (both USE_LR2021).

Tested on hardware with an LR2021 repeater: six consecutive 'set tx' changes
(15/18/21/14/19/14), after which the radio kept receiving and forwarding
traffic (rawrx/rxpkts increasing, no missed IRQs). Noise-floor sampling also
kept updating, which only happens while state == STATE_RX.
fkallay1 added a commit to fkallay1/MeshCore that referenced this pull request Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant