Files
DD3-LoRa-Bridge-MultiSender/src/meter_driver.cpp

166 lines
3.8 KiB
C++

#include "meter_driver.h"
#include "config.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
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<float>(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<char>(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);
}