Skip to content

fix(radio): validate 2.4 GHz bandwidths - #11317

Open
RCGV1 wants to merge 7 commits into
meshtastic:developfrom
RCGV1:codex/lora24-bandwidth-validation
Open

fix(radio): validate 2.4 GHz bandwidths#11317
RCGV1 wants to merge 7 commits into
meshtastic:developfrom
RCGV1:codex/lora24-bandwidth-validation

Conversation

@RCGV1

@RCGV1 RCGV1 commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

  • validate effective LoRa bandwidths against the selected SX128x, LR11x0, or LR20x0 radio before calling RadioLib
  • accept only the exact bandwidth values each radio driver supports, including LR2021 modes through 1000 kHz
  • repair invalid local or persisted settings to a radio-compatible default
  • keep mixed-radio probes transactional: suppress speculative errors, restore configuration, and quiesce/reject candidates whose restored configuration cannot be applied

Why

A 2.4 GHz node could receive a sub-GHz bandwidth such as 125 kHz. Generic validation accepted it, then the radio driver attempted an unsupported mode and the node could become unresponsive. Mixed T3S3 environments also need validation to follow the radio actually populated on the board rather than the shared PlatformIO target.

Validation

  • pio run -e tlora-t3s3-v1 -e muzi-base -e meshnology_w12: passed for the mixed SX128x/LR1121 target, Muzi LR1121, and LR2021 production target
  • trunk fmt, trunk check, and git diff --check: passed
  • real TLORA_T3_S3/SX128x on final commit 88e643e3b: custom LORA_24 1600 kHz applied, survived reboot, and read back as 1600
  • real TLORA_T3_S3/SX128x: invalid custom 125 kHz was corrected to 800 and the node remained responsive
  • real TLORA_T3_S3/SX128x: 200, 400, 800, and 1600 kHz applied and read back successfully
  • exact original LORA_24/SHORT_FAST configuration and node identity were restored and verified after reboot
  • focused native test compilation is blocked locally on macOS before test execution by existing initialized VLA errors in NotificationRenderer.cpp; the added radio tests are included for Linux CI

Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause obvious regressions on:
    • Other: LilyGo TLORA T3S3 with SX128x

Summary by CodeRabbit

  • New Features

    • Added hardware-aware LoRa bandwidth validation for supported radio platforms.
    • Automatically adjusts unsupported bandwidth settings to suitable regional defaults.
    • Improved handling of wide-band and turbo radio configurations.
  • Bug Fixes

    • Prevented radios from restarting reception after unsuccessful configuration.
    • Preserved valid regional settings during radio detection and configuration.
    • Reduced misleading errors during automatic radio probing.
  • Tests

    • Added coverage for platform-specific bandwidth validation, correction, probing, and configuration scenarios.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: aa325c4c-770f-410f-a5a8-bab9fd784e51

📥 Commits

Reviewing files that changed from the base of the PR and between 3d169c9 and 88e643e.

📒 Files selected for processing (6)
  • src/mesh/LR11x0Interface.cpp
  • src/mesh/LR20x0Interface.cpp
  • src/mesh/MeshRadio.h
  • src/mesh/RadioInterface.cpp
  • src/mesh/RadioInterface.h
  • test/test_radio/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/mesh/RadioInterface.h
  • src/mesh/MeshRadio.h
  • test/test_radio/test_main.cpp
  • src/mesh/RadioInterface.cpp

📝 Walkthrough

Walkthrough

RadioInterface now validates LoRa bandwidths against the selected radio. SX128x, LR11x0, and LR20x0 interfaces define supported bandwidth values. Probing suppresses configuration errors and restores configuration. Reconfiguration reports failures and restarts reception only after success. Tests cover these paths.

Changes

LoRa bandwidth validation

Layer / File(s) Summary
Bandwidth capability contracts
src/mesh/RadioInterface.h, src/mesh/MeshRadio.h, src/mesh/LR11x0Interface.h, src/mesh/LR20x0Interface.h, src/mesh/SX128xInterface.h
Radio interfaces expose bandwidth capability checks. Radio-family helpers define supported bandwidth values.
Radio-specific validation and modem application
src/mesh/RadioInterface.cpp, src/mesh/RadioInterface.h
LoRa validation and clamping use the selected radio and configurable error reporting. Modem application uses hardware-aware bandwidth parameters.
Initialization configuration rollback
src/mesh/RadioInterface.cpp
LoRa probing suppresses configuration errors and restores or reapplies configuration for LR1120, LR1121, and LR2021.
Radio reconfiguration and validation coverage
src/mesh/LR11x0Interface.cpp, src/mesh/LR20x0Interface.cpp, test/test_radio/test_main.cpp
Reconfiguration returns failure when radio settings fail and restarts reception only after success. Tests cover bandwidth validation, clamping, preset repair, suppressed errors, and regional behavior.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Suggested labels: bugfix, needs-review

