diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index d18a802e..26de9737 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -198,9 +198,23 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { mesh::Utils::printHex(Serial, raw, len); Serial.println(); #endif + +#ifdef WITH_MQTT_BRIDGE + if (_prefs.bridge_enabled) { + // Store raw radio data for MQTT messages (same as repeater) + bridge.storeRawRadioData(raw, len, snr, rssi); + } +#endif } void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { +#ifdef WITH_MQTT_BRIDGE + if (_prefs.bridge_enabled && _prefs.bridge_pkt_src == 1) { + // Log received packets to MQTT (same as repeater) + bridge.onPacketReceived(pkt); + } +#endif + if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -220,6 +234,13 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { } } void MyMesh::logTx(mesh::Packet *pkt, int len) { +#ifdef WITH_MQTT_BRIDGE + if (_prefs.bridge_enabled && _prefs.bridge_pkt_src == 0) { + // Log transmitted packets to MQTT (same as repeater) + bridge.sendPacket(pkt); + } +#endif + if (_logging) { File f = openAppend(PACKET_LOG_FILE); if (f) { @@ -587,7 +608,11 @@ void MyMesh::onAckRecv(mesh::Packet *packet, uint32_t ack_crc) { MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondClock &ms, mesh::RNG &rng, mesh::RTCClock &rtc, mesh::MeshTables &tables) : mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables), - _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) { + _cli(board, rtc, sensors, &_prefs, this), telemetry(MAX_PACKET_PAYLOAD - 4) +#ifdef WITH_MQTT_BRIDGE + , bridge(&_prefs, _mgr, &rtc, &self_id) +#endif +{ last_millis = 0; uptime_millis = 0; next_local_advert = next_flood_advert = 0; @@ -624,6 +649,34 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.gps_interval = 0; _prefs.advert_loc_policy = ADVERT_LOC_PREFS; + // bridge defaults (same as repeater) + _prefs.bridge_enabled = 1; // enabled + _prefs.bridge_delay = 500; // milliseconds + _prefs.bridge_pkt_src = 1; // logRx (RX packets) + _prefs.bridge_baud = 115200; // baud rate + _prefs.bridge_channel = 1; // channel 1 + + // MQTT defaults (same as repeater) + StrHelper::strncpy(_prefs.mqtt_origin, "MeshCore-RoomServer", sizeof(_prefs.mqtt_origin)); + 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 = 0; // disabled (RX only for now) + _prefs.mqtt_status_interval = 300000; // 5 minutes + + // WiFi defaults (same as repeater) + 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 + + // Let's Mesh Analyzer defaults (same as repeater - both enabled by default) + _prefs.mqtt_analyzer_us_enabled = 1; // enabled + _prefs.mqtt_analyzer_eu_enabled = 1; // enabled + next_post_idx = 0; next_client_idx = 0; next_push = 0; @@ -650,6 +703,41 @@ void MyMesh::begin(FILESYSTEM *fs) { #if ENV_INCLUDE_GPS == 1 applyGpsPrefs(); #endif +#ifdef WITH_MQTT_BRIDGE + // Ensure analyzer servers are enabled by default (in case no prefs were loaded) - same as repeater + if (_prefs.mqtt_analyzer_us_enabled == 0 && _prefs.mqtt_analyzer_eu_enabled == 0) { + _prefs.mqtt_analyzer_us_enabled = 1; // enabled + _prefs.mqtt_analyzer_eu_enabled = 1; // enabled + MESH_DEBUG_PRINTLN("Setting analyzer servers to enabled by default"); + } + + // Set MQTT origin to actual device name (not build-time ADVERT_NAME) - same as repeater + StrHelper::strncpy(_prefs.mqtt_origin, _prefs.node_name, sizeof(_prefs.mqtt_origin)); + MESH_DEBUG_PRINTLN("MQTT origin set to device name: %s", _prefs.mqtt_origin); + + if (_prefs.bridge_enabled) { + // Set device public key for MQTT topics (same as repeater) + char device_id[65]; + mesh::LocalIdentity self_id = getSelfId(); + mesh::Utils::toHex(device_id, self_id.pub_key, PUB_KEY_SIZE); + MESH_DEBUG_PRINTLN("Setting device ID: %s", device_id); + bridge.setDeviceID(device_id); + + // Set firmware version (same as repeater) + bridge.setFirmwareVersion(getFirmwareVer()); + + // Set board model (same as repeater) + bridge.setBoardModel(_cli.getBoard()->getManufacturerName()); + + // Set build date (same as repeater) + bridge.setBuildDate(getBuildDate()); + + // Set stats sources for automatic stats collection (same as repeater) + bridge.setStatsSources(this, _radio, _cli.getBoard(), _ms); + + bridge.begin(); + } +#endif } void MyMesh::applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) { @@ -807,6 +895,9 @@ bool MyMesh::saveFilter(ClientInfo* client) { void MyMesh::loop() { mesh::Mesh::loop(); +#ifdef WITH_MQTT_BRIDGE + bridge.loop(); +#endif if (millisHasNowPassed(next_push) && acl.getNumClients() > 0) { // check for ACK timeouts diff --git a/examples/simple_room_server/MyMesh.h b/examples/simple_room_server/MyMesh.h index e7f1fee8..c8f4f774 100644 --- a/examples/simple_room_server/MyMesh.h +++ b/examples/simple_room_server/MyMesh.h @@ -23,6 +23,11 @@ #include #include +#ifdef WITH_MQTT_BRIDGE +#include "helpers/bridges/MQTTBridge.h" +#define WITH_BRIDGE +#endif + /* ------------------------------ Config -------------------------------- */ #ifndef FIRMWARE_BUILD_DATE @@ -110,6 +115,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t pending_sf; uint8_t pending_cr; int matching_peer_indexes[MAX_CLIENTS]; +#ifdef WITH_MQTT_BRIDGE + MQTTBridge bridge; +#endif void addPost(ClientInfo* client, const char* postData); void pushPostToClient(ClientInfo* client, PostInfo& post); diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 1b19f01a..e415e700 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -638,20 +638,19 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch sprintf(reply, "> %s", _prefs->mqtt_analyzer_us_enabled ? "on" : "off"); } else if (memcmp(config, "mqtt.analyzer.eu", 17) == 0) { sprintf(reply, "> %s", _prefs->mqtt_analyzer_eu_enabled ? "on" : "off"); - } else if (memcmp(config, "mqtt.owner", 10) == 0) { + } else if (sender_timestamp == 0 && memcmp(config, "mqtt.owner", 10) == 0) { // from serial command line only if (_prefs->mqtt_owner_public_key[0] != '\0') { sprintf(reply, "> %s", _prefs->mqtt_owner_public_key); } else { strcpy(reply, "> (not set)"); } - } else if (memcmp(config, "mqtt.email", 10) == 0) { + } else if (sender_timestamp == 0 && memcmp(config, "mqtt.email", 10) == 0) { // from serial command line only if (_prefs->mqtt_email[0] != '\0') { sprintf(reply, "> %s", _prefs->mqtt_email); } else { strcpy(reply, "> (not set)"); } } 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 diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index d264f26a..303f10c7 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -163,6 +163,38 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} ${esp32_ota.lib_deps} +[env:Heltec_v3_room_server_observer_mqtt] +extends = Heltec_lora32_v3 +build_flags = + ${Heltec_lora32_v3.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Heltec Room Observer"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:Heltec_v3_terminal_chat] extends = Heltec_lora32_v3 build_flags = diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index 57751409..77190ffb 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -127,6 +127,38 @@ lib_deps = JChristensen/Timezone paulstoffregen/Time +[env:heltec_v4_room_server_observer_mqtt] +extends = Heltec_lora32_v4 +build_flags = + ${Heltec_lora32_v4.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"Heltec Room Observer"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${Heltec_lora32_v4.build_src_filter} + + + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${Heltec_lora32_v4.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:heltec_v4_room_server] extends = Heltec_lora32_v4 build_flags = diff --git a/variants/lilygo_t3s3/platformio.ini b/variants/lilygo_t3s3/platformio.ini index e06b5ef1..73365a7f 100644 --- a/variants/lilygo_t3s3/platformio.ini +++ b/variants/lilygo_t3s3/platformio.ini @@ -136,6 +136,38 @@ lib_deps = JChristensen/Timezone paulstoffregen/Time +[env:LilyGo_T3S3_sx1262_room_server_observer_mqtt] +extends = LilyGo_T3S3_sx1262 +build_flags = + ${LilyGo_T3S3_sx1262.build_flags} + -D DISPLAY_CLASS=SSD1306Display + -D ADVERT_NAME='"T3S3-1262 Room Observer"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${LilyGo_T3S3_sx1262.build_src_filter} + + + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${LilyGo_T3S3_sx1262.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:LilyGo_T3S3_sx1262_terminal_chat] extends = LilyGo_T3S3_sx1262 build_flags = diff --git a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini index 8078ba93..946e2748 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/platformio.ini +++ b/variants/lilygo_tbeam_supreme_SX1262/platformio.ini @@ -130,6 +130,36 @@ lib_deps = JChristensen/Timezone paulstoffregen/Time +[env:T_Beam_S3_Supreme_SX1262_room_server_observer_mqtt] +extends = T_Beam_S3_Supreme_SX1262 +build_flags = + ${T_Beam_S3_Supreme_SX1262.build_flags} + -D ADVERT_NAME='"T_Beam_S3_Supreme_SX1262 Room Observer"' + -D ADVERT_LAT=0 + -D ADVERT_LON=0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter} + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${T_Beam_S3_Supreme_SX1262.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:T_Beam_S3_Supreme_SX1262_room_server] extends = T_Beam_S3_Supreme_SX1262 build_flags = diff --git a/variants/station_g2/platformio.ini b/variants/station_g2/platformio.ini index e7bb4ff5..59450b63 100644 --- a/variants/station_g2/platformio.ini +++ b/variants/station_g2/platformio.ini @@ -169,6 +169,38 @@ lib_deps = ${Station_G2.lib_deps} ${esp32_ota.lib_deps} +[env:Station_G2_room_server_observer_mqtt] +extends = Station_G2 +board_build.partitions = default_16MB.csv ; standard 16MB partition table +build_flags = + ${Station_G2.build_flags} + -D ADVERT_NAME='"Station G2 Room Observer"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${Station_G2.build_src_filter} + + + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${Station_G2.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:Station_G2_room_server] extends = Station_G2 build_src_filter = ${Station_G2.build_src_filter} diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index f7e727e6..34aea890 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -132,6 +132,36 @@ lib_deps = JChristensen/Timezone paulstoffregen/Time +[env:Xiao_S3_WIO_room_server_observer_mqtt] +extends = Xiao_S3_WIO +build_flags = + ${Xiao_S3_WIO.build_flags} + -D ADVERT_NAME='"XiaoS3 Room Observer"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' + -D WITH_MQTT_BRIDGE=1 + -D MAX_MQTT_BROKERS=3 + -D MQTT_MAX_PACKET_SIZE=1024 +; -D MQTT_DEBUG=1 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 + -D CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +build_src_filter = ${Xiao_S3_WIO.build_src_filter} + + + + + + + +<../examples/simple_room_server> +lib_deps = + ${Xiao_S3_WIO.lib_deps} + ${esp32_ota.lib_deps} + elims/PsychicMqttClient@^0.2.4 + bblanchon/ArduinoJson + arduino-libraries/NTPClient + JChristensen/Timezone + paulstoffregen/Time + [env:Xiao_S3_WIO_room_server] extends = Xiao_S3_WIO build_src_filter = ${Xiao_S3_WIO.build_src_filter}