diff --git a/MQTT_IMPLEMENTATION.md b/MQTT_IMPLEMENTATION.md index e310db42..aab4e04c 100644 --- a/MQTT_IMPLEMENTATION.md +++ b/MQTT_IMPLEMENTATION.md @@ -41,6 +41,27 @@ pio run -e Heltec_v3_repeater_bridge_mqtt pio run -e Station_G2_repeater_bridge_mqtt ``` +### Custom MQTT Server Configuration + +You can configure a custom MQTT server using build flags in `platformio.ini`: + +```ini +[env:Heltec_v3_repeater_bridge_mqtt] +build_flags = + ${Heltec_lora32_v3.build_flags} + -D WITH_MQTT_BRIDGE=1 + -D MQTT_SERVER='"your-mqtt-broker.com"' + -D MQTT_PORT=1883 + -D MQTT_USERNAME='"your-username"' + -D MQTT_PASSWORD='"your-password"' +``` + +**Build Flags:** +- `MQTT_SERVER` - MQTT broker hostname +- `MQTT_PORT` - MQTT broker port (default: 1883) +- `MQTT_USERNAME` - MQTT username +- `MQTT_PASSWORD` - MQTT password + ## Default Configuration The MQTT bridge comes with the following defaults: @@ -71,6 +92,10 @@ The MQTT bridge comes with the following defaults: - `get mqtt.raw` - Get raw message setting (on/off) - `get mqtt.tx` - Get TX message setting (on/off) - `get mqtt.interval` - Get status publish interval (ms) +- `get mqtt.server` - Get MQTT server hostname +- `get mqtt.port` - Get MQTT server port +- `get mqtt.username` - Get MQTT username +- `get mqtt.password` - Get MQTT password - `get mqtt.analyzer.us` - Get US Let's Mesh Analyzer server setting (on/off) - `get mqtt.analyzer.eu` - Get EU Let's Mesh Analyzer server setting (on/off) @@ -82,6 +107,10 @@ The MQTT bridge comes with the following defaults: - `set mqtt.raw on|off` - Enable/disable raw messages - `set mqtt.tx on|off` - Enable/disable TX packet messages - `set mqtt.interval ` - Set status publish interval (1000-3600000 ms) +- `set mqtt.server ` - Set MQTT server hostname +- `set mqtt.port ` - Set MQTT server port (1-65535) +- `set mqtt.username ` - Set MQTT username +- `set mqtt.password ` - Set MQTT password - `set mqtt.analyzer.us on|off` - Enable/disable US Let's Mesh Analyzer server - `set mqtt.analyzer.eu on|off` - Enable/disable EU Let's Mesh Analyzer server @@ -235,14 +264,210 @@ Minimal raw packet data for map integration. - US Server: `mqtt-us-v1.letsmesh.net:443` (WebSocket with TLS) - EU Server: `mqtt-eu-v1.letsmesh.net:443` (WebSocket with TLS) +## First-Time Setup + +### Prerequisites +- MeshCore device with MQTT bridge firmware flashed +- WiFi network credentials +- MQTT broker (optional - default broker is provided) +- LoRa-capable device for configuration (repeater console) +- MeshCore network access + +### Step 1: Initial Boot and Network Connection +1. **Flash the firmware** to your device using PlatformIO or the build script +2. **Deploy the device** in your mesh network location +3. **Ensure WiFi connectivity** - the device will automatically connect to WiFi if credentials are pre-configured +4. **Verify mesh network access** - device should be discoverable by other mesh nodes + +### Step 2: Connect via LoRa Repeater Console +Use a MeshCore companion device to configure the Repeater's MQTT bridge. + +1. **Connect to the mesh** using your companion +2. **Locate the MQTT bridge device** in your contacts +3. **Log into your Repeater** using the default password (password) or whatever you configured via serial console +3. **Tap on the repeater console** on your repeater's settings +4. **Send configuration commands** via LoRa to the MQTT bridge device + +### Step 3: Configure WiFi Connection +The device needs internet connectivity to publish to MQTT brokers. + +**Via LoRa Repeater Console:** +``` +# Set your WiFi credentials +set wifi.ssid "YourWiFiNetwork" +set wifi.pwd "YourWiFiPassword" + +# Verify WiFi settings +get wifi.ssid +get wifi.pwd +``` + +### Step 4: Configure Device Identity +Set up your device's identity for MQTT topics and status messages. + +**Via LoRa Repeater Console:** +``` +# Set IATA code for topic structure (e.g., airport code) +set mqtt.iata "SEA" + +# Verify settings (origin is set automatically to device name) +get mqtt.origin +get mqtt.iata +``` + +### Step 5: Configure Timezone +Set your local timezone for accurate timestamps. + +**Via LoRa Repeater Console:** +``` +# Set timezone (choose one method) +set timezone "America/Los_Angeles" # IANA format +set timezone "PDT" # Abbreviation +set timezone "UTC-8" # UTC offset + +# Verify timezone +get timezone +``` + +### Step 6: Configure MQTT Settings +Customize which messages to publish and how often. + +**Via LoRa Repeater Console:** +``` +# Configure MQTT server (optional - uses defaults if not set) +set mqtt.server "your-mqtt-broker.com" +set mqtt.port 1883 +set mqtt.username "your-username" +set mqtt.password "your-password" + +# Enable/disable message types +set mqtt.status on # Device status messages +set mqtt.packets on # Packet data messages +set mqtt.raw off # Raw packet data (optional) +set mqtt.tx off # Transmitted packets (optional) + +# Set status publish interval (default: 5 minutes) +set mqtt.interval 300000 + +# Verify settings +get mqtt.server +get mqtt.port +get mqtt.username +get mqtt.status +get mqtt.packets +get mqtt.interval +``` + +### Step 7: Verify MQTT Broker Connection +Check that the device can connect to MQTT brokers. + +**Via LoRa Repeater Console:** +``` +# Check bridge status +get bridge.enabled + +# If disabled, enable it +set bridge.enabled on + +# Check MQTT analyzer servers (optional) +get mqtt.analyzer.us +get mqtt.analyzer.eu +``` + +### Step 8: Monitor MQTT Messages +Once configured, the device will automatically publish messages to MQTT brokers. + +**Default MQTT Broker**: `meshtastic.pugetmesh.org:1883` +- Username: `meshdev` +- Password: `large4cats` + +**Topic Structure**: +- Status: `meshcore/{IATA}/{DEVICE_PUBLIC_KEY}/status` +- Packets: `meshcore/{IATA}/{DEVICE_PUBLIC_KEY}/packets` +- Raw: `meshcore/{IATA}/{DEVICE_PUBLIC_KEY}/raw` + +**Example Topics**: +- `meshcore/SEA/7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C9400/status` +- `meshcore/SEA/7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C9400/packets` + +### Step 9: Troubleshooting + +#### Device Won't Connect to WiFi +**Via LoRa Repeater Console:** +``` +# Check WiFi settings +get wifi.ssid +get wifi.pwd + +# Reset WiFi settings +set wifi.ssid "" +set wifi.pwd "" + +# Reconfigure with correct credentials +set wifi.ssid "YourWiFiNetwork" +set wifi.pwd "YourWiFiPassword" +``` + +#### No MQTT Messages Appearing +**Via LoRa Repeater Console:** +``` +# Check bridge status +get bridge.enabled + +# Check message types +get mqtt.status +get mqtt.packets + +# Check device identity (origin is set automatically) +get mqtt.origin +get mqtt.iata + +# Enable bridge if needed +set bridge.enabled on +``` + +#### Timezone Issues +**Via LoRa Repeater Console:** +``` +# Check current timezone +get timezone + +# Try different timezone formats +set timezone "America/New_York" # IANA format +set timezone "EST" # Abbreviation +set timezone "UTC-5" # UTC offset +``` + +#### LoRa Configuration Issues +- **Device not responding**: Ensure both devices are on the same mesh network +- **Commands not working**: Check that the target device is reachable via LoRa +- **No response to get commands**: Verify the device is powered and in range + +### Step 10: Advanced Configuration (Optional) + +#### Custom MQTT Broker +If you want to use your own MQTT broker instead of the default: + +``` +# Note: Custom broker configuration requires code modification +# The default broker is: meshtastic.pugetmesh.org:1883 +# Username: meshdev, Password: large4cats +``` + +#### Let's Mesh Analyzer Servers +The device automatically connects to Let's Mesh Analyzer servers for additional monitoring: + +- **US Server**: `mqtt-us-v1.letsmesh.net:443` (WebSocket with TLS) +- **EU Server**: `mqtt-eu-v1.letsmesh.net:443` (WebSocket with TLS) + +These are enabled by default and use JWT authentication with your device's Ed25519 keys. + ## Testing 1. Flash the MQTT bridge firmware to your device -2. Connect to the device via serial console -3. Configure WiFi connection: `set wifi.ssid "YourSSID"` and `set wifi.pwd "YourPassword"` -4. Configure timezone: `set timezone "America/Los_Angeles"` -5. Check MQTT settings: `get mqtt.origin` -6. Monitor MQTT broker for incoming messages +2. Follow the first-time setup instructions above +3. Monitor MQTT broker for incoming messages +4. Verify message formats match the JSON schemas in this document ## Dependencies diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 08cd00b1..f8925ec7 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -3,6 +3,9 @@ #include "TxtDataHelpers.h" #include "AdvertDataHelpers.h" #include +#ifdef WITH_MQTT_BRIDGE +#include "bridges/MQTTBridge.h" +#endif // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -117,9 +120,15 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { file.read((uint8_t *)&_prefs->timezone_string, sizeof(_prefs->timezone_string)); // 305 file.read((uint8_t *)&_prefs->timezone_offset, sizeof(_prefs->timezone_offset)); // 337 + // MQTT server settings + file.read((uint8_t *)&_prefs->mqtt_server, sizeof(_prefs->mqtt_server)); // 340 + file.read((uint8_t *)&_prefs->mqtt_port, sizeof(_prefs->mqtt_port)); // 341 + file.read((uint8_t *)&_prefs->mqtt_username, sizeof(_prefs->mqtt_username)); // 342 + file.read((uint8_t *)&_prefs->mqtt_password, sizeof(_prefs->mqtt_password)); // 343 + // Let's Mesh Analyzer settings - file.read((uint8_t *)&_prefs->mqtt_analyzer_us_enabled, sizeof(_prefs->mqtt_analyzer_us_enabled)); // 338 - file.read((uint8_t *)&_prefs->mqtt_analyzer_eu_enabled, sizeof(_prefs->mqtt_analyzer_eu_enabled)); // 339 + file.read((uint8_t *)&_prefs->mqtt_analyzer_us_enabled, sizeof(_prefs->mqtt_analyzer_us_enabled)); // 344 + file.read((uint8_t *)&_prefs->mqtt_analyzer_eu_enabled, sizeof(_prefs->mqtt_analyzer_eu_enabled)); // 345 // 209 >>>>>>> 6f42dc3 (Implement Let's Mesh Analyzer integration in MQTT Bridge) @@ -246,9 +255,15 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) { file.write((uint8_t *)&_prefs->timezone_string, sizeof(_prefs->timezone_string)); // 305 file.write((uint8_t *)&_prefs->timezone_offset, sizeof(_prefs->timezone_offset)); // 337 + // MQTT server settings + file.write((uint8_t *)&_prefs->mqtt_server, sizeof(_prefs->mqtt_server)); // 340 + file.write((uint8_t *)&_prefs->mqtt_port, sizeof(_prefs->mqtt_port)); // 341 + file.write((uint8_t *)&_prefs->mqtt_username, sizeof(_prefs->mqtt_username)); // 342 + file.write((uint8_t *)&_prefs->mqtt_password, sizeof(_prefs->mqtt_password)); // 343 + // Let's Mesh Analyzer settings - file.write((uint8_t *)&_prefs->mqtt_analyzer_us_enabled, sizeof(_prefs->mqtt_analyzer_us_enabled)); // 338 - file.write((uint8_t *)&_prefs->mqtt_analyzer_eu_enabled, sizeof(_prefs->mqtt_analyzer_eu_enabled)); // 339 + file.write((uint8_t *)&_prefs->mqtt_analyzer_us_enabled, sizeof(_prefs->mqtt_analyzer_us_enabled)); // 344 + file.write((uint8_t *)&_prefs->mqtt_analyzer_eu_enabled, sizeof(_prefs->mqtt_analyzer_eu_enabled)); // 345 // 209 >>>>>>> 6f42dc3 (Implement Let's Mesh Analyzer integration in MQTT Bridge) @@ -453,6 +468,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", _prefs->mqtt_tx_enabled ? "on" : "off"); } else if (memcmp(config, "mqtt.interval", 13) == 0) { sprintf(reply, "> %d", (uint32_t)_prefs->mqtt_status_interval); + } else if (memcmp(config, "mqtt.server", 11) == 0) { + sprintf(reply, "> %s", _prefs->mqtt_server); + } else if (memcmp(config, "mqtt.port", 9) == 0) { + sprintf(reply, "> %d", _prefs->mqtt_port); + } else if (memcmp(config, "mqtt.username", 13) == 0) { + sprintf(reply, "> %s", _prefs->mqtt_username); + } else if (memcmp(config, "mqtt.password", 13) == 0) { + sprintf(reply, "> %s", _prefs->mqtt_password); } else if (memcmp(config, "wifi.ssid", 9) == 0) { sprintf(reply, "> %s", _prefs->wifi_ssid); } else if (memcmp(config, "wifi.pwd", 8) == 0) { @@ -461,10 +484,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", _prefs->timezone_string); } else if (memcmp(config, "timezone.offset", 15) == 0) { sprintf(reply, "> %d", _prefs->timezone_offset); - } else if (memcmp(config, "mqtt.analyzer.us", 16) == 0) { + } else if (memcmp(config, "mqtt.analyzer.us", 17) == 0) { sprintf(reply, "> %s", _prefs->mqtt_analyzer_us_enabled ? "on" : "off"); - } else if (memcmp(config, "mqtt.analyzer.eu", 16) == 0) { + } else if (memcmp(config, "mqtt.analyzer.eu", 17) == 0) { sprintf(reply, "> %s", _prefs->mqtt_analyzer_eu_enabled ? "on" : "off"); + } else if (memcmp(config, "mqtt.config.valid", 17) == 0) { + // Check if MQTT configuration is valid using static method + bool valid = MQTTBridge::isConfigValid(_prefs); + sprintf(reply, "> %s", valid ? "valid" : "invalid"); #endif } else { sprintf(reply, "??: %s", config); @@ -713,6 +740,27 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch } else { strcpy(reply, "Error: timezone offset must be between -12 and +14"); } + } else if (memcmp(config, "mqtt.server ", 12) == 0) { + StrHelper::strncpy(_prefs->mqtt_server, &config[12], sizeof(_prefs->mqtt_server)); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "mqtt.port ", 10) == 0) { + int port = atoi(&config[10]); + if (port > 0 && port <= 65535) { + _prefs->mqtt_port = port; + savePrefs(); + strcpy(reply, "OK"); + } else { + strcpy(reply, "Error: port must be between 1 and 65535"); + } + } else if (memcmp(config, "mqtt.username ", 14) == 0) { + StrHelper::strncpy(_prefs->mqtt_username, &config[14], sizeof(_prefs->mqtt_username)); + savePrefs(); + strcpy(reply, "OK"); + } else if (memcmp(config, "mqtt.password ", 14) == 0) { + StrHelper::strncpy(_prefs->mqtt_password, &config[14], sizeof(_prefs->mqtt_password)); + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "mqtt.analyzer.us ", 17) == 0) { _prefs->mqtt_analyzer_us_enabled = memcmp(&config[17], "on", 2) == 0; savePrefs(); diff --git a/src/helpers/bridges/MQTTBridge.cpp b/src/helpers/bridges/MQTTBridge.cpp index 78b400b1..70275458 100644 --- a/src/helpers/bridges/MQTTBridge.cpp +++ b/src/helpers/bridges/MQTTBridge.cpp @@ -15,7 +15,7 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc _ntp_client(_ntp_udp, "pool.ntp.org", 0, 60000), _last_ntp_sync(0), _ntp_synced(false), _timezone(nullptr), _last_raw_len(0), _last_snr(0), _last_rssi(0), _last_raw_timestamp(0), _analyzer_us_enabled(false), _analyzer_eu_enabled(false), _identity(identity), - _analyzer_us_client(nullptr), _analyzer_eu_client(nullptr) { + _analyzer_us_client(nullptr), _analyzer_eu_client(nullptr), _config_valid(false) { // Initialize default values strncpy(_origin, "MeshCore-Repeater", sizeof(_origin) - 1); @@ -29,6 +29,26 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc _raw_enabled = false; _tx_enabled = false; // Disable TX packets by default + // Initialize MQTT server settings with defaults + strncpy(_prefs->mqtt_server, "your-mqtt-broker.com", sizeof(_prefs->mqtt_server) - 1); + _prefs->mqtt_port = 1883; + strncpy(_prefs->mqtt_username, "your-username", sizeof(_prefs->mqtt_username) - 1); + strncpy(_prefs->mqtt_password, "your-password", sizeof(_prefs->mqtt_password) - 1); + + // Override with build flags if defined +#ifdef MQTT_SERVER + strncpy(_prefs->mqtt_server, MQTT_SERVER, sizeof(_prefs->mqtt_server) - 1); +#endif +#ifdef MQTT_PORT + _prefs->mqtt_port = MQTT_PORT; +#endif +#ifdef MQTT_USERNAME + strncpy(_prefs->mqtt_username, MQTT_USERNAME, sizeof(_prefs->mqtt_username) - 1); +#endif +#ifdef MQTT_PASSWORD + strncpy(_prefs->mqtt_password, MQTT_PASSWORD, sizeof(_prefs->mqtt_password) - 1); +#endif + // Initialize packet queue memset(_packet_queue, 0, sizeof(_packet_queue)); @@ -39,6 +59,15 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc void MQTTBridge::begin() { MQTT_DEBUG_PRINTLN("Initializing MQTT Bridge..."); + // Validate configuration once and store the result + _config_valid = isMQTTConfigValid(); + if (!_config_valid) { + MQTT_DEBUG_PRINTLN("MQTT Bridge initialization skipped - configuration not valid"); + return; + } + + MQTT_DEBUG_PRINTLN("MQTT configuration is valid - proceeding with initialization"); + // Update origin and IATA from preferences strncpy(_origin, _prefs->mqtt_origin, sizeof(_origin) - 1); _origin[sizeof(_origin) - 1] = '\0'; @@ -137,8 +166,8 @@ void MQTTBridge::begin() { } }); - // Set default broker (meshtastic.pugetmesh.org) - setBroker(0, "meshtastic.pugetmesh.org", 1883, "meshdev", "large4cats", true); + // Set default broker from preferences or build flags + setBroker(0, _prefs->mqtt_server, _prefs->mqtt_port, _prefs->mqtt_username, _prefs->mqtt_password, true); // Setup Let's Mesh Analyzer servers setupAnalyzerServers(); @@ -191,6 +220,37 @@ void MQTTBridge::end() { MQTT_DEBUG_PRINTLN("MQTT Bridge stopped"); } +bool MQTTBridge::isConfigValid() const { + return _config_valid; +} + +bool MQTTBridge::isConfigValid(const NodePrefs* prefs) { + // Check if MQTT server is configured (not default placeholder) + if (strlen(prefs->mqtt_server) == 0 || + strcmp(prefs->mqtt_server, "your-mqtt-broker.com") == 0) { + return false; + } + + // Check if MQTT port is valid + if (prefs->mqtt_port == 0 || prefs->mqtt_port > 65535) { + return false; + } + + // Check if MQTT username is configured (not default placeholder) + if (strlen(prefs->mqtt_username) == 0 || + strcmp(prefs->mqtt_username, "your-username") == 0) { + return false; + } + + // Check if MQTT password is configured (not default placeholder) + if (strlen(prefs->mqtt_password) == 0 || + strcmp(prefs->mqtt_password, "your-password") == 0) { + return false; + } + + return true; +} + void MQTTBridge::loop() { if (!_initialized) return; @@ -216,9 +276,9 @@ void MQTTBridge::loop() { } void MQTTBridge::onPacketReceived(mesh::Packet *packet) { - if (!_initialized || !_packets_enabled) { - MQTT_DEBUG_PRINTLN("Packet received but not processing - initialized: %s, packets_enabled: %s", - _initialized ? "true" : "false", _packets_enabled ? "true" : "false"); + if (!_initialized || !_packets_enabled || !_config_valid) { + MQTT_DEBUG_PRINTLN("Packet received but not processing - initialized: %s, packets_enabled: %s, config_valid: %s", + _initialized ? "true" : "false", _packets_enabled ? "true" : "false", _config_valid ? "true" : "false"); return; } @@ -228,13 +288,45 @@ void MQTTBridge::onPacketReceived(mesh::Packet *packet) { } void MQTTBridge::sendPacket(mesh::Packet *packet) { - if (!_initialized || !_packets_enabled || !_tx_enabled) return; + if (!_initialized || !_packets_enabled || !_tx_enabled || !_config_valid) return; // Queue packet for transmission (only if TX enabled) queuePacket(packet, true); } +bool MQTTBridge::isMQTTConfigValid() { + // Check if MQTT server is configured (not default placeholder) + if (strlen(_prefs->mqtt_server) == 0 || + strcmp(_prefs->mqtt_server, "your-mqtt-broker.com") == 0) { + return false; + } + + // Check if MQTT port is valid + if (_prefs->mqtt_port == 0 || _prefs->mqtt_port > 65535) { + return false; + } + + // Check if MQTT username is configured (not default placeholder) + if (strlen(_prefs->mqtt_username) == 0 || + strcmp(_prefs->mqtt_username, "your-username") == 0) { + return false; + } + + // Check if MQTT password is configured (not default placeholder) + if (strlen(_prefs->mqtt_password) == 0 || + strcmp(_prefs->mqtt_password, "your-password") == 0) { + return false; + } + + return true; +} + void MQTTBridge::connectToBrokers() { + // Check if MQTT configuration is valid before attempting connection + if (!_config_valid) { + return; + } + // For now, connect to the first enabled broker // TODO: Implement multi-broker support with PsychicMqttClient for (int i = 0; i < MAX_MQTT_BROKERS_COUNT; i++) { @@ -282,9 +374,9 @@ void MQTTBridge::connectToBrokers() { } void MQTTBridge::processPacketQueue() { - if (_queue_count == 0 || !isAnyBrokerConnected()) { + if (_queue_count == 0 || !isAnyBrokerConnected() || !_config_valid) { if (_queue_count > 0) { - MQTT_DEBUG_PRINTLN("Queue has %d packets but no brokers connected", _queue_count); + MQTT_DEBUG_PRINTLN("Queue has %d packets but no brokers connected or config invalid", _queue_count); } return; } @@ -313,7 +405,7 @@ void MQTTBridge::processPacketQueue() { } void MQTTBridge::publishStatus() { - if (!isAnyBrokerConnected()) return; + if (!isAnyBrokerConnected() || !_config_valid) return; char json_buffer[512]; char origin_id[65]; @@ -378,7 +470,7 @@ void MQTTBridge::publishStatus() { } void MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx) { - if (!packet) return; + if (!packet || !_config_valid) return; char json_buffer[1024]; char origin_id[65]; @@ -426,7 +518,7 @@ void MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx) { } void MQTTBridge::publishRaw(mesh::Packet* packet) { - if (!packet) return; + if (!packet || !_config_valid) return; char json_buffer[512]; char origin_id[65]; diff --git a/src/helpers/bridges/MQTTBridge.h b/src/helpers/bridges/MQTTBridge.h index fa87cc48..bd7a991b 100644 --- a/src/helpers/bridges/MQTTBridge.h +++ b/src/helpers/bridges/MQTTBridge.h @@ -122,6 +122,9 @@ private: PsychicMqttClient* _analyzer_us_client; PsychicMqttClient* _analyzer_eu_client; + // Configuration validation state + bool _config_valid; + // Internal methods void connectToBrokers(); void processPacketQueue(); @@ -134,6 +137,7 @@ private: void setBrokerDefaults(); void syncTimeWithNTP(); Timezone* createTimezoneFromString(const char* tz_string); + bool isMQTTConfigValid(); public: /** @@ -164,6 +168,21 @@ public: */ void end() override; + /** + * Checks if MQTT configuration is valid + * + * @return true if all required MQTT settings are properly configured + */ + bool isConfigValid() const; + + /** + * Static method to validate MQTT configuration from preferences + * + * @param prefs Node preferences containing MQTT settings + * @return true if all required MQTT settings are properly configured + */ + static bool isConfigValid(const NodePrefs* prefs); + /** * Main loop handler * - Maintains broker connections diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 10644001..4118ec68 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -122,6 +122,10 @@ build_flags = -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y # -D WIFI_SSID='"ssid"' # -D WIFI_PWD='"password"' +# -D MQTT_SERVER='"your-mqtt-broker.com"' +# -D MQTT_PORT=1883 +# -D MQTT_USERNAME='"your-username"' +# -D MQTT_PASSWORD='"your-password"' ; Use ESP32's built-in certificate bundle build_src_filter = ${Heltec_lora32_v3.build_src_filter} diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index a5feb299..85d41d7c 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -252,6 +252,13 @@ build_flags = -D MQTT_DEBUG=1 -D MESH_PACKET_LOGGING=1 -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +# -D WIFI_SSID='"ssid"' +# -D WIFI_PWD='"password"' +# -D MQTT_SERVER='"your-mqtt-broker.com"' +# -D MQTT_PORT=1883 +# -D MQTT_USERNAME='"your-username"' +# -D MQTT_PASSWORD='"your-password"' build_src_filter = ${Station_G2.build_src_filter} + +