mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-07-29 13:29:22 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user