#include "json_codec.h" #include #include #include static float round2(float value) { if (isnan(value)) { return value; } return roundf(value * 100.0f) / 100.0f; } static int32_t round_to_i32(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 const char *short_id_from_device_id(const char *device_id) { if (!device_id) { return ""; } size_t len = strlen(device_id); if (len >= 4) { return device_id + (len - 4); } return device_id; } static void format_float_2(char *buf, size_t buf_len, float value) { if (!buf || buf_len == 0) { return; } if (isnan(value)) { snprintf(buf, buf_len, "null"); return; } 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); doc["ts"] = data.ts_utc; char buf[16]; format_float_2(buf, sizeof(buf), data.energy_total_kwh); doc["e_kwh"] = 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; if (data.link_valid) { doc["rssi"] = data.link_rssi_dbm; doc["snr"] = data.link_snr_db; } if (data.err_meter_read > 0) { doc["err_m"] = data.err_meter_read; } if (data.err_decode > 0) { doc["err_d"] = data.err_decode; } if (data.err_lora_tx > 0) { doc["err_tx"] = data.err_lora_tx; } doc["err_last"] = static_cast(data.last_error); doc["rx_reject"] = data.rx_reject_reason; doc["rx_reject_text"] = rx_reject_reason_text(static_cast(data.rx_reject_reason)); out_json = ""; size_t len = serializeJson(doc, out_json); return len > 0 && len < 256; }