Refactor meter parser to single-pass OBIS dispatch

This commit is contained in:
2026-02-17 01:09:56 +01:00
parent c33fb3274c
commit 557420c200

View File

@@ -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;