diff --git a/include/power_manager.h b/include/power_manager.h index 89c387b..39c6159 100644 --- a/include/power_manager.h +++ b/include/power_manager.h @@ -6,4 +6,5 @@ void power_sender_init(); void power_receiver_init(); void read_battery(MeterData &data); +uint8_t battery_percent_from_voltage(float voltage_v); void go_to_deep_sleep(uint32_t seconds); diff --git a/include/test_mode.h b/include/test_mode.h index e8c2ce8..6de0f84 100644 --- a/include/test_mode.h +++ b/include/test_mode.h @@ -5,5 +5,5 @@ #ifdef ENABLE_TEST_MODE void test_sender_loop(uint16_t short_id, const char *device_id); -void test_receiver_loop(SenderStatus *statuses, uint8_t count); +void test_receiver_loop(SenderStatus *statuses, uint8_t count, uint16_t self_short_id); #endif diff --git a/src/json_codec.cpp b/src/json_codec.cpp index ce2ed7f..39b717b 100644 --- a/src/json_codec.cpp +++ b/src/json_codec.cpp @@ -1,9 +1,10 @@ #include "json_codec.h" #include #include +#include "power_manager.h" bool meterDataToJson(const MeterData &data, String &out_json) { - StaticJsonDocument<256> doc; + StaticJsonDocument<192> doc; doc["id"] = data.device_id; doc["ts"] = data.ts_utc; doc["energy_kwh"] = data.energy_total_kwh; @@ -14,8 +15,9 @@ bool meterDataToJson(const MeterData &data, String &out_json) { doc["v1_v"] = data.phase_voltage_v[0]; doc["v2_v"] = data.phase_voltage_v[1]; doc["v3_v"] = data.phase_voltage_v[2]; - doc["bat_v"] = data.battery_voltage_v; - doc["bat_pct"] = data.battery_percent; + char bat_buf[8]; + snprintf(bat_buf, sizeof(bat_buf), "%.2f", data.battery_voltage_v); + doc["bat_v"] = serialized(bat_buf); out_json = ""; size_t len = serializeJson(doc, out_json); @@ -23,7 +25,7 @@ bool meterDataToJson(const MeterData &data, String &out_json) { } bool jsonToMeterData(const String &json, MeterData &data) { - StaticJsonDocument<256> doc; + StaticJsonDocument<192> doc; DeserializationError err = deserializeJson(doc, json); if (err) { return false; @@ -43,7 +45,11 @@ bool jsonToMeterData(const String &json, MeterData &data) { data.phase_voltage_v[1] = doc["v2_v"] | NAN; data.phase_voltage_v[2] = doc["v3_v"] | NAN; data.battery_voltage_v = doc["bat_v"] | NAN; - data.battery_percent = doc["bat_pct"] | 0; + if (doc["bat_pct"].isNull() && !isnan(data.battery_voltage_v)) { + data.battery_percent = battery_percent_from_voltage(data.battery_voltage_v); + } else { + data.battery_percent = doc["bat_pct"] | 0; + } data.valid = true; if (strlen(data.device_id) >= 8) { diff --git a/src/main.cpp b/src/main.cpp index 0e40289..8b50199 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -164,7 +164,7 @@ void loop() { display_tick(); delay(50); } else { - test_receiver_loop(g_sender_statuses, NUM_SENDERS); + test_receiver_loop(g_sender_statuses, NUM_SENDERS, g_short_id); mqtt_loop(); web_server_loop(); display_set_receiver_status(g_ap_mode, wifi_is_connected() ? wifi_get_ssid().c_str() : "AP", mqtt_is_connected()); diff --git a/src/power_manager.cpp b/src/power_manager.cpp index 7dd204c..9d4701c 100644 --- a/src/power_manager.cpp +++ b/src/power_manager.cpp @@ -32,14 +32,18 @@ void read_battery(MeterData &data) { float v = (avg / 4095.0f) * ADC_REF_V * BATTERY_DIVIDER * BATTERY_CAL; data.battery_voltage_v = v; - float pct = (v - 3.0f) / (4.2f - 3.0f) * 100.0f; + data.battery_percent = battery_percent_from_voltage(v); +} + +uint8_t battery_percent_from_voltage(float voltage_v) { + float pct = (voltage_v - 3.0f) / (4.2f - 3.0f) * 100.0f; if (pct < 0.0f) { pct = 0.0f; } if (pct > 100.0f) { pct = 100.0f; } - data.battery_percent = static_cast(pct + 0.5f); + return static_cast(pct + 0.5f); } void go_to_deep_sleep(uint32_t seconds) { diff --git a/src/test_mode.cpp b/src/test_mode.cpp index 720dbdf..e70d023 100644 --- a/src/test_mode.cpp +++ b/src/test_mode.cpp @@ -8,10 +8,14 @@ #include "time_manager.h" #include "display_ui.h" #include "mqtt_client.h" +#include "power_manager.h" #include +#include static uint32_t g_last_test_ms = 0; +static uint16_t g_test_code_counter = 1000; static uint32_t g_last_timesync_ms = 0; +static constexpr uint32_t TEST_SEND_INTERVAL_MS = 30000; void test_sender_loop(uint16_t short_id, const char *device_id) { LoraPacket rx = {}; @@ -19,25 +23,42 @@ void test_sender_loop(uint16_t short_id, const char *device_id) { time_handle_timesync_payload(rx.payload, rx.payload_len); } - if (millis() - g_last_test_ms < 30000) { + if (millis() - g_last_test_ms < TEST_SEND_INTERVAL_MS) { return; } g_last_test_ms = millis(); char code[5]; - uint16_t val = random(0, 9999); - snprintf(code, sizeof(code), "%04u", val); + snprintf(code, sizeof(code), "%04u", g_test_code_counter); + g_test_code_counter++; + if (g_test_code_counter > 9999) { + g_test_code_counter = 1000; + } display_set_test_code(code); + MeterData data = {}; + data.short_id = short_id; + strncpy(data.device_id, device_id, sizeof(data.device_id)); + read_battery(data); + + uint32_t now_utc = time_get_utc(); + uint32_t ts = now_utc > 0 ? now_utc : millis() / 1000; + StaticJsonDocument<128> doc; doc["id"] = device_id; doc["role"] = "sender"; doc["test_code"] = code; - doc["ts"] = time_get_utc(); + doc["ts"] = ts; + char bat_buf[8]; + snprintf(bat_buf, sizeof(bat_buf), "%.2f", data.battery_voltage_v); + doc["bat_v"] = serialized(bat_buf); String json; serializeJson(doc, json); + data.ts_utc = ts; + display_set_last_meter(data); + uint8_t compressed[LORA_MAX_PAYLOAD]; size_t compressed_len = 0; if (!compressBuffer(reinterpret_cast(json.c_str()), json.length(), compressed, sizeof(compressed), compressed_len)) { @@ -54,10 +75,10 @@ void test_sender_loop(uint16_t short_id, const char *device_id) { lora_send(pkt); } -void test_receiver_loop(SenderStatus *statuses, uint8_t count) { +void test_receiver_loop(SenderStatus *statuses, uint8_t count, uint16_t self_short_id) { if (millis() - g_last_timesync_ms > TIME_SYNC_INTERVAL_SEC * 1000UL) { g_last_timesync_ms = millis(); - time_send_timesync(0); + time_send_timesync(self_short_id); } LoraPacket pkt = {}; @@ -68,7 +89,7 @@ void test_receiver_loop(SenderStatus *statuses, uint8_t count) { return; } - uint8_t decompressed[128]; + uint8_t decompressed[160]; size_t decompressed_len = 0; if (!decompressBuffer(pkt.payload, pkt.payload_len, decompressed, sizeof(decompressed), decompressed_len)) { return; @@ -82,10 +103,15 @@ void test_receiver_loop(SenderStatus *statuses, uint8_t count) { const char *id = doc["id"] | ""; const char *code = doc["test_code"] | ""; + float bat_v = doc["bat_v"] | NAN; for (uint8_t i = 0; i < count; ++i) { if (strncmp(statuses[i].last_data.device_id, id, sizeof(statuses[i].last_data.device_id)) == 0) { display_set_test_code_for_sender(i, code); + if (!isnan(bat_v)) { + statuses[i].last_data.battery_voltage_v = bat_v; + statuses[i].last_data.battery_percent = battery_percent_from_voltage(bat_v); + } statuses[i].has_data = true; statuses[i].last_update_ts_utc = time_get_utc(); break;