feat(mqtt): enhance timestamp formatting with microsecond precision

Updated the MQTTMessageBuilder to include microsecond precision in the
timestamp formatting. The formatIsoTimestampForMqtt function now accepts
a microsecond parameter, allowing for more accurate time representation.
Modified related functions in MQTTBridge to utilize the new timestamp
formatting, ensuring consistency across MQTT messages.
This commit is contained in:
agessaman committed 2026-06-18 15:03:02 -07:00
1 parent 50154563ba
commit 618c849d53
3 files changed
+43 -15

No files matched your search

+29 -11
View File
@@ -3,18 +3,26 @@
#include <cstring>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <Timezone.h>
#include "MeshCore.h"
void MQTTMessageBuilder::formatIsoTimestampForMqtt(time_t now, Timezone* timezone, char* buffer, size_t buffer_size) {
void MQTTMessageBuilder::formatIsoTimestampForMqtt(time_t now, long usec, Timezone* timezone, char* buffer, size_t buffer_size) {
if (!buffer || buffer_size == 0) return;
// Always emit UTC with an explicit "+00:00" offset, matching Python's
// datetime.now(timezone.utc).isoformat(). The system clock is UTC (SNTP offset 0),
// so gmtime() is correct regardless of the prefs Timezone (now unused here).
(void)timezone;
// Clamp the sub-second to a valid microsecond range so the "%06ld" field can never
// overflow to 7 digits or go negative on a bad clock read.
if (usec < 0) usec = 0;
else if (usec > 999999) usec = 999999;
struct tm* tm_info = gmtime(&now);
if (tm_info && strftime(buffer, buffer_size, "%Y-%m-%dT%H:%M:%S.000000+00:00", tm_info) > 0) {
return;
if (tm_info) {
size_t n = strftime(buffer, buffer_size, "%Y-%m-%dT%H:%M:%S", tm_info);
if (n > 0 && snprintf(buffer + n, buffer_size - n, ".%06ld+00:00", usec) > 0) {
return;
}
}
strncpy(buffer, "2024-01-01T12:00:00.000000+00:00", buffer_size - 1);
buffer[buffer_size - 1] = '\0';
@@ -231,9 +239,13 @@ int MQTTMessageBuilder::buildPacketJSON(
) {
if (!packet) return 0;
time_t now = time(nullptr);
// One wall-clock read: tv_sec feeds both the timestamp and the UTC time/date
// fields below (kept consistent), tv_usec is the real sub-second.
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
time_t now = now_tv.tv_sec;
char timestamp[40];
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
formatIsoTimestampForMqtt(now, now_tv.tv_usec, timezone, timestamp, sizeof(timestamp));
// Packet time/date: UTC (gmtime), same family as meshcoretomqtt serial fields
struct tm* utc_timeinfo = gmtime(&now);
@@ -305,9 +317,13 @@ int MQTTMessageBuilder::buildPacketJSONFromRaw(
) {
if (!packet || !raw_data || raw_len <= 0) return 0;
time_t now = time(nullptr);
// One wall-clock read: tv_sec feeds both the timestamp and the UTC time/date
// fields below (kept consistent), tv_usec is the real sub-second.
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
time_t now = now_tv.tv_sec;
char timestamp[40];
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
formatIsoTimestampForMqtt(now, now_tv.tv_usec, timezone, timestamp, sizeof(timestamp));
struct tm* utc_timeinfo = gmtime(&now);
@@ -370,11 +386,13 @@ int MQTTMessageBuilder::buildRawJSON(
size_t buffer_size
) {
if (!packet) return 0;
time_t now = time(nullptr);
// One wall-clock read: tv_sec for the timestamp, tv_usec for the real sub-second.
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
char timestamp[40];
formatIsoTimestampForMqtt(now, timezone, timestamp, sizeof(timestamp));
formatIsoTimestampForMqtt(now_tv.tv_sec, now_tv.tv_usec, timezone, timestamp, sizeof(timestamp));
// Convert packet to hex
// MAX_TRANS_UNIT is 255, so max hex size is 510 chars + null = 511 bytes
char raw_hex[1024];
+7 -2
View File
@@ -26,11 +26,16 @@ public:
/**
* Format the MQTT JSON `timestamp` field (same rule for status, packet, raw).
* Always UTC with an explicit "+00:00" offset, ISO-8601
* "%Y-%m-%dT%H:%M:%S.000000+00:00" (matches Python
* "%Y-%m-%dT%H:%M:%S.uuuuuu+00:00" (matches Python
* datetime.now(timezone.utc).isoformat()). The `timezone` parameter is retained
* for API compatibility but ignored — the system clock is UTC.
*
* `usec` is the sub-second component in microseconds (0..999999), normally taken
* from the same gettimeofday() read as `now` so the two don't tear at a second
* boundary. It is a real sub-second (SNTP-maintained wall clock), not a literal;
* pass 0 if no sub-second source is available.
*/
static void formatIsoTimestampForMqtt(time_t now, Timezone* timezone, char* buffer, size_t buffer_size);
static void formatIsoTimestampForMqtt(time_t now, long usec, Timezone* timezone, char* buffer, size_t buffer_size);
/**
* Build status message JSON
+7 -2
View File
@@ -5,6 +5,7 @@
#include <WiFiUdp.h>
#include <Timezone.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>
#ifdef WITH_SNMP
@@ -1712,7 +1713,9 @@ void MQTTBridge::publishStatusToSlot(int index) {
// Status timestamp: UTC with explicit +00:00 offset, same as packet/raw JSON
// `timestamp` (system clock is UTC — SNTP offset 0; prefs Timezone is separate).
MQTTMessageBuilder::formatIsoTimestampForMqtt(time(nullptr), _timezone, timestamp, sizeof(timestamp));
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
MQTTMessageBuilder::formatIsoTimestampForMqtt(now_tv.tv_sec, now_tv.tv_usec, _timezone, timestamp, sizeof(timestamp));
snprintf(radio_info, sizeof(radio_info), "%.6f,%.1f,%d,%d",
_prefs->freq, _prefs->bw, _prefs->sf, _prefs->cr);
@@ -2423,7 +2426,9 @@ bool MQTTBridge::publishStatus() {
// Status timestamp: UTC with explicit +00:00 offset, same as packet/raw JSON
// `timestamp` (system clock is UTC — SNTP offset 0; prefs Timezone is separate).
MQTTMessageBuilder::formatIsoTimestampForMqtt(time(nullptr), _timezone, timestamp, sizeof(timestamp));
struct timeval now_tv;
gettimeofday(&now_tv, nullptr);
MQTTMessageBuilder::formatIsoTimestampForMqtt(now_tv.tv_sec, now_tv.tv_usec, _timezone, timestamp, sizeof(timestamp));
snprintf(radio_info, sizeof(radio_info), "%.6f,%.1f,%d,%d",
_prefs->freq, _prefs->bw, _prefs->sf, _prefs->cr);