Skip to content

Allow multiple radios to run in Mesh mode simultaneously#2800

Closed
VA2XJM wants to merge 6 commits into
aredn:mainfrom
VA2XJM:main
Closed

Allow multiple radios to run in Mesh mode simultaneously#2800
VA2XJM wants to merge 6 commits into
aredn:mainfrom
VA2XJM:main

Conversation

@VA2XJM

@VA2XJM VA2XJM commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

More changes might be needed, but this

Previously only one radio per node could run in a Mesh role at a time, even on dual-radio hardware. This adds support for two mesh radios at once, e.g. HaLow + 2.4GHz on a MorseMicro HaLowLink1, where HaLow's receiver saturates at close range but 2.4GHz stays stable - letting both run means babel can route over whichever RF path currently works instead of losing connectivity entirely. Bench-validated on four physical HaLowLink1 units.

  • radios.uc: getActiveConfiguration() now tracks every mesh-mode radio instead of just one; added getMeshRadios(), with getMeshRadio() kept as a first-match shim for callers not yet updated (lqm.uc, wireless_monitor.uc, and other diagnostic tools).
  • node-setup: replaced the single mesh_* selection with a mesh_radios[] array, one entry per mesh-mode radio. The first radio found keeps the existing "wifi" network name for backward compatibility; additional radios get "wifi1", "wifi2", etc, each with its own MAC-derived /32 address, wireless config, firewall zone membership, DHCP ignore entry, and TX power management.
  • radio-and-antenna.ut: relaxed the UI's mutual-exclusion so two radios can both be set to Mesh, while still respecting the existing only_one_radio hardware flag and still blocking two radios from sharing the same non-mesh mode (two LAN APs, two WAN clients).
  • config.mesh/firewall, config.mesh/dhcp, 11-meshrouting: generalized the "wifi" network handling to "wifi*" so additional mesh networks get correct firewall zone membership, DHCP behavior, and routing-table rules.
  • local-and-neighbor-devices.ut, neighbor-device.ut: label RF neighbor entries with their band (HaLow/2.4GHz/3GHz/5GHz) so the same peer reachable over two radios is distinguishable in the UI.

This applies to any dual-radio board, not just HaLowLink1, gated by each device's existing only_one_radio/exclude_modes settings in radios.json.

To be continued:

  • Per-radio LQM tuning (lqm.uc) and chipset monitoring (wireless_monitor.uc) still only manage the first mesh radio found. (This is not required for this PR to work but will need quite some work, so will be part of another PR later)
  • Automatic band-tiered babel rxcost (so the higher band is preferred by routing by default) requires a change to the babeld_wrapper script in the separate aredn_packages repo.

Closes #2525

Previously only one radio per node could run in a Mesh role (mesh/meshap/
meshptp/meshsta) at a time, even on dual-radio hardware. This adds support
for two mesh radios at once, e.g. HaLow + 2.4GHz on a MorseMicro HaLowLink1,
where HaLow's receiver saturates at close range but 2.4GHz stays stable -
letting both run means babel can route over whichever RF path currently
works instead of losing connectivity entirely. Bench-validated on four
physical HaLowLink1 units.

- radios.uc: getActiveConfiguration() now tracks every mesh-mode radio
  instead of just one; added getMeshRadios(), with getMeshRadio() kept as
  a first-match shim for callers not yet updated (lqm.uc,
  wireless_monitor.uc, and other diagnostic tools).
- node-setup: replaced the single mesh_* selection with a mesh_radios[]
  array, one entry per mesh-mode radio. The first radio found keeps the
  existing "wifi" network name for backward compatibility; additional
  radios get "wifi1", "wifi2", etc, each with its own MAC-derived /32
  address, wireless config, firewall zone membership, DHCP ignore entry,
  and TX power management.
- radio-and-antenna.ut: relaxed the UI's mutual-exclusion so two radios
  can both be set to Mesh, while still respecting the existing
  only_one_radio hardware flag and still blocking two radios from sharing
  the same non-mesh mode (two LAN APs, two WAN clients).
- config.mesh/firewall, config.mesh/dhcp, 11-meshrouting: generalized the
  "wifi" network handling to "wifi*" so additional mesh networks get
  correct firewall zone membership, DHCP behavior, and routing-table rules.
- local-and-neighbor-devices.ut, neighbor-device.ut: label RF neighbor
  entries with their band (HaLow/2.4GHz/3GHz/5GHz) so the same peer
  reachable over two radios is distinguishable in the UI.

This applies to any dual-radio board, not just HaLowLink1, gated by each
device's existing only_one_radio/exclude_modes settings in radios.json.

Not included here (tracked as follow-up work):
- Per-radio LQM tuning (lqm.uc) and chipset monitoring
  (wireless_monitor.uc) still only manage the first mesh radio found.
- Automatic band-tiered babel rxcost (so the higher band is preferred by
  routing by default) requires a companion change to the babeld_wrapper
  script in the separate aredn_packages repo.
@aanon4

aanon4 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

We actually already have a plan and a branch for multiple mesh radio support. It will probably get released later this year - sorry.

The previous commit let two radios run in a Mesh role simultaneously, but
lqm.uc still only physically tuned whichever radio radios.getMeshRadio()
returned first - noise floor, SNR, nl80211 station stats, max-distance,
RTS/CTS, and hostapd allow/deny lists were all scoped to a single radio.
A neighbor reachable only via the second radio never got its signal/SNR/
bitrate/connected_time populated, and that radio's own distance/RTS tuning
was never applied.

