diff --git a/protobufs b/protobufs index aca181b97b..7b2464c9b8 160000 --- a/protobufs +++ b/protobufs @@ -1 +1 @@ -Subproject commit aca181b97b7db047d76e9f000220a11a234cd389 +Subproject commit 7b2464c9b8c1521f93852261e4123826e5b25e11 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index cf1f19cb99..f6bf9829d9 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -1514,30 +1514,14 @@ void NodeDB::installDefaultModuleConfig() memcpy(moduleConfig.mesh_beacon.broadcast_offer_channel.psk.bytes, beaconOfferPsk, sizeof(beaconOfferPsk)); moduleConfig.mesh_beacon.broadcast_offer_channel.psk.size = sizeof(beaconOfferPsk); #endif -#ifdef USERPREFS_MESH_BEACON_ON_PRESET - moduleConfig.mesh_beacon.has_broadcast_on_preset = true; - moduleConfig.mesh_beacon.broadcast_on_preset = USERPREFS_MESH_BEACON_ON_PRESET; -#endif -#ifdef USERPREFS_MESH_BEACON_ON_REGION - moduleConfig.mesh_beacon.broadcast_on_region = USERPREFS_MESH_BEACON_ON_REGION; -#endif -#ifdef USERPREFS_MESH_BEACON_ON_CHANNEL_NAME - moduleConfig.mesh_beacon.has_broadcast_on_channel = true; - strncpy(moduleConfig.mesh_beacon.broadcast_on_channel.name, USERPREFS_MESH_BEACON_ON_CHANNEL_NAME, - sizeof(moduleConfig.mesh_beacon.broadcast_on_channel.name) - 1); - moduleConfig.mesh_beacon.broadcast_on_channel.name[sizeof(moduleConfig.mesh_beacon.broadcast_on_channel.name) - 1] = '\0'; -#endif -#ifdef USERPREFS_MESH_BEACON_ON_CHANNEL_PSK - moduleConfig.mesh_beacon.has_broadcast_on_channel = true; - static const uint8_t beaconOnPsk[] = USERPREFS_MESH_BEACON_ON_CHANNEL_PSK; - static_assert(sizeof(beaconOnPsk) <= sizeof(moduleConfig.mesh_beacon.broadcast_on_channel.psk.bytes), - "USERPREFS_MESH_BEACON_ON_CHANNEL_PSK exceeds the 32-byte channel PSK buffer"); - memcpy(moduleConfig.mesh_beacon.broadcast_on_channel.psk.bytes, beaconOnPsk, sizeof(beaconOnPsk)); - moduleConfig.mesh_beacon.broadcast_on_channel.psk.size = sizeof(beaconOnPsk); -#endif -#ifdef USERPREFS_MESH_BEACON_ON_CHANNEL_NUM - moduleConfig.mesh_beacon.has_broadcast_on_channel = true; - moduleConfig.mesh_beacon.broadcast_on_channel.channel_num = USERPREFS_MESH_BEACON_ON_CHANNEL_NUM; +// The USERPREFS_MESH_BEACON_ON_* keys were removed with the broadcast_on_* config fields. Fail the +// build rather than silently dropping a preconfigured beacon channel: define the equivalent +// USERPREFS_MESH_BEACON_TARGET_0_{PRESET,REGION,CHANNEL_INDEX} keys instead. CHANNEL_INDEX names a +// slot in the device's channel table, so the channel must also be provisioned on the node. +#if defined(USERPREFS_MESH_BEACON_ON_PRESET) || defined(USERPREFS_MESH_BEACON_ON_REGION) || \ + defined(USERPREFS_MESH_BEACON_ON_CHANNEL_NAME) || defined(USERPREFS_MESH_BEACON_ON_CHANNEL_PSK) || \ + defined(USERPREFS_MESH_BEACON_ON_CHANNEL_NUM) +#error "USERPREFS_MESH_BEACON_ON_* removed; use USERPREFS_MESH_BEACON_TARGET_0_* (channel must be in the channel table)" #endif #ifdef USERPREFS_MESH_BEACON_LEGACY_SPLIT BEACON_APPLY_FLAG(USERPREFS_MESH_BEACON_LEGACY_SPLIT, meshtastic_ModuleConfig_MeshBeaconConfig_Flags_FLAG_LEGACY_SPLIT); diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index fdffd0c260..f6757206c5 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -920,9 +920,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) #ifdef MESHTASTIC_PHONEAPI_ACCESS_CONTROL if (!getAdminAuthorized()) { // Unauthenticated: emit an empty MeshBeaconConfig (zero-init from - // the top-of-loop memset). The embedded ChannelSettings - // (broadcast_offer_channel / broadcast_on_channel) carry PSKs that - // must not be visible to an unauth client. + // the top-of-loop memset). The embedded broadcast_offer_channel + // ChannelSettings carries a PSK that must not be visible to an + // unauth client. } else #endif { diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h index 51e43526e0..ea6286fac5 100644 --- a/src/mesh/generated/meshtastic/deviceonly.pb.h +++ b/src/mesh/generated/meshtastic/deviceonly.pb.h @@ -455,7 +455,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg; /* Maximum encoded size of messages (where known) */ /* meshtastic_NodeDatabase_size depends on runtime parameters */ #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size -#define meshtastic_BackupPreferences_size 2740 +#define meshtastic_BackupPreferences_size 2656 #define meshtastic_ChannelFile_size 718 #define meshtastic_DeviceState_size 1944 #define meshtastic_NodeEnvironmentEntry_size 231 diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h index c560d5447e..35a0d43d55 100644 --- a/src/mesh/generated/meshtastic/localonly.pb.h +++ b/src/mesh/generated/meshtastic/localonly.pb.h @@ -212,7 +212,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; /* Maximum encoded size of messages (where known) */ #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size #define meshtastic_LocalConfig_size 759 -#define meshtastic_LocalModuleConfig_size 1126 +#define meshtastic_LocalModuleConfig_size 1042 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h index c59001f105..375ff4861b 100644 --- a/src/mesh/generated/meshtastic/mesh.pb.h +++ b/src/mesh/generated/meshtastic/mesh.pb.h @@ -341,6 +341,10 @@ typedef enum _meshtastic_HardwareModel { meshtastic_HardwareModel_HELTEC_RCC6 = 143, /* Seeed Wio Tracker L1 Pro 1W, nRF52840 + SX1262 with 1 W external PA */ meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_PRO_1W = 144, + /* Meshnology W12 */ + meshtastic_HardwareModel_MESHNOLOGY_W12 = 145, + /* Seeed Studio MeshPager X2 */ + meshtastic_HardwareModel_MESHPAGER_X2 = 146, /* ------------------------------------------------------------------------------------------------------------------------------------------ Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. ------------------------------------------------------------------------------------------------------------------------------------------ */ @@ -738,7 +742,7 @@ typedef struct _meshtastic_Position { multiplied with DOP to calculate positional accuracy Default: "'bout three meters-ish" :) */ uint32_t gps_accuracy; - /* Ground speed in m/s and True North TRACK in 1/100 degrees + /* Ground speed in km/h and True North TRACK in 1/100 degrees Clarification of terms: - "track" is the direction of motion (measured in horizontal plane) - "heading" is where the fuselage points (measured in horizontal plane) @@ -1273,15 +1277,15 @@ typedef struct _meshtastic_LockdownStatus { /* Current lockdown state being reported. */ meshtastic_LockdownStatus_State state; /* For LOCKED: machine-readable reason. Known values: - "needs_auth" — storage already unlocked, client must auth - "token_missing" — no boot token on flash - "token_expired" — boot token wall-clock TTL elapsed - "token_boots_zero" — boot token boot-count TTL exhausted - "token_hmac_fail" — token tampered or wrong device - "token_dek_fail" — token DEK decrypt failed - "token_wrong_size" — token file corrupted - "token_bad_magic" — token file corrupted - "not_provisioned" — should generally use NEEDS_PROVISION state instead + "needs_auth" - storage already unlocked, client must auth + "token_missing" - no boot token on flash + "token_expired" - boot token wall-clock TTL elapsed + "token_boots_zero" - boot token boot-count TTL exhausted + "token_hmac_fail" - token tampered or wrong device + "token_dek_fail" - token DEK decrypt failed + "token_wrong_size" - token file corrupted + "token_bad_magic" - token file corrupted + "not_provisioned" - should generally use NEEDS_PROVISION state instead Other values may be added; clients should treat unknown values as "locked, ask for passphrase". */ char lock_reason[32]; diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h index b04c358fc4..5d6585f036 100644 --- a/src/mesh/generated/meshtastic/module_config.pb.h +++ b/src/mesh/generated/meshtastic/module_config.pb.h @@ -457,8 +457,8 @@ typedef struct _meshtastic_ModuleConfig_StatusMessageConfig { char node_status[80]; } meshtastic_ModuleConfig_StatusMessageConfig; -/* One entry in the multi-target broadcast list. - The broadcaster transmits one beacon copy per entry, each on its own radio settings. */ +/* One entry in the broadcast destination list. + Each entry names one set of radio settings to send a beacon copy on. */ typedef struct _meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget { /* Modem preset to use for this target. Falls back to the running config preset if unset. */ @@ -478,12 +478,6 @@ typedef struct _meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget { typedef struct _meshtastic_ModuleConfig_MeshBeaconConfig { /* Bitwise-OR of Flags values (listen / broadcast / legacy-split toggles). */ uint32_t flags; - /* Optional: node ID to send beacon messages AS. - When set, the `from` field of outgoing beacon packets is set to this node ID, - making beacons appear to originate from that node. - When unset (0), beacons are sent as the local node. - A remote admin can only set this field to their own node ID. */ - uint32_t broadcast_send_as_node; /* Message to include in each beacon broadcast. Max 100 bytes enforced by firmware. */ char broadcast_message[101]; /* Optional channel (name + PSK) to advertise in the MeshBeacon offer_channel field. */ @@ -494,30 +488,15 @@ typedef struct _meshtastic_ModuleConfig_MeshBeaconConfig { /* Optional modem preset to advertise in the MeshBeacon offer_preset field. */ bool has_broadcast_offer_preset; meshtastic_Config_LoRaConfig_ModemPreset broadcast_offer_preset; - /* Single-target TX channel: channel settings (name + PSK) to send beacons on. - If unset, beacons go out on the primary channel. Used only when broadcast_targets is empty. - NOTE: the single-target path embeds the ChannelSettings inline here, whereas a - broadcast_targets entry references a channel-table slot by channel_index instead — see - BroadcastTarget. The two paths are equal, first-class options; only this representation differs. */ - bool has_broadcast_on_channel; - meshtastic_ChannelSettings broadcast_on_channel; - /* Region to use when sending beacons on broadcast_on_preset. */ - meshtastic_Config_LoRaConfig_RegionCode broadcast_on_region; - /* Modem preset to use when sending beacons. - If different from current config, the radio is temporarily switched for TX. */ - bool has_broadcast_on_preset; - meshtastic_Config_LoRaConfig_ModemPreset broadcast_on_preset; /* How often to broadcast, in seconds. Min 3600 (1 h), default 3600. */ uint32_t broadcast_interval_secs; - /* Multi-target broadcast list. - When non-empty the broadcaster transmits one beacon copy per entry in sequence, - each temporarily switching the radio to that entry's preset/region/channel. - When empty, the broadcaster uses the scalar broadcast_on_preset / broadcast_on_region / - broadcast_on_channel fields instead (the single-target path). - Single- and multi-target are equal, first-class options — neither is preferred or - deprecated. They differ only in how the TX channel is named: broadcast_on_channel embeds a - ChannelSettings inline, while a target references an existing channel-table slot by - channel_index (see BroadcastTarget). */ + /* Broadcast destination list. + The broadcaster sends one beacon copy per distinct destination, in sequence, temporarily + switching the radio to that entry's preset/region/channel for each. + When empty, a single beacon is sent on the node's running preset and region over the + primary channel. + Entries that resolve to the same effective preset, region and channel are deduplicated, so + a duplicate entry does not produce a second transmission. */ pb_size_t broadcast_targets_count; meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget broadcast_targets[4]; } meshtastic_ModuleConfig_MeshBeaconConfig; @@ -654,8 +633,6 @@ extern "C" { #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_region_ENUMTYPE meshtastic_Config_LoRaConfig_RegionCode #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_preset_ENUMTYPE meshtastic_Config_LoRaConfig_ModemPreset -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_region_ENUMTYPE meshtastic_Config_LoRaConfig_RegionCode -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_preset_ENUMTYPE meshtastic_Config_LoRaConfig_ModemPreset #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_preset_ENUMTYPE meshtastic_Config_LoRaConfig_ModemPreset #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_region_ENUMTYPE meshtastic_Config_LoRaConfig_RegionCode @@ -684,7 +661,7 @@ extern "C" { #define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StatusMessageConfig_init_default {""} -#define meshtastic_ModuleConfig_MeshBeaconConfig_init_default {0, 0, "", false, meshtastic_ChannelSettings_init_default, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, false, meshtastic_ChannelSettings_init_default, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, {meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default}} +#define meshtastic_ModuleConfig_MeshBeaconConfig_init_default {0, "", false, meshtastic_ChannelSettings_init_default, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, {meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default}} #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_default {false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, 0} #define meshtastic_ModuleConfig_TAKConfig_init_default {_meshtastic_Team_MIN, _meshtastic_MemberRole_MIN} #define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN} @@ -705,7 +682,7 @@ extern "C" { #define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_AmbientLightingConfig_init_zero {0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StatusMessageConfig_init_zero {""} -#define meshtastic_ModuleConfig_MeshBeaconConfig_init_zero {0, 0, "", false, meshtastic_ChannelSettings_init_zero, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, false, meshtastic_ChannelSettings_init_zero, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, {meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero}} +#define meshtastic_ModuleConfig_MeshBeaconConfig_init_zero {0, "", false, meshtastic_ChannelSettings_init_zero, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, {meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero, meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero}} #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_init_zero {false, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, _meshtastic_Config_LoRaConfig_RegionCode_MIN, false, 0} #define meshtastic_ModuleConfig_TAKConfig_init_zero {_meshtastic_Team_MIN, _meshtastic_MemberRole_MIN} #define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN} @@ -821,14 +798,10 @@ extern "C" { #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_region_tag 2 #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_channel_index_tag 4 #define meshtastic_ModuleConfig_MeshBeaconConfig_flags_tag 1 -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_send_as_node_tag 3 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_message_tag 4 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_channel_tag 5 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_region_tag 6 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_preset_tag 7 -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_channel_tag 8 -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_region_tag 9 -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_preset_tag 10 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_interval_secs_tag 11 #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_targets_tag 13 #define meshtastic_ModuleConfig_TAKConfig_team_tag 1 @@ -1073,20 +1046,15 @@ X(a, STATIC, SINGULAR, STRING, node_status, 1) #define meshtastic_ModuleConfig_MeshBeaconConfig_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, flags, 1) \ -X(a, STATIC, SINGULAR, UINT32, broadcast_send_as_node, 3) \ X(a, STATIC, SINGULAR, STRING, broadcast_message, 4) \ X(a, STATIC, OPTIONAL, MESSAGE, broadcast_offer_channel, 5) \ X(a, STATIC, SINGULAR, UENUM, broadcast_offer_region, 6) \ X(a, STATIC, OPTIONAL, UENUM, broadcast_offer_preset, 7) \ -X(a, STATIC, OPTIONAL, MESSAGE, broadcast_on_channel, 8) \ -X(a, STATIC, SINGULAR, UENUM, broadcast_on_region, 9) \ -X(a, STATIC, OPTIONAL, UENUM, broadcast_on_preset, 10) \ X(a, STATIC, SINGULAR, UINT32, broadcast_interval_secs, 11) \ X(a, STATIC, REPEATED, MESSAGE, broadcast_targets, 13) #define meshtastic_ModuleConfig_MeshBeaconConfig_CALLBACK NULL #define meshtastic_ModuleConfig_MeshBeaconConfig_DEFAULT NULL #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_offer_channel_MSGTYPE meshtastic_ChannelSettings -#define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_on_channel_MSGTYPE meshtastic_ChannelSettings #define meshtastic_ModuleConfig_MeshBeaconConfig_broadcast_targets_MSGTYPE meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_FIELDLIST(X, a) \ @@ -1164,7 +1132,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_MQTTConfig_size 224 #define meshtastic_ModuleConfig_MapReportSettings_size 14 #define meshtastic_ModuleConfig_MeshBeaconConfig_BroadcastTarget_size 10 -#define meshtastic_ModuleConfig_MeshBeaconConfig_size 324 +#define meshtastic_ModuleConfig_MeshBeaconConfig_size 240 #define meshtastic_ModuleConfig_NeighborInfoConfig_size 10 #define meshtastic_ModuleConfig_PaxcounterConfig_size 30 #define meshtastic_ModuleConfig_RangeTestConfig_size 12 @@ -1175,7 +1143,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg; #define meshtastic_ModuleConfig_TAKConfig_size 4 #define meshtastic_ModuleConfig_TelemetryConfig_size 50 #define meshtastic_ModuleConfig_TrafficManagementConfig_size 30 -#define meshtastic_ModuleConfig_size 328 +#define meshtastic_ModuleConfig_size 244 #define meshtastic_RemoteHardwarePin_size 21 #ifdef __cplusplus diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index 0af7ed9e05..07d55b7483 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -343,17 +343,6 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta case meshtastic_AdminMessage_set_module_config_tag: LOG_DEBUG("Client set module config"); -#if !MESHTASTIC_EXCLUDE_BEACON - // broadcast_send_as_node: remote admins may only set this to their own node ID. - if (mp.from != 0 && r->set_module_config.which_payload_variant == meshtastic_ModuleConfig_mesh_beacon_tag) { - auto &b = r->set_module_config.payload_variant.mesh_beacon; - if (b.broadcast_send_as_node != 0 && b.broadcast_send_as_node != mp.from) { - LOG_WARN("Beacon: rejecting broadcast_send_as_node 0x%08x from node 0x%08x (must match sender)", - b.broadcast_send_as_node, mp.from); - b.broadcast_send_as_node = moduleConfig.mesh_beacon.broadcast_send_as_node; - } - } -#endif if (!handleSetModuleConfig(r->set_module_config)) { myReply = allocErrorResponse(meshtastic_Routing_Error_BAD_REQUEST, &mp); } @@ -1371,19 +1360,6 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) if (beaconCfg.broadcast_interval_secs != 0 && beaconCfg.broadcast_interval_secs < default_mesh_beacon_min_broadcast_interval_secs) beaconCfg.broadcast_interval_secs = default_mesh_beacon_min_broadcast_interval_secs; - // Validate broadcast_on_preset against broadcast_on_region (or current region if unset). - if (beaconCfg.has_broadcast_on_preset) { - meshtastic_Config_LoRaConfig probe = config.lora; - probe.use_preset = true; - probe.modem_preset = beaconCfg.broadcast_on_preset; - if (beaconCfg.broadcast_on_region != meshtastic_Config_LoRaConfig_RegionCode_UNSET) - probe.region = beaconCfg.broadcast_on_region; - if (!RadioInterface::validateConfigLora(probe)) { - LOG_WARN("Beacon: broadcast_on_preset %d invalid for region, clearing", beaconCfg.broadcast_on_preset); - beaconCfg.has_broadcast_on_preset = false; - beaconCfg.has_broadcast_on_channel = false; - } - } // Validate broadcast_offer_preset against broadcast_offer_region (or current region if unset). if (beaconCfg.has_broadcast_offer_preset) { meshtastic_Config_LoRaConfig probe = config.lora; @@ -1404,8 +1380,8 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c) beaconCfg.broadcast_offer_region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; } } - // Validate each multi-target entry the same way as the single-target broadcast_on_* fields, - // so a bad preset/region is cleared on write rather than relying on the runtime TX drop. + // Validate each broadcast target so a bad preset/region is cleared on write rather than + // relying on the runtime TX drop. for (pb_size_t i = 0; i < beaconCfg.broadcast_targets_count; i++) { auto &t = beaconCfg.broadcast_targets[i]; // Region must be a known region code (UNSET = use running config at TX time). diff --git a/src/modules/MeshBeaconModule.cpp b/src/modules/MeshBeaconModule.cpp index 1976f054d7..9842de2549 100644 --- a/src/modules/MeshBeaconModule.cpp +++ b/src/modules/MeshBeaconModule.cpp @@ -417,22 +417,6 @@ void MeshBeaconBroadcastModule::sendBeacon() const auto stampPacket = [&](meshtastic_MeshPacket *p) { p->to = NODENUM_BROADCAST; p->from = nodeDB->getNodeNum(); - // broadcast_send_as_node: commented out pending further review. - // Spoof notes preserved for when this is re-enabled: - // broadcast_send_as_node overrides the source NodeNum. NOTE: this is a *node-ID* spoof - // only - it rewrites the 'from' field but does NOT forge any signature. Once 'from' is - // not us, the packet is no longer isFromUs(), so Router::perhapsEncode() skips XEdDSA - // signing and receivers get an unsigned packet attributed to another node. - // When broadcast_send_as_node == 0 the beacon is genuinely from us and Router::perhapsEncode() - // signs it under the same XEdDSA broadcast policy as normal channel messages. - // When broadcast_send_as_node rewrites p->from, perhapsEncode() sees isFromUs()=false and - // skips setting has_bitfield - must be set explicitly so receivers can classify hop_start - // correctly and so ok_to_mqtt is honoured on the spoofed packet. - // if (bcfg.broadcast_send_as_node != 0) { - // p->from = bcfg.broadcast_send_as_node; - // p->decoded.has_bitfield = true; - // p->decoded.bitfield |= (config.lora.config_ok_to_mqtt << BITFIELD_OK_TO_MQTT_SHIFT); - // } p->hop_limit = 0; // all beacon packets are zero hopped to limit spamming. p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; p->want_ack = false; @@ -476,10 +460,9 @@ void MeshBeaconBroadcastModule::sendBeacon() // ── Per-target loop ────────────────────────────────────────────────────── // - // If broadcast_targets is populated, iterate over those. Otherwise use the single-target - // broadcast_on_preset / broadcast_on_region / broadcast_on_channel fields. The two paths are - // equal options; they differ only in how the TX channel is named (single-target embeds a - // ChannelSettings inline; a target references a channel-table slot by channel_index). + // Every destination comes from broadcast_targets. An entry names its TX channel by + // channel_index, a slot in the device's channel table, so the channel must already be + // configured on the node - its key is needed to encrypt. struct EffTarget { meshtastic_Config_LoRaConfig_ModemPreset preset; uint16_t slot; @@ -488,8 +471,9 @@ void MeshBeaconBroadcastModule::sendBeacon() meshtastic_ChannelSettings channel; }; - const bool useTargetList = bcfg.broadcast_targets_count > 0; - const int targetCount = useTargetList ? (int)bcfg.broadcast_targets_count : 1; + // An empty list still beacons once, on the node's running preset and region over the primary + // channel. Each entry below overrides only what it sets. + const int targetCount = bcfg.broadcast_targets_count > 0 ? (int)bcfg.broadcast_targets_count : 1; // Dedup state: the beacon payload is identical across targets, so two targets that resolve to // the same effective radio config (preset + resolved region + channel) would just re-broadcast @@ -510,17 +494,19 @@ void MeshBeaconBroadcastModule::sendBeacon() }; for (int ti = 0; ti < targetCount; ti++) { + // Defaults: running radio config, primary channel. A target entry overrides from here. EffTarget tgt = {}; - if (useTargetList) { + tgt.preset = config.lora.modem_preset; + tgt.slot = config.lora.channel_num; + if (ti < (int)bcfg.broadcast_targets_count) { const auto &bt = bcfg.broadcast_targets[ti]; - tgt.preset = bt.has_preset ? bt.preset : config.lora.modem_preset; + if (bt.has_preset) + tgt.preset = bt.preset; tgt.region = bt.region; // Resolve the channel from the device's channel table by index. A slot is only usable // if it is actually configured (has a name or PSK - its key is needed to encrypt). An // out-of-range index, or a blank slot, falls back to the default channel for the target // preset (see beaconChannelSettings), exactly as an unset channel_index would. - tgt.has_channel = false; - tgt.slot = config.lora.channel_num; if (bt.has_channel_index) { if (bt.channel_index >= (uint32_t)channels.getNumChannels()) { LOG_WARN("Beacon: target %d channel_index %u out of range, use preset default", ti, bt.channel_index); @@ -535,13 +521,6 @@ void MeshBeaconBroadcastModule::sendBeacon() } } } - } else { - tgt.preset = bcfg.has_broadcast_on_preset ? bcfg.broadcast_on_preset : config.lora.modem_preset; - tgt.region = bcfg.broadcast_on_region; - tgt.has_channel = bcfg.has_broadcast_on_channel; - if (tgt.has_channel) - tgt.channel = bcfg.broadcast_on_channel; - tgt.slot = tgt.has_channel ? bcfg.broadcast_on_channel.channel_num : config.lora.channel_num; } // Skip a target whose effective radio config duplicates one already sent this cycle. diff --git a/src/modules/MeshBeaconModule.h b/src/modules/MeshBeaconModule.h index bed9e882c6..9ae6d4ddda 100644 --- a/src/modules/MeshBeaconModule.h +++ b/src/modules/MeshBeaconModule.h @@ -40,7 +40,7 @@ class MeshBeaconModule /** * Reconfigure the radio for beacon TX, or restore to original config if p is NULL. * Returns true if the radio was reconfigured (caller must re-run transmit delay for CCA). - * Driven by broadcast_on_preset / broadcast_on_channel from MeshBeaconConfig. + * Driven by the broadcast_targets entry associated with the packet. */ static bool reconfigureForBeaconTX(RadioInterface *iface, meshtastic_MeshPacket *p); @@ -77,7 +77,7 @@ class MeshBeaconModule protected: /** * Build the ChannelSettings the beacon transmits on: the base (primary) channel overlaid with - * any broadcast_on_channel overrides, defaulting an empty name to the target preset's display + * the target's channel-table slot, defaulting an empty name to the target preset's display * name. Shared by the encrypt-time channel swap and the radio-thread RF swap so the channel * key + hash are identical at both points. */ diff --git a/test/test_mesh_beacon/test_main.cpp b/test/test_mesh_beacon/test_main.cpp index 4c2ddad954..a7b5dbe202 100644 --- a/test/test_mesh_beacon/test_main.cpp +++ b/test/test_mesh_beacon/test_main.cpp @@ -205,13 +205,14 @@ static void test_adminValidation_turboPresetOnEU868_isCleared(void) meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; bcfg.flags |= MESH_BEACON_FLAG_BROADCAST_ENABLED; - bcfg.has_broadcast_on_preset = true; - bcfg.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO; + bcfg.broadcast_targets_count = 1; + bcfg.broadcast_targets[0].has_preset = true; + bcfg.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO; testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); TEST_ASSERT_TRUE(moduleConfig.has_mesh_beacon); - TEST_ASSERT_FALSE_MESSAGE(moduleConfig.mesh_beacon.has_broadcast_on_preset, "SHORT_TURBO must be cleared for EU_868"); + TEST_ASSERT_FALSE_MESSAGE(moduleConfig.mesh_beacon.broadcast_targets[0].has_preset, "SHORT_TURBO must be cleared for EU_868"); } /** @@ -223,12 +224,13 @@ static void test_adminValidation_longTurboPresetOnEU868_isCleared(void) resetConfig(); meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; - bcfg.has_broadcast_on_preset = true; - bcfg.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO; + bcfg.broadcast_targets_count = 1; + bcfg.broadcast_targets[0].has_preset = true; + bcfg.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO; testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); - TEST_ASSERT_FALSE(moduleConfig.mesh_beacon.has_broadcast_on_preset); + TEST_ASSERT_FALSE(moduleConfig.mesh_beacon.broadcast_targets[0].has_preset); } /** @@ -242,13 +244,14 @@ static void test_adminValidation_turboPresetOnUS_isAccepted(void) initRegion(); meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; - bcfg.has_broadcast_on_preset = true; - bcfg.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO; + bcfg.broadcast_targets_count = 1; + bcfg.broadcast_targets[0].has_preset = true; + bcfg.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO; testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); - TEST_ASSERT_TRUE(moduleConfig.mesh_beacon.has_broadcast_on_preset); - TEST_ASSERT_EQUAL(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO, moduleConfig.mesh_beacon.broadcast_on_preset); + TEST_ASSERT_TRUE(moduleConfig.mesh_beacon.broadcast_targets[0].has_preset); + TEST_ASSERT_EQUAL(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO, moduleConfig.mesh_beacon.broadcast_targets[0].preset); } /** @@ -260,13 +263,14 @@ static void test_adminValidation_mediumTurboPresetOnEU868_isCleared(void) resetConfig(); meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; - bcfg.has_broadcast_on_preset = true; - bcfg.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO; + bcfg.broadcast_targets_count = 1; + bcfg.broadcast_targets[0].has_preset = true; + bcfg.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO; testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); TEST_ASSERT_TRUE(moduleConfig.has_mesh_beacon); - TEST_ASSERT_FALSE(moduleConfig.mesh_beacon.has_broadcast_on_preset); + TEST_ASSERT_FALSE(moduleConfig.mesh_beacon.broadcast_targets[0].has_preset); } /** @@ -280,13 +284,15 @@ static void test_adminValidation_mediumTurboPresetOnUS_isAccepted(void) initRegion(); meshtastic_ModuleConfig_MeshBeaconConfig bcfg = meshtastic_ModuleConfig_MeshBeaconConfig_init_zero; - bcfg.has_broadcast_on_preset = true; - bcfg.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO; + bcfg.broadcast_targets_count = 1; + bcfg.broadcast_targets[0].has_preset = true; + bcfg.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO; testAdmin->handleSetModuleConfig(makeBeaconModuleConfig(bcfg)); - TEST_ASSERT_TRUE(moduleConfig.mesh_beacon.has_broadcast_on_preset); - TEST_ASSERT_EQUAL(meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO, moduleConfig.mesh_beacon.broadcast_on_preset); + TEST_ASSERT_TRUE(moduleConfig.mesh_beacon.broadcast_targets[0].has_preset); + TEST_ASSERT_EQUAL(meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_TURBO, + moduleConfig.mesh_beacon.broadcast_targets[0].preset); } /** @@ -324,8 +330,7 @@ static void test_adminValidation_validOfferRegion_isPreserved(void) /** * Verify an out-of-range region in a multi-target entry is sanitised to UNSET on write. - * Important because broadcast_targets entries are validated independently of the single-target - * broadcast_on_* fields, and an invalid enum must never reach the radio-switch path. + * Important because an invalid enum must never reach the radio-switch path. */ static void test_adminValidation_targetUnknownRegion_isCleared(void) { @@ -342,8 +347,8 @@ static void test_adminValidation_targetUnknownRegion_isCleared(void) } /** - * Verify a preset that is illegal for a multi-target entry's region clears that entry's preset - * (and its channel), matching the single-target broadcast_on_preset rule. + * Verify a preset that is illegal for a broadcast target's region clears that entry's preset + * and its channel. */ static void test_adminValidation_targetInvalidPresetForRegion_isCleared(void) { @@ -647,7 +652,7 @@ static void test_broadcaster_rebuildCache_idempotent(void) // =========================================================================== /** - * Verify the 'from' field defaults to the local node number when broadcast_send_as_node is 0. + * Verify the 'from' field is the local node number. * Important for correct source attribution in peer node tables that receive the beacon. */ static void test_broadcaster_sendBeacon_fromIsLocalNodeWhenUnset(void) @@ -655,7 +660,6 @@ static void test_broadcaster_sendBeacon_fromIsLocalNodeWhenUnset(void) resetConfig(); moduleConfig.has_mesh_beacon = true; moduleConfig.mesh_beacon.flags |= MESH_BEACON_FLAG_BROADCAST_ENABLED; - moduleConfig.mesh_beacon.broadcast_send_as_node = 0; strncpy(moduleConfig.mesh_beacon.broadcast_message, "from-local", sizeof(moduleConfig.mesh_beacon.broadcast_message) - 1); MeshBeaconBroadcastModuleTestShim bcast; @@ -665,27 +669,6 @@ static void test_broadcaster_sendBeacon_fromIsLocalNodeWhenUnset(void) TEST_ASSERT_EQUAL_UINT32(kLocalNode, mockRouter->sentPackets[0].from); } -/** - * Verify broadcast_send_as_node is currently disabled: 'from' is always the local node - * even when broadcast_send_as_node is set to a remote node number. - * (broadcast_send_as_node is commented out as "not suitable right now".) - */ -static void test_broadcaster_sendBeacon_fromIsCustomNodeWhenSet(void) -{ - resetConfig(); - moduleConfig.has_mesh_beacon = true; - moduleConfig.mesh_beacon.flags |= MESH_BEACON_FLAG_BROADCAST_ENABLED; - moduleConfig.mesh_beacon.broadcast_send_as_node = kRemoteNode; - strncpy(moduleConfig.mesh_beacon.broadcast_message, "from-remote", sizeof(moduleConfig.mesh_beacon.broadcast_message) - 1); - - MeshBeaconBroadcastModuleTestShim bcast; - bcast.sendBeacon(); - - TEST_ASSERT_EQUAL_UINT32(1, mockRouter->sentPackets.size()); - // broadcast_send_as_node is disabled; from is always the local node - TEST_ASSERT_EQUAL_UINT32(kLocalNode, mockRouter->sentPackets[0].from); -} - /** * Verify the 'to' field is always NODENUM_BROADCAST regardless of other settings. * Important because beacons are mesh-wide announcements and must never be addressed to a single peer. @@ -723,8 +706,8 @@ static void test_broadcaster_sendBeacon_usesBeaconPortnum(void) } /** - * Verify TEXT_MESSAGE_APP portnum is used when no offer content is present, even if - * broadcast_on_preset is set (that field governs which radio config to use for TX, not portnum). + * Verify TEXT_MESSAGE_APP portnum is used when no offer content is present, even if a + * broadcast target preset is set (that governs which radio config to use for TX, not portnum). * Important so standard clients display plain-text beacons without needing a MESH_BEACON_APP decoder. */ static void test_broadcaster_sendBeacon_fallsBackToTextMessagePortnum(void) @@ -733,9 +716,10 @@ static void test_broadcaster_sendBeacon_fallsBackToTextMessagePortnum(void) moduleConfig.has_mesh_beacon = true; const char *msg = "plain-text-beacon"; strncpy(moduleConfig.mesh_beacon.broadcast_message, msg, sizeof(moduleConfig.mesh_beacon.broadcast_message) - 1); - // broadcast_on_preset set, but no offer - should still be TEXT_MESSAGE_APP - moduleConfig.mesh_beacon.has_broadcast_on_preset = true; - moduleConfig.mesh_beacon.broadcast_on_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW; + // Target preset set, but no offer - should still be TEXT_MESSAGE_APP + moduleConfig.mesh_beacon.broadcast_targets_count = 1; + moduleConfig.mesh_beacon.broadcast_targets[0].has_preset = true; + moduleConfig.mesh_beacon.broadcast_targets[0].preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW; MeshBeaconBroadcastModuleTestShim bcast; bcast.sendBeacon(); @@ -1155,52 +1139,11 @@ static void test_broadcaster_legacySplit_secondPacketIsTextMessage(void) } // =========================================================================== -// Group 7: Beacon-channel PSK swap (broadcast_on_channel override) +// Group 7: Beacon-channel PSK swap (target channel-table slot) // =========================================================================== /** - * When broadcast_on_channel overrides the primary channel's name/PSK, the packet must be encrypted - * on the BEACON channel, not the primary. perhapsEncode keys off the primary slot, so sendBeaconPacket - * temporarily installs the beacon channel there for the send and restores it after. Verify both: the - * primary slot IS the beacon channel during send(), and it is restored afterwards (no leak). - */ -static void test_broadcaster_channelPskOverride_swapsBeaconChannelAndRestores(void) -{ - resetConfig(); - static const uint8_t homePsk[16] = {0xAA, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; - installTestPrimaryChannel("Home", homePsk, sizeof(homePsk)); - - moduleConfig.has_mesh_beacon = true; - moduleConfig.mesh_beacon.has_broadcast_offer_preset = true; // gives the beacon radio content to send - moduleConfig.mesh_beacon.broadcast_offer_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW; - moduleConfig.mesh_beacon.has_broadcast_on_channel = true; - strncpy(moduleConfig.mesh_beacon.broadcast_on_channel.name, "BeaconCh", - sizeof(moduleConfig.mesh_beacon.broadcast_on_channel.name) - 1); - static const uint8_t beaconPsk[16] = {0xBB, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}; - moduleConfig.mesh_beacon.broadcast_on_channel.psk.size = sizeof(beaconPsk); - memcpy(moduleConfig.mesh_beacon.broadcast_on_channel.psk.bytes, beaconPsk, sizeof(beaconPsk)); - - MeshBeaconBroadcastModuleTestShim bcast; - bcast.sendBeacon(); - - // During send(), the primary slot must hold the BEACON channel (so encryption uses its PSK). - TEST_ASSERT_TRUE_MESSAGE(mockRouter->primaryAtSend.size() >= 1, "expected at least one send"); - const meshtastic_ChannelSettings &atSend = mockRouter->primaryAtSend[0]; - TEST_ASSERT_EQUAL_STRING_MESSAGE("BeaconCh", atSend.name, "primary must be the beacon channel during send"); - TEST_ASSERT_EQUAL_UINT(sizeof(beaconPsk), atSend.psk.size); - TEST_ASSERT_EQUAL_UINT8_MESSAGE(0xBB, atSend.psk.bytes[0], "encryption must use the beacon channel PSK"); - - // After send(), the primary channel must be restored to the original (no leak into normal traffic). - const meshtastic_ChannelSettings &after = channels.getByIndex(channels.getPrimaryIndex()).settings; - TEST_ASSERT_EQUAL_STRING_MESSAGE("Home", after.name, "primary channel must be restored after send"); - TEST_ASSERT_EQUAL_UINT(sizeof(homePsk), after.psk.size); - TEST_ASSERT_EQUAL_UINT8(0xAA, after.psk.bytes[0]); -} - -/** - * Without a broadcast_on_channel override, the beacon must transmit on the primary channel unchanged + * With no target channel_index, the beacon must transmit on the primary channel unchanged * (no swap). Guards against the swap firing - and churning the channel table - when it isn't needed. */ static void test_broadcaster_noChannelOverride_doesNotSwapPrimary(void) @@ -1213,7 +1156,7 @@ static void test_broadcaster_noChannelOverride_doesNotSwapPrimary(void) moduleConfig.has_mesh_beacon = true; moduleConfig.mesh_beacon.has_broadcast_offer_preset = true; moduleConfig.mesh_beacon.broadcast_offer_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW; - // No broadcast_on_channel override. + // No target channel_index. MeshBeaconBroadcastModuleTestShim bcast; bcast.sendBeacon(); @@ -1249,10 +1192,15 @@ static void test_broadcaster_targetChannelIndex_usesTableSlot(void) bcast.sendBeacon(); TEST_ASSERT_TRUE_MESSAGE(mockRouter->primaryAtSend.size() >= 1, "expected at least one send"); - TEST_ASSERT_EQUAL_STRING_MESSAGE("BeaconNet", mockRouter->primaryAtSend[0].name, - "beacon must be encrypted on the referenced slot's channel"); - // Primary slot restored to home after send (no leak). - TEST_ASSERT_EQUAL_STRING("Home", channels.getByIndex(channels.getPrimaryIndex()).settings.name); + const meshtastic_ChannelSettings &atSend = mockRouter->primaryAtSend[0]; + TEST_ASSERT_EQUAL_STRING_MESSAGE("BeaconNet", atSend.name, "beacon must be encrypted on the referenced slot's channel"); + TEST_ASSERT_EQUAL_UINT(sizeof(beaconPsk), atSend.psk.size); + TEST_ASSERT_EQUAL_UINT8_MESSAGE(0xBB, atSend.psk.bytes[0], "encryption must use the slot's PSK"); + // Primary slot restored to home after send (no leak into normal traffic). + const meshtastic_ChannelSettings &after = channels.getByIndex(channels.getPrimaryIndex()).settings; + TEST_ASSERT_EQUAL_STRING_MESSAGE("Home", after.name, "primary channel must be restored after send"); + TEST_ASSERT_EQUAL_UINT(sizeof(homePsk), after.psk.size); + TEST_ASSERT_EQUAL_UINT8(0xAA, after.psk.bytes[0]); } /** @@ -1753,7 +1701,6 @@ BEACON_TEST_ENTRY void setup() printf("\n=== Broadcaster sendBeacon ===\n"); RUN_TEST(test_broadcaster_sendBeacon_fromIsLocalNodeWhenUnset); - RUN_TEST(test_broadcaster_sendBeacon_fromIsCustomNodeWhenSet); RUN_TEST(test_broadcaster_sendBeacon_addressedToBroadcast); RUN_TEST(test_broadcaster_sendBeacon_usesBeaconPortnum); RUN_TEST(test_broadcaster_sendBeacon_fallsBackToTextMessagePortnum); @@ -1783,7 +1730,6 @@ BEACON_TEST_ENTRY void setup() printf("\n=== Beacon-channel PSK swap ===\n"); - RUN_TEST(test_broadcaster_channelPskOverride_swapsBeaconChannelAndRestores); RUN_TEST(test_broadcaster_noChannelOverride_doesNotSwapPrimary); RUN_TEST(test_broadcaster_targetChannelIndex_usesTableSlot); RUN_TEST(test_broadcaster_targetChannelIndex_blankSlotFallsBackToPreset); diff --git a/userPrefs.jsonc b/userPrefs.jsonc index 8daddd8f21..f50bbaf0f8 100644 --- a/userPrefs.jsonc +++ b/userPrefs.jsonc @@ -96,15 +96,11 @@ // "USERPREFS_MESH_BEACON_OFFER_REGION": "meshtastic_Config_LoRaConfig_RegionCode_EU_N_868", // Region advertised in the beacon payload // "USERPREFS_MESH_BEACON_OFFER_CHANNEL_NAME": "'MyChannel'", // Channel name advertised in the beacon payload // "USERPREFS_MESH_BEACON_OFFER_CHANNEL_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }", // PSK for the offered channel (32-byte AES-256) - // "USERPREFS_MESH_BEACON_ON_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST", // Modem preset to use when transmitting beacons (radio temporarily switched for TX) - // "USERPREFS_MESH_BEACON_ON_REGION": "meshtastic_Config_LoRaConfig_RegionCode_EU_868", // Region to use when transmitting beacons - // "USERPREFS_MESH_BEACON_ON_CHANNEL_NAME": "'LongFast'", // Channel name to use on the TX radio config - uses default if unset. - // "USERPREFS_MESH_BEACON_ON_CHANNEL_PSK": "{ 0x01 }", // PSK for the TX channel (0x01 = Meshtastic default PSK) - // "USERPREFS_MESH_BEACON_ON_CHANNEL_NUM": "0", // LoRa channel/frequency slot to use on the TX radio config - zero is default, 20 is standard for US LongFast, etc. // "USERPREFS_MESH_BEACON_LEGACY_SPLIT": "true", // When both text and offer are present, split into a separate MESH_BEACON_APP (offer) and TEXT_MESSAGE_APP (text) for legacy client compatibility - // Multi-target broadcast: when any TARGET__* key is set, broadcast_targets overrides the - // single-target broadcast_on_* fields above. Each target transmits its own copy of the beacon. - // Up to 4 targets (0-3). Only TARGET_0 is used here; uncomment TARGET_1 to add a second preset. + // Broadcast targets: every beacon destination is a TARGET__* entry. With none set, the node + // beacons once on its running preset and region over the primary channel. Up to 4 targets (0-3); + // only TARGET_0 is used here, uncomment TARGET_1 to add a second preset. Targets that resolve to + // the same preset, region and channel are deduplicated. // CHANNEL_INDEX references a slot in the device's channel table (0..MAX_NUM_CHANNELS-1); that // channel must already be configured on the node (its key is needed to encrypt the beacon). // "USERPREFS_MESH_BEACON_TARGET_0_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST",