Skip to content

SX126x: re-apply 0x8B5 register in resetAGC() to preserve RX sensitivity#10219

Merged
thebentern merged 1 commit into
meshtastic:developfrom
nightjoker7:fix/sx126x-0x8b5-resetagc
Apr 21, 2026
Merged

SX126x: re-apply 0x8B5 register in resetAGC() to preserve RX sensitivity#10219
thebentern merged 1 commit into
meshtastic:developfrom
nightjoker7:fix/sx126x-0x8b5-resetagc

Conversation

@nightjoker7
Copy link
Copy Markdown
Contributor

Summary

SX126xInterface<T>::resetAGC() (added in #9705) performs a warm-sleep + CALIBRATE_ALL (0x7F) every 60 seconds. This sequence clears bit 0 of the undocumented 0x8B5 register — the register that PR #9571 / #9777 sets once at init to improve RX sensitivity. The AGC-reset path never re-applies it, so the benefit of #9571 is silently lost ~60 seconds after boot and is never restored until the device is rebooted.

The fix is to re-write 0x8B5 bit 0 at the end of resetAGC(), alongside the other register re-applies that are already there (setDio2AsRfSwitch, setRxBoostedGainMode).

Empirical evidence

Reproduced on a Heltec Mesh Node T114 (nRF52840 + SX1262) by instrumenting resetAGC() with module.SPIgetRegValue(0x8B5) reads before and after the re-apply:

[boot+12.1s] SX126x AGC reset: warm sleep + Calibrate(0x7F)
[boot+12.1s] DIAG 0x8B5 post-calibration read = 0x04   <- bit 0 CLEARED by 0x7F
[boot+12.1s] DIAG 0x8B5 post-reapply     read = 0x05   <- bit 0 restored by fix

[boot+72.1s] SX126x AGC reset: warm sleep + Calibrate(0x7F)
[boot+72.1s] DIAG 0x8B5 post-calibration read = 0x04   <- cleared again
[boot+72.1s] DIAG 0x8B5 post-reapply     read = 0x05   <- restored again

Deterministic, reproduces every AGC_RESET_INTERVAL_MS tick. Bit 2 survives (that's why we see 0x04 rather than 0x00), but bit 0 — the one #9571 cares about — is consistently wiped by the calibration.

Why warm sleep + calibration clears it

Two independent signals that undocumented 0x8xx-family registers are NOT auto-retained across the warm-sleep + calibration sequence:

  1. Datasheet (SX1261/2 v1.1 p. 66): warm sleep "restores register state" — but only the documented register set. 0x8B5 is not in the documented set.
  2. RadioLib source (proof by analogy): SX126x::setRxBoostedGainMode() explicitly adds register 0x08AC (RX_GAIN, documented) to the chip's retention RAM at offsets 0x029F-0x02A1. If warm sleep preserved everything in the 0x8xx space automatically, that retention-RAM code would be unnecessary. 0x8B5 is in the same family and is not whitelisted, so it gets wiped.

Additionally, CALIBRATE_ALL (0x7F) retrains the analog front-end, which is exactly what 0x8B5 bit 0 configures — so even without warm sleep, the calibration step alone would clear it.

Real-world impact

Every release from 2.7.20 (where #9705 landed) through current develop is affected. This includes 2.7.20, 2.7.21, 2.7.22 and 2.7.23. All SX1262-based boards that have the 0x8B5 init-time patch compiled in (post-#9777 that is every SX1262 board) lose the sensitivity improvement ~60 s after boot.

Consistent with the ~-15 dB RX regression reported by @tonicb78 on #9571 (comment Mar 11) for a Heltec V4 near cell towers on 2.7.20 vs 2.7.15. The bug is most visible in noisy RF environments (cell-tower proximity, dense mesh), which is exactly the scenario #9571 was meant to fix.

Scope of this PR

Minimal and narrowly scoped to the empirically-verified bug: one re-apply call inside SX126xInterface<T>::resetAGC(), right after the existing setRxBoostedGainMode() re-apply, guarded with a LOG_WARN on SPI failure.

Does not touch reconfigure() — I only have empirical proof that the bug exists in the resetAGC() path. If reviewers have data that reconfigure() (region change / bandwidth change) also clears 0x8B5, that could be a follow-up PR.

Does not touch LR11x0Interface::resetAGC(). The LR1110 / LR1121 AGC is firmware-black-boxed (per @weebl2000 on #9705), and no one has reported a matching regression on LR11x0 boards yet. Happy to add if there's demand.

Test plan

  1. Flash onto any SX1262 board (tested: Heltec Mesh Node T114).
  2. Either:
    • Quick check: add a LOG_INFO("0x8B5 = 0x%02X", module.SPIgetRegValue(0x8B5) & 0xFF) immediately before and after the new re-apply and observe 0x04 → 0x05 every 60 s.
    • Field check: observe RX sensitivity / packet loss in a noisy environment over >60 s; compared to pre-fix firmware, RX should no longer degrade ~1 minute after boot.
  3. Verify TX path is unaffected (register write is touch-only on bit 0).
  4. Verify no-op on boards where 0x8B5 doesn't do anything (safe: we're setting a single bit, same value Apply SX1262 register 0x8B5 patch for improved GC1109 RX sensitivity #9571 / Unlock 0x8B5 register macro guard for SX162 #9777 set at init).

References

The CALIBRATE_ALL (0x7F) command inside resetAGC() clears bit 0 of the
undocumented 0x8B5 register. That bit is set once in init() by meshtastic#9571 and
meshtastic#9777 to improve SX1262 RX sensitivity, and the AGC-reset path was not
re-applying it. Result: every SX1262 node silently loses the RX
sensitivity patch ~60s after boot and never recovers until reboot.

Empirically confirmed on Heltec Mesh Node T114 (nRF52840 + SX1262):
  - Post-calibration read of 0x8B5 = 0x04 (bit 0 cleared)
  - After re-apply: 0x05 (bit 0 set)
Reproducible every AGC_RESET_INTERVAL_MS tick.

Fix re-applies the register bit alongside the existing post-calibration
re-applies (setDio2AsRfSwitch, setRxBoostedGainMode).
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 21, 2026

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added needs-review Needs human review bugfix Pull request that fixes bugs labels Apr 21, 2026
@thebentern thebentern requested a review from Copilot April 21, 2026 11:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an interaction between the periodic SX126x “AGC reset” maintenance routine and the previously-added SX1262 RX sensitivity patch by re-applying the undocumented 0x8B5 bit-0 setting after CALIBRATE_ALL (0x7F) inside SX126xInterface<T>::resetAGC().

Changes:

  • Re-apply the 0x8B5 bit-0 RX sensitivity patch at the end of resetAGC() after calibration.
  • Add a warning log if the register re-apply fails.

@thebentern thebentern merged commit 4090d9f into meshtastic:develop Apr 21, 2026
83 of 84 checks passed
thebentern pushed a commit that referenced this pull request Apr 21, 2026
…ity (#10219)

The CALIBRATE_ALL (0x7F) command inside resetAGC() clears bit 0 of the
undocumented 0x8B5 register. That bit is set once in init() by #9571 and
#9777 to improve SX1262 RX sensitivity, and the AGC-reset path was not
re-applying it. Result: every SX1262 node silently loses the RX
sensitivity patch ~60s after boot and never recovers until reboot.

Empirically confirmed on Heltec Mesh Node T114 (nRF52840 + SX1262):
  - Post-calibration read of 0x8B5 = 0x04 (bit 0 cleared)
  - After re-apply: 0x05 (bit 0 set)
Reproducible every AGC_RESET_INTERVAL_MS tick.

Fix re-applies the register bit alongside the existing post-calibration
re-applies (setDio2AsRfSwitch, setRxBoostedGainMode).
@nightjoker7 nightjoker7 deleted the fix/sx126x-0x8b5-resetagc branch April 21, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull request that fixes bugs needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants