* Gut beacon send-as-node and consolidate TX onto broadcast_targets
Two MeshBeaconConfig changes, both against fields that never reached a tagged
release, so there is no migration for existing nodes.
broadcast_send_as_node let a client name a node ID to send beacons AS, rewriting
the packet's `from`. Firmware never applied it - the assignment was commented
out, so `from` was always the local node and the field was a settable, persisted
no-op. It was also unsound as designed: rewriting `from` forges no signature, it
only makes isFromUs() false, so perhapsEncode() skips XEdDSA signing and
receivers get an unsigned packet attributed to another node.
broadcast_on_channel / broadcast_on_region / broadcast_on_preset were a second
way to name a beacon destination alongside broadcast_targets, chosen silently on
whether broadcast_targets was empty. The comments claimed the two were
equivalent; they were not. An inline ChannelSettings carries name and PSK, so
broadcast_on_channel could transmit on a channel absent from the node's channel
table, which channel_index cannot express. That is dropped deliberately - the
channel must exist on the node.
Empty broadcast_targets now synthesises one target on the running preset and
region over the primary channel, matching what the scalar path produced when
left unset, so an otherwise unconfigured node still beacons.
The USERPREFS_MESH_BEACON_ON_* keys go with the fields. A preconfigured build
that still defines one now fails at compile time with a pointer to the
USERPREFS_MESH_BEACON_TARGET_0_* equivalents, rather than silently losing its
beacon channel. The replacement names a channel-table slot, so such a build must
also provision that channel.
MeshBeaconConfig shrinks 324 -> 240 bytes and ModuleConfig 328 -> 244, against
the 512-byte MAX_TO_FROM_RADIO_SIZE ceiling that FromRadio sits 2 bytes under.
The protobufs submodule points at a branch carrying both proto changes; it needs
re-pointing to master once meshtastic/protobufs#1047 and #1048 merge.
* Point protobufs submodule at master now that the beacon protos are merged
meshtastic/protobufs#1047 and #1048 are in master, so drop the temporary
beacon-proto-integration pin. MeshBeaconConfig stays 240 bytes and ModuleConfig
244, unchanged from the integration branch.
The bump also picks up master's unrelated additions: the MESHNOLOGY_W12 and
MESHPAGER_X2 hardware models, and a ground-speed unit correction in Position.
* Add explicit presence for MeshPacket.rx_time (arrival time)
rx_time is now proto3 optional with a has_rx_time presence bit, matching
the rx_rssi treatment. A node with no GPS and no phone connected yet has
no time source at all, so a bare 0 was indistinguishable from a genuine
1970-01-01 reading; downstream consumers (replay packets, JSON
serialization) now check has_rx_time instead of the value.
* Dedupe rx_time stamping into a shared helper; trim a debug log string
Extract the repeated haveTime/rx_time/has_rx_time stamp logic (5 call
sites across Router.cpp, MeshBeaconModule.cpp, MeshService.cpp) into
Router::computeRxTimeStamp()/stampRxTime(). Also shorten the new RTC.cpp
LOG_DEBUG string. Saves 48 bytes of flash on rak4631 (measured), no
behavior change.
* Fix has_rx_rssi presence carried unconditionally through StoreForward replay
preparePayload() set has_rx_rssi = true unconditionally on replay, regardless
of whether the packet's rx_rssi at store time was a genuine measurement (e.g.
MQTT-relayed packets carry no real RSSI). Store the presence bit alongside
rx_rssi in PacketHistoryStruct and restore it on replay instead.
Flagged by Copilot on #11271 (same root cause the has_rx_time explicit
presence work fixes) but never addressed before that PR merged.
* Trim comment blocks to the repo's 1-2 line guideline
.github/copilot-instructions.md:338 caps code comments at 1-2 lines; several
blocks added across the rx_time explicit-presence work ran well past that.
Also consolidates Time.cpp's file-level doc comment into Time.h, where the
rest of the Time:: API contract already lives.
No behavior change.
* Add rx_time explicit-presence test coverage
- test_meshpacket_serializer: has_rx_time=false fixture plus tests asserting
JsonSerialize/JsonSerializeEncrypted emit 0 rather than leaking the
millis() placeholder, alongside the has_rx_time=true baseline.
- test_stream_api: two tests driving a real PhoneAPI handshake (want_config_id
through STATE_SEND_PACKETS) that simulate a phone time-giving transaction
arriving before vs. after a queued packet is drained - covering both the
reconciled and the ships-with-placeholder-absent paths of
MeshService::reconcilePendingRxTimes().
* Fix three correctness issues flagged in review
- Time.h: drop the reserved-identifier include guard (_MT_TIME_H); pragma
once already covers it, matching convention elsewhere (e.g. RTC.h).
- Time.cpp: rebase getMillis64()'s wrap accumulator when the test seam
swaps clock sources, so a real<->injected clock jump isn't miscounted
as a genuine 32-bit wrap.
- NodeInfoModule: the 12h reply-suppression window is a local dedup
duration, not a wall-clock reading - switch it to Time::getMillis64()
so RTC-quality jumps and replayed packets' stale rx_time can't perturb
it.
- StoreForwardModule: has_rx_time was derived from *current* RTC quality
at replay time rather than stored at capture time, so a history entry
saved while time-blind could be misreported as a valid epoch once the
clock later improved. Persist the presence bit in PacketHistoryStruct
instead.
* tryfix CI
* post review fixes
* more test fixes
* Protobufs
* Wire up MEDIUM_TURBO modem preset
MEDIUM_TURBO (500 kHz, SF9, CR 4/5) already existed in the protobuf enum but
was never wired into firmware, so selecting it silently fell through to the
LONG_FAST default and rendered an "Invalid" display name.
Add its bw/sf/cr mapping (modemPresetToParams), display name (MediumTurbo/MedT),
PRESETS_STD membership (standard regions only — 500 kHz does not fit EU868's
250 kHz band, so it stays out of PRESETS_EU_868 and is rejected/clamped there),
and the MEDIUM SNR-grading bucket. Includes positive coverage in test_radio,
EU868-reject + US-accept coverage in test_admin_radio and test_mesh_beacon,
the STD preset count 9->10, an extended fuzz range, and the client-spec doc.
* Address review feedback on MEDIUM_TURBO tests
- test_mesh_beacon: assert has_mesh_beacon before checking the invalid preset was
cleared, so the EU868-cleared test can't pass on a dropped message (matches the
existing SHORT_TURBO test).
- test_fuzz_packets: draw modem presets from _ModemPreset_ARRAYSIZE instead of a
hard-coded 17 so the fuzz range tracks future enum additions automatically.
* second pass adding beacons admin
* Bump protobufs submodule to merged MESHBEACON_CONFIG (PR #970)
clod helped.
* Gate MeshBeacon module config behind admin auth in PhoneAPI
The FromRadio module-config sync copied moduleConfig.mesh_beacon unconditionally. MeshBeaconConfig embeds two ChannelSettings (broadcast_offer_channel, broadcast_on_channel) that carry PSKs, so an unauthorized client could exfiltrate channel PSKs when MESHTASTIC_PHONEAPI_ACCESS_CONTROL is enabled - bypassing the redaction already applied to mqtt/network/security config.
Gate the payload copy on getAdminAuthorized(), mirroring the mqtt case; unauth clients now receive an empty MeshBeaconConfig.
clod helped
Adds the Traffic Management module (TMM) plus the NodeDB/warm-store and
next-hop foundations it builds on:
- Unified per-node cache (flat array, 8-bit relative ticks) shared by all
features; role-aware throttles for tracker / lost-and-found.
- Position deduplication: drop unchanged position rebroadcasts within a
configurable interval; precision driven off the channel ceiling (clamped to
the public-key max on well-known channels). Enabled by default at 11h.
- Per-node rate limiting and unknown-packet filtering (config-driven; a
non-zero companion field enables each feature -- no bool toggles).
- NodeInfo direct response from cache with role-based hop clamps.
- Persistent next-hop overflow store: confirmed hops have no TTL, are seeded
from NodeInfoLite at boot, and survive hot-store eviction.
- Three-tier sender-role resolution (hot NodeInfoLite -> warm store -> TMM
cache). Role is cached write-time (seeded on first track, refreshed from
NodeInfo), pins its cache entry like a next-hop hint, and is evicted last.
- Warm store caches device role + protected category across reboot/eviction.
- PositionModule stationary floor for tracker / lost-and-found.
- PSRAM gating for warm/satellite/TMM cache sizes; STM32WL excluded.
Protobufs: TrafficManagementConfig trimmed to the five uint32 fields actually
used; submodule repointed to protobufs develop.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Added lora region and preset maps
* Protos
* Address PR review feedback
- Log (and break/skip) when the region preset map exceeds its array bounds
instead of silently dropping regions
- Derive test bounds from the generated nanopb array sizes via sizeof()
instead of hard-coded magic numbers
- Fix want_config sequence comment (missing comma, STATE_SEND_MODULECONFIG)
- Specify a language on the spec's fenced code blocks (markdownlint MD040)
* Fix want_config stall: handle STATE_SEND_REGION_PRESETS in PhoneAPI::available()
available() had a separate per-state switch that wasn't updated for the new
state, so it returned false ('unexpected state 5') and getFromRadio() was
never called - the config handshake stalled after metadata and the client
timed out. Verified via the native simulator integration test.