added timezone handling, updated mqtt packet output to use the proper timezones for timestamp and time/date, updated mqtt packet to use raw data instead of parsed packet structure, updated packet upload to use proper packet hash

This commit is contained in:
agessaman
2026-01-02 13:36:41 -08:00
parent 26c4b9704c
commit 787af7b026
8 changed files with 171 additions and 22 deletions
+10 -2
View File
@@ -343,12 +343,19 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
mesh::Utils::printHex(Serial, raw, len);
Serial.println();
#endif
#ifdef WITH_BRIDGE
if (_prefs.bridge_enabled) {
// Store raw radio data for MQTT messages
bridge.storeRawRadioData(raw, len, snr, rssi);
}
#endif
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_BRIDGE
if (_prefs.bridge_pkt_src == 1) {
bridge.sendPacket(pkt);
bridge.onPacketReceived(pkt);
}
#endif
@@ -712,7 +719,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
// bridge defaults
_prefs.bridge_enabled = 1; // enabled
_prefs.bridge_delay = 500; // milliseconds
_prefs.bridge_pkt_src = 0; // logTx
_prefs.bridge_pkt_src = 1; // logRx (RX packets)
_prefs.bridge_baud = 115200; // baud rate
_prefs.bridge_channel = 1; // channel 1
@@ -729,6 +736,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_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
+12 -8
View File
@@ -70,7 +70,6 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156
file.read((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157
file.read((uint8_t *)&_prefs->advert_loc_policy, sizeof (_prefs->advert_loc_policy)); // 161
<<<<<<< HEAD
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 (from upstream)
// MQTT settings - skip reading from main prefs file (now stored separately)
// For backward compatibility, we'll skip these bytes if they exist in old files
@@ -178,7 +177,6 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->gps_enabled, sizeof(_prefs->gps_enabled)); // 156
file.write((uint8_t *)&_prefs->gps_interval, sizeof(_prefs->gps_interval)); // 157
file.write((uint8_t *)&_prefs->advert_loc_policy, sizeof(_prefs->advert_loc_policy)); // 161
<<<<<<< HEAD
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162 (from upstream)
// MQTT settings - no longer saved here (stored in separate /mqtt_prefs file)
// Write zeros/padding to maintain file format compatibility
@@ -401,8 +399,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "> %s", _prefs->mqtt_status_enabled ? "on" : "off");
} else if (memcmp(config, "mqtt.packets", 12) == 0) {
sprintf(reply, "> %s", _prefs->mqtt_packets_enabled ? "on" : "off");
} else if (memcmp(config, "mqtt.raw", 8) == 0) {
sprintf(reply, "> %s", _prefs->mqtt_raw_enabled ? "on" : "off");
} 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");
} else if (memcmp(config, "mqtt.interval", 13) == 0) {
sprintf(reply, "> %d", (uint32_t)_prefs->mqtt_status_interval);
} else if (memcmp(config, "wifi.ssid", 9) == 0) {
@@ -623,10 +623,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_prefs->mqtt_packets_enabled = memcmp(&config[13], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.raw ", 9) == 0) {
_prefs->mqtt_raw_enabled = memcmp(&config[9], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.raw ", 9) == 0) {
_prefs->mqtt_raw_enabled = memcmp(&config[9], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.tx ", 8) == 0) {
_prefs->mqtt_tx_enabled = memcmp(&config[8], "on", 2) == 0;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "mqtt.interval ", 15) == 0) {
uint32_t interval = _atoi(&config[15]);
if (interval >= 1000 && interval <= 3600000) { // 1 second to 1 hour
-2
View File
@@ -111,8 +111,6 @@ struct MQTTPrefs {
uint8_t mqtt_analyzer_eu_enabled; // Enable EU analyzer server
char mqtt_owner_public_key[65]; // Owner public key (hex string, same length as repeater public key)
char mqtt_email[64]; // Owner email address for matching nodes with owners
=======
>>>>>>> 0b761e8 (Add timezone support to MQTT Bridge and CLI configuration)
};
#endif
+86 -2
View File
@@ -2,6 +2,7 @@
#include <ArduinoJson.h>
#include <time.h>
#include <Timezone.h>
#include "MeshCore.h"
int MQTTMessageBuilder::buildStatusMessage(
const char* origin,
@@ -146,9 +147,11 @@ int MQTTMessageBuilder::buildPacketJSON(
int packet_type = packet->getPayloadType();
const char* route_str = getRouteTypeString(packet->isRouteDirect() ? 1 : 0);
// Create hash (simplified - use first 8 bytes of packet)
// Create proper packet hash using MeshCore's calculatePacketHash method
char hash_str[17];
bytesToHex(packet->payload, min(8, (int)packet->payload_len), hash_str, sizeof(hash_str));
uint8_t packet_hash[MAX_HASH_SIZE];
packet->calculatePacketHash(packet_hash);
bytesToHex(packet_hash, MAX_HASH_SIZE, hash_str, sizeof(hash_str));
// Build path string for direct packets
char path_str[128] = "";
@@ -173,6 +176,87 @@ int MQTTMessageBuilder::buildPacketJSON(
);
}
int MQTTMessageBuilder::buildPacketJSONFromRaw(
const uint8_t* raw_data,
int raw_len,
mesh::Packet* packet,
bool is_tx,
const char* origin,
const char* origin_id,
float snr,
float rssi,
Timezone* timezone,
char* buffer,
size_t buffer_size
) {
if (!packet || !raw_data || raw_len <= 0) return 0;
// Get current device time (should be UTC since system timezone is set to UTC)
time_t now = time(nullptr);
// Convert to local time using timezone library (for timestamp field only)
time_t local_time = timezone ? timezone->toLocal(now) : now;
struct tm* local_timeinfo = localtime(&local_time);
// Format timestamp in ISO 8601 format (LOCAL TIME)
char timestamp[32];
if (local_timeinfo) {
strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S.000000", local_timeinfo);
} else {
strcpy(timestamp, "2024-01-01T12:00:00.000000");
}
// Get UTC time (since system timezone is UTC, time() returns UTC)
struct tm* utc_timeinfo = gmtime(&now);
// Format time and date (ALWAYS UTC)
char time_str[16];
char date_str[16];
if (utc_timeinfo) {
strftime(time_str, sizeof(time_str), "%H:%M:%S", utc_timeinfo);
strftime(date_str, sizeof(date_str), "%d/%m/%Y", utc_timeinfo);
} else {
strcpy(time_str, "12:00:00");
strcpy(date_str, "01/01/2024");
}
// Convert raw radio data to hex (this includes radio headers)
char raw_hex[512];
bytesToHex(raw_data, raw_len, raw_hex, sizeof(raw_hex));
// Get packet characteristics from the parsed packet
int packet_type = packet->getPayloadType();
const char* route_str = getRouteTypeString(packet->isRouteDirect() ? 1 : 0);
// Create proper packet hash using MeshCore's calculatePacketHash method
char hash_str[17];
uint8_t packet_hash[MAX_HASH_SIZE];
packet->calculatePacketHash(packet_hash);
bytesToHex(packet_hash, MAX_HASH_SIZE, hash_str, sizeof(hash_str));
// Build path string for direct packets
char path_str[128] = "";
if (packet->isRouteDirect() && packet->path_len > 0) {
// Simplified path representation
snprintf(path_str, sizeof(path_str), "path_len_%d", packet->path_len);
}
return buildPacketMessage(
origin, origin_id, timestamp,
is_tx ? "tx" : "rx",
time_str, date_str,
raw_len, // Use actual raw radio data length
packet_type, route_str,
packet->payload_len,
raw_hex,
snr, // Use actual SNR from radio
rssi, // Use actual RSSI from radio
hash_str,
packet->isRouteDirect() ? path_str : nullptr,
buffer, buffer_size
);
}
int MQTTMessageBuilder::buildRawJSON(
mesh::Packet* packet,
const char* origin,
+14
View File
@@ -128,6 +128,20 @@ public:
size_t buffer_size
);
static int buildPacketJSONFromRaw(
const uint8_t* raw_data,
int raw_len,
mesh::Packet* packet,
bool is_tx,
const char* origin,
const char* origin_id,
float snr,
float rssi,
Timezone* timezone,
char* buffer,
size_t buffer_size
);
/**
* Convert packet to raw JSON message
*
+30 -7
View File
@@ -11,7 +11,7 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc
_active_brokers(0), _queue_head(0), _queue_tail(0), _queue_count(0),
_last_status_publish(0), _status_interval(300000), // 5 minutes default
_ntp_client(_ntp_udp, "pool.ntp.org", 0, 60000), _last_ntp_sync(0), _ntp_synced(false),
_timezone(nullptr) {
_timezone(nullptr), _last_raw_len(0), _last_snr(0), _last_rssi(0), _last_raw_timestamp(0) {
// Initialize default values
strncpy(_origin, "MeshCore-Repeater", sizeof(_origin) - 1);
@@ -22,6 +22,7 @@ MQTTBridge::MQTTBridge(NodePrefs *prefs, mesh::PacketManager *mgr, mesh::RTCCloc
_status_enabled = true;
_packets_enabled = true;
_raw_enabled = false;
_tx_enabled = false; // Disable TX packets by default
// Initialize packet queue
memset(_packet_queue, 0, sizeof(_packet_queue));
@@ -71,6 +72,7 @@ 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;
_status_interval = _prefs->mqtt_status_interval;
MQTT_DEBUG_PRINTLN("Origin: %s, IATA: %s", _origin, _iata);
@@ -173,9 +175,9 @@ void MQTTBridge::onPacketReceived(mesh::Packet *packet) {
}
void MQTTBridge::sendPacket(mesh::Packet *packet) {
if (!_initialized || !_packets_enabled) return;
if (!_initialized || !_packets_enabled || !_tx_enabled) return;
// Queue packet for transmission
// Queue packet for transmission (only if TX enabled)
queuePacket(packet, true);
}
@@ -319,10 +321,20 @@ void MQTTBridge::publishPacket(mesh::Packet* packet, bool is_tx) {
strncpy(origin_id, _device_id, sizeof(origin_id) - 1);
origin_id[sizeof(origin_id) - 1] = '\0';
// Build packet message
int len = MQTTMessageBuilder::buildPacketJSON(
packet, is_tx, _origin, origin_id, _timezone, json_buffer, sizeof(json_buffer)
);
// Build packet message using raw radio data if available
int len;
if (_last_raw_len > 0 && (millis() - _last_raw_timestamp) < 1000) {
// Use raw radio data (within 1 second of packet)
len = MQTTMessageBuilder::buildPacketJSONFromRaw(
_last_raw_data, _last_raw_len, packet, is_tx, _origin, origin_id,
_last_snr, _last_rssi, _timezone, json_buffer, sizeof(json_buffer)
);
} else {
// Fallback to reconstructed packet data
len = MQTTMessageBuilder::buildPacketJSON(
packet, is_tx, _origin, origin_id, _timezone, json_buffer, sizeof(json_buffer)
);
}
if (len > 0) {
// Publish to all connected brokers
@@ -450,6 +462,17 @@ void MQTTBridge::setBoardModel(const char* board_model) {
_board_model[sizeof(_board_model) - 1] = '\0';
}
void MQTTBridge::storeRawRadioData(const uint8_t* raw_data, int len, float snr, float rssi) {
if (len > 0 && len <= sizeof(_last_raw_data)) {
memcpy(_last_raw_data, raw_data, len);
_last_raw_len = len;
_last_snr = snr;
_last_rssi = rssi;
_last_raw_timestamp = millis();
MQTT_DEBUG_PRINTLN("Stored raw radio data: %d bytes, SNR=%.1f, RSSI=%.1f", len, snr, rssi);
}
}
void MQTTBridge::setMessageTypes(bool status, bool packets, bool raw) {
_status_enabled = status;
_packets_enabled = packets;
+18
View File
@@ -74,6 +74,7 @@ private:
bool _status_enabled;
bool _packets_enabled;
bool _raw_enabled;
bool _tx_enabled;
unsigned long _last_status_publish;
unsigned long _status_interval;
@@ -99,6 +100,13 @@ private:
// Timezone handling
Timezone* _timezone;
// Raw radio data storage
uint8_t _last_raw_data[256];
int _last_raw_len;
float _last_snr;
float _last_rssi;
unsigned long _last_raw_timestamp;
// Internal methods
void connectToBrokers();
void processPacketQueue();
@@ -212,6 +220,16 @@ public:
*/
void setBoardModel(const char* board_model);
/**
* Stores raw radio data for MQTT messages
*
* @param raw_data Raw radio transmission data
* @param len Length of raw data
* @param snr Signal-to-noise ratio
* @param rssi Received signal strength indicator
*/
void storeRawRadioData(const uint8_t* raw_data, int len, float snr, float rssi);
/**
* Enable/disable message types
*
+1 -1
View File
@@ -104,7 +104,7 @@ lib_deps =
[env:Heltec_v3_repeater_bridge_mqtt]
extends = Heltec_lora32_v3
upload_port = /dev/cu.usbserial-2
upload_port = /dev/cu.usbserial-4
build_flags =
${Heltec_lora32_v3.build_flags}
-D DISPLAY_CLASS=SSD1306Display