#include "meter_driver.h" #include "config.h" #include #include #include static constexpr uint32_t METER_READ_TIMEOUT_MS = 2000; void meter_init() { Serial2.begin(9600, SERIAL_7E1, PIN_METER_RX, -1); } static bool parse_obis_ascii_value(const char *line, const char *obis, float &out_value) { const char *p = strstr(line, obis); if (!p) { return false; } const char *lparen = strchr(p, '('); if (!lparen) { return false; } const char *cur = lparen + 1; char num_buf[24]; size_t n = 0; while (*cur && *cur != ')' && *cur != '*') { char c = *cur++; if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == ',') { if (c == ',') { c = '.'; } if (n + 1 < sizeof(num_buf)) { num_buf[n++] = c; } } else if (n == 0) { continue; } else { break; } } if (n == 0) { return false; } num_buf[n] = '\0'; out_value = static_cast(atof(num_buf)); return true; } static bool parse_obis_ascii_unit_scale(const char *line, const char *obis, float &value) { const char *p = strstr(line, obis); if (!p) { return false; } const char *asterisk = strchr(p, '*'); if (!asterisk) { return false; } const char *end = strchr(asterisk, ')'); if (!end) { return false; } char unit_buf[8]; size_t ulen = 0; for (const char *c = asterisk + 1; c < end && ulen + 1 < sizeof(unit_buf); ++c) { if (*c == ' ') { continue; } unit_buf[ulen++] = *c; } unit_buf[ulen] = '\0'; if (ulen == 0) { return false; } if (strcmp(unit_buf, "Wh") == 0) { value *= 0.001f; return true; } return false; } static bool meter_read_ascii(MeterData &data) { const uint32_t start_ms = millis(); bool in_telegram = false; bool got_any = false; bool energy_ok = false; bool total_p_ok = false; bool p1_ok = false; bool p2_ok = false; bool p3_ok = false; char line[128]; size_t line_len = 0; while (millis() - start_ms < METER_READ_TIMEOUT_MS) { while (Serial2.available()) { char c = static_cast(Serial2.read()); if (!in_telegram) { if (c == '/') { in_telegram = true; line_len = 0; line[line_len++] = c; } continue; } if (c == '\r') { continue; } if (c == '\n') { line[line_len] = '\0'; if (line[0] == '!') { return got_any; } float value = NAN; if (parse_obis_ascii_value(line, "1-0:1.8.0", value)) { parse_obis_ascii_unit_scale(line, "1-0:1.8.0", value); data.energy_total_kwh = value; energy_ok = true; got_any = true; } if (parse_obis_ascii_value(line, "1-0:16.7.0", value)) { data.total_power_w = value; total_p_ok = true; got_any = true; } if (parse_obis_ascii_value(line, "1-0:36.7.0", value)) { data.phase_power_w[0] = value; p1_ok = true; got_any = true; } if (parse_obis_ascii_value(line, "1-0:56.7.0", value)) { data.phase_power_w[1] = value; p2_ok = true; got_any = true; } if (parse_obis_ascii_value(line, "1-0:76.7.0", value)) { data.phase_power_w[2] = value; p3_ok = true; got_any = true; } line_len = 0; continue; } if (line_len + 1 < sizeof(line)) { line[line_len++] = c; } } delay(5); } data.valid = energy_ok || total_p_ok || p1_ok || p2_ok || p3_ok; return data.valid; } bool meter_read(MeterData &data) { data.energy_total_kwh = NAN; data.total_power_w = NAN; data.phase_power_w[0] = NAN; data.phase_power_w[1] = NAN; data.phase_power_w[2] = NAN; data.valid = false; return meter_read_ascii(data); }