diff --git a/src/meter_driver.cpp b/src/meter_driver.cpp index bc34210..5645206 100644 --- a/src/meter_driver.cpp +++ b/src/meter_driver.cpp @@ -33,12 +33,47 @@ 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; +enum class ObisField : uint8_t { + None = 0, + Energy = 1, + TotalPower = 2, + Phase1 = 3, + Phase2 = 4, + Phase3 = 5, + MeterSeconds = 6 +}; + +static ObisField detect_obis_field(const char *line) { + if (!line) { + return ObisField::None; } - const char *lparen = strchr(p, '('); + const char *p = line; + while (*p == ' ' || *p == '\t') { + ++p; + } + if (strncmp(p, "1-0:1.8.0", 9) == 0) { + return ObisField::Energy; + } + if (strncmp(p, "1-0:16.7.0", 10) == 0) { + return ObisField::TotalPower; + } + if (strncmp(p, "1-0:36.7.0", 10) == 0) { + return ObisField::Phase1; + } + if (strncmp(p, "1-0:56.7.0", 10) == 0) { + return ObisField::Phase2; + } + if (strncmp(p, "1-0:76.7.0", 10) == 0) { + return ObisField::Phase3; + } + if (strncmp(p, "0-0:96.8.0*255", 14) == 0) { + return ObisField::MeterSeconds; + } + return ObisField::None; +} + +static bool parse_obis_ascii_payload_value(const char *line, float &out_value) { + const char *lparen = strchr(line, '('); if (!lparen) { return false; } @@ -68,12 +103,12 @@ static bool parse_obis_ascii_value(const char *line, const char *obis, float &ou 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) { +static bool parse_obis_ascii_unit_scale(const char *line, float &value) { + const char *lparen = strchr(line, '('); + if (!lparen) { return false; } - const char *asterisk = strchr(p, '*'); + const char *asterisk = strchr(lparen, '*'); if (!asterisk) { return false; } @@ -113,12 +148,8 @@ static int8_t hex_nibble(char c) { return -1; } -static bool parse_obis_hex_u32(const char *line, const char *obis, uint32_t &out_value) { - const char *p = strstr(line, obis); - if (!p) { - return false; - } - const char *lparen = strchr(p, '('); +static bool parse_obis_hex_payload_u32(const char *line, uint32_t &out_value) { + const char *lparen = strchr(line, '('); if (!lparen) { return false; } @@ -261,37 +292,54 @@ bool meter_parse_frame(const char *frame, size_t len, MeterData &data) { return data.valid; } + ObisField field = detect_obis_field(line); 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; - } uint32_t meter_seconds = 0; - if (parse_obis_hex_u32(line, "0-0:96.8.0*255", meter_seconds)) { - data.meter_seconds = meter_seconds; - data.meter_seconds_valid = true; + switch (field) { + case ObisField::Energy: + if (parse_obis_ascii_payload_value(line, value)) { + parse_obis_ascii_unit_scale(line, value); + data.energy_total_kwh = value; + energy_ok = true; + got_any = true; + } + break; + case ObisField::TotalPower: + if (parse_obis_ascii_payload_value(line, value)) { + data.total_power_w = value; + total_p_ok = true; + got_any = true; + } + break; + case ObisField::Phase1: + if (parse_obis_ascii_payload_value(line, value)) { + data.phase_power_w[0] = value; + p1_ok = true; + got_any = true; + } + break; + case ObisField::Phase2: + if (parse_obis_ascii_payload_value(line, value)) { + data.phase_power_w[1] = value; + p2_ok = true; + got_any = true; + } + break; + case ObisField::Phase3: + if (parse_obis_ascii_payload_value(line, value)) { + data.phase_power_w[2] = value; + p3_ok = true; + got_any = true; + } + break; + case ObisField::MeterSeconds: + if (parse_obis_hex_payload_u32(line, meter_seconds)) { + data.meter_seconds = meter_seconds; + data.meter_seconds_valid = true; + } + break; + default: + break; } line_len = 0;