Replaced the single device/wlan/phy/radio/devtype set with a radioState[]
array built from radios.getMeshRadios(), and looped every place that used
to assume one radio: initial distance-mode/RTS-off/retry setup, the
nl80211 station-stats and noise-floor averaging (each radio scores against
its own noise floor and chanbw), per-radio max-distance accumulation and
clamping, and per-radio hostapd allow/deny list management. Hidden-node
detection stays a single whole-node computation (doing this precisely per
radio would be considerably more speculative for limited benefit), but the
resulting RTS on/off action now applies to every mesh radio instead of
just the first. Also fixed the hidden-node self-exclusion (myip -> myips)
to cover every "wifi"/"wifiN" mesh network, not just the first.

Bench-validated on two HaLowLink1 units running both HaLow and 2.4GHz as
mesh radios simultaneously: /tmp/lqm.info now shows independent, fully
populated signal/snr/bitrate/connected_time for both radios' neighbor
entries on both nodes, where previously only one radio's neighbor ever
got those fields.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@aanon4
aanon4 marked this pull request as draft July 6, 2026 18:52
VA2XJM and others added 4 commits July 6, 2026 15:02
Same follow-up as the previous lqm.uc commit: wireless_monitor.uc still
only watched whichever radio radios.getMeshRadio() returned first for
unresponsive-station detection, connected-station-count tracking, and
the chipset-specific reset actions (IBSS rejoin, scan, interface
restart). A second mesh radio was never monitored or reset at all.

Replaced the single wifi/frequency/ssid/mode/chipset set with a
radioState[] array built from radios.getMeshRadios(), one entry per mesh
radio (each also carrying its own "wifi"/"wifiN" network name, needed by
resetNetwork's interface restart case). Startup chipset discovery,
unresponsive-station monitoring, station-count tracking, and the
daily/zero/unresponsive reset actions all loop over every radio now; a
radio that fails startup discovery is skipped (logged, not fatal) rather
than aborting monitoring for every other radio, and the whole task only
exits if none of them came up. The HaLow "chipset missing, reboot" check
stays an unconditional reboot as before, since that's a whole-device
recovery, not something scoped to one radio's monitoring loop.

/tmp/wireless_monitor.json changed shape from flat single-radio fields to
a "radios" object keyed by iface, since nothing else in this repo reads
that file (confirmed via grep - only the supportdata file list references
it by name).

Bench-validated on two HaLowLink1 units running both HaLow and 2.4GHz as
mesh radios: /tmp/wireless_monitor.json now shows independent
unresponsive/stationCount/actionState tracking for both wlan0 and wlan1
on both nodes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three more diagnostic-only getMeshRadio() callers that only ever
considered the first mesh radio, now generalized:

- messages.uc: the "Select an antenna"/"Set antenna azimuth" todo nags
  now check every mesh radio's antenna options, not just the first, so a
  second radio needing one isn't silently skipped. The underlying
  antenna/azimuth setting is still a single node-wide value
  (aredn.@location[0]) - this only fixes which radios get checked against
  it, not the storage model.
- s-snr.ut: this endpoint is queried by a remote peer's wifisignal.ut to
  read our signal reading for a given neighbor mac. Since each mesh radio
  has its own mac, it now searches every local mesh radio for the one
  actually associated with that mac instead of only checking the first,
  using that radio's own noise floor for the SNR calculation.
- wifisignal.ut: "average" mode now aggregates stations from every mesh
  radio (matching the tool's own description - "average signal strength
  of all currently visible nodes"). Selecting a specific neighbor now
  searches every radio to find which one it's on, and - importantly -
  uses that radio's own interface as the ipv6 scope-id when fetching the
  remote peer's s-snr reading, since a second radio's link-local address
  is only reachable via that radio's own interface, not the first one's.
  Previously a neighbor reachable only via the second radio would show no
  signal/SNR at all in this tool.

Bench-validated on two HaLowLink1 units running HaLow + 2.4GHz
simultaneously.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
sysinfo.json is the node's external monitoring API (mesh maps,
dashboards, and this codebase's own lqm.uc remote-peer lookups all read
it) and its meshrf field only ever reported the first mesh-mode radio it
found - a second mesh radio's channel/ssid/chanbw/antenna/polarization
was completely absent, silently, from external monitoring.

info.meshrf is left exactly as before (first mesh radio found) for
backward compatibility with existing external consumers. Added
info.meshrf_radios: a new, purely-additive array listing every mesh
radio, each tagged with its interface and band (HaLow/2.4GHz/etc), so
tools that understand multi-radio nodes can see all of them. Only
populated when there's more than one mesh radio, so single-radio nodes
see no change at all.

Bench-validated on a HaLowLink1 running HaLow + 2.4GHz simultaneously:
meshrf_radios correctly lists both radios with their own channel/ssid/
chanbw/freq while meshrf itself stays unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- resetNetwork's "unknown chipset" log line was missing its closing
  quote (pre-existing typo, carried forward from the single-radio
  version).
- save() now skips a radio that never got a chipset assigned (failed
  startup discovery) instead of writing its stale/default zero values
  to /tmp/wireless_monitor.json forever.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@VA2XJM

VA2XJM commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

I'll remake tests later when the kids are to bed, but so far it works awesomely well. I'll get back with results.

@aanon4

aanon4 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Just want to draw your attention to my earlier comment on your original submission - we have a branch with multiple mesh radio support (and a lot of other network changes) pending and it will be merged into nightly once the next release goes out. We appreciate you looking at this, but this wont be merged. I'm sorry.

@VA2XJM

VA2XJM commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Closing the PR. I need it to work this week for a demo so I'll make it happen until you do.

On another subject, it is not the first time it is requested, but it would be interesting to have access to a Roadmap so people can know what is coming and on what we can give a hand...

@VA2XJM VA2XJM closed this Jul 6, 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.

Feature Request : Allow compatible dual AP devices to run both interface in mesh modes

2 participants