fix repeater-observer hybrid freezing

This commit is contained in:
liquidraver
2026-04-09 12:23:27 +02:00
parent 0898699c33
commit 4f6b0cca27
7 changed files with 184 additions and 3 deletions
+1 -1
View File
@@ -22,6 +22,7 @@ namespace mesh {
LoRaRadioBase::LoRaRadioBase(const struct device *lora_dev, MainBoard &board,
NodePrefs *prefs)
: _dev(lora_dev), _prefs(prefs), _board(&board),
_loramac_node(false),
_in_recv_mode(0), _tx_active(0),
_last_rssi(0), _last_snr(0),
_rx_head(0), _rx_tail(0),
@@ -29,7 +30,6 @@ LoRaRadioBase::LoRaRadioBase(const struct device *lora_dev, MainBoard &board,
_rx_duty_cycle_enabled(IS_ENABLED(CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE)),
_rx_boost_enabled(true),
_tx_power_reduction_db(0),
_loramac_node(false),
_config_cached(false),
_rx_cb(nullptr), _rx_cb_user_data(nullptr),
_tx_done_cb(nullptr), _tx_done_cb_user_data(nullptr),
+78 -1
View File
@@ -39,7 +39,7 @@ namespace mesh {
ObserverMesh::ObserverMesh(Radio &radio, MillisecondClock &ms, RNG &rng, RTCClock &rtc)
: Dispatcher(radio, ms, _pkt_mgr),
_last_rssi(0.0f), _last_score(0.0f), _last_raw_len(0),
_store(nullptr), _creds(nullptr), _rng(&rng), _rtc(&rtc)
_store(nullptr), _creds(nullptr), _rng(&rng), _rtc(&rtc), _start_uptime_secs(0)
{
memset(_pubkey_hex, 0, sizeof(_pubkey_hex));
memset(_packets_topic, 0, sizeof(_packets_topic));
@@ -52,6 +52,7 @@ void ObserverMesh::begin(RepeaterDataStore *store, struct ObserverCreds *creds)
{
_store = store;
_creds = creds;
_start_uptime_secs = (uint32_t)(k_uptime_get() / 1000);
/* Initialize prefs with observer-specific defaults */
initNodePrefs(&_prefs);
@@ -92,6 +93,82 @@ void ObserverMesh::begin(RepeaterDataStore *store, struct ObserverCreds *creds)
Dispatcher::begin();
}
void ObserverMesh::buildStatusJson(const char *status, char *out, size_t out_size)
{
uint32_t now_epoch = _rtc ? _rtc->getCurrentTime() : 0;
struct tm tm_now;
time_t t = (time_t)now_epoch;
gmtime_r(&t, &tm_now);
char ts_buf[48];
snprintf(ts_buf, sizeof(ts_buf), "%04d-%02d-%02dT%02d:%02d:%02d.000000",
tm_now.tm_year + 1900, tm_now.tm_mon + 1, tm_now.tm_mday,
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
char radio_buf[48];
snprintf(radio_buf, sizeof(radio_buf), "%.3f,%.1f,%u,%u",
(double)_prefs.freq, (double)_prefs.bw,
(unsigned)_prefs.sf, (unsigned)_prefs.cr);
uint32_t uptime_secs = (uint32_t)(k_uptime_get() / 1000);
if (uptime_secs >= _start_uptime_secs) {
uptime_secs -= _start_uptime_secs;
} else {
uptime_secs = 0;
}
int noise_floor = ((LoRaRadioBase *)_radio)->getNoiseFloor();
uint32_t recv_errors = ((LoRaRadioBase *)_radio)->getPacketsRecvErrors();
snprintf(out, out_size,
"{"
"\"status\":\"%s\","
"\"timestamp\":\"%s\","
"\"origin\":\"%s\","
"\"origin_id\":\"%s\","
"\"radio\":\"%s\","
"\"model\":\"%s\","
"\"firmware_version\":\"%s\","
"\"client_version\":\"zephcoretomqtt/1.1\","
"\"stats\":{"
"\"battery_mv\":%u,"
"\"uptime_secs\":%u,"
"\"debug_flags\":%u,"
"\"queue_len\":%u,"
"\"noise_floor\":%d,"
"\"tx_air_secs\":%u,"
"\"rx_air_secs\":%u,"
"\"recv_errors\":%u"
"}"
"}",
status,
ts_buf,
_prefs.node_name,
_pubkey_hex,
radio_buf,
#ifdef CONFIG_ZEPHCORE_BOARD_NAME
CONFIG_ZEPHCORE_BOARD_NAME,
#else
"unknown",
#endif
FIRMWARE_VERSION,
0u,
uptime_secs,
0u,
0u,
noise_floor,
0u,
0u,
recv_errors);
}
void ObserverMesh::publishStatus(const char *status)
{
static char json_buf[768];
buildStatusJson(status, json_buf, sizeof(json_buf));
mqtt_publisher_enqueue(_status_topic, json_buf, strlen(json_buf));
}
void ObserverMesh::buildTopics()
{
const char *iata = (_creds && _creds->mqtt_iata[0] != '\0')
+3
View File
@@ -53,6 +53,8 @@ class ObserverMesh : public Dispatcher {
/* Private helpers */
void buildTopics();
void enqueuePacket(Packet *pkt);
void buildStatusJson(const char *status, char *out, size_t out_size);
uint32_t _start_uptime_secs;
protected:
/* Capture RSSI + raw bytes before packet is parsed */
@@ -78,6 +80,7 @@ public:
* topic so that CoreScope can place it on the map. No-op if lat/lon
* are not configured in the creds struct. */
void publishSelfAdvert();
void publishStatus(const char *status);
/* Accessors used by main_observer.cpp */
NodePrefs *getNodePrefs() { return &_prefs; }
+83
View File
@@ -841,6 +841,7 @@ RepeaterMesh::RepeaterMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Mil
_uplink_last_score = 0.0f;
_uplink_last_rssi = 0.0f;
_uplink_last_raw_len = 0;
_uplink_next_status_at = 0;
#endif
}
@@ -890,6 +891,12 @@ void RepeaterMesh::begin(RepeaterDataStore* store) {
zc_wifi_station_start(&_uplink_creds, uplink_time_sync_cb);
mqtt_publisher_start(&_uplink_creds, _prefs.node_name,
_uplink_status_topic, _uplink_packets_topic);
mqtt_publisher_set_connect_cb([]() {
if (s_uplink_mesh) {
s_uplink_mesh->publishUplinkStatus("online");
}
});
_uplink_next_status_at = futureMillis(300000);
LOG_INF("Repeater uplink active: %s", _uplink_packets_topic);
} else {
LOG_INF("Repeater uplink inactive");
@@ -1430,6 +1437,75 @@ void RepeaterMesh::publishUplinkPacket(mesh::Packet *pkt)
}
mqtt_publisher_enqueue(_uplink_packets_topic, json_buf, json_len);
}
void RepeaterMesh::publishUplinkStatus(const char *status)
{
if (!isUplinkEnabled()) return;
if (_uplink_status_topic[0] == '\0') return;
auto& radio_driver = getRadioDriver(_radio);
uint32_t now_epoch = getRTCClock()->getCurrentTime();
struct tm tm_now;
time_t t = (time_t)now_epoch;
gmtime_r(&t, &tm_now);
char ts_buf[48];
snprintf(ts_buf, sizeof(ts_buf), "%04d-%02d-%02dT%02d:%02d:%02d.000000",
tm_now.tm_year + 1900, tm_now.tm_mon + 1, tm_now.tm_mday,
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
char radio_buf[48];
snprintf(radio_buf, sizeof(radio_buf), "%.3f,%.1f,%u,%u",
(double)_prefs.freq, (double)_prefs.bw,
(unsigned)_prefs.sf, (unsigned)_prefs.cr);
static char json_buf[768];
int json_len = snprintf(json_buf, sizeof(json_buf),
"{"
"\"status\":\"%s\","
"\"timestamp\":\"%s\","
"\"origin\":\"%s\","
"\"origin_id\":\"%s\","
"\"radio\":\"%s\","
"\"model\":\"%s\","
"\"firmware_version\":\"%s\","
"\"client_version\":\"zephcoretomqtt/1.1\","
"\"stats\":{"
"\"battery_mv\":%u,"
"\"uptime_secs\":%u,"
"\"debug_flags\":%u,"
"\"queue_len\":%u,"
"\"noise_floor\":%d,"
"\"tx_air_secs\":%u,"
"\"rx_air_secs\":%u,"
"\"recv_errors\":%u"
"}"
"}",
status,
ts_buf,
_prefs.node_name,
_uplink_pubkey_hex,
radio_buf,
#ifdef CONFIG_ZEPHCORE_BOARD_NAME
CONFIG_ZEPHCORE_BOARD_NAME,
#else
"unknown",
#endif
FIRMWARE_VERSION,
(unsigned)_board.getBattMilliVolts(),
(unsigned)(uptime_millis / 1000),
(unsigned)_err_flags,
(unsigned)_mgr->getOutboundTotal(),
_radio->getNoiseFloor(),
(unsigned)(getTotalAirTime() / 1000),
(unsigned)(getReceiveAirTime() / 1000),
(unsigned)radio_driver.getPacketsRecvErrors());
if (json_len <= 0 || json_len >= (int)sizeof(json_buf)) {
return;
}
mqtt_publisher_enqueue(_uplink_status_topic, json_buf, json_len);
}
#endif
void RepeaterMesh::loop() {
@@ -1463,6 +1539,13 @@ void RepeaterMesh::loop() {
dirty_contacts_expiry = 0;
}
#if IS_ENABLED(CONFIG_ZEPHCORE_REPEATER_UPLINK) && IS_ENABLED(CONFIG_MQTT_LIB)
if (_uplink_next_status_at && millisHasNowPassed(_uplink_next_status_at)) {
publishUplinkStatus("online");
_uplink_next_status_at = futureMillis(300000);
}
#endif
uint32_t now = k_uptime_get();
uptime_millis += now - last_millis;
last_millis = now;
+2
View File
@@ -118,6 +118,7 @@ class RepeaterMesh : public mesh::Mesh, public CommonCLICallbacks {
float _uplink_last_rssi;
uint8_t _uplink_last_raw[MAX_TRANS_UNIT];
int _uplink_last_raw_len;
unsigned long _uplink_next_status_at;
#endif
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
@@ -177,6 +178,7 @@ protected:
}
bool saveUplinkCreds();
void publishUplinkPacket(mesh::Packet *pkt);
void publishUplinkStatus(const char *status);
#endif
public:
+15 -1
View File
@@ -43,7 +43,8 @@ static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
#define MESH_EVENT_LORA_RX BIT(0)
#define MESH_EVENT_CLI_RX BIT(1)
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_CLI_RX)
#define MESH_EVENT_STATUS BIT(2)
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_CLI_RX | MESH_EVENT_STATUS)
static struct k_event mesh_events;
@@ -94,6 +95,14 @@ static void lora_rx_callback(void *user_data)
k_event_post(&mesh_events, MESH_EVENT_LORA_RX);
}
static void status_timer_fn(struct k_timer *timer)
{
ARG_UNUSED(timer);
k_event_post(&mesh_events, MESH_EVENT_STATUS);
}
K_TIMER_DEFINE(status_timer, status_timer_fn, NULL);
/* Observer never transmits — TX done callback not needed */
/* ========== Help banner ========== */
@@ -326,9 +335,11 @@ int main(void)
* 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();
}
});
k_timer_start(&status_timer, K_SECONDS(300), K_SECONDS(300));
LOG_INF("Observer event loop running");
@@ -345,6 +356,9 @@ int main(void)
if (ev & MESH_EVENT_CLI_RX) {
process_cli_rx();
}
if (ev & MESH_EVENT_STATUS) {
observer_mesh.publishStatus("online");
}
}
return 0;
+2
View File
@@ -36,3 +36,5 @@ CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# ESP32 HAL's mcuboot_config.h is patched (patches/modules/hal/espressif/...)
# to respect Zephyr Kconfig: mbedTLS detection, AUTO sector count, slot0 validation.
CONFIG_BOOT_RSA_PSA=y
CONFIG_BOOT_RSA_TF_PSA_CRYPTO_LEGACY=n