* Tips robot virtual node / relayer to different LoRa modes & channels
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;
}
/*
-----
* fix: repair mesh tips CI build
* feat: add MeshBeacon module (Phase 1 — proto + generated code + initial stub)
* feat(beacon): implement broadcaster + listener (phases 2-5)
* feat(beacon): wire RadioLibInterface hooks + admin validation (phases 6-7)
* fix(beacon): fix LocalModuleConfig flat access (no payload_variant), add localonly proto field
* feat(beacon): fix broadcaster inheritance, add preset/region validation + 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.
* feat(beacon): add unit tests for MeshBeaconModule and AdminModule configuration validation
* remove old meshtips
* more validation in NodeDB and AdminModule, and userprefs for baked in goodness
* copilot is my gravity
* mmmmm... beacon
* oops
* Enhance unit tests for MeshBeaconModule with detailed validation checks and output formatting
* new lines. Why not?
* finally
* legacy mode activate!
* Update protobufs (#17)
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
* better logic, fixed a test
* updated for packet signing
fixed a test
added guards for licensed/ham mode
* channel numbers
* beacon: encrypt on the beacon channel PSK; fix split note
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>
* test/beacon: drain toPhoneQueue in tearDown to fix LSan leak abort
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>
* legacy hop override for zero-hoppers
* ever more beacons
* beacon: comment out broadcast_send_as_node pending further review
Functionality preserved in comments with full signing/has_bitfield notes
for when it is re-enabled. Proto tag 3 retained on the wire.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test/beacon: fix fromIsCustomNodeWhenSet now that send-as-node is disabled
broadcast_send_as_node is commented out; from is always the local node.
Update the test assertion and doc comment to match current behaviour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update protobufs (#21)
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
* flags for beacons
* beacon: do more with less — slot-index targets + validation
Multi-target beacons embedded a full ChannelSettings in every BroadcastTarget,
blowing ModuleConfig past the 512-byte BLE FromRadio budget so the firmware would
not compile. Targets now reference an existing channel-table slot by channel_index
and the broadcaster resolves it via channels.getByIndex() at TX time. Net effect:
the same multi-target capability for a fraction of the bytes —
FromRadio 609 -> 510 B, MeshBeaconConfig 596 -> 324 B, AdminMessage 615 -> 511 B.
- proto: BroadcastTarget.channel (embedded) -> channel_index (uint32 ref); regen all
generated headers (size constants propagate to admin/localonly/deviceonly/mesh).
- broadcaster: resolve channel_index from the channel table; an out-of-range or blank
slot falls back to the default channel for the target preset rather than borrowing
the primary's name/PSK.
- AdminModule: validate broadcast_targets entries on write (region/preset sanitised
like the single-target fields; channel_index range-checked).
- userPrefs: TARGET_<n>_CHANNEL_{NAME,NUM,PSK} collapse to a single CHANNEL_INDEX.
- docs: two-step (set_channel -> set_module_config) multi-target setup, inline-vs-
reference distinction, and single-/multi-target are equal (not "legacy") options.
- tests: target validation + channel-index resolution incl. blank-slot fallback
(47/47 green on `./bin/run-tests.sh -e native -f test_mesh_beacon`).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRAF5csgsMn6p1zEcFL8Qz
* throttling after reboot
* address copilot review
* simplify
* fix(beacon): use 0x%08x for node/packet IDs in logs; register test suite
The %#08lx log specifiers passed uint32_t (NodeNum/PacketId) to a %lx
length modifier — undefined behaviour on 64-bit (native test) targets and
non-standard width. Switch to the project-standard 0x%08x. Also bump
test/native-suite-count to 25 for the added test_mesh_beacon suite.
clod helped too
* copilot & clarity
clod helped too
* refactor(beacon): use auto for the sanitized config copy
clod helped too
* fix(beacon): guard empty-payload sends; gate has_mesh_beacon on build flag; document ISR_TX pre-switch
clod helped too
---------
Co-authored-by: Steve Gilberd <steve@erayd.net>
Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
280 lines
8.9 KiB
C++
280 lines
8.9 KiB
C++
#include "configuration.h"
|
|
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
|
|
#include "buzz/BuzzerFeedbackThread.h"
|
|
#include "modules/SystemCommandsModule.h"
|
|
#endif
|
|
#include "modules/StatusLEDModule.h"
|
|
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
#include "ReplyBotModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_PKI
|
|
#include "KeyVerificationModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_ADMIN
|
|
#include "modules/AdminModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_ATAK
|
|
#include "modules/AtakPluginModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_CANNEDMESSAGES
|
|
#include "modules/CannedMessageModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR
|
|
#include "modules/DetectionSensorModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_NEIGHBORINFO
|
|
#include "modules/NeighborInfoModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_NODEINFO
|
|
#include "modules/NodeInfoModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_BEACON
|
|
#include "modules/MeshBeaconModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
|
#include "modules/PositionModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
|
|
#include "modules/RemoteHardwareModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
|
|
#include "modules/PowerStressModule.h"
|
|
#endif
|
|
#include "modules/RoutingModule.h"
|
|
#if HAS_TRAFFIC_MANAGEMENT && !MESHTASTIC_EXCLUDE_TRAFFIC_MANAGEMENT
|
|
#include "modules/TrafficManagementModule.h"
|
|
#endif
|
|
#if HAS_VARIABLE_HOPS
|
|
#include "modules/HopScalingModule.h"
|
|
#endif
|
|
#include "modules/TextMessageModule.h"
|
|
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
|
#include "modules/TraceRouteModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_WAYPOINT
|
|
#include "modules/WaypointModule.h"
|
|
#endif
|
|
#if ARCH_PORTDUINO
|
|
#include "modules/Telemetry/HostMetrics.h"
|
|
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
|
|
#include "modules/StoreForwardModule.h"
|
|
#endif
|
|
#endif
|
|
#if HAS_TELEMETRY
|
|
#include "modules/Telemetry/DeviceTelemetry.h"
|
|
#endif
|
|
#if HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
|
#include "main.h"
|
|
#include "modules/Telemetry/EnvironmentTelemetry.h"
|
|
#include "modules/Telemetry/HealthTelemetry.h"
|
|
#include "modules/Telemetry/Sensor/TelemetrySensor.h"
|
|
#endif
|
|
#if HAS_SENSOR && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
|
#include "main.h"
|
|
#include "modules/Telemetry/AirQualityTelemetry.h"
|
|
#include "modules/Telemetry/Sensor/TelemetrySensor.h"
|
|
#endif
|
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY
|
|
#include "modules/Telemetry/PowerTelemetry.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE
|
|
#include "modules/GenericThreadModule.h"
|
|
#endif
|
|
|
|
#ifdef ARCH_ESP32
|
|
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
|
#include "modules/esp32/AudioModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
|
#include "modules/esp32/PaxcounterModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
|
|
#include "modules/StoreForwardModule.h"
|
|
#endif
|
|
#endif
|
|
|
|
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
|
|
#include "modules/ExternalNotificationModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS
|
|
#include "modules/RangeTestModule.h"
|
|
#endif
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_SERIAL
|
|
#include "modules/SerialModule.h"
|
|
#endif
|
|
|
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
|
#include "modules/DropzoneModule.h"
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_STATUS
|
|
#include "modules/StatusMessageModule.h"
|
|
#endif
|
|
|
|
#if defined(HAS_HARDWARE_WATCHDOG)
|
|
#include "watchdog/watchdogThread.h"
|
|
#endif
|
|
/**
|
|
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
|
|
*/
|
|
void setupModules()
|
|
{
|
|
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
|
inputBroker = new InputBroker();
|
|
systemCommandsModule = new SystemCommandsModule();
|
|
buzzerFeedbackThread = new BuzzerFeedbackThread();
|
|
}
|
|
#endif
|
|
statusLEDModule = new StatusLEDModule();
|
|
#if !MESHTASTIC_EXCLUDE_REPLYBOT
|
|
new ReplyBotModule();
|
|
#endif
|
|
|
|
#if HAS_TRAFFIC_MANAGEMENT && !MESHTASTIC_EXCLUDE_TRAFFIC_MANAGEMENT
|
|
if (moduleConfig.has_traffic_management) {
|
|
trafficManagementModule = new TrafficManagementModule();
|
|
}
|
|
#endif
|
|
|
|
#if HAS_VARIABLE_HOPS
|
|
hopScalingModule = new HopScalingModule();
|
|
#endif
|
|
|
|
#if !MESHTASTIC_EXCLUDE_ADMIN
|
|
adminModule = new AdminModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_NODEINFO
|
|
nodeInfoModule = new NodeInfoModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_BEACON
|
|
meshBeaconBroadcastModule = new MeshBeaconBroadcastModule();
|
|
meshBeaconListenerModule = new MeshBeaconListenerModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_GPS
|
|
positionModule = new PositionModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_WAYPOINT
|
|
waypointModule = new WaypointModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_TEXTMESSAGE
|
|
textMessageModule = new TextMessageModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
|
traceRouteModule = new TraceRouteModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_NEIGHBORINFO
|
|
if (moduleConfig.has_neighbor_info && moduleConfig.neighbor_info.enabled) {
|
|
neighborInfoModule = new NeighborInfoModule();
|
|
}
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_DETECTIONSENSOR
|
|
if (moduleConfig.has_detection_sensor && moduleConfig.detection_sensor.enabled) {
|
|
detectionSensorModule = new DetectionSensorModule();
|
|
}
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_ATAK
|
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK ||
|
|
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) {
|
|
atakPluginModule = new AtakPluginModule();
|
|
}
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_PKI
|
|
keyVerificationModule = new KeyVerificationModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
|
dropzoneModule = new DropzoneModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_STATUS
|
|
statusMessageModule = new StatusMessageModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE
|
|
new GenericThreadModule();
|
|
#endif
|
|
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
|
|
// to a global variable.
|
|
|
|
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
|
|
new RemoteHardwareModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
|
|
new PowerStressModule();
|
|
#endif
|
|
// Example: Put your module here
|
|
// new ReplyModule();
|
|
#if HAS_SCREEN && !MESHTASTIC_EXCLUDE_CANNEDMESSAGES
|
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
|
cannedMessageModule = new CannedMessageModule();
|
|
}
|
|
#endif
|
|
#if ARCH_PORTDUINO
|
|
new HostMetricsModule();
|
|
#endif
|
|
#if HAS_TELEMETRY
|
|
new DeviceTelemetryModule();
|
|
#endif
|
|
#if HAS_TELEMETRY && HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
|
if (moduleConfig.has_telemetry &&
|
|
(moduleConfig.telemetry.environment_measurement_enabled || moduleConfig.telemetry.environment_screen_enabled)) {
|
|
new EnvironmentTelemetryModule();
|
|
}
|
|
#if HAS_TELEMETRY && HAS_SENSOR && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
|
if (moduleConfig.has_telemetry &&
|
|
(moduleConfig.telemetry.air_quality_enabled || moduleConfig.telemetry.air_quality_screen_enabled)) {
|
|
new AirQualityTelemetryModule();
|
|
}
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY
|
|
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 ||
|
|
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) {
|
|
new HealthTelemetryModule();
|
|
}
|
|
#endif
|
|
#endif
|
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_POWER_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
|
if (moduleConfig.has_telemetry &&
|
|
(moduleConfig.telemetry.power_measurement_enabled || moduleConfig.telemetry.power_screen_enabled)) {
|
|
new PowerTelemetryModule();
|
|
}
|
|
#endif
|
|
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)) && \
|
|
!defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
|
#if !MESHTASTIC_EXCLUDE_SERIAL
|
|
if (moduleConfig.has_serial && moduleConfig.serial.enabled &&
|
|
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
|
new SerialModule();
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef ARCH_ESP32
|
|
// Only run on an esp32 based device.
|
|
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
|
audioModule = new AudioModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
|
if (moduleConfig.has_paxcounter && moduleConfig.paxcounter.enabled) {
|
|
paxcounterModule = new PaxcounterModule();
|
|
}
|
|
#endif
|
|
#endif
|
|
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
|
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
|
|
if (moduleConfig.has_store_forward && moduleConfig.store_forward.enabled) {
|
|
storeForwardModule = new StoreForwardModule();
|
|
}
|
|
#endif
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
|
|
externalNotificationModule = new ExternalNotificationModule();
|
|
#endif
|
|
#if !MESHTASTIC_EXCLUDE_RANGETEST && !MESHTASTIC_EXCLUDE_GPS
|
|
if (moduleConfig.has_range_test && moduleConfig.range_test.enabled)
|
|
new RangeTestModule();
|
|
#endif
|
|
#if defined(HAS_HARDWARE_WATCHDOG)
|
|
watchdogThread = new WatchdogThread();
|
|
#endif
|
|
// NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra
|
|
// acks
|
|
routingModule = new RoutingModule();
|
|
}
|