diff --git a/MQTT_IMPLEMENTATION.md b/MQTT_IMPLEMENTATION.md index e5c8b849..f813e068 100644 --- a/MQTT_IMPLEMENTATION.md +++ b/MQTT_IMPLEMENTATION.md @@ -109,6 +109,8 @@ The MQTT bridge uses a slot-based architecture with up to 6 concurrent connectio | `meshat.se` | mqtts://mqtt.meshat.se:8883 | Username/password (fixed in firmware) | MQTT over TLS | | `eastidahomesh` | wss://broker.eastidahomesh.net:443 | None | WSS | | `coloradomesh` | wss://mqtt.meshcore.coloradomesh.org:1883 | JWT (Ed25519) | WSS | +| `meshcore-ca-1` | mqtt1.meshcore.ca:443 | JWT (Ed25519) | WSS | +| `meshcore-ca-2` | mqtt2.meshcore.ca:443 | JWT (Ed25519) | WSS | | `custom` | User-configured | Username/Password | MQTT or WSS | | `none` | (disabled) | — | — | @@ -207,11 +209,37 @@ You can flash the merged firmware using either the web flasher or the command li - `MQTT_WIFI_TX_POWER` - WiFi TX power level (default: `WIFI_POWER_11dBm`) - ~~`MQTT_WIFI_POWER_SAVE_DEFAULT`~~ - Removed; all builds now default to `none` (no power save) +#### Compile-time fresh-install defaults (`src/helpers/MQTTDefaults.h`) + +Optional PlatformIO `build_flags` override defaults written when `/mqtt_prefs` is first created. They do **not** change existing saved prefs on upgrade or reflash (unless `/mqtt_prefs` is erased). + +| Macro | Default | Notes | +|-------|---------|-------| +| `MQTT_DEFAULT_SLOT1_PRESET` … `MQTT_DEFAULT_SLOT6_PRESET` | slots 1–2: `analyzer-us` / `analyzer-eu`; slots 3–6: `none` | Must be a built-in preset name, `none`, or `custom` | +| `MQTT_DEFAULT_IATA` | (empty) | e.g. `'"YYZ"'` | +| `MQTT_DEFAULT_TIMEZONE` | (empty) | e.g. `'"America/Toronto"'` | +| `MQTT_DEFAULT_TIMEZONE_OFFSET` | `0` | Fallback hours when TZ string is empty | + +Example community build: + +```ini +build_flags = + -D MQTT_DEFAULT_SLOT1_PRESET='"meshcore-ca-1"' + -D MQTT_DEFAULT_SLOT2_PRESET='"meshcore-ca-2"' + -D MQTT_DEFAULT_IATA='"YYZ"' + -D MQTT_DEFAULT_TIMEZONE='"America/Toronto"' + -D MQTT_DEFAULT_TIMEZONE_OFFSET=-5 +``` + +WiFi SSID/password are not compile-time configurable (operators set them per device via CLI). + +Legacy `get mqtt.analyzer_us` / `set mqtt.analyzer_us` still refer to the preset name `analyzer-us`, not “whatever slot 1 default is”. + ## Default Configuration -The MQTT bridge comes with the following defaults for fresh installs: +The MQTT bridge comes with the following defaults for fresh installs (unless overridden by the macros above): - **Origin**: Device name (set automatically from `set name`) -- **IATA**: (blank — must be configured for MeshCore-style topic presets such as Analyzer and TennMesh) +- **IATA**: (blank — must be configured for MeshCore-style topic presets such as Analyzer and TennMesh, unless `MQTT_DEFAULT_IATA` is set at build time) - **Status Messages**: Enabled - **Packet Messages**: Enabled - **Raw Messages**: Disabled @@ -224,8 +252,8 @@ The MQTT bridge comes with the following defaults for fresh installs: - **WiFi SSID**: (blank — must be configured) - **WiFi Password**: (blank — optional for open networks) - **WiFi Power Save**: `none` (no power save) -- **Timezone**: (blank — uses UTC until configured) -- **Timezone Offset**: 0 (fallback, no offset) +- **Timezone**: (blank — uses UTC until configured, unless `MQTT_DEFAULT_TIMEZONE` is set at build time) +- **Timezone Offset**: 0 (fallback, no offset, unless `MQTT_DEFAULT_TIMEZONE_OFFSET` is set) - **Repeat (forwarding)**: On (set `repeat off` for receive-only observers) ## CLI Commands diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index ab43809c..f13353e4 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -979,29 +979,13 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; - // MQTT defaults (mqtt_origin empty => effective origin follows node_name at publish time) + // MQTT slot/IATA/timezone defaults come from /mqtt_prefs via loadPrefs (see MQTTDefaults.h) _prefs.mqtt_origin[0] = '\0'; - StrHelper::strncpy(_prefs.mqtt_iata, "SEA", sizeof(_prefs.mqtt_iata)); - _prefs.mqtt_status_enabled = 1; // enabled - _prefs.mqtt_packets_enabled = 1; // enabled - _prefs.mqtt_raw_enabled = 0; // disabled - _prefs.mqtt_tx_enabled = 2; // advert: own adverts only (matches MQTTPrefs default) - _prefs.mqtt_rx_enabled = 1; // RX packets enabled by default - _prefs.mqtt_status_interval = 300000; // 5 minutes - // WiFi defaults + // WiFi defaults (user-configured via CLI; placeholders until set) StrHelper::strncpy(_prefs.wifi_ssid, "ssid_here", sizeof(_prefs.wifi_ssid)); StrHelper::strncpy(_prefs.wifi_password, "password_here", sizeof(_prefs.wifi_password)); - // Timezone defaults (Pacific Time with DST support) - StrHelper::strncpy(_prefs.timezone_string, "America/Los_Angeles", sizeof(_prefs.timezone_string)); - _prefs.timezone_offset = -8; // fallback - - // MQTT slot presets (analyzer-us and analyzer-eu enabled by default) - StrHelper::strncpy(_prefs.mqtt_slot_preset[0], "analyzer-us", sizeof(_prefs.mqtt_slot_preset[0])); - StrHelper::strncpy(_prefs.mqtt_slot_preset[1], "analyzer-eu", sizeof(_prefs.mqtt_slot_preset[1])); - StrHelper::strncpy(_prefs.mqtt_slot_preset[2], "none", sizeof(_prefs.mqtt_slot_preset[2])); - _prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier #if defined(USE_SX1262) || defined(USE_SX1268) diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 47ca52d4..e401fd5d 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -692,28 +692,12 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.bridge_baud = 115200; // baud rate _prefs.bridge_channel = 1; // channel 1 - // MQTT defaults (same as repeater; empty mqtt_origin follows node_name when publishing) + // MQTT slot/IATA/timezone defaults come from /mqtt_prefs via loadPrefs (see MQTTDefaults.h) _prefs.mqtt_origin[0] = '\0'; - StrHelper::strncpy(_prefs.mqtt_iata, "SEA", sizeof(_prefs.mqtt_iata)); - _prefs.mqtt_status_enabled = 1; // enabled - _prefs.mqtt_packets_enabled = 1; // enabled - _prefs.mqtt_raw_enabled = 0; // disabled - _prefs.mqtt_tx_enabled = 2; // advert: own adverts only (matches MQTTPrefs default) - _prefs.mqtt_rx_enabled = 1; // RX packets enabled by default - _prefs.mqtt_status_interval = 300000; // 5 minutes - - // WiFi defaults (same as repeater) + + // WiFi defaults (user-configured via CLI; placeholders until set) StrHelper::strncpy(_prefs.wifi_ssid, "ssid_here", sizeof(_prefs.wifi_ssid)); StrHelper::strncpy(_prefs.wifi_password, "password_here", sizeof(_prefs.wifi_password)); - - // Timezone defaults (same as repeater - Pacific Time with DST support) - StrHelper::strncpy(_prefs.timezone_string, "America/Los_Angeles", sizeof(_prefs.timezone_string)); - _prefs.timezone_offset = -8; // fallback - - // MQTT slot presets (analyzer-us and analyzer-eu enabled by default) - StrHelper::strncpy(_prefs.mqtt_slot_preset[0], "analyzer-us", sizeof(_prefs.mqtt_slot_preset[0])); - StrHelper::strncpy(_prefs.mqtt_slot_preset[1], "analyzer-eu", sizeof(_prefs.mqtt_slot_preset[1])); - StrHelper::strncpy(_prefs.mqtt_slot_preset[2], "none", sizeof(_prefs.mqtt_slot_preset[2])); next_post_idx = 0; next_client_idx = 0; diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 64a11aa4..6d214306 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -17,6 +17,7 @@ #endif #ifdef WITH_MQTT_BRIDGE #include "bridges/MQTTBridge.h" +#include "MQTTDefaults.h" // Helper function to calculate total size of MQTT fields for file format compatibility // Uses NodePrefs struct to get accurate field sizes @@ -449,25 +450,7 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { #ifdef WITH_MQTT_BRIDGE // Set default values for MQTT preferences (used when file doesn't exist or is corrupted) static void setMQTTPrefsDefaults(MQTTPrefs* prefs) { - memset(prefs, 0, sizeof(MQTTPrefs)); - // Set sensible defaults matching MQTTBridge expectations - prefs->mqtt_status_enabled = 1; // enabled by default - prefs->mqtt_packets_enabled = 1; // enabled by default - prefs->mqtt_raw_enabled = 0; // disabled by default - prefs->mqtt_tx_enabled = 2; // advert: own adverts only, by default - prefs->mqtt_rx_enabled = 1; // RX packets enabled by default - prefs->mqtt_status_interval = 300000; // 5 minutes default - // Slot presets: analyzer-us and analyzer-eu enabled by default, rest = none - strncpy(prefs->mqtt_slot_preset[0], "analyzer-us", sizeof(prefs->mqtt_slot_preset[0]) - 1); - prefs->mqtt_slot_preset[0][sizeof(prefs->mqtt_slot_preset[0]) - 1] = '\0'; - strncpy(prefs->mqtt_slot_preset[1], "analyzer-eu", sizeof(prefs->mqtt_slot_preset[1]) - 1); - prefs->mqtt_slot_preset[1][sizeof(prefs->mqtt_slot_preset[1]) - 1] = '\0'; - for (int i = 2; i < MAX_MQTT_SLOTS; i++) { - strncpy(prefs->mqtt_slot_preset[i], "none", sizeof(prefs->mqtt_slot_preset[i]) - 1); - prefs->mqtt_slot_preset[i][sizeof(prefs->mqtt_slot_preset[i]) - 1] = '\0'; - } - prefs->wifi_power_save = 1; // Default to none (0=min, 1=none, 2=max) - // String fields are already zero-initialized by memset + applyMQTTDefaults(prefs); } void CommonCLI::loadMQTTPrefs(FILESYSTEM* fs) { diff --git a/src/helpers/MQTTDefaults.h b/src/helpers/MQTTDefaults.h new file mode 100644 index 00000000..cf58b997 --- /dev/null +++ b/src/helpers/MQTTDefaults.h @@ -0,0 +1,98 @@ +#pragma once + +#ifdef WITH_MQTT_BRIDGE + +#include +#include +#include "CommonCLI.h" +#include "MQTTPresets.h" + +// Compile-time defaults for fresh /mqtt_prefs (override via platformio build_flags). +// Example: +// -D MQTT_DEFAULT_SLOT1_PRESET='"meshcore-ca-1"' +// -D MQTT_DEFAULT_IATA='"YYZ"' +// -D MQTT_DEFAULT_TIMEZONE='"America/Toronto"' +// -D MQTT_DEFAULT_TIMEZONE_OFFSET=-5 + +#ifndef MQTT_DEFAULT_SLOT1_PRESET +#define MQTT_DEFAULT_SLOT1_PRESET "analyzer-us" +#endif +#ifndef MQTT_DEFAULT_SLOT2_PRESET +#define MQTT_DEFAULT_SLOT2_PRESET "analyzer-eu" +#endif +#ifndef MQTT_DEFAULT_SLOT3_PRESET +#define MQTT_DEFAULT_SLOT3_PRESET "none" +#endif +#ifndef MQTT_DEFAULT_SLOT4_PRESET +#define MQTT_DEFAULT_SLOT4_PRESET "none" +#endif +#ifndef MQTT_DEFAULT_SLOT5_PRESET +#define MQTT_DEFAULT_SLOT5_PRESET "none" +#endif +#ifndef MQTT_DEFAULT_SLOT6_PRESET +#define MQTT_DEFAULT_SLOT6_PRESET "none" +#endif + +#ifndef MQTT_DEFAULT_IATA +#define MQTT_DEFAULT_IATA "" +#endif + +#ifndef MQTT_DEFAULT_TIMEZONE +#define MQTT_DEFAULT_TIMEZONE "" +#endif + +#ifndef MQTT_DEFAULT_TIMEZONE_OFFSET +#define MQTT_DEFAULT_TIMEZONE_OFFSET 0 +#endif + +static inline void mqttDefaultSlotPreset(char* dest, size_t dest_size, const char* preset) { + const char* resolved = MQTT_PRESET_NONE; + if (preset && preset[0] != '\0') { + if (strcmp(preset, MQTT_PRESET_NONE) == 0 || + strcmp(preset, MQTT_PRESET_CUSTOM) == 0 || + findMQTTPreset(preset) != nullptr) { + resolved = preset; + } else { + MESH_DEBUG_PRINTLN("MQTT: invalid default preset '%s', using none", preset); + } + } + strncpy(dest, resolved, dest_size - 1); + dest[dest_size - 1] = '\0'; +} + +static inline void applyMQTTDefaults(MQTTPrefs* prefs) { + memset(prefs, 0, sizeof(MQTTPrefs)); + prefs->mqtt_status_enabled = 1; + prefs->mqtt_packets_enabled = 1; + prefs->mqtt_raw_enabled = 0; + prefs->mqtt_tx_enabled = 2; + prefs->mqtt_rx_enabled = 1; + prefs->mqtt_status_interval = 300000; + prefs->wifi_power_save = 1; + + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[0], sizeof(prefs->mqtt_slot_preset[0]), + MQTT_DEFAULT_SLOT1_PRESET); + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[1], sizeof(prefs->mqtt_slot_preset[1]), + MQTT_DEFAULT_SLOT2_PRESET); + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[2], sizeof(prefs->mqtt_slot_preset[2]), + MQTT_DEFAULT_SLOT3_PRESET); + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[3], sizeof(prefs->mqtt_slot_preset[3]), + MQTT_DEFAULT_SLOT4_PRESET); + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[4], sizeof(prefs->mqtt_slot_preset[4]), + MQTT_DEFAULT_SLOT5_PRESET); + mqttDefaultSlotPreset(prefs->mqtt_slot_preset[5], sizeof(prefs->mqtt_slot_preset[5]), + MQTT_DEFAULT_SLOT6_PRESET); + + if (MQTT_DEFAULT_IATA[0] != '\0') { + strncpy(prefs->mqtt_iata, MQTT_DEFAULT_IATA, sizeof(prefs->mqtt_iata) - 1); + prefs->mqtt_iata[sizeof(prefs->mqtt_iata) - 1] = '\0'; + } + + if (MQTT_DEFAULT_TIMEZONE[0] != '\0') { + strncpy(prefs->timezone_string, MQTT_DEFAULT_TIMEZONE, sizeof(prefs->timezone_string) - 1); + prefs->timezone_string[sizeof(prefs->timezone_string) - 1] = '\0'; + } + prefs->timezone_offset = MQTT_DEFAULT_TIMEZONE_OFFSET; +} + +#endif // WITH_MQTT_BRIDGE diff --git a/src/helpers/MQTTPresets.h b/src/helpers/MQTTPresets.h index bca9516e..5ce6fb07 100644 --- a/src/helpers/MQTTPresets.h +++ b/src/helpers/MQTTPresets.h @@ -119,8 +119,8 @@ static const MQTTPresetDef MQTT_PRESETS[MQTT_PRESET_COUNT] = { { "coloradomesh", "wss://mqtt.meshcore.coloradomesh.org:1883","mqtt.meshcore.coloradomesh.org", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, { "dutchmeshcore-1", "wss://collector1.dutchmeshcore.nl:443/mqtt", "collector1.dutchmeshcore.nl", GTS_ROOT_R4, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, { "dutchmeshcore-2", "wss://collector2.dutchmeshcore.nl:443/mqtt", "collector2.dutchmeshcore.nl", GTS_ROOT_R4, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, - { "meshcore-ca-1", "wss://mqtt1.meshcore.ca:443/mqtt", "mqtt1.meshcore.ca", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, - { "meshcore-ca-2", "wss://mqtt2.meshcore.ca:443/mqtt", "mqtt2.meshcore.ca", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, + { "meshcore-ca-1", "wss://mqtt1.meshcore.ca:443/mqtt", "mqtt1.meshcore.ca", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, + { "meshcore-ca-2", "wss://mqtt2.meshcore.ca:443/mqtt", "mqtt2.meshcore.ca", ISRG_ROOT_X1, MQTT_AUTH_JWT, MQTT_TOPIC_MESHCORE, 0, true, 55, nullptr, nullptr }, }; // Find a preset by name, returns nullptr if not found