#include "test_mode.h" #ifdef ENABLE_TEST_MODE #include "config.h" #include "compressor.h" #include "lora_transport.h" #include "json_codec.h" #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 = {}; if (lora_receive(rx, 0) && rx.payload_type == PayloadType::TimeSync) { time_handle_timesync_payload(rx.payload, rx.payload_len); } if (millis() - g_last_test_ms < TEST_SEND_INTERVAL_MS) { return; } g_last_test_ms = millis(); char code[5]; 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"] = 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)) { return; } LoraPacket pkt = {}; pkt.protocol_version = PROTOCOL_VERSION; pkt.role = DeviceRole::Sender; pkt.device_id_short = short_id; pkt.payload_type = PayloadType::TestCode; pkt.payload_len = compressed_len; memcpy(pkt.payload, compressed, compressed_len); lora_send(pkt); } 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(self_short_id); } LoraPacket pkt = {}; if (!lora_receive(pkt, 0)) { return; } if (pkt.payload_type != PayloadType::TestCode) { return; } uint8_t decompressed[160]; size_t decompressed_len = 0; if (!decompressBuffer(pkt.payload, pkt.payload_len, decompressed, sizeof(decompressed), decompressed_len)) { return; } decompressed[decompressed_len] = '\0'; StaticJsonDocument<128> doc; if (deserializeJson(doc, reinterpret_cast(decompressed)) != DeserializationError::Ok) { return; } 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; } } mqtt_publish_test(id, String(reinterpret_cast(decompressed))); } #endif