Refactor MQTT bridge handling in MyMesh to support RX and TX packet logging based on MQTT settings. Introduce mqtt_rx_enabled preference for default RX packet handling and update related logic in CommonCLI and MQTTBridge for improved configuration management. Enhance MQTTMessageBuilder to conditionally include SNR and RSSI for RX packets only.

This commit is contained in:
agessaman
2026-04-04 16:01:35 -07:00
parent 258a0d724c
commit 47b632aaa9
11 changed files with 2739 additions and 37 deletions
+24 -10
View File
@@ -56,7 +56,8 @@ reboot
```bash
get wifi.ssid
get bridge.enabled
get bridge.source
get mqtt.rx
get mqtt.tx
get mqtt.origin
get mqtt.iata
get mqtt1.preset
@@ -190,7 +191,8 @@ The MQTT bridge comes with the following defaults for fresh installs:
- **Status Messages**: Enabled
- **Packet Messages**: Enabled
- **Raw Messages**: Disabled
- **TX Messages**: Disabled (RX only by default)
- **RX Packets**: Enabled (uplink received packets)
- **TX Packets**: Disabled (`off` — set to `on` or `advert` to enable)
- **Status Interval**: 5 minutes (300000 ms)
- **Slot 1**: `analyzer-us`
- **Slot 2**: `analyzer-eu`
@@ -316,7 +318,8 @@ These settings apply across all MQTT slots:
- `get mqtt.status` - Get MQTT status summary (connection info per slot)
- `get mqtt.packets` - Get packet message setting (on/off)
- `get mqtt.raw` - Get raw message setting (on/off)
- `get mqtt.tx` - Get TX message setting (on/off)
- `get mqtt.rx` - Get RX packet uplinking setting (on/off)
- `get mqtt.tx` - Get TX packet uplinking setting (on/off/advert)
- `get mqtt.interval` - Get status publish interval
- `get mqtt.owner` - Get owner public key (serial console only)
- `get mqtt.email` - Get owner email address (serial console only)
@@ -327,7 +330,11 @@ These settings apply across all MQTT slots:
- `set mqtt.status on|off` - Enable/disable status messages
- `set mqtt.packets on|off` - Enable/disable packet messages
- `set mqtt.raw on|off` - Enable/disable raw messages
- `set mqtt.tx on|off` - Enable/disable TX packet messages
- `set mqtt.rx on|off` - Enable/disable RX (received) packet uplinking
- `set mqtt.tx on|off|advert` - Set TX packet uplinking mode:
- `on` - Uplink all transmitted packets
- `advert` - Uplink only this node's own advert packets (self-originated)
- `off` - Disable TX packet uplinking
- `set mqtt.interval <minutes>` - Set status publish interval (1-60 minutes)
- `set mqtt.owner <64-hex-char-public-key>` - Set owner public key
- `set mqtt.email <email>` - Set owner email address
@@ -412,10 +419,11 @@ The CLI commands are organized into two levels:
### Bridge Commands (`bridge.*`)
**Low-level bridge control** - These settings apply to all bridge types (MQTT, RS232, ESP-NOW, etc.):
- `bridge.enabled` - Master switch for the entire bridge system
- `bridge.source` - Controls which packet events to capture (RX vs TX)
- `bridge.source` - Controls which packet events to capture for non-MQTT bridges (RS232, ESP-NOW). For MQTT, use `mqtt.rx` and `mqtt.tx` instead.
### Bridge-Specific Commands (`mqtt.*`, `mqttN.*`, `wifi.*`, `timezone.*`)
**Implementation-specific settings** - These only apply to the MQTT bridge:
- `mqtt.rx` / `mqtt.tx` - Independent per-direction packet uplinking control
- `mqttN.*` - Per-slot MQTT broker configuration (N = 1-6)
- `mqtt.*` - Shared MQTT settings (message types, origin, IATA, etc.)
- `wifi.*` - WiFi connection settings for MQTT connectivity
@@ -474,6 +482,10 @@ Minimal raw packet data for map integration.
}
```
**Notes:**
- `SNR` and `RSSI` are only present for RX packets (received from radio). TX packets omit these fields since the packet originates from this node.
- `path` is only present for direct-route packets with path data.
### Raw Message
```json
{
@@ -501,8 +513,9 @@ Minimal raw packet data for map integration.
### Raw Radio Data Capture
- Captures actual raw radio transmission data (including radio headers)
- Uses proper MeshCore packet hashing (SHA256-based)
- Provides accurate SNR/RSSI values from actual radio reception
- Supports both RX and TX packet uplinking (configurable)
- Provides accurate SNR/RSSI values from actual radio reception (RX packets only)
- Independent RX and TX packet uplinking — both can be active simultaneously
- TX advert mode: selectively uplink only this node's own advert packets
### Timezone Support
- Full timezone support with automatic DST handling
@@ -605,7 +618,8 @@ set mqtt3.preset meshmapper
### Step 8: Verify Connection
```
get bridge.enabled
get bridge.source
get mqtt.rx
get mqtt.tx
get mqtt.status
get wifi.status
```
@@ -624,8 +638,8 @@ reboot
```
get bridge.enabled
set bridge.enabled on
get bridge.source # Should be "rx"
set bridge.source rx
get mqtt.rx # Should be "on"
set mqtt.rx on
get mqtt.status # Check per-slot connection status
get mqtt1.diag # Last slot error details (TLS/sock/time)
get mqtt2.diag
+32 -1
View File
@@ -947,12 +947,43 @@ region save
- `set bridge.source <source>`
**Parameters:**
- `source`:
- `source`:
- `logRx`: bridges received packets
- `logTx`: bridges transmitted packets
**Default:** `logTx`
> **Note:** For MQTT bridges, use `mqtt.rx` and `mqtt.tx` instead of `bridge.source`. These provide independent per-direction control and support both RX and TX simultaneously. `bridge.source` still works as a convenience alias for MQTT (setting `bridge.source rx` sets `mqtt.rx on` + `mqtt.tx off`, and vice versa), but `mqtt.rx`/`mqtt.tx` are preferred.
---
#### View or change MQTT RX packet uplinking
**Usage:**
- `get mqtt.rx`
- `set mqtt.rx <on|off>`
**Parameters:**
- `on`: uplink received (RX) packets to MQTT brokers
- `off`: disable RX packet uplinking
**Default:** `on`
---
#### View or change MQTT TX packet uplinking
**Usage:**
- `get mqtt.tx`
- `set mqtt.tx <on|off|advert>`
**Parameters:**
- `on`: uplink all transmitted (TX) packets to MQTT brokers
- `advert`: uplink only this node's own advert packets (self-originated advertisements only — forwarded adverts from other nodes are filtered out)
- `off`: disable TX packet uplinking
**Default:** `off`
> **Note:** `mqtt.rx` and `mqtt.tx` take effect immediately — no restart required. Both can be enabled simultaneously.
---
#### View or change the speed of the bridge (RS-232 only)
+11 -2
View File
@@ -504,7 +504,11 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_BRIDGE
#ifdef WITH_MQTT_BRIDGE
// MQTT bridge: always feed RX packets — bridge decides based on mqtt.rx setting
if (bridge) bridge->onPacketReceived(pkt);
#elif defined(WITH_BRIDGE)
// Non-MQTT bridge (ESP-NOW): use bridge.source setting
if (_prefs.bridge_pkt_src == 1) {
if (bridge) bridge->onPacketReceived(pkt);
}
@@ -530,7 +534,11 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
}
void MyMesh::logTx(mesh::Packet *pkt, int len) {
#ifdef WITH_BRIDGE
#ifdef WITH_MQTT_BRIDGE
// MQTT bridge: always feed TX packets — bridge decides based on mqtt.tx setting
if (bridge) bridge->sendPacket(pkt);
#elif defined(WITH_BRIDGE)
// Non-MQTT bridge (ESP-NOW): use bridge.source setting
if (_prefs.bridge_pkt_src == 0) {
if (bridge) bridge->sendPacket(pkt);
}
@@ -948,6 +956,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.mqtt_packets_enabled = 1; // enabled
_prefs.mqtt_raw_enabled = 0; // disabled
_prefs.mqtt_tx_enabled = 0; // disabled (RX only for now)
_prefs.mqtt_rx_enabled = 1; // RX packets enabled by default
_prefs.mqtt_status_interval = 300000; // 5 minutes
// WiFi defaults
+5 -8
View File
@@ -219,10 +219,8 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
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)
if (bridge) bridge->onPacketReceived(pkt);
}
// MQTT bridge: always feed RX packets — bridge decides based on mqtt.rx setting
if (_prefs.bridge_enabled && bridge) bridge->onPacketReceived(pkt);
#endif
if (_logging) {
@@ -245,10 +243,8 @@ 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)
if (bridge) bridge->sendPacket(pkt);
}
// MQTT bridge: always feed TX packets — bridge decides based on mqtt.tx setting
if (_prefs.bridge_enabled && bridge) bridge->sendPacket(pkt);
#endif
if (_logging) {
@@ -673,6 +669,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.mqtt_packets_enabled = 1; // enabled
_prefs.mqtt_raw_enabled = 0; // disabled
_prefs.mqtt_tx_enabled = 0; // disabled (RX only for now)
_prefs.mqtt_rx_enabled = 1; // RX packets enabled by default
_prefs.mqtt_status_interval = 300000; // 5 minutes
// WiFi defaults (same as repeater)
Binary file not shown.
+28 -3
View File
@@ -132,6 +132,9 @@ void CommonCLI::loadPrefs(FILESYSTEM* fs) {
_prefs->bridge_pkt_src = 1; // Set to RX (logRx)
savePrefs(fs); // Save the updated preference
}
// mqtt_rx_enabled: new field appended to end of MQTTPrefs. On upgrade from older firmware,
// the shorter /mqtt_prefs file won't contain it, so it keeps the default value (1 = on)
// set by setMQTTPrefsDefaults(). No explicit migration needed.
#endif
}
@@ -353,7 +356,8 @@ static void setMQTTPrefsDefaults(MQTTPrefs* prefs) {
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 = 0; // disabled by default (RX only)
prefs->mqtt_tx_enabled = 0; // disabled 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);
@@ -530,6 +534,7 @@ void CommonCLI::syncMQTTPrefsToNodePrefs() {
_prefs->mqtt_packets_enabled = _mqtt_prefs.mqtt_packets_enabled;
_prefs->mqtt_raw_enabled = _mqtt_prefs.mqtt_raw_enabled;
_prefs->mqtt_tx_enabled = _mqtt_prefs.mqtt_tx_enabled;
_prefs->mqtt_rx_enabled = _mqtt_prefs.mqtt_rx_enabled;
_prefs->mqtt_status_interval = _mqtt_prefs.mqtt_status_interval;
StrHelper::strncpy(_prefs->wifi_ssid, _mqtt_prefs.wifi_ssid, sizeof(_prefs->wifi_ssid));
StrHelper::strncpy(_prefs->wifi_password, _mqtt_prefs.wifi_password, sizeof(_prefs->wifi_password));
@@ -560,6 +565,7 @@ void CommonCLI::syncNodePrefsToMQTTPrefs() {
_mqtt_prefs.mqtt_packets_enabled = _prefs->mqtt_packets_enabled;
_mqtt_prefs.mqtt_raw_enabled = _prefs->mqtt_raw_enabled;
_mqtt_prefs.mqtt_tx_enabled = _prefs->mqtt_tx_enabled;
_mqtt_prefs.mqtt_rx_enabled = _prefs->mqtt_rx_enabled;
_mqtt_prefs.mqtt_status_interval = _prefs->mqtt_status_interval;
StrHelper::strncpy(_mqtt_prefs.wifi_ssid, _prefs->wifi_ssid, sizeof(_mqtt_prefs.wifi_ssid));
StrHelper::strncpy(_mqtt_prefs.wifi_password, _prefs->wifi_password, sizeof(_mqtt_prefs.wifi_password));
@@ -864,7 +870,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else if (memcmp(config, "mqtt.raw", 8) == 0) {
sprintf(reply, "> %s", _prefs->mqtt_raw_enabled ? "on" : "off");
} else if (memcmp(config, "mqtt.tx", 7) == 0) {
sprintf(reply, "> %s", _prefs->mqtt_tx_enabled ? "on" : "off");
const char* tx_str = _prefs->mqtt_tx_enabled == 2 ? "advert" : (_prefs->mqtt_tx_enabled ? "on" : "off");
sprintf(reply, "> %s", tx_str);
} else if (memcmp(config, "mqtt.rx", 7) == 0) {
sprintf(reply, "> %s", _prefs->mqtt_rx_enabled ? "on" : "off");
} else if (memcmp(config, "mqtt.interval", 13) == 0) {
// Display interval in minutes (rounded)
uint32_t minutes = (_prefs->mqtt_status_interval + 29999) / 60000; // Round up
@@ -1262,6 +1271,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
}
} else if (memcmp(config, "bridge.source ", 14) == 0) {
_prefs->bridge_pkt_src = memcmp(&config[14], "rx", 2) == 0;
// Also update mqtt.rx/mqtt.tx for MQTT bridge compatibility
if (_prefs->bridge_pkt_src == 1) {
_prefs->mqtt_rx_enabled = 1;
_prefs->mqtt_tx_enabled = 0;
} else {
_prefs->mqtt_rx_enabled = 0;
_prefs->mqtt_tx_enabled = 1;
}
savePrefs();
strcpy(reply, "OK");
#endif
@@ -1321,7 +1338,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.tx ", 8) == 0) {
_prefs->mqtt_tx_enabled = memcmp(&config[8], "on", 2) == 0;
if (memcmp(&config[8], "advert", 6) == 0) {
_prefs->mqtt_tx_enabled = 2;
} else {
_prefs->mqtt_tx_enabled = memcmp(&config[8], "on", 2) == 0 ? 1 : 0;
}
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.rx ", 8) == 0) {
_prefs->mqtt_rx_enabled = memcmp(&config[8], "on", 2) == 0 ? 1 : 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.interval ", 14) == 0) {
+6 -2
View File
@@ -66,9 +66,10 @@ struct NodePrefs { // persisted to file
uint8_t mqtt_status_enabled; // Enable status messages
uint8_t mqtt_packets_enabled; // Enable packet messages
uint8_t mqtt_raw_enabled; // Enable raw messages
uint8_t mqtt_tx_enabled; // Enable TX packet uplinking
uint8_t mqtt_tx_enabled; // TX packet uplinking: 0=off, 1=all, 2=advert (self-originated only)
uint32_t mqtt_status_interval; // Status publish interval (ms)
uint8_t mqtt_rx_enabled; // Enable RX packet uplinking (default: on)
// WiFi settings
char wifi_ssid[32]; // WiFi SSID
char wifi_password[64]; // WiFi password
@@ -175,6 +176,9 @@ struct MQTTPrefs {
char mqtt_slot_token[MAX_MQTT_SLOTS][48]; // Per-slot token (e.g., MeshRank account token)
char mqtt_slot_topic[MAX_MQTT_SLOTS][96]; // Per-slot custom topic template (custom preset only)
char mqtt_slot_audience[MAX_MQTT_SLOTS][64]; // JWT audience (non-empty enables JWT auth for custom slots)
// --- Appended fields (added after initial 6-slot migration) ---
uint8_t mqtt_rx_enabled; // Enable RX packet uplinking (default: on)
};
// 3-slot MQTTPrefs layout — used for migrating from 3-slot to 6-slot format.
+5 -2
View File
@@ -122,8 +122,11 @@ int MQTTMessageBuilder::buildPacketMessage(
root["route"] = route;
root["payload_len"] = payload_len_str;
root["raw"] = raw;
root["SNR"] = snr_str;
root["RSSI"] = rssi_str;
// SNR and RSSI are only meaningful for RX packets (received from radio)
if (strcmp(direction, "rx") == 0) {
root["SNR"] = snr_str;
root["RSSI"] = rssi_str;
}
root["hash"] = hash;
if (path && strlen(path) > 0) {
+19 -8
View File
@@ -319,7 +319,8 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc
_status_enabled = true;
_packets_enabled = true;
_raw_enabled = false;
_tx_enabled = false;
_rx_enabled = true;
_tx_mode = 0;
// Initialize all slots to empty/disabled state
for (int i = 0; i < RUNTIME_MQTT_SLOTS; i++) {
@@ -439,7 +440,8 @@ void MQTTBridge::begin() {
_status_enabled = _prefs->mqtt_status_enabled;
_packets_enabled = _prefs->mqtt_packets_enabled;
_raw_enabled = _prefs->mqtt_raw_enabled;
_tx_enabled = _prefs->mqtt_tx_enabled;
_rx_enabled = _prefs->mqtt_rx_enabled;
_tx_mode = _prefs->mqtt_tx_enabled; // 0=off, 1=all, 2=advert
// Set status interval to 5 minutes (300000 ms), or use preference if set and valid
if (_prefs->mqtt_status_interval >= 1000 && _prefs->mqtt_status_interval <= 3600000) {
_status_interval = _prefs->mqtt_status_interval;
@@ -1764,11 +1766,11 @@ void MQTTBridge::setSlotCustomBroker(int slot_index, const char* host, uint16_t
// ---------------------------------------------------------------------------
void MQTTBridge::checkConfigurationMismatch() {
// Check if bridge.source is set to tx (logTx) but mqtt.tx is disabled
if (_prefs->bridge_pkt_src == 0 && _packets_enabled && !_tx_enabled) {
// Warn if packets are enabled but both rx and tx are off — nothing will be published
if (_prefs->mqtt_packets_enabled && !_prefs->mqtt_rx_enabled && _prefs->mqtt_tx_enabled == 0) {
unsigned long now = millis();
if (_last_config_warning == 0 || (now - _last_config_warning > CONFIG_WARNING_INTERVAL)) {
MQTT_DEBUG_PRINTLN("MQTT: Configuration mismatch detected! bridge.source=tx (logTx) but mqtt.tx=off. Packets will not be published. Run 'set bridge.source rx' or 'set mqtt.tx on' to fix.");
MQTT_DEBUG_PRINTLN("MQTT: Both mqtt.rx and mqtt.tx are off — no packets will be published. Run 'set mqtt.rx on' or 'set mqtt.tx on' to fix.");
_last_config_warning = now;
}
} else {
@@ -2038,7 +2040,7 @@ void MQTTBridge::loop() {
// ---------------------------------------------------------------------------
void MQTTBridge::onPacketReceived(mesh::Packet *packet) {
if (!_initialized || !_packets_enabled) return;
if (!_initialized || !_prefs->mqtt_packets_enabled || !_prefs->mqtt_rx_enabled) return;
// Check if we have any enabled slots to send to
bool has_valid_slots = false;
@@ -2055,9 +2057,18 @@ void MQTTBridge::onPacketReceived(mesh::Packet *packet) {
}
void MQTTBridge::sendPacket(mesh::Packet *packet) {
if (!_initialized || !_packets_enabled || !_tx_enabled) return;
uint8_t tx_mode = _prefs->mqtt_tx_enabled; // Read live from prefs (no restart needed)
if (!_initialized || !_prefs->mqtt_packets_enabled || tx_mode == 0) return;
// Queue packet for transmission (only if TX enabled)
// Advert mode: only queue self-originated advert packets
if (tx_mode == 2) {
if (packet->getPayloadType() != PAYLOAD_TYPE_ADVERT) return;
if (packet->payload_len < PUB_KEY_SIZE) return;
// Advert payload starts with advertiser's 32-byte public key — compare to our identity
if (!_identity || memcmp(_identity->pub_key, packet->payload, PUB_KEY_SIZE) != 0) return;
}
// Queue packet for transmission
queuePacket(packet, true);
}
+2 -1
View File
@@ -110,7 +110,8 @@ private:
bool _status_enabled;
bool _packets_enabled;
bool _raw_enabled;
bool _tx_enabled;
bool _rx_enabled;
uint8_t _tx_mode; // 0=off, 1=all TX, 2=self-advert only
unsigned long _last_status_publish;
unsigned long _status_interval;
+2607
View File
File diff suppressed because it is too large Load Diff