Skip to content

Feat/mesh beacon#10618

Open
NomDeTom wants to merge 25 commits into
meshtastic:developfrom
NomDeTom:feat/mesh-beacon
Open

Feat/mesh beacon#10618
NomDeTom wants to merge 25 commits into
meshtastic:developfrom
NomDeTom:feat/mesh-beacon

Conversation

@NomDeTom

@NomDeTom NomDeTom commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

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:

  • Broadcaster (MeshBeaconBroadcastModule) — sends beacons on a timer.
  • Listener (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

Scenario Config
Announce a migration channel to nearby nodes broadcast_enabled=true, broadcast_offer_preset, broadcast_offer_channel set, broadcast_message="Switch to NarrowSlow"
Publish a community text bulletin every hour 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

Field Type Default Description
listen_enabled bool true Accept incoming MESH_BEACON_APP packets. Delivers the text portion to the local inbox; caches the radio offer for the client app.
broadcast_enabled bool false Periodically send beacon packets from this node.

Broadcast content

Field Type Default Description
broadcast_message string (max 100 B) "" Text included in every beacon. When no offer fields are set, sent as TEXT_MESSAGE_APP so standard clients display it.
broadcast_send_as_node uint32 0 Override the from field. When 0, the local node number is used. A remote admin may only set this to their own node ID.
broadcast_interval_secs uint32 3600 How often to broadcast. Minimum 3600 (1 h). 0 = use default.

Radio offer (advertised to listeners)

Field Type Description
broadcast_offer_channel ChannelSettings (optional) Channel name + PSK to advertise. Listeners store this for the client app to present as a join offer.
broadcast_offer_region RegionCode Region to advertise in the offer.
broadcast_offer_preset ModemPreset (optional) Modem preset to advertise.

When 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)

Field Type Description
broadcast_on_channel ChannelSettings (optional) Override the channel used for TX. If only name/PSK differ from the primary, the radio switches to this channel for the beacon TX then restores.
broadcast_on_region RegionCode Override the region used for TX.
broadcast_on_preset ModemPreset (optional) Override the modem preset used for TX. If this differs from the running config, the radio is temporarily reconfigured for beacon TX, then restored.

These 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

  • 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)

@NomDeTom NomDeTom requested review from GUVWAF and Copilot June 3, 2026 02:59
@github-actions github-actions Bot added the needs-review Needs human review label Jun 3, 2026
@NomDeTom NomDeTom requested review from caveman99 and removed request for GUVWAF June 3, 2026 02:59
@github-actions github-actions Bot added the enhancement New feature or request label Jun 3, 2026
@NomDeTom NomDeTom requested a review from GUVWAF June 3, 2026 02:59

Copilot AI 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.

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) and MeshBeaconListenerModule (receives beacons, delivers text to inbox, caches offers).
  • Extends module configuration/protobuf surface to represent beacon settings and introduces a new MESH_BEACON_APP portnum.
  • 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.

Comment thread src/modules/Modules.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp
Comment thread src/modules/MeshBeaconModule.cpp
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/AdminModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated

Copilot AI 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.

Pull request overview

Copilot reviewed 13 out of 20 changed files in this pull request and generated 7 comments.

Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/MeshBeaconModule.cpp Outdated
Comment thread src/modules/AdminModule.cpp
Comment thread src/mesh/NodeDB.cpp
Comment thread test/test_mesh_beacon/test_main.cpp Outdated
@SpudGunMan

Copy link
Copy Markdown

What about a QSY request as part of it to move/change?

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 22 increased, net +126,832 (+123.9 KB)

Target Size vs develop
station-g2 2,253,488 📈 +6,768 (+6.6 KB)
heltec-v3 2,242,016 📈 +6,752 (+6.6 KB)
station-g3 2,243,632 📈 +6,512 (+6.4 KB)
seeed-xiao-s3 2,252,672 📈 +6,400 (+6.2 KB)
t-deck-tft 3,788,320 📈 +6,352 (+6.2 KB)
Show 17 more target(s)
Target Size vs develop
rak3312 2,248,592 📈 +6,336 (+6.2 KB)
tlora-c6 2,346,960 📈 +6,224 (+6.1 KB)
rak11200 1,838,240 📈 +6,160 (+6.0 KB)
heltec-ht62-esp32c3-sx1262 2,113,216 📈 +6,112 (+6.0 KB)
picow 1,227,044 📈 +5,960 (+5.8 KB)
elecrow-adv-35-tft 3,393,872 📈 +5,952 (+5.8 KB)
heltec-v4 2,261,632 📈 +5,872 (+5.7 KB)
heltec-vision-master-e213-inkhud 2,201,136 📈 +5,760 (+5.6 KB)
pico 766,568 📈 +5,752 (+5.6 KB)
rak11310 789,168 📈 +5,752 (+5.6 KB)
seeed_xiao_rp2040 764,768 📈 +5,752 (+5.6 KB)
pico2w 1,203,100 📈 +5,544 (+5.4 KB)
t-eth-elite 2,467,328 📈 +5,440 (+5.3 KB)
pico2 754,024 📈 +5,336 (+5.2 KB)
seeed_xiao_rp2350 752,168 📈 +5,320 (+5.2 KB)
rak3172 184,000 📈 +3,408 (+3.3 KB)
wio-e5 236,332 📈 +3,368 (+3.3 KB)

Updated for 4f4aebc

erayd and others added 14 commits June 14, 2026 15:08
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;
 }

 /*
-----
…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.
NomDeTom and others added 5 commits June 14, 2026 15:16
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (24)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-07-15. Updated for 5eeaa88.

NomDeTom and others added 6 commits June 15, 2026 11:05
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants