95 lines
2.4 KiB
C++
95 lines
2.4 KiB
C++
#include "json_codec.h"
|
|
#include <ArduinoJson.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
|
|
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<int32_t>(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<uint8_t>(data.last_error);
|
|
doc["rx_reject"] = data.rx_reject_reason;
|
|
doc["rx_reject_text"] = rx_reject_reason_text(static_cast<RxRejectReason>(data.rx_reject_reason));
|
|
|
|
out_json = "";
|
|
size_t len = serializeJson(doc, out_json);
|
|
return len > 0 && len < 256;
|
|
}
|