diff --git a/include/display_ui.h b/include/display_ui.h index fb9427d..b71bc25 100644 --- a/include/display_ui.h +++ b/include/display_ui.h @@ -11,6 +11,8 @@ void display_set_sender_statuses(const SenderStatus *statuses, uint8_t count); void display_set_last_meter(const MeterData &data); void display_set_last_read(bool ok, uint32_t ts_utc); void display_set_last_tx(bool ok, uint32_t ts_utc); +void display_set_sender_queue(uint8_t depth, bool build_pending); +void display_set_sender_batches(uint16_t last_acked_batch_id, uint16_t current_batch_id); void display_set_last_error(FaultType type, uint32_t ts_utc, uint32_t ts_ms); void display_set_receiver_status(bool ap_mode, const char *ssid, bool mqtt_ok); void display_power_down(); diff --git a/src/display_ui.cpp b/src/display_ui.cpp index e0ba429..3df8ecc 100644 --- a/src/display_ui.cpp +++ b/src/display_ui.cpp @@ -19,6 +19,10 @@ static uint32_t g_last_read_ts = 0; static uint32_t g_last_tx_ts = 0; static uint32_t g_last_read_ms = 0; static uint32_t g_last_tx_ms = 0; +static uint8_t g_sender_queue_depth = 0; +static bool g_sender_build_pending = false; +static uint16_t g_sender_last_acked_batch_id = 0; +static uint16_t g_sender_current_batch_id = 0; static FaultType g_last_error = FaultType::None; static uint32_t g_last_error_ts = 0; static uint32_t g_last_error_ms = 0; @@ -111,6 +115,16 @@ void display_set_last_tx(bool ok, uint32_t ts_utc) { g_last_tx_ms = millis(); } +void display_set_sender_queue(uint8_t depth, bool build_pending) { + g_sender_queue_depth = depth; + g_sender_build_pending = build_pending; +} + +void display_set_sender_batches(uint16_t last_acked_batch_id, uint16_t current_batch_id) { + g_sender_last_acked_batch_id = last_acked_batch_id; + g_sender_current_batch_id = current_batch_id; +} + void display_set_last_error(FaultType type, uint32_t ts_utc, uint32_t ts_ms) { g_last_error = type; g_last_error_ts = ts_utc; @@ -195,7 +209,13 @@ static void render_sender_status() { display.printf("Read %s %lus ago", g_last_read_ok ? "OK" : "ERR", static_cast(age_seconds(g_last_read_ts, g_last_read_ms))); display.setCursor(0, 36); - display.printf("TX %s %lus ago", g_last_tx_ok ? "OK" : "ERR", static_cast(age_seconds(g_last_tx_ts, g_last_tx_ms))); + display.printf("TX %s %lus Q%u%s A%u C%u", + g_last_tx_ok ? "OK" : "ERR", + static_cast(age_seconds(g_last_tx_ts, g_last_tx_ms)), + g_sender_queue_depth, + g_sender_build_pending ? "+" : "", + g_sender_last_acked_batch_id, + g_sender_current_batch_id); #ifdef ENABLE_TEST_MODE if (strlen(g_test_code) > 0) { diff --git a/src/json_codec.cpp b/src/json_codec.cpp index f57694e..1f369e7 100644 --- a/src/json_codec.cpp +++ b/src/json_codec.cpp @@ -183,6 +183,8 @@ bool meterBatchToJson(const MeterData *samples, size_t count, uint16_t batch_id, doc["sender"] = sender_label; doc["batch_id"] = batch_id; doc["t0"] = samples[0].ts_utc; + doc["t_first"] = samples[0].ts_utc; + doc["t_last"] = samples[count - 1].ts_utc; uint32_t dt_s = METER_SAMPLE_INTERVAL_MS / 1000; doc["dt_s"] = dt_s > 0 ? dt_s : 1; doc["n"] = static_cast(count); @@ -247,6 +249,8 @@ bool jsonToMeterBatch(const String &json, MeterData *out_samples, size_t max_cou } uint32_t t0 = doc["t0"] | 0; + uint32_t t_first = doc["t_first"] | t0; + uint32_t t_last = doc["t_last"] | t_first; uint32_t dt_s = doc["dt_s"] | 1; JsonArray energy = doc["energy_wh"].as(); JsonArray p_w = doc["p_w"].as(); @@ -268,7 +272,13 @@ bool jsonToMeterBatch(const String &json, MeterData *out_samples, size_t max_cou snprintf(data.device_id, sizeof(data.device_id), "dd3-0000"); } - data.ts_utc = t0 + static_cast(idx) * dt_s; + if (count > 1 && t_last >= t_first) { + uint32_t span = t_last - t_first; + uint32_t step = span / static_cast(count - 1); + data.ts_utc = t_first + static_cast(idx) * step; + } else { + data.ts_utc = t0 + static_cast(idx) * dt_s; + } data.energy_total_kwh = static_cast((energy[idx] | 0)) / 1000.0f; data.total_power_w = static_cast(p_w[idx] | 0); data.phase_power_w[0] = static_cast(p1_w[idx] | 0); diff --git a/src/main.cpp b/src/main.cpp index 00bcbba..04d404e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,6 +76,7 @@ static uint8_t g_last_battery_percent = 0; static uint32_t g_last_battery_ms = 0; static uint16_t g_batch_id = 1; static uint16_t g_last_sent_batch_id = 0; +static uint16_t g_last_acked_batch_id = 0; static uint8_t g_batch_retry_count = 0; static bool g_batch_ack_pending = false; static MeterData g_inflight_samples[METER_BATCH_MAX_SAMPLES]; @@ -542,6 +543,8 @@ void setup() { static void sender_loop() { watchdog_kick(); uint32_t now_ms = millis(); + display_set_sender_queue(g_batch_count, g_build_count > 0); + display_set_sender_batches(g_last_acked_batch_id, g_batch_id); if (now_ms - g_last_sample_ms >= METER_SAMPLE_INTERVAL_MS) { g_last_sample_ms = now_ms; @@ -589,6 +592,7 @@ static void sender_loop() { uint16_t ack_receiver = read_u16_le(&rx.payload[4]); if (ack_sender == g_short_id && ack_receiver == rx.device_id_short && g_batch_ack_pending && ack_id == g_last_sent_batch_id) { + g_last_acked_batch_id = ack_id; finish_inflight_batch(); } }