Feat/mesh beacon#10618
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Mesh Beacon feature to Meshtastic firmware: nodes can periodically broadcast a “beacon” message (optionally including an offer of radio/channel settings) and temporarily switch modem preset/channel parameters for beacon TX, integrating with the existing module and RadioLib TX pipeline.
Changes:
- Adds
MeshBeaconBroadcastModule(periodic sender with payload caching) andMeshBeaconListenerModule(receives beacons, delivers text to inbox, caches offers). - Extends module configuration/protobuf surface to represent beacon settings and introduces a new
MESH_BEACON_APPportnum. - Hooks RadioLib transmit flow to support per-packet temporary radio reconfiguration for beacon transmissions, plus adds a Unity unit test suite for the new module.
Reviewed changes
Copilot reviewed 11 out of 18 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
src/modules/MeshBeaconModule.h |
Declares beacon broadcast/listen modules and radio-switch sidecar helpers. |
src/modules/MeshBeaconModule.cpp |
Implements beacon TX/RX logic, payload caching, and radio reconfiguration/restore behavior. |
src/modules/AdminModule.cpp |
Adds validation/sanitization for MeshBeacon module config on set-module-config. |
src/modules/AdminModule.h |
Adjusts visibility of handleSetModuleConfig under unit testing for test access. |
src/modules/Modules.cpp |
Registers/instantiates beacon modules during module setup. |
src/mesh/RadioLibInterface.cpp |
Integrates beacon radio switching into TX timing/ISR flow and clears sidecar entries after send. |
src/mesh/MeshRadio.h |
Exposes getRegion() for beacon config validation. |
src/mesh/Channels.h |
Makes Channels::fixupChannel() public (used during temporary channel-name/hash adjustments). |
src/mesh/Default.h |
Adds a beacon interval constant used for minimum/default scheduling. |
src/mesh/generated/meshtastic/portnums.pb.h |
Adds meshtastic_PortNum_MESH_BEACON_APP = 37. |
src/mesh/generated/meshtastic/mesh_beacon.pb.{h,cpp} |
Adds generated nanopb types for the MeshBeacon payload. |
src/mesh/generated/meshtastic/module_config.pb.{h,cpp} |
Adds generated nanopb types for MeshBeacon module config. |
src/mesh/generated/meshtastic/localonly.pb.h |
Extends LocalModuleConfig to include MeshBeacon config. |
src/mesh/generated/meshtastic/deviceonly.pb.h |
Updates generated size constants to reflect schema changes. |
test/test_mesh_beacon/test_main.cpp |
Adds unit tests covering admin validation, broadcaster cache/packet formation, and listener caching/guards. |
|
What about a QSY request as part of it to move/change? |
Firmware Size Report22 targets | vs
Show 17 more target(s)
Updated for 4f4aebc |
Note that this commit has details hardcoded for the Wellington (NZ) mesh, and also requires the following patch to the protobufs: ----- diff --git a/meshtastic/mesh.proto b/meshtastic/mesh.proto index 03162d8..ec54c99 100644 --- a/meshtastic/mesh.proto +++ b/meshtastic/mesh.proto @@ -1393,6 +1393,21 @@ message MeshPacket { * Set by the firmware internally, clients are not supposed to set this. */ uint32 tx_after = 20; + + /* + * The modem preset to use fo rthis packet + */ + uint32 modem_preset = 21; + + /* + * The frequency slot to use for this packet + */ + uint32 frequency_slot = 22; + + /* + * Whether the packet has a nonstandard radio config + */ + bool nonstandard_radio_config = 23; } /* -----
…add localonly proto field
…on + proto cache - MeshBeaconBroadcastModule now inherits ProtobufModule<meshtastic_MeshBeacon> (alongside private MeshBeaconModule + OSThread), giving it allocDataPacket() and setStartDelay() without extra includes. - Payload cache: rebuildCache() encodes the MeshBeacon protobuf once and stores it in payloadCache[]/payloadCacheSize; sendBeacon() only calls rebuildCache() when payloadCacheDirty==true. AdminModule calls invalidateCache() after saving new config so the next broadcast picks up changes. - Region/preset validation in handleSetModuleConfig (mesh_beacon_tag): broadcast_on_preset is validated against the device's current region via RadioInterface::validateConfigLora(); broadcast_offer_region is validated via RadioInterface::validateConfigRegion(). Invalid values are zeroed with a LOG_WARN before saving.
…figuration validation
…ks and output formatting
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
d06d4af to
98c14b4
Compare
⚡ Try this PR in the Web FlasherWarning This is an automated, unreviewed CI test build. Back up your device configuration Supported boards built by this PR (24)
Build artifacts expire on 2026-07-15. Updated for |
fixed a test added guards for licensed/ham mode
When broadcast_on_channel overrides the primary channel's name/PSK, the beacon was encrypted with the PRIMARY PSK: perhapsEncode keys encryption off the primary slot, but the radio-thread channel switch happens only after encryption. sendBeaconPacket() now installs the beacon channel into the primary slot for the synchronous duration of send() (cooperative threading => no interleaving) so encryption/hash use the beacon channel, then restores it. A shared beaconChannelSettings() helper builds the channel for both the encrypt-time swap and the RF-time swap so the key+hash cannot drift. Also: correct the legacy-split comments (both packets go out on the same beacon radio settings, not the normal config) and merge the two consecutive `if (hasText)` blocks in the listener (cppcheck duplicateCondition). Tests: add channelPskOverride_swapsBeaconChannelAndRestores and noChannelOverride_doesNotSwapPrimary; MockRouter snapshots the primary channel at send() time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The listener delivers received text via MeshService::sendToPhone(), which enqueues the packet into toPhoneQueue and takes ownership. Nothing dequeues it in tests, so the three listener tests carrying message text stranded a MeshPacket each — 1272 bytes / 3 allocations that LeakSanitizer flagged at process exit, aborting the coverage run (surfaced by pio as [ERRORED] / SIGHUP even though all 40 assertions passed). Drain the phone queue in tearDown (getForPhone()/releaseToPool) so the packets return to packetPool. Suite is now GREEN with no sanitizer abort. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Building on the work in #7183 and brought back up in erayd#9.
Basically it brings in the "beacon" feature - a node (any node) can be configured either remotely or locally to swap to another preset, broadcast a message and return to the earlier preset at an interval of not less than an hour.
The node can send it "from" the admin-ing node as well, to help with consistency.
I've dropped the "tips" function and the channel admin-ing aspect. Once other 2.8 features are in place, maybe they can come back.
Supported by protobufs in meshtastic/protobufs#938.
Note: whitecollar code at the moment. Needs testing.
MeshBeacon Module
What it is
The MeshBeacon module lets a node periodically announce itself to the mesh with a configurable text message and/or a structured radio offer. Listeners store the offer; the client app can then invite the user to adopt it. The module has two independent halves:
MeshBeaconBroadcastModule) — sends beacons on a timer.MeshBeaconListenerModule) — receives beacons and caches the latest offer.Both halves can be enabled independently; a pure-relay node might enable the listener but not the broadcaster, while a gateway might run both.
Use Cases
broadcast_enabled=true,broadcast_offer_preset,broadcast_offer_channelset,broadcast_message="Switch to NarrowSlow"broadcast_enabled=true,broadcast_message="Community net: 146.52 MHz at 8pm"Configuration Reference
All fields are in
moduleConfig.mesh_beacon(meshtastic_ModuleConfig_MeshBeaconConfig).Feature flags
listen_enabledbroadcast_enabledBroadcast content
broadcast_messagebroadcast_send_as_nodefromfield. When 0, the local node number is used. A remote admin may only set this to their own node ID.broadcast_interval_secsRadio offer (advertised to listeners)
broadcast_offer_channelbroadcast_offer_regionbroadcast_offer_presetWhen any offer field is set, the packet uses MESH_BEACON_APP portnum and carries a structured protobuf payload. When none are set, the packet falls back to TEXT_MESSAGE_APP.
Note: offered settings are not applied by a node itself - it is transferred to a client app for review before applying.
Radio config for TX (where the beacon is sent)
broadcast_on_channelbroadcast_on_regionbroadcast_on_presetThese fields control where the beacon is transmitted, not what it advertises. A beacon can advertise one preset while being transmitted on a different one.
🤝 Attestations