Suggested reviewers: caveman99, nomdetom, thebentern

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. 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 summarizes the main change: validating 2.4 GHz LoRa bandwidths.
Description check ✅ Passed The description explains the changes, rationale, validation results, test limitation, and completed attestations.
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 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Note

Building this pull request… the flash button, badges and supported-board
list will appear here automatically once CI finishes.

@NomDeTom

NomDeTom commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

should this also cover LR2021?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mesh/RadioInterface.cpp (1)

1154-1171: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not pass the non-wide default to an SX128x.

When wideBand is false, supportsSx128xLoRaBandwidth rejects every bandwidth. The clamp path then derives the regional default as 250 kHz and leaves that value for applyModemConfig. The rejected bandwidth can still reach the 2.4 GHz radio.

Handle the incompatible region before clamping, or revalidate the replacement and stop before applying it. Add a regression test for an SX128x with an UNSET or sub-GHz region.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/RadioInterface.cpp` around lines 1154 - 1171, Ensure the clamp path
in the bandwidth validation around supportsLoRaBandwidth does not pass a
non-wide regional default to an SX128x radio. Detect the incompatible
SX128x/region combination before applying the replacement, or revalidate the
clamped bandwidth and abort before applyModemConfig; preserve valid clamping for
compatible radios. Add a regression test covering an SX128x with an UNSET or
sub-GHz region.
🧹 Nitpick comments (1)
src/mesh/RadioInterface.cpp (1)

1256-1264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pass the active radio explicitly.

applyModemConfig should pass this to validateConfigLora and clampConfigLora. The current calls use the default argument and depend on RadioLibInterface::instance. That skips radio-specific validation when the active implementation is not a RadioLibInterface.

Suggested call-site update
-        if (!validateConfigLora(loraConfig)) {
+        if (!validateConfigLora(loraConfig, this)) {
             loraConfig.modem_preset = newRegion->getDefaultPreset();
         }
...
-        if (validateConfigLora(loraConfig)) {
+        if (validateConfigLora(loraConfig, this)) {
         } else {
             LOG_WARN("Invalid LoRa config settings, cannot apply requested modem config - falling back to %s defaults",
                      newRegion->name);
-            clampConfigLora(loraConfig);
+            clampConfigLora(loraConfig, this);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/RadioInterface.cpp` around lines 1256 - 1264, Update the
applyModemConfig call sites for validateConfigLora and clampConfigLora to pass
the current RadioInterface instance explicitly as this. Do not rely on their
default radio argument or RadioLibInterface::instance, so radio-specific
validation uses the active implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/mesh/RadioInterface.cpp`:
- Around line 1154-1171: Ensure the clamp path in the bandwidth validation
around supportsLoRaBandwidth does not pass a non-wide regional default to an
SX128x radio. Detect the incompatible SX128x/region combination before applying
the replacement, or revalidate the clamped bandwidth and abort before
applyModemConfig; preserve valid clamping for compatible radios. Add a
regression test covering an SX128x with an UNSET or sub-GHz region.

---

Nitpick comments:
In `@src/mesh/RadioInterface.cpp`:
- Around line 1256-1264: Update the applyModemConfig call sites for
validateConfigLora and clampConfigLora to pass the current RadioInterface
instance explicitly as this. Do not rely on their default radio argument or
RadioLibInterface::instance, so radio-specific validation uses the active
implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f93c2837-6b03-4150-9cd0-1d59dd9a1efa

📥 Commits

Reviewing files that changed from the base of the PR and between e57ebb0 and e884072.

📒 Files selected for processing (6)
  • src/mesh/LR11x0Interface.h
  • src/mesh/LR20x0Interface.h
  • src/mesh/MeshRadio.h
  • src/mesh/RadioInterface.cpp
  • src/mesh/SX128xInterface.h
  • test/test_radio/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/mesh/SX128xInterface.h
  • src/mesh/LR11x0Interface.h
  • test/test_radio/test_main.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/mesh/RadioInterface.cpp (1)

376-376: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Apply probe rollback to the ARCH_PORTDUINO initialization path.

Line 376 snapshots the configuration, but the ARCH_PORTDUINO path calls rIf->init() directly at line 430. A selected LR1120, LR1121, or LR2021 can therefore clamp config.lora during probing without suppressed errors, restoration, or reconfiguration.

Use one shared probe-initialization path for both Portduino-selected radios and compile-time radio probes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mesh/RadioInterface.cpp` at line 376, Unify the radio probing flow around
the shared initialization logic so the ARCH_PORTDUINO path no longer calls
rIf->init() directly. Ensure Portduino-selected LR1120, LR1121, and LR2021
radios use the existing loraConfigBeforeProbe snapshot, suppress probe errors,
restore config.lora, and reconfigure after probing just like compile-time radio
probes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mesh/RadioInterface.cpp`:
- Around line 585-594: Make post-probe reconfiguration failure reject
initialization for all three radio paths: update the LR1120 handling around
`rIf->reconfigure()` at `src/mesh/RadioInterface.cpp` lines 585-594, the LR1121
handling at lines 605-614, and the LR2021 handling at lines 625-634. When
reconfiguration fails, clear or invalidate the radio interface using the
existing initialization-failure flow instead of accepting it; preserve
restoration of `config.lora` in each path.

---

Outside diff comments:
In `@src/mesh/RadioInterface.cpp`:
- Line 376: Unify the radio probing flow around the shared initialization logic
so the ARCH_PORTDUINO path no longer calls rIf->init() directly. Ensure
Portduino-selected LR1120, LR1121, and LR2021 radios use the existing
loraConfigBeforeProbe snapshot, suppress probe errors, restore config.lora, and
reconfigure after probing just like compile-time radio probes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 74885946-c261-41cb-a417-714643c27f82

📥 Commits

Reviewing files that changed from the base of the PR and between 74c86dc and 3d169c9.

📒 Files selected for processing (4)
  • src/mesh/MeshRadio.h
  • src/mesh/RadioInterface.cpp
  • src/mesh/RadioInterface.h
  • test/test_radio/test_main.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/test_radio/test_main.cpp
  • src/mesh/MeshRadio.h

Comment thread src/mesh/RadioInterface.cpp
@RCGV1

RCGV1 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@NomDeTom Yes. LR2021 now uses the full exact bandwidth set accepted by the bundled RadioLib driver, through 1000 kHz, while rejecting 1600 kHz and lossy near-value aliases. The meshnology_w12 LR2021 production target builds successfully, and tests cover every accepted wire code plus invalid aliases.

The Portduino initialization path remains intentionally unchanged: it creates one explicitly configured radio and exits if that radio fails, so its validation result is not speculative fallback state. Suppressing/restoring that path would hide genuine invalid configuration on the selected Portduino radio.

@RCGV1

RCGV1 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

Final implementation and hardware validation are complete at 88e643e3b. The remaining check-label failure is administrative: this account cannot add repository labels. A maintainer will need to apply the bugfix label.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

@RCGV1 I will review the final changes in commit 88e643e3b.

The check-label failure is administrative. A repository maintainer must apply the bugfix label.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@NomDeTom

NomDeTom commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

The meshnology_w12 LR2021 production target builds successfully, and tests cover every accepted wire code plus invalid aliases.

The Portduino initialization path remains intentionally unchanged: it creates one explicitly configured radio and exits if that radio fails, so its validation result is not speculative fallback state. Suppressing/restoring that path would hide genuine invalid configuration on the selected Portduino radio.

I'm not hearing hardware confirmation.

@RCGV1

RCGV1 commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

You are right: I do not have LR2021 hardware confirmation. The attached bench covers a T3S3 with SX128x and Muzi/T-Beam devices with LR1121; LR2021 is build- and unit-tested only.

The author of #11279 does have dual-band LR2021 hardware, so I opened a focused companion contribution against that branch for the relevant band-switch guards and tests: Ethan-chen1234-zy#1. I will keep #11317’s hardware claim limited to SX128x/LR1121 unless the LR2021 owner reports a successful live matrix.

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.

2 participants