diff --git a/hardware/README.md b/hardware/README.md index b37dcb8e..a191f9e6 100644 --- a/hardware/README.md +++ b/hardware/README.md @@ -27,7 +27,7 @@ You can also use a ESP based development board and combine this with a M-Bus mod - Jump GPIO4 to GND to force AP mode during boot [Adafruit HUZZAH32](https://www.adafruit.com/product/3405) -- M-Bus connected to RX +- M-Bus connected to GPIO16 Combine one of above board with an M-Bus module. Connect 3.3v and GND together between the boards and connect the TX pin from the M-Bus board to the dedicated M-Bus pin on the ESP board. diff --git a/lib/HanToJson/src/HanToJson.cpp b/lib/HanToJson/src/HanToJson.cpp index ee8f7185..3b084861 100644 --- a/lib/HanToJson/src/HanToJson.cpp +++ b/lib/HanToJson/src/HanToJson.cpp @@ -10,9 +10,11 @@ static void hanToJsonKaifa3phase(int listSize, JsonObject& data, HanReader& hanR { data["lv"] = hanReader.getString( (int)Kaifa_List3Phase::ListVersionIdentifier); data["id"] = hanReader.getString( (int)Kaifa_List3Phase::MeterID); - data["type"] = hanReader.getString( (int)Kaifa_List3Phase::MeterType); + data["type"] = hanReader.getString( (int)Kaifa_List3Phase::MeterType); data["P"] = hanReader.getInt( (int)Kaifa_List3Phase::ActiveImportPower); data["Q"] = hanReader.getInt( (int)Kaifa_List3Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Kaifa_List3Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Kaifa_List3Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL1)) / 1000; data["I2"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL2)) / 1000; data["I3"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL3)) / 1000; @@ -23,6 +25,7 @@ static void hanToJsonKaifa3phase(int listSize, JsonObject& data, HanReader& hanR if (listSize >= (int)Kaifa::List3PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Kaifa_List3Phase::MeterClock); data["tPI"] = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeReactiveImportEnergy); @@ -39,12 +42,15 @@ static void hanToJsonKaifa1phase(int listSize, JsonObject& data, HanReader& hanR data["type"] = hanReader.getString( (int)Kaifa_List1Phase::MeterType); data["P"] = hanReader.getInt( (int)Kaifa_List1Phase::ActiveImportPower); data["Q"] = hanReader.getInt( (int)Kaifa_List1Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Kaifa_List1Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Kaifa_List1Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt((int)Kaifa_List1Phase::CurrentL1)) / 1000; data["U1"] = ((double) hanReader.getInt((int)Kaifa_List1Phase::VoltageL1)) / 10; } if (listSize >= (int)Kaifa::List1PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Kaifa_List1Phase::MeterClock); data["tPI"] = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeReactiveImportEnergy); @@ -77,15 +83,15 @@ static void hanToJsonKaifa(JsonObject& data, HanReader& hanReader, Stream *debug } -static void hanToJsonAidon3phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Aidon::List3PhaseShort) - { +static void hanToJsonAidon3phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) { + if (listSize >= (int)Aidon::List3PhaseShort) { data["lv"] = hanReader.getString( (int)Aidon_List3Phase::ListVersionIdentifier); data["id"] = hanReader.getString( (int)Aidon_List3Phase::MeterID); data["type"] = hanReader.getString( (int)Aidon_List3Phase::MeterType); data["P"] = hanReader.getInt( (int)Aidon_List3Phase::ActiveImportPower); - data["Q"] = hanReader.getInt( (int)Aidon_List3Phase::ReactiveExportPower); + data["Q"] = hanReader.getInt( (int)Aidon_List3Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Aidon_List3Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Aidon_List3Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL1)) / 10; data["I2"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL2)) / 10; data["I3"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL3)) / 10; @@ -94,8 +100,8 @@ static void hanToJsonAidon3phase(int listSize, JsonObject& data, HanReader& hanR data["U3"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL3)) / 10; } - if (listSize >= (int)Aidon::List3PhaseLong) - { + if (listSize >= (int)Aidon::List3PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Aidon_List3Phase::Timestamp); data["tPI"] = hanReader.getInt( (int)Aidon_List3Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Aidon_List3Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Aidon_List3Phase::CumulativeReactiveImportEnergy); @@ -111,13 +117,16 @@ static void hanToJsonAidon1phase(int listSize, JsonObject& data, HanReader& hanR data["id"] = hanReader.getString( (int)Aidon_List1Phase::MeterID); data["type"] = hanReader.getString( (int)Aidon_List1Phase::MeterType); data["P"] = hanReader.getInt( (int)Aidon_List1Phase::ActiveImportPower); - data["Q"] = hanReader.getInt( (int)Aidon_List1Phase::ReactiveExportPower); + data["Q"] = hanReader.getInt( (int)Aidon_List1Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Aidon_List1Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Aidon_List1Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt( (int)Aidon_List1Phase::CurrentL1)) / 10; data["U1"] = ((double) hanReader.getInt( (int)Aidon_List1Phase::VoltageL1)) / 10; } if (listSize >= (int)Aidon::List1PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Aidon_List1Phase::Timestamp); data["tPI"] = hanReader.getInt( (int)Aidon_List1Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Aidon_List1Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Aidon_List1Phase::CumulativeReactiveImportEnergy); @@ -132,25 +141,21 @@ static void hanToJsonAidon3phaseIT(int listSize, JsonObject& data, HanReader& ha data["lv"] = hanReader.getString( (int)Aidon_List3PhaseIT::ListVersionIdentifier); data["id"] = hanReader.getString( (int)Aidon_List3PhaseIT::MeterID); data["type"] = hanReader.getString( (int)Aidon_List3PhaseIT::MeterType); - int p = hanReader.getInt( (int)Aidon_List3PhaseIT::ActiveImportPower); - data["P"] = p; - data["Q"] = hanReader.getInt( (int)Aidon_List3PhaseIT::ReactiveExportPower); - double i1 = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL1)) / 10; - double i3 = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL3)) / 10; - double u1 = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL1)) / 10; - double u2 = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL2)) / 10; - double u3 = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL3)) / 10; - double i2 = 0.00; - data["I1"] = i1; - data["I2"] = i2; - data["I3"] = i3; - data["U1"] = u1; - data["U2"] = u2; - data["U3"] = u3; + data["P"] = hanReader.getInt( (int)Aidon_List3PhaseIT::ActiveImportPower); + data["Q"] = hanReader.getInt( (int)Aidon_List3PhaseIT::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Aidon_List3PhaseIT::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Aidon_List3PhaseIT::ReactiveExportPower); + data["I1"] = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL1)) / 10; + data["I1"] = 0; + data["I3"] = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL3)) / 10; + data["U1"] = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL1)) / 10; + data["U2"] = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL2)) / 10; + data["U3"] = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL3)) / 10; } if (listSize >= (int)Aidon::List3PhaseITLong) { + data["rtc"] = hanReader.getTime( (int)Aidon_List3PhaseIT::Timestamp); data["tPI"] = hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeReactiveImportEnergy); @@ -187,15 +192,15 @@ static void hanToJsonAidon(JsonObject& data, HanReader& hanReader, Stream *debug } } -static void hanToJsonKamstrup3phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Kamstrup::List3PhaseShort) - { +static void hanToJsonKamstrup3phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) { + if (listSize >= (int)Kamstrup::List3PhaseShort) { data["lv"] = hanReader.getString( (int)Kamstrup_List3Phase::ListVersionIdentifier); data["id"] = hanReader.getString( (int)Kamstrup_List3Phase::MeterID); data["type"] = hanReader.getString( (int)Kamstrup_List3Phase::MeterType); data["P"] = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveImportPower); data["Q"] = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL1)) / 100; data["I2"] = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL2)) / 100; data["I3"] = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL3)) / 100; @@ -204,8 +209,8 @@ static void hanToJsonKamstrup3phase(int listSize, JsonObject& data, HanReader& h data["U3"] = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL3); } - if (listSize >= (int)Kamstrup::List3PhaseLong) - { + if (listSize >= (int)Kamstrup::List3PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Kamstrup_List3Phase::MeterClock); data["tPI"] = hanReader.getInt( (int)Kamstrup_List3Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Kamstrup_List3Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Kamstrup_List3Phase::CumulativeReactiveImportEnergy); @@ -213,21 +218,21 @@ static void hanToJsonKamstrup3phase(int listSize, JsonObject& data, HanReader& h } } -static void hanToJsonKamstrup1phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Kamstrup::List1PhaseShort) - { +static void hanToJsonKamstrup1phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) { + if (listSize >= (int)Kamstrup::List1PhaseShort) { data["lv"] = hanReader.getString( (int)Kamstrup_List1Phase::ListVersionIdentifier); data["id"] = hanReader.getString( (int)Kamstrup_List1Phase::MeterID); data["type"] = hanReader.getString( (int)Kamstrup_List1Phase::MeterType); data["P"] = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveImportPower); data["Q"] = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveImportPower); + data["PO"] = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveExportPower); + data["QO"] = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveExportPower); data["I1"] = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CurrentL1)) / 100; data["U1"] = hanReader.getInt( (int)Kamstrup_List1Phase::VoltageL1); } - if (listSize >= (int)Kamstrup::List1PhaseLong) - { + if (listSize >= (int)Kamstrup::List1PhaseLong) { + data["rtc"] = hanReader.getTime( (int)Kamstrup_List1Phase::MeterClock); data["tPI"] = hanReader.getInt( (int)Kamstrup_List1Phase::CumulativeActiveImportEnergy); data["tPO"] = hanReader.getInt( (int)Kamstrup_List1Phase::CumulativeActiveExportEnergy); data["tQI"] = hanReader.getInt( (int)Kamstrup_List1Phase::CumulativeReactiveImportEnergy); @@ -235,8 +240,7 @@ static void hanToJsonKamstrup1phase(int listSize, JsonObject& data, HanReader& h } } -static void hanToJsonKamstrup(JsonObject& data, HanReader& hanReader, Stream *debugger) -{ +static void hanToJsonKamstrup(JsonObject& data, HanReader& hanReader, Stream *debugger) { int listSize = hanReader.getListSize(); switch (listSize) { diff --git a/src/AmsToMqttBridge.h b/src/AmsToMqttBridge.h index 0b984c9f..61431869 100644 --- a/src/AmsToMqttBridge.h +++ b/src/AmsToMqttBridge.h @@ -17,7 +17,13 @@ #include #define TEMP_SENSOR_PIN 5 // Temperature sensor connected to GPIO5 +#if DEBUG_MODE +#define SOFTWARE_SERIAL 1 +#include +SoftwareSerial *hanSerial = new SoftwareSerial(3); +#else HardwareSerial *hanSerial = &Serial; +#endif // Build settings for Wemos Lolin D32 #elif defined(ARDUINO_LOLIN_D32) @@ -45,7 +51,7 @@ SoftwareSerial *hanSerial = new SoftwareSerial(D1); #define LED_ACTIVE_HIGH 1 #define AP_BUTTON_PIN INVALID_BUTTON_PIN -HardwareSerial *hanSerial = &Serial; +HardwareSerial *hanSerial = &Serial2; // Default build settings #else diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 1e62e723..2a73e32e 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -24,10 +24,10 @@ AmsWebServer ws; // WiFi client and MQTT client WiFiClient *client; -MQTTClient mqtt(384); +MQTTClient mqtt(512); // Object used for debugging -HardwareSerial* debugger = NULL; +Stream* debugger = NULL; // The HAN Port reader, used to read serial data and decode DLMS HanReader hanReader; @@ -39,17 +39,15 @@ void setup() { } #if DEBUG_MODE - debugger = &Serial; - #if SOFTWARE_SERIAL - debugger->begin(115200, SERIAL_8N1); - #else - if(config.meterType == 3) { - hanSerial->begin(2400, SERIAL_8N1); - } else { - hanSerial->begin(2400, SERIAL_8E1); - } - #endif - while (!&debugger); +#if HW_ROARFRED + SoftwareSerial *ser = new SoftwareSerial(-1, 1); + ser->begin(115200, SWSERIAL_8N1); + debugger = ser; +#else + HardwareSerial *ser = &Serial; + ser->begin(115200, SERIAL_8N1); +#endif + debugger = ser; #endif if (debugger) { @@ -72,12 +70,6 @@ void setup() { { setupWiFi(); - if(config.mqttHost) { - mqtt.begin(config.mqttHost, *client); - - // Notify everyone we're here! - sendMqttData("Connected!"); - } // Configure uart for AMS data #if defined SOFTWARE_SERIAL if(config.meterType == 3) { @@ -118,15 +110,16 @@ void loop() // Reconnect to WiFi and MQTT as needed if (WiFi.status() != WL_CONNECTED) { WiFi_connect(); - } - - if (config.mqttHost) { - mqtt.loop(); - delay(10); // <- fixes some issues with WiFi stability - if(!mqtt.connected()) { - MQTT_connect(); + } else { + if (config.mqttHost) { + mqtt.loop(); + yield(); + if(!mqtt.connected()) { + MQTT_connect(); + } } } + readHanPort(); } else @@ -136,6 +129,7 @@ void loop() else led_off(); } ws.loop(); + delay(1); // Needed for auto modem sleep } @@ -207,7 +201,7 @@ void readHanPort() if (debugger) debugger->println(time); // Define a json object to keep the data - StaticJsonDocument<500> json; + StaticJsonDocument<1024> json; // Any generic useful info here json["id"] = WiFi.macAddress(); @@ -297,8 +291,18 @@ void WiFi_connect() { // Function to connect and reconnect as necessary to the MQTT server. // Should be called in the loop function and it will take care if connecting. -void MQTT_connect() -{ + +unsigned long lastMqttRetry = -10000; +void MQTT_connect() { + if(!config.mqttHost) { + if(debugger) debugger->println("No MQTT config"); + return; + } + if(millis() - lastMqttRetry < 5000) { + yield(); + return; + } + lastMqttRetry = millis(); if(debugger) { debugger->print("Connecting to MQTT: "); debugger->print(config.mqttHost); @@ -306,40 +310,30 @@ void MQTT_connect() debugger->print(config.mqttPort); debugger->println(); } - // Wait for the MQTT connection to complete - while (!mqtt.connected()) { - // Connect to a unsecure or secure MQTT server - if ((config.mqttUser == 0 && mqtt.connect(config.mqttClientID)) || - (config.mqttUser != 0 && mqtt.connect(config.mqttClientID, config.mqttUser, config.mqttPass))) - { - if (debugger) debugger->println("\nSuccessfully connected to MQTT!"); - // Subscribe to the chosen MQTT topic, if set in configuration - if (config.mqttSubscribeTopic != 0 && strlen(config.mqttSubscribeTopic) > 0) - { - mqtt.subscribe(config.mqttSubscribeTopic); - if (debugger) debugger->printf(" Subscribing to [%s]\r\n", config.mqttSubscribeTopic); - } - } - else - { - if (debugger) - { - debugger->print("."); - debugger->print("failed, "); - debugger->println(" trying again in 5 seconds"); - } + mqtt.disconnect(); - // Wait 2 seconds before retrying - mqtt.disconnect(); + mqtt.begin(config.mqttHost, config.mqttPort, *client); - delay(2000); + // Connect to a unsecure or secure MQTT server + if ((config.mqttUser == 0 && mqtt.connect(config.mqttClientID)) || + (config.mqttUser != 0 && mqtt.connect(config.mqttClientID, config.mqttUser, config.mqttPass))) { + if (debugger) debugger->println("\nSuccessfully connected to MQTT!"); + + // Subscribe to the chosen MQTT topic, if set in configuration + if (config.mqttSubscribeTopic != 0 && strlen(config.mqttSubscribeTopic) > 0) { + mqtt.subscribe(config.mqttSubscribeTopic); + if (debugger) debugger->printf(" Subscribing to [%s]\r\n", config.mqttSubscribeTopic); } - // Allow some resources for the WiFi connection - yield(); - delay(2000); + sendMqttData("Connected!"); + } else { + if (debugger) { + debugger->print(" failed, "); + debugger->println(" trying again in 5 seconds"); + } } + yield(); } // Send a simple string embedded in json over MQTT @@ -349,11 +343,6 @@ void sendMqttData(String data) if (config.mqttPublishTopic == 0 || strlen(config.mqttPublishTopic) == 0) return; - // Make sure we're connected - if (!client->connected() || !mqtt.connected()) { - MQTT_connect(); - } - // Build a json with the message in a "data" attribute StaticJsonDocument<500> json; json["id"] = WiFi.macAddress(); diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index 06fa5546..ff52f5ba 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -41,7 +41,7 @@ void AmsWebServer::loop() { server.handleClient(); } -void AmsWebServer::setJson(StaticJsonDocument<500> json) { +void AmsWebServer::setJson(StaticJsonDocument<1024> json) { if(!json.isNull()) { p = json["data"]["P"].as(); @@ -87,7 +87,7 @@ void AmsWebServer::setJson(StaticJsonDocument<500> json) { } bool AmsWebServer::checkSecurity(byte level) { - bool access = !config->hasConfig() || config->authSecurity < level; + bool access = WiFi.getMode() == WIFI_AP || !config->hasConfig() || config->authSecurity < level; if(!access && config->authSecurity >= level && server.hasHeader("Authorization")) { println(" forcing web security"); String expectedAuth = String(config->authUser) + ":" + String(config->authPass); diff --git a/src/web/AmsWebServer.h b/src/web/AmsWebServer.h index cc9e0d01..317a78b9 100644 --- a/src/web/AmsWebServer.h +++ b/src/web/AmsWebServer.h @@ -24,12 +24,12 @@ class AmsWebServer { public: void setup(configuration* config, Stream* debugger); void loop(); - void setJson(StaticJsonDocument<500> json); + void setJson(StaticJsonDocument<1024> json); private: configuration* config; Stream* debugger; - StaticJsonDocument<500> json; + StaticJsonDocument<1024> json; int maxPwr; int p; double u1, u2, u3, i1, i2, i3;