Allow multiple radios to run in Mesh mode simultaneously#2800
Conversation
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.
|
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>
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>
|
I'll remake tests later when the kids are to bed, but so far it works awesomely well. I'll get back with results. |
|
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. |
|
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... |
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.
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:
Closes #2525