Normalize power/energy output formatting

This commit is contained in:
2026-02-04 02:33:43 +01:00
parent 7e5e23e56c
commit f08d9a34d3
3 changed files with 69 additions and 28 deletions

View File

@@ -4,6 +4,8 @@
#include <Wire.h> #include <Wire.h>
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h> #include <Adafruit_SSD1306.h>
#include <limits.h>
#include <math.h>
#include <time.h> #include <time.h>
static Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1); 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; 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<int32_t>(rounded);
}
static bool render_last_error_line(uint8_t y) { static bool render_last_error_line(uint8_t y) {
if (g_last_error == FaultType::None) { if (g_last_error == FaultType::None) {
return false; return false;
@@ -238,15 +254,15 @@ static void render_sender_status() {
static void render_sender_measurement() { static void render_sender_measurement() {
display.clearDisplay(); display.clearDisplay();
display.setCursor(0, 0); 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.setCursor(0, 12);
display.printf("P %.0fW", g_last_meter.total_power_w); display.printf("P %dW", static_cast<int>(round_power_w(g_last_meter.total_power_w)));
display.setCursor(0, 24); display.setCursor(0, 24);
display.printf("L1 %.0fW", g_last_meter.phase_power_w[0]); display.printf("L1 %dW", static_cast<int>(round_power_w(g_last_meter.phase_power_w[0])));
display.setCursor(0, 36); display.setCursor(0, 36);
display.printf("L2 %.0fW", g_last_meter.phase_power_w[1]); display.printf("L2 %dW", static_cast<int>(round_power_w(g_last_meter.phase_power_w[1])));
display.setCursor(0, 48); display.setCursor(0, 48);
display.printf("L3 %.0fW", g_last_meter.phase_power_w[2]); display.printf("L3 %dW", static_cast<int>(round_power_w(g_last_meter.phase_power_w[2])));
display.display(); display.display();
} }
@@ -336,17 +352,17 @@ static void render_receiver_sender(uint8_t index) {
#endif #endif
display.setCursor(0, 12); 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.setCursor(0, 22);
display.printf("L1 %.0fW", status.last_data.phase_power_w[0]); display.printf("L1 %dW", static_cast<int>(round_power_w(status.last_data.phase_power_w[0])));
display.setCursor(0, 32); display.setCursor(0, 32);
display.printf("L2 %.0fW", status.last_data.phase_power_w[1]); display.printf("L2 %dW", static_cast<int>(round_power_w(status.last_data.phase_power_w[1])));
display.setCursor(0, 42); display.setCursor(0, 42);
display.printf("L3 %.0fW", status.last_data.phase_power_w[2]); display.printf("L3 %dW", static_cast<int>(round_power_w(status.last_data.phase_power_w[2])));
display.setCursor(0, 52); display.setCursor(0, 52);
display.print("P"); display.print("P");
char p_buf[16]; 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<int>(round_power_w(status.last_data.total_power_w)));
int16_t x1 = 0; int16_t x1 = 0;
int16_t y1 = 0; int16_t y1 = 0;
uint16_t w = 0; uint16_t w = 0;

View File

@@ -87,6 +87,17 @@ static void format_float_2(char *buf, size_t buf_len, float value) {
snprintf(buf, buf_len, "%.2f", round2(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) { bool meterDataToJson(const MeterData &data, String &out_json) {
StaticJsonDocument<256> doc; StaticJsonDocument<256> doc;
doc["id"] = short_id_from_device_id(data.device_id); 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]; char buf[16];
format_float_2(buf, sizeof(buf), data.energy_total_kwh); format_float_2(buf, sizeof(buf), data.energy_total_kwh);
doc["e_kwh"] = serialized(buf); doc["e_kwh"] = serialized(buf);
format_float_2(buf, sizeof(buf), data.total_power_w); set_int_or_null(doc, "p_w", data.total_power_w);
doc["p_w"] = serialized(buf); set_int_or_null(doc, "p1_w", data.phase_power_w[0]);
format_float_2(buf, sizeof(buf), data.phase_power_w[0]); set_int_or_null(doc, "p2_w", data.phase_power_w[1]);
doc["p1_w"] = serialized(buf); set_int_or_null(doc, "p3_w", data.phase_power_w[2]);
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);
format_float_2(buf, sizeof(buf), data.battery_voltage_v); format_float_2(buf, sizeof(buf), data.battery_voltage_v);
doc["bat_v"] = serialized(buf); doc["bat_v"] = serialized(buf);
doc["bat_pct"] = data.battery_percent; doc["bat_pct"] = data.battery_percent;

View File

@@ -9,6 +9,8 @@
#include <WiFi.h> #include <WiFi.h>
#include <time.h> #include <time.h>
#include <new> #include <new>
#include <limits.h>
#include <math.h>
#include <stdlib.h> #include <stdlib.h>
static WebServer server(80); 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_LIST_MAX_FILES = 200;
static constexpr size_t SD_DOWNLOAD_MAX_PATH = 160; 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<int32_t>(rounded);
}
static bool auth_required() { static bool auth_required() {
return g_is_ap ? WEB_AUTH_REQUIRE_AP : WEB_AUTH_REQUIRE_STA; 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) { if (!status.has_data) {
s += "No data"; s += "No data";
} else { } else {
s += "Energy: " + String(status.last_data.energy_total_kwh, 3) + " kWh<br>"; s += "Energy: " + String(status.last_data.energy_total_kwh, 2) + " kWh<br>";
s += "Power: " + String(status.last_data.total_power_w, 1) + " W<br>"; s += "Power: " + String(round_power_w(status.last_data.total_power_w)) + " W<br>";
s += "P1/P2/P3: " + String(status.last_data.phase_power_w[0], 1) + " / " + String(status.last_data.phase_power_w[1], 1) + s += "P1/P2/P3: " + String(round_power_w(status.last_data.phase_power_w[0])) + " / " +
" / " + String(status.last_data.phase_power_w[2], 1) + " W<br>"; String(round_power_w(status.last_data.phase_power_w[1])) + " / " +
String(round_power_w(status.last_data.phase_power_w[2])) + " W<br>";
s += "Battery: " + String(status.last_data.battery_percent) + "% (" + String(status.last_data.battery_voltage_v, 2) + " V)"; s += "Battery: " + String(status.last_data.battery_percent) + "% (" + String(status.last_data.battery_voltage_v, 2) + " V)";
} }
s += "</div>"; s += "</div>";
@@ -597,11 +614,11 @@ static void handle_sender() {
html += "<tr>"; html += "<tr>";
html += "<td>" + String(r) + "</td>"; html += "<td>" + String(r) + "</td>";
html += "<td>" + String(d.ts_utc) + "</td>"; html += "<td>" + String(d.ts_utc) + "</td>";
html += "<td>" + String(d.energy_total_kwh, 3) + "</td>"; html += "<td>" + String(d.energy_total_kwh, 2) + "</td>";
html += "<td>" + String(d.total_power_w, 1) + "</td>"; html += "<td>" + String(round_power_w(d.total_power_w)) + "</td>";
html += "<td>" + String(d.phase_power_w[0], 1) + "</td>"; html += "<td>" + String(round_power_w(d.phase_power_w[0])) + "</td>";
html += "<td>" + String(d.phase_power_w[1], 1) + "</td>"; html += "<td>" + String(round_power_w(d.phase_power_w[1])) + "</td>";
html += "<td>" + String(d.phase_power_w[2], 1) + "</td>"; html += "<td>" + String(round_power_w(d.phase_power_w[2])) + "</td>";
html += "<td>" + String(d.battery_voltage_v, 2) + "</td>"; html += "<td>" + String(d.battery_voltage_v, 2) + "</td>";
html += "<td>" + String(d.battery_percent) + "</td>"; html += "<td>" + String(d.battery_percent) + "</td>";
html += "<td>" + String(d.link_rssi_dbm) + "</td>"; html += "<td>" + String(d.link_rssi_dbm) + "</td>";
@@ -754,7 +771,8 @@ static void handle_history_data() {
if (bin.count == 0) { if (bin.count == 0) {
server.sendContent(String("[") + bin.ts + ",null]"); server.sendContent(String("[") + bin.ts + ",null]");
} else { } 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("]}"); server.sendContent("]}");