Merge branch 'master' into use11b

This commit is contained in:
Gunnar Skjold
2023-09-23 14:03:55 +02:00
committed by GitHub
24 changed files with 450 additions and 180 deletions

View File

@@ -113,6 +113,7 @@ SoftwareSerial *swSerial = NULL;
HardwareSerial *hwSerial = NULL;
uint8_t rxBufferErrors = 0;
SystemConfig sysConfig;
GpioConfig gpioConfig;
MeterConfig meterConfig;
bool mqttEnabled = false;
@@ -123,7 +124,12 @@ bool ntpEnabled = false;
bool mdnsEnabled = false;
AmsDataStorage ds(&Debug);
EnergyAccounting ea(&Debug);
#if defined(ESP32)
__NOINIT_ATTR EnergyAccountingRealtimeData rtd;
#else
EnergyAccountingRealtimeData rtd;
#endif
EnergyAccounting ea(&Debug, &rtd);
uint8_t wifiReconnectCount = 0;
bool wifiDisable11b = false;
@@ -145,6 +151,7 @@ void configFileParse();
void swapWifiMode();
void WiFi_connect();
void WiFi_post_connect();
void WiFi_disconnect(unsigned long timeout);
void MQTT_connect();
void handleNtpChange();
void handleDataSuccess(AmsData* data);
@@ -155,6 +162,7 @@ void handleButton(unsigned long now);
void handlePriceApi(unsigned long now);
void handleClear(unsigned long now);
void handleEnergyAccountingChanged();
bool handleVoltageCheck();
bool readHanPort();
void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal, bool invert);
void rxerr(int err);
@@ -194,15 +202,21 @@ void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
}
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
wifi_err_reason_t reason = (wifi_err_reason_t) info.wifi_sta_disconnected.reason;
debugI_P(PSTR("WiFi disconnected, reason %s"), WiFi.disconnectReasonName(reason));
const char* descr = WiFi.disconnectReasonName(reason);
debugI_P(PSTR("WiFi disconnected, reason %s"), descr);
switch(reason) {
case WIFI_REASON_AUTH_FAIL:
case WIFI_REASON_NO_AP_FOUND:
SystemConfig sys;
if(!config.getSystemConfig(sys) || sys.dataCollectionConsent == 0) {
if(sysConfig.dataCollectionConsent == 0) {
swapWifiMode();
} else if(strlen(descr) > 0) {
WiFi_disconnect(5000);
}
break;
default:
if(strlen(descr) > 0) {
WiFi_disconnect(5000);
}
}
break;
}
@@ -218,6 +232,12 @@ void setup() {
if(!config.getGpioConfig(gpioConfig)) {
config.clearGpio(gpioConfig);
}
if(!config.getSystemConfig(sysConfig)) {
sysConfig.boardType = 0;
sysConfig.vendorConfigured = false;
sysConfig.userConfigured = false;
sysConfig.dataCollectionConsent = false;
}
delay(1);
config.loadTempSensors();
@@ -290,7 +310,11 @@ void setup() {
break;
}
#if defined(ESP32)
Serial.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud, serialConfig, -1, -1, meterConfig.invert);
#if ARDUINO_USB_CDC_ON_BOOT
Serial0.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud, serialConfig, -1, -1, meterConfig.invert);
#else
Serial.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud, serialConfig, -1, -1, meterConfig.invert);
#endif
#else
Serial.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud, serialConfig, SERIAL_FULL, 1, meterConfig.invert);
#endif
@@ -458,6 +482,7 @@ bool wifiConnected = false;
unsigned long lastTemperatureRead = 0;
unsigned long lastSysupdate = 0;
unsigned long lastErrorBlink = 0;
unsigned long lastVoltageCheck = 0;
int lastError = 0;
bool meterAutodetect = false;
@@ -554,6 +579,11 @@ void loop() {
debugW_P(PSTR("Used %dms to handle mqtt"), millis()-start);
}
}
if(now - lastVoltageCheck > 1000) {
handleVoltageCheck();
lastVoltageCheck = now;
}
} else {
if(dnsServer != NULL) {
dnsServer->processNextRequest();
@@ -646,6 +676,7 @@ void handleEnergyAccountingChanged() {
}
char ntpServerName[64] = "";
float maxVcc = 2.9;
void handleNtpChange() {
NtpConfig ntp;
@@ -814,6 +845,26 @@ void handleButton(unsigned long now) {
}
}
bool handleVoltageCheck() {
if(sysConfig.boardType == 7 && maxVcc > 2.8) { // Pow-U
float vcc = hw.getVcc();
if(vcc > 3.4 || vcc < 2.8) {
maxVcc = 0;
} else if(vcc > maxVcc) {
debugD_P(PSTR("Setting new max Vcc to %.2f"), vcc);
maxVcc = vcc;
} else if(WiFi.getMode() != WIFI_OFF) {
float diff = maxVcc-vcc;
if(diff > 0.3) {
debugW_P(PSTR("Vcc dropped to %.2f, disconnecting WiFi for 5 seconds to preserve power"), vcc);
WiFi_disconnect(5000);
return false;
}
}
}
return true;
}
void rxerr(int err) {
if(err == 0) return;
switch(err) {
@@ -837,7 +888,8 @@ void rxerr(int err) {
debugW_P(PSTR("Serial parity error"));
break;
}
meterState.setLastError(90+err);
// Do not include serial break
if(err > 1) meterState.setLastError(90+err);
}
void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal, bool invert) {
@@ -854,16 +906,18 @@ void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal,
parityOrdinal = 3; // 8N1
}
SystemConfig sys;
config.getSystemConfig(sys);
switch(sys.boardType) {
switch(sysConfig.boardType) {
case 8: // HAN mosquito: has inverting level shifter
invert = !invert;
break;
}
if(pin == 3 || pin == 113) {
hwSerial = &Serial;
#if ARDUINO_USB_CDC_ON_BOOT
hwSerial = &Serial0;
#else
hwSerial = &Serial;
#endif
}
#if defined(ESP32)
@@ -912,7 +966,7 @@ void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal,
hwSerial->setRxBufferSize(64 * meterConfig.bufferSize);
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
hwSerial->begin(baud, serialConfig, -1, -1, invert);
hwSerial->begin(baud, serialConfig, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, invert);
uart_set_pin(UART_NUM_1, UART_PIN_NO_CHANGE, pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
#elif defined(ESP32)
hwSerial->begin(baud, serialConfig, -1, -1, invert);
@@ -930,6 +984,23 @@ void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal,
}
#endif
// Prevent pullup on TX pin if not uart0
#if defined(CONFIG_IDF_TARGET_ESP32S2)
pinMode(17, INPUT);
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
pinMode(7, INPUT);
#elif defined(ESP32)
if(pin == 9) {
pinMode(10, INPUT);
} else if(pin == 16) {
pinMode(17, INPUT);
}
#elif defined(ESP8266)
if(pin == 113) {
pinMode(15, INPUT);
}
#endif
#if defined(ESP32)
hwSerial->onReceiveError(rxerr);
#endif
@@ -1185,7 +1256,12 @@ bool readHanPort() {
// Rudimentary detector for L&G proprietary format, this is terrible code... Fix later
if(payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
debugV_P(PSTR("LNG"));
data = new LNG(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
LNG lngData = LNG(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
if(lngData.getListType() >= 3) {
data = new AmsData();
data->apply(meterState);
data->apply(lngData);
}
} else if(payload[0] == CosemTypeStructure &&
payload[2] == CosemTypeLongUnsigned &&
payload[5] == CosemTypeLongUnsigned &&
@@ -1195,11 +1271,16 @@ bool readHanPort() {
payload[17] == CosemTypeLongUnsigned
) {
debugV_P(PSTR("LNG2"));
data = new LNG2(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
LNG2 lngData = LNG2(payload, meterState.getMeterType(), &meterConfig, ctx, &Debug);
if(lngData.getListType() >= 3) {
data = new AmsData();
data->apply(meterState);
data->apply(lngData);
}
} else {
debugV_P(PSTR("DLMS"));
// TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats
data = new IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx);
data = new IEC6205675(payload, meterState.getMeterType(), &meterConfig, ctx, meterState);
}
} else if(ctx.type == DATA_TAG_DSMR) {
data = new IEC6205621(payload, tz);
@@ -1336,6 +1417,41 @@ void debugPrint(byte *buffer, int start, int length) {
unsigned long wifiTimeout = WIFI_CONNECTION_TIMEOUT;
unsigned long lastWifiRetry = -WIFI_CONNECTION_TIMEOUT;
void WiFi_disconnect(unsigned long timeout) {
if (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Not connected to WiFi, closing resources"));
if(mqtt != NULL) {
mqtt->disconnect();
mqtt->loop();
delay(10);
yield();
delete mqtt;
mqtt = NULL;
ws.setMqtt(NULL);
}
if(mqttClient != NULL) {
mqttClient->stop();
delete mqttClient;
mqttClient = NULL;
if(mqttSecureClient != NULL) {
mqttSecureClient = NULL;
}
}
#if defined(ESP8266)
WiFiClient::stopAll();
#endif
MDNS.end();
WiFi.disconnect(true);
WiFi.softAPdisconnect(true);
WiFi.enableAP(false);
WiFi.mode(WIFI_OFF);
yield();
wifiTimeout = timeout;
}
void WiFi_connect() {
if(millis() - lastWifiRetry < wifiTimeout) {
delay(50);
@@ -1343,6 +1459,11 @@ void WiFi_connect() {
}
lastWifiRetry = millis();
if(!handleVoltageCheck()) {
debugW_P(PSTR("Voltage is not high enough to reconnect"));
return;
}
if (WiFi.status() != WL_CONNECTED) {
WiFiConfig wifi;
if(!config.getWiFiConfig(wifi) || strlen(wifi.ssid) == 0) {
@@ -1352,42 +1473,14 @@ void WiFi_connect() {
if(WiFi.getMode() != WIFI_OFF) {
if(wifiReconnectCount > 3 && wifi.autoreboot) {
if (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Unable to connect to WiFi, rebooting because auto reboot is enabled"));
ESP.restart();
return;
}
if (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Not connected to WiFi, closing resources"));
if(mqtt != NULL) {
mqtt->disconnect();
mqtt->loop();
delay(10);
yield();
delete mqtt;
mqtt = NULL;
ws.setMqtt(NULL);
}
if(mqttClient != NULL) {
mqttClient->stop();
delete mqttClient;
mqttClient = NULL;
if(mqttSecureClient != NULL) {
mqttSecureClient = NULL;
}
}
#if defined(ESP8266)
WiFiClient::stopAll();
#endif
MDNS.end();
WiFi.disconnect(true);
WiFi.softAPdisconnect(true);
WiFi.enableAP(false);
WiFi.mode(WIFI_OFF);
yield();
wifiTimeout = 5000;
WiFi_disconnect(5000);
return;
}
wifiTimeout = WIFI_CONNECTION_TIMEOUT;
if (Debug.isActive(RemoteDebug::INFO)) debugI_P(PSTR("Connecting to WiFi network: %s"), wifi.ssid);
@@ -1719,10 +1812,8 @@ void MQTT_connect() {
break;
case 4:
HomeAssistantConfig haconf;
SystemConfig sys;
config.getHomeAssistantConfig(haconf);
config.getSystemConfig(sys);
mqttHandler = new HomeAssistantMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, sys.boardType, haconf, &hw);
mqttHandler = new HomeAssistantMqttHandler(mqtt, (char*) commonBuffer, mqttConfig.clientId, mqttConfig.publishTopic, sysConfig.boardType, haconf, &hw);
break;
}

View File

@@ -1,4 +1,5 @@
#include "IEC6205621.h"
#include "Uptime.h"
IEC6205621::IEC6205621(const char* p, Timezone* tz) {
if(strlen(p) < 16)
@@ -6,7 +7,7 @@ IEC6205621::IEC6205621(const char* p, Timezone* tz) {
String payload(p+1);
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));
if(listId.startsWith(F("ADN"))) {

View File

@@ -2,8 +2,9 @@
#include "lwip/def.h"
#include "Timezone.h"
#include "ntohll.h"
#include "Uptime.h"
IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx) {
IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, AmsData &state) {
float val;
char str[64];
@@ -127,14 +128,89 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
reactiveExportCounter = ntohl(data->dlu.data) / 1000.0;
}
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
} else if(listId.startsWith("ISK")) { // Iskra special case
this->listId = listId;
meterType = AmsTypeIskra;
int idx = 0;
data = getCosemDataAt(idx++, ((char *) (d)));
if(data->base.length == 0x12) {
listType = 2;
idx++;
data = getCosemDataAt(idx++, ((char *) (d)));
memcpy(str, data->oct.data, data->oct.length);
str[data->oct.length] = 0x00;
meterId = String(str);
data = getCosemDataAt(idx++, ((char *) (d)));
activeImportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
activeExportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
reactiveImportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
reactiveExportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l1voltage = ntohs(data->lu.data) / 10.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l2voltage = ntohs(data->lu.data) / 10.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l3voltage = ntohs(data->lu.data) / 10.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l1current = ntohs(data->lu.data) / 100.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l2current = ntohs(data->lu.data) / 100.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l3current = ntohs(data->lu.data) / 100.0;
data = getCosemDataAt(idx++, ((char *) (d)));
l1activeImportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l2activeImportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l3activeImportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l1activeExportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l2activeExportPower = ntohl(data->dlu.data);
data = getCosemDataAt(idx++, ((char *) (d)));
l3activeExportPower = ntohl(data->dlu.data);
lastUpdateMillis = millis64();
} else if(data->base.length == 0x0C) {
apply(state);
listType = 3;
idx += 4;
data = getCosemDataAt(idx++, ((char *) (d)));
activeImportCounter = ntohl(data->dlu.data) / 1000.0;
idx += 2;
data = getCosemDataAt(idx++, ((char *) (d)));
activeExportCounter = ntohl(data->dlu.data) / 1000.0;
idx += 2;
data = getCosemDataAt(idx++, ((char *) (d)));
reactiveImportCounter = ntohl(data->dlu.data) / 1000.0;
data = getCosemDataAt(idx++, ((char *) (d)));
reactiveExportCounter = ntohl(data->dlu.data) / 1000.0;
lastUpdateMillis = millis64();
}
}
} else if(useMeterType == AmsTypeKaifa && data->base.type == CosemTypeDLongUnsigned) {
this->packageTimestamp = this->packageTimestamp > 0 ? tz.toUTC(this->packageTimestamp) : 0;
listType = 1;
meterType = AmsTypeKaifa;
activeImportPower = ntohl(data->dlu.data);
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
}
// Kaifa end
} else {
@@ -378,7 +454,7 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
}
}
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
}
if(meterConfig->wattageMultiplier > 0) {
@@ -538,37 +614,37 @@ float IEC6205675::getNumber(CosemData* item) {
switch(item->base.type) {
case CosemTypeLongSigned: {
int16_t i16 = ntohs(item->ls.data);
ret = i16;
ret = (i16 * 1.0);
pos += 3;
break;
}
case CosemTypeLongUnsigned: {
uint16_t u16 = ntohs(item->lu.data);
ret = u16;
ret = (u16 * 1.0);
pos += 3;
break;
}
case CosemTypeDLongSigned: {
int32_t i32 = ntohl(item->dlu.data);
ret = i32;
ret = (i32 * 1.0);
pos += 5;
break;
}
case CosemTypeDLongUnsigned: {
uint32_t u32 = ntohl(item->dlu.data);
ret = u32;
ret = (u32 * 1.0);
pos += 5;
break;
}
case CosemTypeLong64Signed: {
int64_t i64 = ntohll(item->l64s.data);
ret = i64;
ret = (i64 * 1.0);
pos += 9;
break;
}
case CosemTypeLong64Unsigned: {
uint64_t u64 = ntohll(item->l64u.data);
ret = u64;
ret = (u64 * 1.0);
pos += 9;
break;
}

View File

@@ -15,7 +15,7 @@ struct AmsOctetTimestamp {
class IEC6205675 : public AmsData {
public:
IEC6205675(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx);
IEC6205675(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, AmsData &state);
private:
CosemData* getCosemDataAt(uint8_t index, const char* ptr);

View File

@@ -1,6 +1,7 @@
#include "LNG.h"
#include "lwip/def.h"
#include "ntohll.h"
#include "Uptime.h"
LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) {
LngHeader* h = (LngHeader*) payload;
@@ -117,7 +118,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
data += 3;
}
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
}
}
}

View File

@@ -1,4 +1,5 @@
#include "LNG2.h"
#include "Uptime.h"
LNG2::LNG2(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) {
CosemBasic* h = (CosemBasic*) payload;
@@ -26,7 +27,7 @@ LNG2::LNG2(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig,
this->meterId = String(str);
}
listType = 3;
lastUpdateMillis = millis();
lastUpdateMillis = millis64();
}
}