From f08d9a34d32012c06184b35ea49e95c757382f11 Mon Sep 17 00:00:00 2001 From: acidburns Date: Wed, 4 Feb 2026 02:33:43 +0100 Subject: [PATCH] Normalize power/energy output formatting --- src/display_ui.cpp | 36 ++++++++++++++++++++++++++---------- src/json_codec.cpp | 23 +++++++++++++++-------- src/web_server.cpp | 38 ++++++++++++++++++++++++++++---------- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/src/display_ui.cpp b/src/display_ui.cpp index c276d46..7fcc63e 100644 --- a/src/display_ui.cpp +++ b/src/display_ui.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include static Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1); @@ -161,6 +163,20 @@ static uint32_t age_seconds(uint32_t ts_utc, uint32_t ts_ms) { return (millis() - ts_ms) / 1000; } +static int32_t round_power_w(float value) { + if (isnan(value)) { + return 0; + } + long rounded = lroundf(value); + if (rounded > INT32_MAX) { + return INT32_MAX; + } + if (rounded < INT32_MIN) { + return INT32_MIN; + } + return static_cast(rounded); +} + static bool render_last_error_line(uint8_t y) { if (g_last_error == FaultType::None) { return false; @@ -238,15 +254,15 @@ static void render_sender_status() { static void render_sender_measurement() { display.clearDisplay(); display.setCursor(0, 0); - display.printf("E %.1f kWh", g_last_meter.energy_total_kwh); + display.printf("E %.2f kWh", g_last_meter.energy_total_kwh); display.setCursor(0, 12); - display.printf("P %.0fW", g_last_meter.total_power_w); + display.printf("P %dW", static_cast(round_power_w(g_last_meter.total_power_w))); display.setCursor(0, 24); - display.printf("L1 %.0fW", g_last_meter.phase_power_w[0]); + display.printf("L1 %dW", static_cast(round_power_w(g_last_meter.phase_power_w[0]))); display.setCursor(0, 36); - display.printf("L2 %.0fW", g_last_meter.phase_power_w[1]); + display.printf("L2 %dW", static_cast(round_power_w(g_last_meter.phase_power_w[1]))); display.setCursor(0, 48); - display.printf("L3 %.0fW", g_last_meter.phase_power_w[2]); + display.printf("L3 %dW", static_cast(round_power_w(g_last_meter.phase_power_w[2]))); display.display(); } @@ -336,17 +352,17 @@ static void render_receiver_sender(uint8_t index) { #endif display.setCursor(0, 12); - display.printf("E %.1f kWh", status.last_data.energy_total_kwh); + display.printf("E %.2f kWh", status.last_data.energy_total_kwh); display.setCursor(0, 22); - display.printf("L1 %.0fW", status.last_data.phase_power_w[0]); + display.printf("L1 %dW", static_cast(round_power_w(status.last_data.phase_power_w[0]))); display.setCursor(0, 32); - display.printf("L2 %.0fW", status.last_data.phase_power_w[1]); + display.printf("L2 %dW", static_cast(round_power_w(status.last_data.phase_power_w[1]))); display.setCursor(0, 42); - display.printf("L3 %.0fW", status.last_data.phase_power_w[2]); + display.printf("L3 %dW", static_cast(round_power_w(status.last_data.phase_power_w[2]))); display.setCursor(0, 52); display.print("P"); char p_buf[16]; - snprintf(p_buf, sizeof(p_buf), "%.0fW", status.last_data.total_power_w); + snprintf(p_buf, sizeof(p_buf), "%dW", static_cast(round_power_w(status.last_data.total_power_w))); int16_t x1 = 0; int16_t y1 = 0; uint16_t w = 0; diff --git a/src/json_codec.cpp b/src/json_codec.cpp index 765b53d..8f4343c 100644 --- a/src/json_codec.cpp +++ b/src/json_codec.cpp @@ -87,6 +87,17 @@ static void format_float_2(char *buf, size_t buf_len, float value) { snprintf(buf, buf_len, "%.2f", round2(value)); } +static void set_int_or_null(JsonDocument &doc, const char *key, float value) { + if (!key || key[0] == '\0') { + return; + } + if (isnan(value)) { + doc[key] = nullptr; + return; + } + doc[key] = round_to_i32(value); +} + bool meterDataToJson(const MeterData &data, String &out_json) { StaticJsonDocument<256> doc; doc["id"] = short_id_from_device_id(data.device_id); @@ -94,14 +105,10 @@ bool meterDataToJson(const MeterData &data, String &out_json) { char buf[16]; format_float_2(buf, sizeof(buf), data.energy_total_kwh); doc["e_kwh"] = serialized(buf); - format_float_2(buf, sizeof(buf), data.total_power_w); - doc["p_w"] = serialized(buf); - format_float_2(buf, sizeof(buf), data.phase_power_w[0]); - doc["p1_w"] = serialized(buf); - format_float_2(buf, sizeof(buf), data.phase_power_w[1]); - doc["p2_w"] = serialized(buf); - format_float_2(buf, sizeof(buf), data.phase_power_w[2]); - doc["p3_w"] = serialized(buf); + set_int_or_null(doc, "p_w", data.total_power_w); + set_int_or_null(doc, "p1_w", data.phase_power_w[0]); + set_int_or_null(doc, "p2_w", data.phase_power_w[1]); + set_int_or_null(doc, "p3_w", data.phase_power_w[2]); format_float_2(buf, sizeof(buf), data.battery_voltage_v); doc["bat_v"] = serialized(buf); doc["bat_pct"] = data.battery_percent; diff --git a/src/web_server.cpp b/src/web_server.cpp index ae40b03..dd60774 100644 --- a/src/web_server.cpp +++ b/src/web_server.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include static WebServer server(80); @@ -55,6 +57,20 @@ static HistoryJob g_history = {}; static constexpr size_t SD_LIST_MAX_FILES = 200; static constexpr size_t SD_DOWNLOAD_MAX_PATH = 160; +static int32_t round_power_w(float value) { + if (isnan(value)) { + return 0; + } + long rounded = lroundf(value); + if (rounded > INT32_MAX) { + return INT32_MAX; + } + if (rounded < INT32_MIN) { + return INT32_MIN; + } + return static_cast(rounded); +} + static bool auth_required() { return g_is_ap ? WEB_AUTH_REQUIRE_AP : WEB_AUTH_REQUIRE_STA; } @@ -360,10 +376,11 @@ static String render_sender_block(const SenderStatus &status) { if (!status.has_data) { s += "No data"; } else { - s += "Energy: " + String(status.last_data.energy_total_kwh, 3) + " kWh
"; - s += "Power: " + String(status.last_data.total_power_w, 1) + " W
"; - s += "P1/P2/P3: " + String(status.last_data.phase_power_w[0], 1) + " / " + String(status.last_data.phase_power_w[1], 1) + - " / " + String(status.last_data.phase_power_w[2], 1) + " W
"; + s += "Energy: " + String(status.last_data.energy_total_kwh, 2) + " kWh
"; + s += "Power: " + String(round_power_w(status.last_data.total_power_w)) + " W
"; + s += "P1/P2/P3: " + String(round_power_w(status.last_data.phase_power_w[0])) + " / " + + String(round_power_w(status.last_data.phase_power_w[1])) + " / " + + String(round_power_w(status.last_data.phase_power_w[2])) + " W
"; s += "Battery: " + String(status.last_data.battery_percent) + "% (" + String(status.last_data.battery_voltage_v, 2) + " V)"; } s += ""; @@ -597,11 +614,11 @@ static void handle_sender() { html += ""; html += "" + String(r) + ""; html += "" + String(d.ts_utc) + ""; - html += "" + String(d.energy_total_kwh, 3) + ""; - html += "" + String(d.total_power_w, 1) + ""; - html += "" + String(d.phase_power_w[0], 1) + ""; - html += "" + String(d.phase_power_w[1], 1) + ""; - html += "" + String(d.phase_power_w[2], 1) + ""; + html += "" + String(d.energy_total_kwh, 2) + ""; + html += "" + String(round_power_w(d.total_power_w)) + ""; + html += "" + String(round_power_w(d.phase_power_w[0])) + ""; + html += "" + String(round_power_w(d.phase_power_w[1])) + ""; + html += "" + String(round_power_w(d.phase_power_w[2])) + ""; html += "" + String(d.battery_voltage_v, 2) + ""; html += "" + String(d.battery_percent) + ""; html += "" + String(d.link_rssi_dbm) + ""; @@ -754,7 +771,8 @@ static void handle_history_data() { if (bin.count == 0) { server.sendContent(String("[") + bin.ts + ",null]"); } else { - server.sendContent(String("[") + bin.ts + "," + String(value, 2) + "]"); + int32_t rounded = round_power_w(value); + server.sendContent(String("[") + bin.ts + "," + String(rounded) + "]"); } } server.sendContent("]}");