Skip to content

fix(sx126x): let CalibrateImage settle before re-applying RX registers in resetAGC() - #11774

Merged
NomDeTom merged 4 commits into
meshtastic:developfrom
ip2k:fix/sx126x-calibrateimage-settle
Sep 9, 2026
Merged

fix(sx126x): let CalibrateImage settle before re-applying RX registers in resetAGC()#11774
NomDeTom merged 4 commits into
meshtastic:developfrom
ip2k:fix/sx126x-calibrateimage-settle

Conversation

@ip2k

@ip2k ip2k commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What

resetAGC() (#9705) recalibrates the SX126x every interval and then re-applies the RX boosted-gain and 0x8B5 RX-sensitivity registers (#10219). This adds a 50 ms settle after calibrateImage() before those register writes.

Why

On a RAK WisMesh Repeater Mini (RAK3401 nRF52840 + RAK13302 1 W SX1262) the radio periodically went deaf until reboot. Traced into resetAGC(): SX126x::calibrateImage() returns as soon as the command is accepted, and the chip does not hold BUSY for the rest of that calibration. The next register write (setRxBoostedGainMode, then the 0x8B5 patch) lands in that window, fails write-verify (RADIOLIB_ERR_SPI_WRITE_FAILED, -16), and leaves the radio needing a full re-init.

Related to #9933 (closed): that was a duty-cycle wake BUSY race; this is a sibling at a different site, the calibrateImage() call inside resetAGC().

How it works

Calibrate(0x7F) earlier in the same function is already followed by a BUSY poll; the image calibration is not. Instrumentation confirmed BUSY never asserts after calibrateImage() (0 of ~2,000 cycles), so polling cannot guard this window - a fixed settle is required. Register writes issued inside ~10 ms fail; none fail at >=20 ms; 50 ms is ~2.5x that floor. nRF52 delay() is 10 ms-tick quantized (measured 49-51 ms). The pass already holds the radio out of RX for the calibration, so the cost is immaterial (<=60 ms per 60 s).

What it changes

One line in SX126xInterface::resetAGC(). No API, config or wire-format change. SX126x only.

Testing

RAK3401 + RAK13302, nRF52840, live 915 MHz mesh, AGC interval shortened to 10 s to accelerate. Each resetAGC pass logged Calibrate / CalibrateImage / register-write status, so the settle-window failure (both calibrations return 0, the following write fails -16) is counted distinctly from unrelated chip faults.

settle after CalibrateImage settle-window -16 failures
0 ms 9 / 13 (69%)
10 ms 1 / 38
20 ms 0 / 205
50 ms (continuous RX) 0 / 182
50 ms (duty-cycle RX) 0 / 216
50 ms, 16.3 h soak 0 / 3,085 resets, 0 reboots

The dose-response and soak ran on a 2.8.0-based build on this hardware; this commit is the identical one-line change on develop, compile-verified for rak3401-1watt. A develop-based on-air soak of this exact commit is running now; I will post the result here.

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other (please specify below)

Tested on RAK WisBlock RAK3401 + RAK13302 (1 W, nRF52840). No other SX126x boards on hand; regression testing on other SX126x hardware is welcome.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved radio calibration reliability by allowing the operation to complete before applying subsequent settings.

…s in resetAGC()

CalibrateImage returns as soon as the command is accepted and BUSY does not
stay asserted for the rest of the calibration. resetAGC() then re-applies the
RX boosted-gain and 0x8B5 registers immediately, and a register write landing
in that window fails write-verify (RADIOLIB_ERR_SPI_WRITE_FAILED), leaving the
chip needing a full re-init. On RAK3401 + RAK13302 (nRF52840, busy mesh) this
hit ~69% of resets with no delay, ~3% at 10-20 ms, and 0 at 50 ms.
@CLAassistant

CLAassistant commented Sep 8, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

@ip2k, Welcome to Meshtastic!

Thanks for opening your first pull request. We really appreciate it.

We discuss work as a team in discord, please join us in the #firmware channel.
There's a big backlog of patches at the moment. If you have time,
please help us with some code review and testing of other PRs!

Welcome to the team 😄

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 0821c63a-60e0-4c25-9440-d4e664cd1388

📥 Commits

Reviewing files that changed from the base of the PR and between ebab212 and 1db6707.

📒 Files selected for processing (1)
  • src/mesh/SX126xInterface.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mesh/SX126xInterface.cpp

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

resetAGC() now waits 50 ms after image calibration before it writes further radio settings. The final startReceive() step comment reflects the added delay.

Changes

SX126x AGC reset

Layer / File(s) Summary
Wait for image calibration
src/mesh/SX126xInterface.cpp
resetAGC() waits 50 ms after calibrateImage() before continuing with register writes. The following step comment is renumbered from 6 to 7.

Priority: ➖ Normal

Estimated code review effort: 1 (Trivial) | ~2 minutes

Merge Risk: ⚪ Minimal · up to 1233c

SX126x AGC resets now wait briefly after image calibration before restoring receiver settings, avoiding a documented register-write timing window. No current merge-blocking risk is identified.

Suggested reviewers: virtualjmills

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the SX126x calibration timing fix in resetAGC().
Description check ✅ Passed The description explains what changed, why it changed, how it works, and provides detailed hardware testing results. It also includes the required attestations and identifies the tested hardware.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ip2k

ip2k commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

Hi, just wanted to note here that while Claude helped me dig deep into this issue and did most of the legwork, there’s a real human who was having a real issue for a long time that was hard to pin down previously after banging my head against the wall for quite a while and putting my Meshtastic gear on a shelf because it didn’t work well. I’ll be on Discord too, please let me know what I can do better or how I can otherwise help :)

Comment thread src/mesh/SX126xInterface.cpp Outdated
Comment thread src/mesh/SX126xInterface.cpp Outdated
…he comment

Review feedback: the settle is the wait for step 5's image calibration, just as
step 4 waits for step 3, so number it and wrap to the width of the other steps.
Resume receiving becomes step 7. No functional change.
@ip2k

ip2k commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

On-air result of this exact commit on develop (RAK3401 + RAK13302, nRF52840; AGC interval shortened to 10 s to accelerate):

  • 5.2 h, 899 resetAGC passes: 0 settle-window failures (0x8B5 RX patch re-apply failed never logged), 0 reboots. The fix holds on develop.
  • Corrected figures for the 2.8.0-based soak in the description (that table was a snapshot at 16.3 h): full window 17.4 h / 3,302 passes / 0 settle failures / 0 re-inits / 0 reboots.

One thing to disclose plainly because it is not this PR's bug but appears in the same log: during the develop soak there were 5 chip-state-loss recoveries via the #11676/#11678 path, each triggered by SX126x standby … err=-705 or startReceiveDutyCycleAuto … err=-705, each re-init succeeding in ~70 ms with the queued packet then sent. All 5 (and 4 similar -705 transients that the 2.8.0 build merely logged) fall 0.09–0.77 s after a resetAGC() that ran with a TX already queued — the guard checks in-flight TX/RX, not the queue. It is unrelated to the CalibrateImage settle (the register writes never fail), about 6× rarer at the default 60 s interval, and correlated with a warm afternoon. I'll propose a small separate change (skip resetAGC() while the TX queue is non-empty) once it has soaked. #11782 (limitPower() re-seed) kept TX power at the configured level through each re-init.

@NomDeTom
NomDeTom added this pull request to the merge queue Sep 9, 2026
Merged via the queue into meshtastic:develop with commit 0c4bee7 Sep 9, 2026
142 of 143 checks passed
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 first-contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants