diff --git a/.gitignore b/.gitignore index cd98f23..359f911 100644 --- a/.gitignore +++ b/.gitignore @@ -106,3 +106,4 @@ CRYPTO_AUDIT_INDEX.md AUDIT_MASTER_PLAN.md usb_companion_probe.py CORE_MESH_AUDIT_INDEX.md +UPLINK_AUDIT_INDEX.md diff --git a/zephcore/app/RepeaterMesh.cpp b/zephcore/app/RepeaterMesh.cpp index bb0aefd..60aa6a0 100644 --- a/zephcore/app/RepeaterMesh.cpp +++ b/zephcore/app/RepeaterMesh.cpp @@ -935,6 +935,7 @@ RepeaterMesh::RepeaterMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Mil _uplink_last_rssi = 0.0f; _uplink_last_raw_len = 0; _uplink_next_status_at = 0; + atomic_set(&_uplink_connect_pending, 0); #endif } @@ -992,8 +993,12 @@ void RepeaterMesh::begin(RepeaterDataStore* store) { mqtt_publisher_start(&_uplink_creds, _prefs.node_name, _uplink_status_topic, _uplink_packets_topic); mqtt_publisher_set_connect_cb([]() { + /* Runs on the MQTT publisher thread — DON'T publish here. It would + * race the main-thread status path on the shared static JSON buffer + * and toggle the battery-ADC regulator off-main. Defer to the main + * loop via an atomic flag drained in maintenanceLoop(). */ if (s_uplink_mesh) { - s_uplink_mesh->publishUplinkStatus("online"); + atomic_set(&s_uplink_mesh->_uplink_connect_pending, 1); } }); _uplink_next_status_at = futureMillis(300000); @@ -1346,6 +1351,11 @@ void RepeaterMesh::loop() { } #if IS_ENABLED(CONFIG_ZEPHCORE_REPEATER_UPLINK) && IS_ENABLED(CONFIG_MQTT_LIB) + /* MQTT (re)connected — publish the initial "online" status here on the main + * thread (the CONNACK callback only sets this flag, off-main). */ + if (atomic_cas(&_uplink_connect_pending, 1, 0)) { + publishUplinkStatus("online"); + } if (_uplink_next_status_at && millisHasNowPassed(_uplink_next_status_at)) { publishUplinkStatus("online"); _uplink_next_status_at = futureMillis(300000); diff --git a/zephcore/app/RepeaterMesh.h b/zephcore/app/RepeaterMesh.h index 77cc925..20a22cf 100644 --- a/zephcore/app/RepeaterMesh.h +++ b/zephcore/app/RepeaterMesh.h @@ -12,6 +12,7 @@ #pragma once +#include #include #include #include @@ -122,6 +123,11 @@ class RepeaterMesh : public mesh::Mesh, public CommonCLICallbacks { uint8_t _uplink_last_raw[MAX_TRANS_UNIT]; int _uplink_last_raw_len; unsigned long _uplink_next_status_at; + /* Set by the MQTT CONNACK callback (runs on the MQTT publisher thread); + * drained on the main thread in maintenanceLoop() so the "online" status + * publish never runs off-main (avoids the shared static-JSON-buffer race + * and off-main battery-ADC regulator toggling). */ + atomic_t _uplink_connect_pending; #endif void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr); diff --git a/zephcore/app/main_observer.cpp b/zephcore/app/main_observer.cpp index 3fac239..c51d096 100644 --- a/zephcore/app/main_observer.cpp +++ b/zephcore/app/main_observer.cpp @@ -49,10 +49,11 @@ static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios); /* ========== Event loop bits ========== */ -#define MESH_EVENT_LORA_RX BIT(0) -#define MESH_EVENT_CLI_RX BIT(1) -#define MESH_EVENT_STATUS BIT(2) -#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_CLI_RX | MESH_EVENT_STATUS) +#define MESH_EVENT_LORA_RX BIT(0) +#define MESH_EVENT_CLI_RX BIT(1) +#define MESH_EVENT_STATUS BIT(2) +#define MESH_EVENT_MQTT_CONNECT BIT(3) /* MQTT (re)connected — publish status+self-advert on main */ +#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_CLI_RX | MESH_EVENT_STATUS | MESH_EVENT_MQTT_CONNECT) static struct k_event mesh_events; @@ -343,10 +344,10 @@ int main(void) /* Publish self-advert once on each MQTT connect so CoreScope can pin * this observer on the map (requires lat/lon to be configured). */ mqtt_publisher_set_connect_cb([]() { - if (s_mesh_ptr) { - s_mesh_ptr->publishStatus("online"); - s_mesh_ptr->publishSelfAdvert(); - } + /* Runs on the MQTT publisher thread — defer to the main loop. Publishing + * here would race the periodic-status path on publishStatus()'s shared + * static JSON buffer; publishSelfAdvert() also signs + formats. */ + k_event_post(&mesh_events, MESH_EVENT_MQTT_CONNECT); }); k_timer_start(&status_timer, K_SECONDS(300), K_SECONDS(300)); @@ -368,6 +369,10 @@ int main(void) if (ev & MESH_EVENT_STATUS) { observer_mesh.publishStatus("online"); } + if (ev & MESH_EVENT_MQTT_CONNECT) { + observer_mesh.publishStatus("online"); + observer_mesh.publishSelfAdvert(); + } } return 0;