diff --git a/doc/Aidon-HAN-Interface-Description-v11A-ID-34331.pdf b/doc/Aidon-HAN-Interface-Description-v11A-ID-34331.pdf new file mode 100644 index 00000000..7f9d43d8 Binary files /dev/null and b/doc/Aidon-HAN-Interface-Description-v11A-ID-34331.pdf differ diff --git a/hardware/README.md b/hardware/README.md index a191f9e6..cc83e8fb 100644 --- a/hardware/README.md +++ b/hardware/README.md @@ -19,12 +19,14 @@ You can also use a ESP based development board and combine this with a M-Bus mod [Wemos D1 mini](https://docs.wemos.cc/en/latest/d1/d1_mini.html) - M-Bus connected to GPIO5 (D1) - Jump GPIO4 (D2) to GND to force AP mode during boot +- Dallas temp sensor connected to GPIO14 (D5) ### ESP32 based boards [Wemos D32](https://docs.wemos.cc/en/latest/d32/d32.html) -- M-Bus connected to GPIO21 +- M-Bus connected to GPIO16 - Jump GPIO4 to GND to force AP mode during boot +- Dallas temp sensor connected to GPIO14 [Adafruit HUZZAH32](https://www.adafruit.com/product/3405) - M-Bus connected to GPIO16 diff --git a/hardware/v1/kicad/HAN_ESP_TSS721-rescue.dcm b/hardware/v1/kicad/HAN_ESP_TSS721-rescue.dcm new file mode 100644 index 00000000..5f3ed79b --- /dev/null +++ b/hardware/v1/kicad/HAN_ESP_TSS721-rescue.dcm @@ -0,0 +1,3 @@ +EESchema-DOCLIB Version 2.0 +# +#End Doc Library diff --git a/hardware/v1/kicad/HAN_ESP_TSS721-rescue.lib b/hardware/v1/kicad/HAN_ESP_TSS721-rescue.lib new file mode 100644 index 00000000..f0eda9a3 --- /dev/null +++ b/hardware/v1/kicad/HAN_ESP_TSS721-rescue.lib @@ -0,0 +1,356 @@ +EESchema-LIBRARY Version 2.4 +#encoding utf-8 +# +# BSS84-transistors +# +DEF BSS84-transistors Q 0 0 Y N 1 F N +F0 "Q" 200 75 50 H V L CNN +F1 "BSS84-transistors" 200 0 50 H V L CNN +F2 "TO_SOT_Packages_SMD:SOT-23" 200 -75 50 H I L CIN +F3 "" 0 0 50 H I L CNN +$FPLIST + SOT?23* +$ENDFPLIST +DRAW +C 65 0 111 0 1 10 N +C 100 -70 11 0 1 0 F +C 100 70 11 0 1 0 F +P 2 0 1 0 0 0 10 0 N +P 2 0 1 0 30 -70 100 -70 N +P 2 0 1 10 30 -50 30 -90 N +P 2 0 1 0 30 0 100 0 N +P 2 0 1 10 30 20 30 -20 N +P 2 0 1 0 30 70 100 70 N +P 2 0 1 10 30 90 30 50 N +P 2 0 1 0 100 -70 100 -100 N +P 2 0 1 0 100 -70 100 0 N +P 2 0 1 0 100 100 100 70 N +P 3 0 1 10 10 75 10 -75 10 -75 N +P 4 0 1 0 90 0 50 -15 50 15 90 0 F +P 4 0 1 0 100 -70 130 -70 130 70 100 70 N +P 4 0 1 0 110 -20 115 -15 145 -15 150 -10 N +P 4 0 1 0 130 -15 115 10 145 10 130 -15 N +X G 1 -200 0 200 R 50 50 1 1 I +X S 2 100 -200 100 U 50 50 1 1 P +X D 3 100 200 100 D 50 50 1 1 P +ENDDRAW +ENDDEF +# +# CP-device +# +DEF CP-device C 0 10 N Y 1 F N +F0 "C" 25 100 50 H V L CNN +F1 "CP-device" 25 -100 50 H V L CNN +F2 "" 38 -150 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + CP_* +$ENDFPLIST +DRAW +S -90 20 -90 40 0 1 0 N +S -90 20 90 20 0 1 0 N +S 90 -20 -90 -40 0 1 0 F +S 90 40 -90 40 0 1 0 N +S 90 40 90 20 0 1 0 N +P 2 0 1 0 -70 90 -30 90 N +P 2 0 1 0 -50 110 -50 70 N +X ~ 1 0 150 110 D 50 50 1 1 P +X ~ 2 0 -150 110 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# CP_Small-device +# +DEF CP_Small-device C 0 10 N N 1 F N +F0 "C" 10 70 50 H V L CNN +F1 "CP_Small-device" 10 -80 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + CP_* +$ENDFPLIST +DRAW +S -60 -12 60 -27 0 1 0 F +S -60 27 60 12 0 1 0 N +P 2 0 1 0 -50 60 -30 60 N +P 2 0 1 0 -40 50 -40 70 N +X ~ 1 0 100 73 D 50 50 1 1 P +X ~ 2 0 -100 73 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# C_Small-device +# +DEF C_Small-device C 0 10 N N 1 F N +F0 "C" 10 70 50 H V L CNN +F1 "C_Small-device" 10 -80 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + C_* +$ENDFPLIST +DRAW +P 2 0 1 13 -60 -20 60 -20 N +P 2 0 1 12 -60 20 60 20 N +X ~ 1 0 100 80 D 50 50 1 1 P +X ~ 2 0 -100 80 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# DS18B20-maxim +# +DEF DS18B20-maxim U 0 40 Y Y 1 F N +F0 "U" -150 250 50 H V C CNN +F1 "DS18B20-maxim" 0 -250 50 H V C CNN +F2 "" -150 250 50 H I C CNN +F3 "" -150 250 50 H I C CNN +$FPLIST + TO-92_* +$ENDFPLIST +DRAW +S -200 200 200 -200 0 1 0 N +X GND 1 -300 -100 100 R 50 50 1 1 W +X DQ 2 -300 0 100 R 50 50 1 1 B +X VDD 3 -300 100 100 R 50 50 1 1 W +ENDDRAW +ENDDEF +# +# ESP-12E-ESP8266 +# +DEF ESP-12E-ESP8266 U 0 40 Y Y 1 F N +F0 "U" 0 -100 50 H V C CNN +F1 "ESP-12E-ESP8266" 0 100 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + ESP-12E + ESP-12E_SMD +$ENDFPLIST +DRAW +S -600 -600 600 600 1 0 0 N +X REST 1 -900 300 300 R 50 50 1 1 I +X GPIO15 10 900 -300 300 L 50 50 1 1 B +X GPIO2 11 900 -200 300 L 50 50 1 1 B +X GPIO0 12 900 -100 300 L 50 50 1 1 B +X GPIO4 13 900 0 300 L 50 50 1 1 B +X GPIO5 14 900 100 300 L 50 50 1 1 B +X RXD 15 900 200 300 L 50 50 1 1 I +X TXD 16 900 300 300 L 50 50 1 1 O +X CS0 17 -250 -900 300 U 50 50 1 1 B +X MISO 18 -150 -900 300 U 50 50 1 1 B +X GPIO9 19 -50 -900 300 U 50 50 1 1 B +X ADC 2 -900 200 300 R 50 50 1 1 P +X GPIO10 20 50 -900 300 U 50 50 1 1 B +X MOSI 21 150 -900 300 U 50 50 1 1 B +X SCLK 22 250 -900 300 U 50 50 1 1 B +X CH_PD 3 -900 100 300 R 50 50 1 1 I +X GPIO16 4 -900 0 300 R 50 50 1 1 B +X GPIO14 5 -900 -100 300 R 50 50 1 1 B +X GPIO12 6 -900 -200 300 R 50 50 1 1 B +X GPIO13 7 -900 -300 300 R 50 50 1 1 B +X VCC 8 -900 -400 300 R 50 50 1 1 W +X GND 9 900 -400 300 L 50 50 1 1 W +ENDDRAW +ENDDEF +# +# FTDI_PROG_HDR-ESPProgHeader +# +DEF FTDI_PROG_HDR-ESPProgHeader J 0 40 Y N 1 F N +F0 "J" 0 300 50 H V C CNN +F1 "FTDI_PROG_HDR-ESPProgHeader" 0 -400 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + Connector*:*_??x*mm* + Connector*:*1x??x*mm* + Pin?Header?Straight?1X* + Pin?Header?Angled?1X* + Socket?Strip?Straight?1X* + Socket?Strip?Angled?1X* +$ENDFPLIST +DRAW +S -50 -295 0 -305 1 1 6 N +S -50 -195 0 -205 1 1 6 N +S -50 -95 0 -105 1 1 6 N +S -50 5 0 -5 1 1 6 N +S -50 105 0 95 1 1 6 N +S -50 205 0 195 1 1 6 N +S -50 250 50 -350 1 1 10 f +X Pin_1 1 -200 200 150 R 50 50 1 1 N +X Pin_2 2 -200 100 150 R 50 50 1 1 P +X Pin_3 3 -200 0 150 R 50 50 1 1 P +X Pin_4 4 -200 -100 150 R 50 50 1 1 N +X Pin_5 5 -200 -200 150 R 50 50 1 1 N +X Pin_6 6 -200 -300 150 R 50 50 1 1 P +ENDDRAW +ENDDEF +# +# Jumper-device +# +DEF Jumper-device JP 0 30 Y N 1 F N +F0 "JP" 0 150 50 H V C CNN +F1 "Jumper-device" 0 -80 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +DRAW +A 0 -26 125 1426 373 0 1 0 N -98 50 99 50 +C -100 0 35 0 1 0 N +C 100 0 35 0 1 0 N +X 1 1 -300 0 165 R 50 50 0 1 P +X 2 2 300 0 165 L 50 50 0 1 P +ENDDRAW +ENDDEF +# +# LM1117-3.3-regul +# +DEF LM1117-3.3-regul U 0 10 Y Y 1 F N +F0 "U" -150 125 50 H V C CNN +F1 "LM1117-3.3-regul" 0 125 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + SOT?223* + TO?263* + TO?252* + TO?220* +$ENDFPLIST +DRAW +S -200 -200 200 75 0 1 10 f +X GND 1 0 -300 100 U 50 50 1 1 W +X VO 2 300 0 100 L 50 50 1 1 w +X VI 3 -300 0 100 R 50 50 1 1 W +ENDDRAW +ENDDEF +# +# RJ45-conn +# +DEF RJ45-conn J 0 40 Y Y 1 F N +F0 "J" 200 500 50 H V C CNN +F1 "RJ45-conn" -150 500 50 H V C CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +DRAW +S -400 -300 400 450 0 1 10 f +P 3 0 1 0 -175 200 -175 250 -175 250 N +P 3 0 1 0 -125 250 -125 200 -125 200 N +P 3 0 1 0 -75 250 -75 200 -75 200 N +P 3 0 1 0 -25 250 -25 200 -25 200 N +P 3 0 1 0 25 250 25 200 25 200 N +P 3 0 1 0 75 250 75 200 75 200 N +P 3 0 1 0 125 200 125 250 125 250 N +P 3 0 1 0 175 200 175 250 175 250 N +P 14 0 1 0 -225 250 225 250 225 -150 125 -150 125 -200 75 -200 75 -250 -75 -250 -75 -200 -125 -200 -125 -150 -225 -150 -225 250 -225 250 N +X ~ 1 -350 -450 150 U 50 50 1 1 P +X ~ 2 -250 -450 150 U 50 50 1 1 P +X ~ 3 -150 -450 150 U 50 50 1 1 P +X ~ 4 -50 -450 150 U 50 50 1 1 P +X ~ 5 50 -450 150 U 50 50 1 1 P +X ~ 6 150 -450 150 U 50 50 1 1 P +X ~ 7 250 -450 150 U 50 50 1 1 P +X ~ 8 350 -450 150 U 50 50 1 1 P +X SHIELD 9 550 350 150 L 50 50 1 1 P +ENDDRAW +ENDDEF +# +# R_Small-device +# +DEF R_Small-device R 0 10 N N 1 F N +F0 "R" 30 20 50 H V L CNN +F1 "R_Small-device" 30 -40 50 H V L CNN +F2 "" 0 0 50 H I C CNN +F3 "" 0 0 50 H I C CNN +$FPLIST + R_* +$ENDFPLIST +DRAW +S -30 70 30 -70 0 1 8 N +X ~ 1 0 100 30 D 50 50 1 1 P +X ~ 2 0 -100 30 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +# SW_Push-switches +# +DEF SW_Push-switches SW 0 40 N N 1 F N +F0 "SW" 50 100 50 H V L CNN +F1 "SW_Push-switches" 0 -60 50 H V C CNN +F2 "" 0 200 50 H I C CNN +F3 "" 0 200 50 H I C CNN +DRAW +C -80 0 20 0 1 0 N +C 80 0 20 0 1 0 N +P 2 0 1 0 0 50 0 120 N +P 2 0 1 0 100 50 -100 50 N +X 1 1 -200 0 100 R 50 50 0 1 P +X 2 2 200 0 100 L 50 50 0 1 P +ENDDRAW +ENDDEF +# +# TSS721-tss721 +# +DEF TSS721-tss721 U 0 40 Y Y 1 F N +F0 "U" 200 850 50 H V L CNN +F1 "TSS721-tss721" 200 750 50 H V L CNN +F2 "" 0 -850 50 H V C CIN +F3 "" -200 -800 50 H V C CNN +$FPLIST + SOIC*3.9x9.9mm*Pitch1.27mm* + TSSOP*4.4x5mm*Pitch0.65mm* +$ENDFPLIST +DRAW +S -500 -700 500 700 0 1 10 f +X BUSL2 1 -600 400 100 R 50 50 1 1 I +X VS 10 -600 0 100 R 50 50 1 1 P +X VDD 11 0 800 100 D 50 50 1 1 W +X RX 12 600 500 100 L 50 50 1 1 I +X RXI 13 600 400 100 L 50 50 1 1 I I +X RIS 14 300 -800 100 U 50 50 1 1 I +X GND 15 0 -800 100 U 50 50 1 1 W +X BUSL1 16 -600 500 100 R 50 50 1 1 I +X VB 2 -600 -150 100 R 50 50 1 1 P +X STC 3 -600 -500 100 R 50 50 1 1 P +X RIDD 4 200 -800 100 U 50 50 1 1 O +X PF 5 -600 100 100 R 50 50 1 1 I +X SC 6 400 -800 100 U 50 50 1 1 P +X TXI 7 600 100 100 L 50 50 1 1 O I +X TX 8 600 200 100 L 50 50 1 1 O +X BAT 9 -100 800 100 D 50 50 1 1 I +ENDDRAW +ENDDEF +# +# USB_OTG-conn +# +DEF USB_OTG-conn J 0 40 Y Y 1 F N +F0 "J" -200 450 50 H V L CNN +F1 "USB_OTG-conn" -200 350 50 H V L CNN +F2 "" 150 -50 50 H I C CNN +F3 "" 150 -50 50 H I C CNN +$FPLIST + USB* +$ENDFPLIST +DRAW +C -150 85 25 0 1 10 F +C -25 135 15 0 1 10 F +S -200 -300 200 300 0 1 10 f +S -5 -300 5 -270 0 1 0 N +S 10 50 -20 20 0 1 10 F +S 200 -205 170 -195 0 1 0 N +S 200 -105 170 -95 0 1 0 N +S 200 -5 170 5 0 1 0 N +S 200 195 170 205 0 1 0 N +P 2 0 1 10 -75 85 25 85 N +P 4 0 1 10 -125 85 -100 85 -50 135 -25 135 N +P 4 0 1 10 -100 85 -75 85 -50 35 0 35 N +P 4 0 1 10 25 110 25 60 75 85 25 110 F +P 5 0 1 0 -170 220 -70 220 -80 190 -160 190 -170 220 F +P 9 0 1 0 -185 230 -185 220 -175 190 -175 180 -65 180 -65 190 -55 220 -55 230 -185 230 N +X VBUS 1 300 200 100 L 50 50 1 1 W +X D- 2 300 -100 100 L 50 50 1 1 P +X D+ 3 300 0 100 L 50 50 1 1 P +X ID 4 300 -200 100 L 50 50 1 1 P +X GND 5 0 -400 100 U 50 50 1 1 W +X Shield 6 -100 -400 100 U 50 50 1 1 P +ENDDRAW +ENDDEF +# +#End Library diff --git a/hardware/v1/kicad/sym-lib-table b/hardware/v1/kicad/sym-lib-table index 9c00ee7e..4bc9a47c 100644 --- a/hardware/v1/kicad/sym-lib-table +++ b/hardware/v1/kicad/sym-lib-table @@ -1,2 +1,3 @@ (sym_lib_table + (lib (name HAN_ESP_TSS721-rescue)(type Legacy)(uri ${KIPRJMOD}/HAN_ESP_TSS721-rescue.lib)(options "")(descr "")) ) diff --git a/lib/HanConfigAp/library.properties b/lib/HanConfigAp/library.properties deleted file mode 100644 index 6fa3db03..00000000 --- a/lib/HanConfigAp/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=HanConfigAp -version=1.0.0 -author=roarfred -maintainer=roarfred -sentence=HAN Configuraiton accesspoint -paragraph=HAN Configuraiton accesspoint -category=Sensors -url=https://github.com/roarfred/AmsToMqttBridge -architectures=* diff --git a/lib/HanConfigAp/src/Base64.cpp b/lib/HanConfigAp/src/Base64.cpp deleted file mode 100644 index 5cf68987..00000000 --- a/lib/HanConfigAp/src/Base64.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright (C) 2016 Arturo Guadalupi. All right reserved. - -This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -*/ - -#include "Base64.h" -#include -#if (defined(__AVR__)) -#include -#else -#include -#endif -const char PROGMEM _Base64AlphabetTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -int Base64Class::encode(char *output, char *input, int inputLength) { - int i = 0, j = 0; - int encodedLength = 0; - unsigned char A3[3]; - unsigned char A4[4]; - - while(inputLength--) { - A3[i++] = *(input++); - if(i == 3) { - fromA3ToA4(A4, A3); - - for(i = 0; i < 4; i++) { - output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[i]]); - } - - i = 0; - } - } - - if(i) { - for(j = i; j < 3; j++) { - A3[j] = '\0'; - } - - fromA3ToA4(A4, A3); - - for(j = 0; j < i + 1; j++) { - output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[j]]); - } - - while((i++ < 3)) { - output[encodedLength++] = '='; - } - } - output[encodedLength] = '\0'; - return encodedLength; -} - -int Base64Class::decode(char * output, char * input, int inputLength) { - int i = 0, j = 0; - int decodedLength = 0; - unsigned char A3[3]; - unsigned char A4[4]; - - - while (inputLength--) { - if(*input == '=') { - break; - } - - A4[i++] = *(input++); - if (i == 4) { - for (i = 0; i <4; i++) { - A4[i] = lookupTable(A4[i]); - } - - fromA4ToA3(A3,A4); - - for (i = 0; i < 3; i++) { - output[decodedLength++] = A3[i]; - } - i = 0; - } - } - - if (i) { - for (j = i; j < 4; j++) { - A4[j] = '\0'; - } - - for (j = 0; j <4; j++) { - A4[j] = lookupTable(A4[j]); - } - - fromA4ToA3(A3,A4); - - for (j = 0; j < i - 1; j++) { - output[decodedLength++] = A3[j]; - } - } - output[decodedLength] = '\0'; - return decodedLength; -} - -int Base64Class::encodedLength(int plainLength) { - int n = plainLength; - return (n + 2 - ((n + 2) % 3)) / 3 * 4; -} - -int Base64Class::decodedLength(char * input, int inputLength) { - int i = 0; - int numEq = 0; - for(i = inputLength - 1; input[i] == '='; i--) { - numEq++; - } - - return ((6 * inputLength) / 8) - numEq; -} - -//Private utility functions -inline void Base64Class::fromA3ToA4(unsigned char * A4, unsigned char * A3) { - A4[0] = (A3[0] & 0xfc) >> 2; - A4[1] = ((A3[0] & 0x03) << 4) + ((A3[1] & 0xf0) >> 4); - A4[2] = ((A3[1] & 0x0f) << 2) + ((A3[2] & 0xc0) >> 6); - A4[3] = (A3[2] & 0x3f); -} - -inline void Base64Class::fromA4ToA3(unsigned char * A3, unsigned char * A4) { - A3[0] = (A4[0] << 2) + ((A4[1] & 0x30) >> 4); - A3[1] = ((A4[1] & 0xf) << 4) + ((A4[2] & 0x3c) >> 2); - A3[2] = ((A4[2] & 0x3) << 6) + A4[3]; -} - -inline unsigned char Base64Class::lookupTable(char c) { - if(c >='A' && c <='Z') return c - 'A'; - if(c >='a' && c <='z') return c - 71; - if(c >='0' && c <='9') return c + 4; - if(c == '+') return 62; - if(c == '/') return 63; - return -1; -} - -Base64Class Base64; diff --git a/lib/HanConfigAp/src/Base64.h b/lib/HanConfigAp/src/Base64.h deleted file mode 100644 index 7330225e..00000000 --- a/lib/HanConfigAp/src/Base64.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright (C) 2016 Arturo Guadalupi. All right reserved. - -This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -*/ - -#ifndef _BASE64_H -#define _BASE64_H - -class Base64Class{ - public: - int encode(char *output, char *input, int inputLength); - int decode(char * output, char * input, int inputLength); - int encodedLength(int plainLength); - int decodedLength(char * input, int inputLength); - - private: - inline void fromA3ToA4(unsigned char * A4, unsigned char * A3); - inline void fromA4ToA3(unsigned char * A3, unsigned char * A4); - inline unsigned char lookupTable(char c); -}; -extern Base64Class Base64; - -#endif // _BASE64_H diff --git a/lib/HanConfigAp/src/HanConfigAp.cpp b/lib/HanConfigAp/src/HanConfigAp.cpp deleted file mode 100644 index bd51b760..00000000 --- a/lib/HanConfigAp/src/HanConfigAp.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "HanConfigAp.h" - -Stream* HanConfigAp::debugger; - -bool HanConfigAp::hasConfig() { - return config->hasConfig(); -} - -void HanConfigAp::setup(int accessPointButtonPin, configuration* config, Stream* debugger) -{ - this->debugger = debugger; - this->config = config; - - // Test if we're missing configuration - if (!config->hasConfig()) - { - print("No config. We're booting as AP. Look for SSID "); - println(this->AP_SSID); - isActivated = true; - } - else - { - // Load the configuration - if (this->debugger) config->print(this->debugger); - - if (accessPointButtonPin != INVALID_BUTTON_PIN) - { - // Assign pin for boot as AP - pinMode(accessPointButtonPin, INPUT_PULLUP); - - // Test if we're holding down the AP pin, over 5 seconds - int time = millis() + 5000; - print("Press the AP button now to boot as access point"); - while (millis() < time) - { - print("."); - if (digitalRead(accessPointButtonPin) == LOW) - { - print("AP button was pressed. Booting as access point now. Look for SSID "); - println(this->AP_SSID); - isActivated = true; - break; - } - delay(100); - } - println(""); - } - } - - if (isActivated) - { - // Setup AP - WiFi.disconnect(true); - WiFi.softAPdisconnect(true); - WiFi.mode(WIFI_OFF); - delay(2000); - - WiFi.softAP(AP_SSID); - WiFi.mode(WIFI_AP); - - /* Setup the DNS server redirecting all the domains to this IP */ - dnsServer.setErrorReplyCode(DNSReplyCode::NoError); - dnsServer.start(DNS_PORT, "*", WiFi.softAPIP()); - } -} - -bool HanConfigAp::loop() { - if(isActivated) { - //DNS - dnsServer.processNextRequest(); - } - - return isActivated; -} - -size_t HanConfigAp::print(const char* text) -{ - if (debugger) debugger->print(text); -} -size_t HanConfigAp::println(const char* text) -{ - if (debugger) debugger->println(text); -} -size_t HanConfigAp::print(const Printable& data) -{ - if (debugger) debugger->print(data); -} -size_t HanConfigAp::println(const Printable& data) -{ - if (debugger) debugger->println(data); -} diff --git a/lib/HanConfigAp/src/HanConfigAp.h b/lib/HanConfigAp/src/HanConfigAp.h deleted file mode 100644 index 401b8656..00000000 --- a/lib/HanConfigAp/src/HanConfigAp.h +++ /dev/null @@ -1,50 +0,0 @@ -// ap.h - -#ifndef _ACCESSPOINT_h -#define _ACCESSPOINT_h - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -#if defined(ESP8266) - #include -#elif defined(ESP32) // ARDUINO_ARCH_ESP32 - #include -#else - #warning "Unsupported board type" -#endif - -#include -#include "configuration.h" - -#define INVALID_BUTTON_PIN 0xFFFFFFFF - -class HanConfigAp { -public: - void setup(int accessPointButtonPin, configuration* config, Stream* debugger); - bool loop(); - bool hasConfig(); - bool isActivated = false; - -private: - const char* AP_SSID = "AMS2MQTT"; - - configuration* config; - - // DNS server - const byte DNS_PORT = 53; - DNSServer dnsServer; - - static size_t print(const char* text); - static size_t println(const char* text); - static size_t print(const Printable& data); - static size_t println(const Printable& data); - - static Stream* debugger; -}; - -#endif - diff --git a/lib/HanConfigAp/src/configuration.cpp b/lib/HanConfigAp/src/configuration.cpp deleted file mode 100644 index 3c14091c..00000000 --- a/lib/HanConfigAp/src/configuration.cpp +++ /dev/null @@ -1,265 +0,0 @@ -// -// -// - -#include "configuration.h" - -bool configuration::hasConfig() -{ - bool hasConfig = false; - EEPROM.begin(EEPROM_SIZE); - hasConfig = EEPROM.read(EEPROM_CONFIG_ADDRESS) == EEPROM_CHECK_SUM; - EEPROM.end(); - return hasConfig; -} - -bool configuration::save() -{ - int address = EEPROM_CONFIG_ADDRESS; - - EEPROM.begin(EEPROM_SIZE); - EEPROM.put(address, EEPROM_CHECK_SUM); - address++; - - address += saveString(address, ssid); - address += saveString(address, ssidPassword); - address += saveByte(address, meterType); - - - if(mqttHost) { - address += saveBool(address, true); - address += saveString(address, mqttHost); - address += saveInt(address, mqttPort); - address += saveString(address, mqttClientID); - address += saveString(address, mqttPublishTopic); - address += saveString(address, mqttSubscribeTopic); - } else { - address += saveBool(address, false); - } - - if (isSecure()) { - address += saveBool(address, true); - address += saveString(address, mqttUser); - address += saveString(address, mqttPass); - } - else - address += saveBool(address, false); - - - address += saveByte(address, authSecurity); - if (authSecurity > 0) { - address += saveString(address, authUser); - address += saveString(address, authPass); - } - - address += saveInt(address, fuseSize); - address += saveInt(address, distSys); - - bool success = EEPROM.commit(); - EEPROM.end(); - - return success; -} - - -bool configuration::load() -{ - int address = EEPROM_CONFIG_ADDRESS; - bool success = false; - - ssid = 0; - ssidPassword = 0; - meterType = (byte)0; - mqttHost = 0; - mqttClientID = 0; - mqttPublishTopic = 0; - mqttSubscribeTopic = 0; - mqttUser = 0; - mqttPass = 0; - mqttPort = 1883; - authSecurity = 0; - authUser = 0; - authPass = 0; - fuseSize = 0; - distSys = 0; - - EEPROM.begin(EEPROM_SIZE); - int cs = EEPROM.read(address); - if (cs == EEPROM_CHECK_SUM) - { - address++; - - address += readString(address, &ssid); - address += readString(address, &ssidPassword); - address += readByte(address, &meterType); - - bool mqtt = false; - address += readBool(address, &mqtt); - if(mqtt) { - address += readString(address, &mqttHost); - address += readInt(address, &mqttPort); - address += readString(address, &mqttClientID); - address += readString(address, &mqttPublishTopic); - address += readString(address, &mqttSubscribeTopic); - } - - bool secure = false; - address += readBool(address, &secure); - if (secure) - { - address += readString(address, &mqttUser); - address += readString(address, &mqttPass); - } - else - { - mqttUser = 0; - mqttPass = 0; - } - - address += readByte(address, &authSecurity); - if (authSecurity > 0) { - address += readString(address, &authUser); - address += readString(address, &authPass); - } else { - authUser = 0; - authPass = 0; - } - - address += readInt(address, &fuseSize); - address += readByte(address, &distSys); - - success = true; - } - EEPROM.end(); - return success; -} - -bool configuration::isSecure() -{ - return (mqttUser != 0) && (String(mqttUser).length() > 0); -} - -int configuration::readInt(int address, int *value) -{ - int lower = EEPROM.read(address); - int higher = EEPROM.read(address + 1); - *value = lower + (higher << 8); - return 2; -} -int configuration::saveInt(int address, int value) -{ - byte lowByte = value & 0xFF; - byte highByte = ((value >> 8) & 0xFF); - - EEPROM.write(address, lowByte); - EEPROM.write(address + 1, highByte); - - return 2; -} - -int configuration::readBool(int address, bool *value) -{ - byte y = EEPROM.read(address); - *value = (bool)y; - return 1; -} - -int configuration::saveBool(int address, bool value) -{ - byte y = (byte)value; - EEPROM.write(address, y); - return 1; -} - -int configuration::readByte(int address, byte *value) -{ - *value = EEPROM.read(address); - return 1; -} - -int configuration::saveByte(int address, byte value) -{ - EEPROM.write(address, value); - return 1; -} -void configuration::print(Stream* debugger) -{ - debugger->println("Configuration:"); - debugger->println("-----------------------------------------------"); - debugger->printf("ssid: %s\r\n", this->ssid); - debugger->printf("ssidPassword: %s\r\n", this->ssidPassword); - debugger->printf("meterType: %i\r\n", this->meterType); - if(this->mqttHost) { - debugger->printf("mqttHost: %s\r\n", this->mqttHost); - debugger->printf("mqttPort: %i\r\n", this->mqttPort); - debugger->printf("mqttClientID: %s\r\n", this->mqttClientID); - debugger->printf("mqttPublishTopic: %s\r\n", this->mqttPublishTopic); - debugger->printf("mqttSubscribeTopic: %s\r\n", this->mqttSubscribeTopic); - } - - if (this->isSecure()) - { - debugger->printf("SECURE MQTT CONNECTION:\r\n"); - debugger->printf("mqttUser: %s\r\n", this->mqttUser); - debugger->printf("mqttPass: %s\r\n", this->mqttPass); - } - - if (this->authSecurity > 0) { - debugger->printf("WEB AUTH:\r\n"); - debugger->printf("authSecurity: %i\r\n", this->authSecurity); - debugger->printf("authUser: %s\r\n", this->authUser); - debugger->printf("authPass: %s\r\n", this->authPass); - } - debugger->printf("fuseSize: %i\r\n", this->fuseSize); - debugger->printf("distSys: %i\r\n", this->distSys); - - debugger->println("-----------------------------------------------"); -} - -template int configuration::writeAnything(int ee, const T& value) -{ - const byte* p = (const byte*)(const void*)&value; - unsigned int i; - for (i = 0; i < sizeof(value); i++) - EEPROM.write(ee++, *p++); - return i; -} - -template int configuration::readAnything(int ee, T& value) -{ - byte* p = (byte*)(void*)&value; - unsigned int i; - for (i = 0; i < sizeof(value); i++) - *p++ = EEPROM.read(ee++); - return i; -} - -int configuration::readString(int pAddress, char* pString[]) -{ - int address = 0; - byte length = EEPROM.read(pAddress + address); - address++; - - char* buffer = new char[length]; - for (int i = 0; i - - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -class configuration { -public: - char* ssid; - char* ssidPassword; - char* mqttHost; - int mqttPort; - char* mqttClientID; - char* mqttPublishTopic; - char* mqttSubscribeTopic; - char* mqttUser; - char* mqttPass; - byte meterType; - - byte authSecurity; - char* authUser; - char* authPass; - - int fuseSize; - byte distSys; - - bool hasConfig(); - bool isSecure(); - bool save(); - bool load(); - - void print(Stream* debugger); -protected: - -private: - const int EEPROM_SIZE = 512; - const byte EEPROM_CHECK_SUM = 75; // Used to check if config is stored. Change if structure changes - const int EEPROM_CONFIG_ADDRESS = 0; - - int saveString(int pAddress, char* pString); - int readString(int pAddress, char* pString[]); - int saveInt(int pAddress, int pValue); - int readInt(int pAddress, int *pValue); - int saveBool(int pAddress, bool pValue); - int readBool(int pAddress, bool *pValue); - int saveByte(int pAddress, byte pValue); - int readByte(int pAddress, byte *pValue); - - - template int writeAnything(int ee, const T& value); - template int readAnything(int ee, T& value); -}; - -#endif - diff --git a/lib/HanReader/src/Aidon.h b/lib/HanReader/src/Aidon.h index ffe0dc87..dbac985a 100644 --- a/lib/HanReader/src/Aidon.h +++ b/lib/HanReader/src/Aidon.h @@ -3,7 +3,6 @@ #ifndef _AIDON_h #define _AIDON_h - enum class Aidon { List1 = 0x01, diff --git a/lib/HanReader/src/HanReader.cpp b/lib/HanReader/src/HanReader.cpp index 9d6df8b4..40b0eac6 100644 --- a/lib/HanReader/src/HanReader.cpp +++ b/lib/HanReader/src/HanReader.cpp @@ -80,10 +80,10 @@ void HanReader::debugPrint(byte *buffer, int start, int length) bool HanReader::read() { - if (han->available()) - { - byte newByte = han->read(); - return read(newByte); + while(han->available()) { + if(read(han->read())) { + return true; + } } return false; } diff --git a/lib/HanReader/src/Kamstrup.h b/lib/HanReader/src/Kamstrup.h index 71513916..7cb3470e 100644 --- a/lib/HanReader/src/Kamstrup.h +++ b/lib/HanReader/src/Kamstrup.h @@ -3,7 +3,6 @@ #ifndef _KAMSTRUP_h #define _KAMSTRUP_h - enum class Kamstrup { List3PhaseShort = 0x19, diff --git a/lib/HanToJson/library.properties b/lib/HanToJson/library.properties deleted file mode 100644 index 204ea958..00000000 --- a/lib/HanToJson/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=HanToJson -version=1.0.0 -author=roarfred -maintainer=roarfred -sentence=HAN reader data to Json -paragraph=HAN reader data to Json -category=Sensors -url=https://github.com/roarfred/AmsToMqttBridge -architectures=* diff --git a/lib/HanToJson/src/HanToJson.cpp b/lib/HanToJson/src/HanToJson.cpp deleted file mode 100644 index d5c19b8d..00000000 --- a/lib/HanToJson/src/HanToJson.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "HanToJson.h" -#include "Aidon.h" -#include "Kaifa.h" -#include "Kamstrup.h" - - -static void hanToJsonKaifa3phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Kaifa::List3PhaseShort) - { - 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["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; - data["U1"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL1)) / 10; - data["U2"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL2)) / 10; - data["U3"] = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL3)) / 10; - } - - 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); - data["tQO"] = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonKaifa1phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Kaifa::List1PhaseShort) - { - data["lv"] = hanReader.getString( (int)Kaifa_List1Phase::ListVersionIdentifier); - data["id"] = hanReader.getString( (int)Kaifa_List1Phase::MeterID); - 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); - data["tQO"] = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonKaifa(JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - int listSize = hanReader.getListSize(); - - if (listSize == (int)Kaifa::List1) - { - // Handle listSize == 1 specially - data["P"] = hanReader.getInt( (int)Kaifa_List1::ActivePowerImported); - return; - } - - switch (listSize) { - case (int)Kaifa::List3PhaseShort: - case (int)Kaifa::List3PhaseLong: - return hanToJsonKaifa3phase(listSize, data, hanReader, debugger); - case (int)Kaifa::List1PhaseShort: - case (int)Kaifa::List1PhaseLong: - return hanToJsonKaifa1phase(listSize, data, hanReader, debugger); - default: - if (debugger) debugger->printf("Warning: Unknown listSize %d\n", listSize); - return; - } -} - - -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::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; - data["U1"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL1)) / 10; - data["U2"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL2)) / 10; - data["U3"] = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL3)) / 10; - } - - 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); - data["tQO"] = hanReader.getInt( (int)Aidon_List3Phase::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonAidon1phase(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Aidon::List1PhaseShort) - { - data["lv"] = hanReader.getString( (int)Aidon_List1Phase::ListVersionIdentifier); - 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::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); - data["tQO"] = hanReader.getInt( (int)Aidon_List1Phase::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonAidon3phaseIT(int listSize, JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - if (listSize >= (int)Aidon::List3PhaseITShort) - { - data["lv"] = hanReader.getString( (int)Aidon_List3PhaseIT::ListVersionIdentifier); - data["id"] = hanReader.getString( (int)Aidon_List3PhaseIT::MeterID); - data["type"] = hanReader.getString( (int)Aidon_List3PhaseIT::MeterType); - 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["I2"] = 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); - data["tQO"] = hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonAidon(JsonObject& data, HanReader& hanReader, Stream *debugger) -{ - int listSize = hanReader.getListSize(); - - // Based on the list number, get all details - // according to OBIS specifications for the meter - if (listSize == (int)Aidon::List1) - { - // Handle listSize == 1 specially - data["P"] = hanReader.getInt((int)Aidon_List1::ActiveImportPower); - return; - } - - switch (listSize) { - case (int)Aidon::List3PhaseShort: - case (int)Aidon::List3PhaseLong: - return hanToJsonAidon3phase(listSize, data, hanReader, debugger); - case (int)Aidon::List1PhaseShort: - case (int)Aidon::List1PhaseLong: - return hanToJsonAidon1phase(listSize, data, hanReader, debugger); - case (int)Aidon::List3PhaseITShort: - case (int)Aidon::List3PhaseITLong: - return hanToJsonAidon3phaseIT(listSize, data, hanReader, debugger); - default: - if (debugger) debugger->printf("Warning: Unknown listSize %d\n", listSize); - return; - } -} - -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; - data["U1"] = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL1); - data["U2"] = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL2); - data["U3"] = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL3); - } - - 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); - data["tQO"] = hanReader.getInt( (int)Kamstrup_List3Phase::CumulativeReactiveExportEnergy); - } -} - -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) { - 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); - data["tQO"] = hanReader.getInt( (int)Kamstrup_List1Phase::CumulativeReactiveExportEnergy); - } -} - -static void hanToJsonKamstrup(JsonObject& data, HanReader& hanReader, Stream *debugger) { - int listSize = hanReader.getListSize(); - - switch (listSize) { - case (int)Kamstrup::List3PhaseShort: - case (int)Kamstrup::List3PhaseLong: - return hanToJsonKamstrup3phase(listSize, data, hanReader, debugger); - case (int)Kamstrup::List1PhaseShort: - case (int)Kamstrup::List1PhaseLong: - return hanToJsonKamstrup1phase(listSize, data, hanReader, debugger); - default: - if (debugger) debugger->printf("Warning: Unknown listSize %d\n", listSize); - return; - } -} - -void hanToJson(JsonObject& data, byte meterType, HanReader& hanReader, Stream *debugger) -{ - // Based on the list number, get all details - // according to OBIS specifications for the meter - switch (meterType) - { - case 1: // Kaifa - return hanToJsonKaifa(data, hanReader, debugger); - case 2: // Aidon - return hanToJsonAidon(data, hanReader, debugger); - case 3: // Kamstrup - return hanToJsonKamstrup(data, hanReader, debugger); - default: - if (debugger) { - debugger->print("Meter type "); - debugger->print(meterType, HEX); - debugger->println(" is unknown"); - } - break; - } -} - -void hanToJson(JsonObject& data, byte meterType, HanReader& hanReader) -{ - return hanToJson(data, meterType, hanReader, NULL); -} diff --git a/lib/HanToJson/src/HanToJson.h b/lib/HanToJson/src/HanToJson.h deleted file mode 100644 index a06f4caf..00000000 --- a/lib/HanToJson/src/HanToJson.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _HANTOJSON_h -#define _HANTOJSON_h - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "Arduino.h" -#else - #include "WProgram.h" -#endif - -#include -#include "HanReader.h" - -void hanToJson(JsonObject& data, byte meterType, HanReader& hanReader); -void hanToJson(JsonObject& root, byte meterType, HanReader& hanReader, Stream *debugPort); - - -#endif diff --git a/platformio.ini b/platformio.ini index 326212b3..fde67eb4 100755 --- a/platformio.ini +++ b/platformio.ini @@ -4,10 +4,10 @@ extra_configs = platformio-user.ini [common] framework = arduino -lib_deps = HanConfigAp@1.0.0, HanReader@1.0.0, HanToJson@1.0.0, ArduinoJson@^6.0.0, MQTT@^2.4.0, DallasTemperature@^3.8.0, EspSoftwareSerial@^6.7.1 +lib_deps = HanReader@1.0.0, ArduinoJson@6.14.1, MQTT@2.4.7, DallasTemperature@3.8.1, EspSoftwareSerial@6.7.1, Base64@1.0.0 [env:hw1esp12e] -platform = espressif8266 +platform = espressif8266@2.3.3 board = esp12e framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -18,7 +18,7 @@ extra_scripts = scripts/makeweb.py [env:esp12e] -platform = espressif8266 +platform = espressif8266@2.3.3 board = esp12e framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -27,7 +27,7 @@ extra_scripts = scripts/makeweb.py [env:d1mini] -platform = espressif8266 +platform = espressif8266@2.3.3 board = d1_mini framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -36,7 +36,7 @@ extra_scripts = scripts/makeweb.py [env:esp32] -platform = espressif32 +platform = espressif32@1.11.2 board = esp32dev framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -45,7 +45,7 @@ extra_scripts = scripts/makeweb.py [env:lolind32] -platform = espressif32 +platform = espressif32@1.11.2 board = lolin_d32 framework = ${common.framework} lib_deps = ${common.lib_deps} @@ -54,7 +54,7 @@ extra_scripts = scripts/makeweb.py [env:featheresp32] -platform = espressif32 +platform = espressif32@1.11.2 board = featheresp32 framework = ${common.framework} lib_deps = ${common.lib_deps} diff --git a/src/AmsConfiguration.cpp b/src/AmsConfiguration.cpp new file mode 100644 index 00000000..eff237ed --- /dev/null +++ b/src/AmsConfiguration.cpp @@ -0,0 +1,602 @@ +#include "AmsConfiguration.h" + +String AmsConfiguration::getWifiSsid() { + return wifiSsid; +} + +void AmsConfiguration::setWifiSsid(String wifiSsid) { + wifiChanged |= this->wifiSsid != wifiSsid; + this->wifiSsid = String(wifiSsid); +} + +String AmsConfiguration::getWifiPassword() { + return wifiPassword; +} + +void AmsConfiguration::setWifiPassword(String wifiPassword) { + wifiChanged |= this->wifiPassword != wifiPassword; + this->wifiPassword = String(wifiPassword); +} + +String AmsConfiguration::getWifiIp() { + return wifiIp; +} + +void AmsConfiguration::setWifiIp(String wifiIp) { + wifiChanged |= this->wifiIp != wifiIp; + this->wifiIp = String(wifiIp); +} + +String AmsConfiguration::getWifiGw() { + return wifiGw; +} + +void AmsConfiguration::setWifiGw(String wifiGw) { + wifiChanged |= this->wifiGw != wifiGw; + this->wifiGw = String(wifiGw); +} + +String AmsConfiguration::getWifiSubnet() { + return wifiSubnet; +} + +void AmsConfiguration::setWifiSubnet(String wifiSubnet) { + wifiChanged |= this->wifiSubnet != wifiSubnet; + this->wifiSubnet = String(wifiSubnet); +} + +void AmsConfiguration::clearWifiIp() { + setWifiIp(""); + setWifiGw(""); + setWifiSubnet(""); +} + +bool AmsConfiguration::isWifiChanged() { + return wifiChanged; +} + +void AmsConfiguration::ackWifiChange() { + wifiChanged = false; +} + + +String AmsConfiguration::getMqttHost() { + return mqttHost; +} + +void AmsConfiguration::setMqttHost(String mqttHost) { + mqttChanged |= this->mqttHost != mqttHost; + this->mqttHost = String(mqttHost); +} + +int AmsConfiguration::getMqttPort() { + return mqttPort; +} + +void AmsConfiguration::setMqttPort(int mqttPort) { + mqttChanged |= this->mqttPort != mqttPort; + this->mqttPort = mqttPort; +} + +String AmsConfiguration::getMqttClientId() { + return mqttClientId; +} + +void AmsConfiguration::setMqttClientId(String mqttClientId) { + mqttChanged |= this->mqttClientId != mqttClientId; + this->mqttClientId = String(mqttClientId); +} + +String AmsConfiguration::getMqttPublishTopic() { + return mqttPublishTopic; +} + +void AmsConfiguration::setMqttPublishTopic(String mqttPublishTopic) { + mqttChanged |= this->mqttPublishTopic != mqttPublishTopic; + this->mqttPublishTopic = String(mqttPublishTopic); +} + +String AmsConfiguration::getMqttSubscribeTopic() { + return mqttSubscribeTopic; +} + +void AmsConfiguration::setMqttSubscribeTopic(String mqttSubscribeTopic) { + mqttChanged |= this->mqttSubscribeTopic != mqttSubscribeTopic; + this->mqttSubscribeTopic = String(mqttSubscribeTopic); +} + +String AmsConfiguration::getMqttUser() { + return mqttUser; +} + +void AmsConfiguration::setMqttUser(String mqttUser) { + mqttChanged |= this->mqttUser != mqttUser; + this->mqttUser = String(mqttUser); +} + +String AmsConfiguration::getMqttPassword() { + return mqttPassword; +} + +void AmsConfiguration::setMqttPassword(String mqttPassword) { + mqttChanged |= this->mqttPassword != mqttPassword; + this->mqttPassword = String(mqttPassword); +} + +void AmsConfiguration::clearMqtt() { + setMqttHost(""); + setMqttPort(1883); + setMqttClientId(""); + setMqttPublishTopic(""); + setMqttSubscribeTopic(""); + setMqttUser(""); + setMqttPassword(""); +} + +bool AmsConfiguration::isMqttChanged() { + return mqttChanged; +} + +void AmsConfiguration::ackMqttChange() { + mqttChanged = false; +} + + +byte AmsConfiguration::getAuthSecurity() { + return authSecurity; +} + +void AmsConfiguration::setAuthSecurity(byte authSecurity) { + this->authSecurity = authSecurity; +} + +String AmsConfiguration::getAuthUser() { + return authUser; +} + +void AmsConfiguration::setAuthUser(String authUser) { + this->authUser = String(authUser); +} + +String AmsConfiguration::getAuthPassword() { + return authPassword; +} + +void AmsConfiguration::setAuthPassword(String authPassword) { + this->authPassword = String(authPassword); +} + +void AmsConfiguration::clearAuth() { + setAuthSecurity(0); + setAuthUser(""); + setAuthPassword(""); +} + +int AmsConfiguration::getMeterType() { + return this->meterType; +} + +void AmsConfiguration::setMeterType(int meterType) { + this->meterType = meterType; +} + +int AmsConfiguration::getDistributionSystem() { + return this->distributionSystem; +} + +void AmsConfiguration::setDistributionSystem(int distributionSystem) { + this->distributionSystem = distributionSystem; +} + +int AmsConfiguration::getMainFuse() { + return this->mainFuse; +} + +void AmsConfiguration::setMainFuse(int mainFuse) { + this->mainFuse = mainFuse; +} + +int AmsConfiguration::getProductionCapacity() { + return this->productionCapacity; +} + +void AmsConfiguration::setProductionCapacity(int productionCapacity) { + this->productionCapacity = productionCapacity; +} + + +bool AmsConfiguration::hasConfig() { + EEPROM.begin(EEPROM_SIZE); + int configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS); + EEPROM.end(); + switch(configVersion) { + case 71: + case 72: + case 75: + case 80: + return true; + default: + return false; + } +} + +bool AmsConfiguration::load() { + int address = EEPROM_CONFIG_ADDRESS; + bool success = false; + + EEPROM.begin(EEPROM_SIZE); + int cs = EEPROM.read(address); + address++; + switch(cs) { + case 71: // Same as 72 + case 72: + success = loadConfig72(address); + break; + case 75: + success = loadConfig75(address); + break; + case 80: + success = loadConfig80(address); + break; + } + EEPROM.end(); + return success; +} + +bool AmsConfiguration::loadConfig72(int address) { + char* temp; + + address += readString(address, &temp); + setWifiSsid(temp); + address += readString(address, &temp); + setWifiPassword(temp); + + byte b; + address += readByte(address, &b); + setMeterType(b); + + address += readString(address, &temp); + setMqttHost(temp); + int port; + address += readInt(address, &port); + setMqttPort(port); + address += readString(address, &temp); + setMqttClientId(temp); + address += readString(address, &temp); + setMqttPublishTopic(temp); + address += readString(address, &temp); + setMqttSubscribeTopic(temp); + + bool secure = false; + address += readBool(address, &secure); + if (secure) { + address += readString(address, &temp); + setMqttUser(temp); + address += readString(address, &temp); + setMqttPassword(temp); + } else { + setMqttUser(""); + setMqttPassword(""); + } + + clearAuth(); + + setWifiIp(""); + setWifiGw(""); + setWifiSubnet(""); + setMainFuse(0); + setProductionCapacity(0); + setDistributionSystem(0); + + ackWifiChange(); + + return true; +} + +bool AmsConfiguration::loadConfig75(int address) { + char* temp; + + address += readString(address, &temp); + setWifiSsid(temp); + address += readString(address, &temp); + setWifiPassword(temp); + + byte b; + address += readByte(address, &b); + setMeterType(b); + + bool mqtt = false; + address += readBool(address, &mqtt); + if(mqtt) { + address += readString(address, &temp); + setMqttHost(temp); + int port; + address += readInt(address, &port); + setMqttPort(port); + address += readString(address, &temp); + setMqttClientId(temp); + address += readString(address, &temp); + setMqttPublishTopic(temp); + address += readString(address, &temp); + setMqttSubscribeTopic(temp); + } + + bool secure = false; + address += readBool(address, &secure); + if (secure) { + address += readString(address, &temp); + setMqttUser(temp); + address += readString(address, &temp); + setMqttPassword(temp); + } else { + setMqttUser(""); + setMqttPassword(""); + } + + address += readByte(address, &authSecurity); + if (authSecurity > 0) { + address += readString(address, &temp); + setAuthUser(temp); + address += readString(address, &temp); + setAuthPassword(temp); + } else { + clearAuth(); + } + + int i; + address += readInt(address, &i); + setMainFuse(i); + address += readByte(address, &b); + setDistributionSystem(b); + + setWifiIp(""); + setWifiGw(""); + setWifiSubnet(""); + setProductionCapacity(0); + + ackWifiChange(); + + return true; +} + +bool AmsConfiguration::loadConfig80(int address) { + char* temp; + + address += readString(address, &temp); + setWifiSsid(temp); + address += readString(address, &temp); + setWifiPassword(temp); + address += readString(address, &temp); + setWifiIp(temp); + address += readString(address, &temp); + setWifiGw(temp); + address += readString(address, &temp); + setWifiSubnet(temp); + + bool mqtt = false; + address += readBool(address, &mqtt); + if(mqtt) { + address += readString(address, &temp); + setMqttHost(temp); + int port; + address += readInt(address, &port); + setMqttPort(port); + address += readString(address, &temp); + setMqttClientId(temp); + address += readString(address, &temp); + setMqttPublishTopic(temp); + address += readString(address, &temp); + setMqttSubscribeTopic(temp); + + bool secure = false; + address += readBool(address, &secure); + if (secure) + { + address += readString(address, &temp); + setMqttUser(temp); + address += readString(address, &temp); + setMqttPassword(temp); + } else { + setMqttUser(""); + setMqttPassword(""); + } + } else { + clearMqtt(); + } + + address += readByte(address, &authSecurity); + if (authSecurity > 0) { + address += readString(address, &temp); + setAuthUser(temp); + address += readString(address, &temp); + setAuthPassword(temp); + } else { + clearAuth(); + } + + int i; + address += readInt(address, &i); + setMeterType(i); + address += readInt(address, &i); + setDistributionSystem(i); + address += readInt(address, &i); + setMainFuse(i); + address += readInt(address, &i); + setProductionCapacity(i); + + ackWifiChange(); + + return true; +} + +bool AmsConfiguration::save() { + int address = EEPROM_CONFIG_ADDRESS; + + EEPROM.begin(EEPROM_SIZE); + EEPROM.put(address, EEPROM_CHECK_SUM); + address++; + + address += saveString(address, wifiSsid.c_str()); + address += saveString(address, wifiPassword.c_str()); + address += saveString(address, wifiIp.c_str()); + address += saveString(address, wifiGw.c_str()); + address += saveString(address, wifiSubnet.c_str()); + if(mqttHost) { + address += saveBool(address, true); + address += saveString(address, mqttHost.c_str()); + address += saveInt(address, mqttPort); + address += saveString(address, mqttClientId.c_str()); + address += saveString(address, mqttPublishTopic.c_str()); + address += saveString(address, mqttSubscribeTopic.c_str()); + if (mqttUser) { + address += saveBool(address, true); + address += saveString(address, mqttUser.c_str()); + address += saveString(address, mqttPassword.c_str()); + } else { + address += saveBool(address, false); + } + } else { + address += saveBool(address, false); + } + + address += saveByte(address, authSecurity); + if (authSecurity > 0) { + address += saveString(address, authUser.c_str()); + address += saveString(address, authPassword.c_str()); + } + + address += saveInt(address, meterType); + address += saveInt(address, distributionSystem); + address += saveInt(address, mainFuse); + address += saveInt(address, productionCapacity); + + bool success = EEPROM.commit(); + EEPROM.end(); + + return success; +} + +int AmsConfiguration::readString(int pAddress, char* pString[]) { + int address = 0; + byte length = EEPROM.read(pAddress + address); + address++; + + char* buffer = new char[length]; + for (int i = 0; i> 8) & 0xFF); + + EEPROM.write(address, lowByte); + EEPROM.write(address + 1, highByte); + + return 2; +} + +int AmsConfiguration::readBool(int address, bool *value) { + byte y = EEPROM.read(address); + *value = (bool)y; + return 1; +} + +int AmsConfiguration::saveBool(int address, bool value) { + byte y = (byte)value; + EEPROM.write(address, y); + return 1; +} + +int AmsConfiguration::readByte(int address, byte *value) { + *value = EEPROM.read(address); + return 1; +} + +int AmsConfiguration::saveByte(int address, byte value) { + EEPROM.write(address, value); + return 1; +} + +template int AmsConfiguration::writeAnything(int ee, const T& value) { + const byte* p = (const byte*)(const void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + EEPROM.write(ee++, *p++); + return i; +} + +template int AmsConfiguration::readAnything(int ee, T& value) { + byte* p = (byte*)(void*)&value; + unsigned int i; + for (i = 0; i < sizeof(value); i++) + *p++ = EEPROM.read(ee++); + return i; +} + +void AmsConfiguration::print(Stream* debugger) +{ + debugger->println("Configuration:"); + debugger->println("-----------------------------------------------"); + debugger->printf("WiFi SSID: %s\r\n", this->getWifiSsid().c_str()); + debugger->printf("WiFi Psk: %s\r\n", this->getWifiPassword().c_str()); + + if(getWifiIp()) { + debugger->printf("IP: %s\r\n", this->getWifiIp().c_str()); + debugger->printf("Gateway: %s\r\n", this->getWifiGw().c_str()); + debugger->printf("Subnet: %s\r\n", this->getWifiSubnet().c_str()); + } + + if(getMqttHost()) { + debugger->printf("mqttHost: %s\r\n", this->getMqttHost().c_str()); + debugger->printf("mqttPort: %i\r\n", this->getMqttPort()); + debugger->printf("mqttClientID: %s\r\n", this->getMqttClientId().c_str()); + debugger->printf("mqttPublishTopic: %s\r\n", this->getMqttPublishTopic().c_str()); + debugger->printf("mqttSubscribeTopic: %s\r\n", this->getMqttSubscribeTopic().c_str()); + if (this->getMqttUser()) { + debugger->printf("SECURE MQTT CONNECTION:\r\n"); + debugger->printf("mqttUser: %s\r\n", this->getMqttUser().c_str()); + debugger->printf("mqttPass: %s\r\n", this->getMqttPassword().c_str()); + } + } + + if (this->getAuthSecurity()) { + debugger->printf("WEB AUTH:\r\n"); + debugger->printf("authSecurity: %i\r\n", this->getAuthSecurity()); + debugger->printf("authUser: %s\r\n", this->getAuthUser().c_str()); + debugger->printf("authPass: %s\r\n", this->getAuthPassword().c_str()); + } + + debugger->printf("meterType: %i\r\n", this->getMeterType()); + debugger->printf("distSys: %i\r\n", this->getDistributionSystem()); + debugger->printf("fuseSize: %i\r\n", this->getMainFuse()); + debugger->printf("productionCapacity: %i\r\n", this->getProductionCapacity()); + + debugger->println("-----------------------------------------------"); +} diff --git a/src/AmsConfiguration.h b/src/AmsConfiguration.h new file mode 100644 index 00000000..d0282779 --- /dev/null +++ b/src/AmsConfiguration.h @@ -0,0 +1,110 @@ +#ifndef _AMSCONFIGURATION_h +#define _AMSCONFIGURATION_h +#include +#include "Arduino.h" + +class AmsConfiguration { +public: + bool hasConfig(); + bool load(); + bool save(); + + String getWifiSsid(); + void setWifiSsid(String wifiSsid); + String getWifiPassword(); + void setWifiPassword(String wifiPassword); + String getWifiIp(); + void setWifiIp(String wifiIp); + String getWifiGw(); + void setWifiGw(String wifiGw); + String getWifiSubnet(); + void setWifiSubnet(String wifiSubnet); + void clearWifiIp(); + + bool isWifiChanged(); + void ackWifiChange(); + + String getMqttHost(); + void setMqttHost(String mqttHost); + int getMqttPort(); + void setMqttPort(int mqttPort); + String getMqttClientId(); + void setMqttClientId(String mqttClientId); + String getMqttPublishTopic(); + void setMqttPublishTopic(String mqttPublishTopic); + String getMqttSubscribeTopic(); + void setMqttSubscribeTopic(String mqttSubscribeTopic); + String getMqttUser(); + void setMqttUser(String mqttUser); + String getMqttPassword(); + void setMqttPassword(String mqttPassword); + void clearMqtt(); + + bool isMqttChanged(); + void ackMqttChange(); + + byte getAuthSecurity(); + void setAuthSecurity(byte authSecurity); + String getAuthUser(); + void setAuthUser(String authUser); + String getAuthPassword(); + void setAuthPassword(String authPassword); + void clearAuth(); + + int getMeterType(); + void setMeterType(int meterType); + int getDistributionSystem(); + void setDistributionSystem(int distributionSystem); + int getMainFuse(); + void setMainFuse(int mainFuse); + int getProductionCapacity(); + void setProductionCapacity(int productionCapacity); + + void print(Stream* debugger); + +protected: + +private: + String wifiSsid; + String wifiPassword; + String wifiIp; + String wifiGw; + String wifiSubnet; + bool wifiChanged; + + String mqttHost; + int mqttPort; + String mqttClientId; + String mqttPublishTopic; + String mqttSubscribeTopic; + String mqttUser; + String mqttPassword; + bool mqttChanged; + + byte authSecurity; + String authUser; + String authPassword; + + int meterType, distributionSystem, mainFuse, productionCapacity; + + const int EEPROM_SIZE = 512; + const int EEPROM_CHECK_SUM = 80; // Used to check if config is stored. Change if structure changes + const int EEPROM_CONFIG_ADDRESS = 0; + + bool loadConfig72(int address); + bool loadConfig75(int address); + bool loadConfig80(int address); + + int saveString(int pAddress, const char* pString); + int readString(int pAddress, char* pString[]); + int saveInt(int pAddress, int pValue); + int readInt(int pAddress, int *pValue); + int saveBool(int pAddress, bool pValue); + int readBool(int pAddress, bool *pValue); + int saveByte(int pAddress, byte pValue); + int readByte(int pAddress, byte *pValue); + + template int writeAnything(int ee, const T& value); + template int readAnything(int ee, T& value); +}; +#endif diff --git a/src/AmsData.cpp b/src/AmsData.cpp new file mode 100644 index 00000000..fdd9c890 --- /dev/null +++ b/src/AmsData.cpp @@ -0,0 +1,350 @@ +#include "AmsData.h" +#include "Kaifa.h" +#include "Aidon.h" +#include "Kamstrup.h" + +AmsData::AmsData() {} + +AmsData::AmsData(int meterType, HanReader& hanReader) { + lastUpdateMillis = millis(); + packageTimestamp = hanReader.getPackageTime(); + + int listSize = hanReader.getListSize(); + switch(meterType) { + case METER_TYPE_KAIFA: + extractFromKaifa(hanReader, listSize); + break; + case METER_TYPE_AIDON: + extractFromAidon(hanReader, listSize); + break; + case METER_TYPE_KAMSTRUP: + extractFromKamstrup(hanReader, listSize); + break; + + } +} + +void AmsData::extractFromKaifa(HanReader& hanReader, int listSize) { + switch(listSize) { + case (int)Kaifa::List1: + listType = 1; + break; + case (int)Kaifa::List3PhaseShort: + threePhase = true; + case (int)Kaifa::List1PhaseShort: + listType = 2; + break; + case (int)Kaifa::List3PhaseLong: + threePhase = true; + case (int)Kaifa::List1PhaseLong: + listType = 3; + break; + } + + if(listSize == (int)Kaifa::List1) { + activeImportPower = hanReader.getInt((int)Kaifa_List1::ActivePowerImported); + } else { + switch(listSize) { + case (int)Kaifa::List3PhaseLong: + meterTimestamp = hanReader.getTime( (int)Kaifa_List3Phase::MeterClock); + activeImportCounter = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeActiveImportEnergy); + activeExportCounter = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeActiveExportEnergy); + reactiveImportCounter = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeReactiveImportEnergy); + reactiveExportCounter = hanReader.getInt( (int)Kaifa_List3Phase::CumulativeReactiveExportEnergy); + case (int)Kaifa::List3PhaseShort: + listId = hanReader.getString( (int)Kaifa_List3Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Kaifa_List3Phase::MeterID); + meterType = hanReader.getString( (int)Kaifa_List3Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Kaifa_List3Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Kaifa_List3Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Kaifa_List3Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Kaifa_List3Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL1)) / 1000; + l2current = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL2)) / 1000; + l3current = ((double) hanReader.getInt((int)Kaifa_List3Phase::CurrentL3)) / 1000; + l1voltage = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL1)) / 10; + l2voltage = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL2)) / 10; + l3voltage = ((double) hanReader.getInt((int)Kaifa_List3Phase::VoltageL3)) / 10; + break; + case (int)Kaifa::List1PhaseLong: + meterTimestamp = hanReader.getTime( (int)Kaifa_List1Phase::MeterClock); + activeImportCounter = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeActiveImportEnergy); + activeExportCounter = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeActiveExportEnergy); + reactiveImportCounter = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeReactiveImportEnergy); + reactiveExportCounter = hanReader.getInt( (int)Kaifa_List1Phase::CumulativeReactiveExportEnergy); + case (int)Kaifa::List1PhaseShort: + listId = hanReader.getString( (int)Kaifa_List1Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Kaifa_List1Phase::MeterID); + meterType = hanReader.getString( (int)Kaifa_List1Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Kaifa_List1Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Kaifa_List1Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Kaifa_List1Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Kaifa_List1Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt((int)Kaifa_List1Phase::CurrentL1)) / 1000; + l1voltage = ((double) hanReader.getInt((int)Kaifa_List1Phase::VoltageL1)) / 10; + break; + } + } +} + +void AmsData::extractFromAidon(HanReader& hanReader, int listSize) { + switch(listSize) { + case (int)Aidon::List1: + listType = 1; + break; + case (int)Aidon::List3PhaseITShort: + case (int)Aidon::List3PhaseShort: + threePhase = true; + case (int)Aidon::List1PhaseShort: + listType = 2; + break; + case (int)Aidon::List3PhaseITLong: + case (int)Aidon::List3PhaseLong: + threePhase = true; + case (int)Aidon::List1PhaseLong: + listType = 3; + break; + } + + if(listSize == (int)Aidon::List1) { + activeImportPower = hanReader.getInt((int)Aidon_List1::ActiveImportPower); + } else { + switch(listSize) { + case (int)Aidon::List3PhaseLong: + meterTimestamp = hanReader.getTime( (int)Aidon_List3Phase::Timestamp); + activeImportCounter = ((double) hanReader.getInt( (int)Aidon_List3Phase::CumulativeActiveImportEnergy)) / 100; + activeExportCounter = ((double) hanReader.getInt( (int)Aidon_List3Phase::CumulativeActiveExportEnergy)) / 100; + reactiveImportCounter = ((double) hanReader.getInt( (int)Aidon_List3Phase::CumulativeReactiveImportEnergy)) / 100; + reactiveExportCounter = ((double) hanReader.getInt( (int)Aidon_List3Phase::CumulativeReactiveExportEnergy)) / 100; + case (int)Aidon::List3PhaseShort: + listId = hanReader.getString( (int)Aidon_List3Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Aidon_List3Phase::MeterID); + meterType = hanReader.getString( (int)Aidon_List3Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Aidon_List3Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Aidon_List3Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Aidon_List3Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Aidon_List3Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL1)) / 10; + l2current = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL2)) / 10; + l3current = ((double) hanReader.getInt( (int)Aidon_List3Phase::CurrentL3)) / 10; + l1voltage = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL1)) / 10; + l2voltage = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL2)) / 10; + l3voltage = ((double) hanReader.getInt( (int)Aidon_List3Phase::VoltageL3)) / 10; + break; + case (int)Aidon::List1PhaseLong: + meterTimestamp = hanReader.getTime( (int)Aidon_List1Phase::Timestamp); + activeImportCounter = ((double) hanReader.getInt( (int)Aidon_List1Phase::CumulativeActiveImportEnergy)) / 100; + activeExportCounter = ((double) hanReader.getInt( (int)Aidon_List1Phase::CumulativeActiveExportEnergy)) / 100; + reactiveImportCounter = ((double) hanReader.getInt( (int)Aidon_List1Phase::CumulativeReactiveImportEnergy)) / 100; + reactiveExportCounter = ((double) hanReader.getInt( (int)Aidon_List1Phase::CumulativeReactiveExportEnergy)) / 100; + case (int)Aidon::List1PhaseShort: + listId = hanReader.getString( (int)Aidon_List1Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Aidon_List1Phase::MeterID); + meterType = hanReader.getString( (int)Aidon_List1Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Aidon_List1Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Aidon_List1Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Aidon_List1Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Aidon_List1Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt( (int)Aidon_List1Phase::CurrentL1)) / 10; + l1voltage = ((double) hanReader.getInt( (int)Aidon_List1Phase::VoltageL1)) / 10; + break; + case (int)Aidon::List3PhaseITLong: + meterTimestamp = hanReader.getTime( (int)Aidon_List3PhaseIT::Timestamp); + activeImportCounter = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeActiveImportEnergy)) / 100; + activeExportCounter = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeActiveExportEnergy)) / 100; + reactiveImportCounter = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeReactiveImportEnergy)) / 100; + reactiveExportCounter = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CumulativeReactiveExportEnergy)) / 100; + case (int)Aidon::List3PhaseITShort: + listId = hanReader.getString( (int)Aidon_List3PhaseIT::ListVersionIdentifier); + meterId = hanReader.getString( (int)Aidon_List3PhaseIT::MeterID); + meterType = hanReader.getString( (int)Aidon_List3PhaseIT::MeterType); + activeImportPower = hanReader.getInt( (int)Aidon_List3PhaseIT::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Aidon_List3PhaseIT::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Aidon_List3PhaseIT::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Aidon_List3PhaseIT::ReactiveExportPower); + l1current = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL1)) / 10; + l2current = 0; + l3current = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::CurrentL3)) / 10; + l1voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL1)) / 10; + l2voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL2)) / 10; + l3voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL3)) / 10; + //l2current = ((activeImportPower * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage; + threePhase = true; + break; + } + } +} + +void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize) { + switch(listSize) { + case (int)Kamstrup::List3PhaseShort: + threePhase = true; + case (int)Kamstrup::List1PhaseShort: + listType = 2; + break; + case (int)Kamstrup::List3PhaseLong: + threePhase = true; + case (int)Kamstrup::List1PhaseLong: + listType = 3; + break; + } + + switch(listSize) { + case (int)Kamstrup::List3PhaseLong: + meterTimestamp = hanReader.getTime( (int)Kamstrup_List3Phase::MeterClock); + activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeActiveImportEnergy)) / 100; + activeExportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeActiveExportEnergy)) / 100; + reactiveImportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeReactiveImportEnergy)) / 100; + reactiveExportCounter = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CumulativeReactiveExportEnergy)) / 100; + case (int)Kamstrup::List3PhaseShort: + listId = hanReader.getString( (int)Kamstrup_List3Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Kamstrup_List3Phase::MeterID); + meterType = hanReader.getString( (int)Kamstrup_List3Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Kamstrup_List3Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL1)) / 100; + l2current = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL2)) / 100; + l3current = ((double) hanReader.getInt((int)Kamstrup_List3Phase::CurrentL3)) / 100; + l1voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL1); + l2voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL2); + l3voltage = hanReader.getInt( (int)Kamstrup_List3Phase::VoltageL3); + break; + case (int)Kamstrup::List1PhaseLong: + meterTimestamp = hanReader.getTime( (int)Kamstrup_List1Phase::MeterClock); + activeImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveImportEnergy)) / 100; + activeExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeActiveExportEnergy)) / 100; + reactiveImportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveImportEnergy)) / 100; + reactiveExportCounter = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CumulativeReactiveExportEnergy)) / 100; + case (int)Kamstrup::List1PhaseShort: + listId = hanReader.getString( (int)Kamstrup_List1Phase::ListVersionIdentifier); + meterId = hanReader.getString( (int)Kamstrup_List1Phase::MeterID); + meterType = hanReader.getString( (int)Kamstrup_List1Phase::MeterType); + activeImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveImportPower); + reactiveImportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveImportPower); + activeExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ActiveExportPower); + reactiveExportPower = hanReader.getInt( (int)Kamstrup_List1Phase::ReactiveExportPower); + l1current = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CurrentL1)) / 100; + l1voltage = hanReader.getInt( (int)Kamstrup_List1Phase::VoltageL1); + break; + } +} + +void AmsData::apply(AmsData& other) { + this->lastUpdateMillis = other.getLastUpdateMillis(); + this->packageTimestamp = other.getPackageTimestamp(); + this->listType = max(this->listType, other.getListType()); + switch(other.getListType()) { + case 3: + this->meterTimestamp = other.getMeterTimestamp(); + this->activeImportCounter = other.getActiveImportCounter(); + this->activeExportCounter = other.getActiveExportCounter(); + this->reactiveImportCounter = other.getReactiveImportCounter(); + this->reactiveExportCounter = other.getReactiveExportCounter(); + case 2: + this->listId = other.getListId(); + this->meterId = other.getMeterId(); + this->meterType = other.getMeterType(); + this->reactiveImportPower = other.getReactiveImportPower(); + this->activeExportPower = other.getActiveExportPower(); + this->reactiveExportPower = other.getReactiveExportPower(); + this->l1current = other.getL1Current(); + this->l2current = other.getL2Current(); + this->l3current = other.getL3Current(); + this->l1voltage = other.getL1Voltage(); + this->l2voltage = other.getL2Voltage(); + this->l3voltage = other.getL3Voltage(); + this->threePhase = other.isThreePhase(); + case 1: + this->activeImportPower = other.getActiveImportPower(); + } +} + +unsigned long AmsData::getLastUpdateMillis() { + return this->lastUpdateMillis; +} + +unsigned long AmsData::getPackageTimestamp() { + return this->packageTimestamp; +} + +int AmsData::getListType() { + return this->listType; +} + +String AmsData::getListId() { + return this->listId; +} + +String AmsData::getMeterId() { + return this->meterId; +} + +String AmsData::getMeterType() { + return this->meterType; +} + +unsigned long AmsData::getMeterTimestamp() { + return this->meterTimestamp; +} + +int AmsData::getActiveImportPower() { + return this->activeImportPower; +} + +int AmsData::getReactiveImportPower() { + return this->reactiveImportPower; +} + +int AmsData::getActiveExportPower() { + return this->activeExportPower; +} + +int AmsData::getReactiveExportPower() { + return this->reactiveExportPower; +} + +double AmsData::getL1Voltage() { + return this->l1voltage; +} + +double AmsData::getL2Voltage() { + return this->l2voltage; +} + +double AmsData::getL3Voltage() { + return this->l3voltage; +} + +double AmsData::getL1Current() { + return this->l1current; +} + +double AmsData::getL2Current() { + return this->l2current; +} + +double AmsData::getL3Current() { + return this->l3current; +} + +double AmsData::getActiveImportCounter() { + return this->activeImportCounter; +} + +double AmsData::getReactiveImportCounter() { + return this->reactiveImportCounter; +} + +double AmsData::getActiveExportCounter() { + return this->activeExportCounter; +} + +double AmsData::getReactiveExportCounter() { + return this->reactiveExportCounter; +} + +bool AmsData::isThreePhase() { + return this->threePhase; +} diff --git a/src/AmsData.h b/src/AmsData.h new file mode 100644 index 00000000..df9f3ec5 --- /dev/null +++ b/src/AmsData.h @@ -0,0 +1,66 @@ +#ifndef _AMSDATA_H +#define _AMSDATA_H + +#include "Arduino.h" +#include "HanReader.h" + +#define METER_TYPE_KAIFA 1 +#define METER_TYPE_AIDON 2 +#define METER_TYPE_KAMSTRUP 3 + +class AmsData { +public: + AmsData(); + AmsData(int meterType, HanReader& hanReader); + + void apply(AmsData& other); + + unsigned long getLastUpdateMillis(); + + unsigned long getPackageTimestamp(); + + int getListType(); + + String getListId(); + String getMeterId(); + String getMeterType(); + + unsigned long getMeterTimestamp(); + + int getActiveImportPower(); + int getReactiveImportPower(); + int getActiveExportPower(); + int getReactiveExportPower(); + + double getL1Voltage(); + double getL2Voltage(); + double getL3Voltage(); + + double getL1Current(); + double getL2Current(); + double getL3Current(); + + double getActiveImportCounter(); + double getReactiveImportCounter(); + double getActiveExportCounter(); + double getReactiveExportCounter(); + + bool isThreePhase(); + +private: + unsigned long lastUpdateMillis = 0; + int listType = 0; + unsigned long packageTimestamp = 0; + String listId, meterId, meterType; + unsigned long meterTimestamp = 0; + int activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0; + double l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0; + double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0; + bool threePhase = false; + + void extractFromKaifa(HanReader& hanReader, int listSize); + void extractFromAidon(HanReader& hanReader, int listSize); + void extractFromKamstrup(HanReader& hanReader, int listSize); +}; + +#endif diff --git a/src/AmsToMqttBridge.h b/src/AmsToMqttBridge.h index 61431869..6d02b5d9 100644 --- a/src/AmsToMqttBridge.h +++ b/src/AmsToMqttBridge.h @@ -1,5 +1,10 @@ +#ifndef _AMSTOMQTTBRIDGE_H +#define _AMSTOMQTTBRIDGE_H + #define WIFI_CONNECTION_TIMEOUT 30000; +#define INVALID_BUTTON_PIN 0xFFFFFFFF + #if defined(ESP8266) #include @@ -13,17 +18,16 @@ #define LED_ACTIVE_HIGH 0 #define AP_BUTTON_PIN 0 -#include -#include -#define TEMP_SENSOR_PIN 5 // Temperature sensor connected to GPIO5 - #if DEBUG_MODE -#define SOFTWARE_SERIAL 1 +#if SOFTWARE_SERIAL #include SoftwareSerial *hanSerial = new SoftwareSerial(3); #else HardwareSerial *hanSerial = &Serial; #endif +#else +HardwareSerial *hanSerial = &Serial; +#endif // Build settings for Wemos Lolin D32 #elif defined(ARDUINO_LOLIN_D32) @@ -31,9 +35,7 @@ HardwareSerial *hanSerial = &Serial; #define LED_ACTIVE_HIGH 0 #define AP_BUTTON_PIN 4 -#define SOFTWARE_SERIAL 1 -#include -SoftwareSerial *hanSerial = new SoftwareSerial(GPIO_NUM_21); +HardwareSerial *hanSerial = &Serial2; // Build settings for Wemos D1 mini #elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) @@ -63,8 +65,4 @@ HardwareSerial *hanSerial = &Serial2; SoftwareSerial *hanSerial = new SoftwareSerial(5); #endif - -#if defined TEMP_SENSOR_PIN -OneWire oneWire(TEMP_SENSOR_PIN); -DallasTemperature tempSensor(&oneWire); -#endif \ No newline at end of file +#endif diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 2a73e32e..dae6a981 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -7,32 +7,38 @@ #include "AmsToMqttBridge.h" #include #include +#include + +#if defined(ESP8266) +ADC_MODE(ADC_VCC); +#endif + +#include "HwTools.h" #include "web/AmsWebServer.h" -#include "HanConfigAp.h" +#include "AmsConfiguration.h" #include "HanReader.h" #include "HanToJson.h" -// Configuration -configuration config; +#include "Aidon.h" +#include "Kaifa.h" +#include "Kamstrup.h" -// Object used to boot as Access Point -HanConfigAp ap; +HwTools hw; + +DNSServer dnsServer; + +AmsConfiguration config; -// Web server AmsWebServer ws; -// WiFi client and MQTT client WiFiClient *client; -MQTTClient mqtt(512); +MQTTClient mqtt(384); -// Object used for debugging Stream* debugger = NULL; -// The HAN Port reader, used to read serial data and decode DLMS HanReader hanReader; -// the setup function runs once when you press reset or power the board void setup() { if(config.hasConfig()) { config.load(); @@ -40,9 +46,18 @@ void setup() { #if DEBUG_MODE #if HW_ROARFRED +#if SOFTWARE_SERIAL SoftwareSerial *ser = new SoftwareSerial(-1, 1); ser->begin(115200, SWSERIAL_8N1); debugger = ser; +#else + HardwareSerial *ser = &Serial; + if(config.getMeterType() == 3) { + ser->begin(2400, SERIAL_8N1); + } else { + ser->begin(2400, SERIAL_8E1); + } +#endif #else HardwareSerial *ser = &Serial; ser->begin(115200, SERIAL_8N1); @@ -50,83 +65,181 @@ void setup() { debugger = ser; #endif + double vcc = hw.getVcc(); + if (debugger) { debugger->println(""); debugger->println("Started..."); + debugger->print("Voltage: "); + debugger->print(vcc); + debugger->println("mV"); } - // Flash the LED, to indicate we can boot as AP now + if (vcc > 0 && vcc < 3.1) { + if(debugger) { + debugger->println("Voltage is too low, sleeping"); + debugger->flush(); + } + ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up + } + + #if HAS_RGB_LED + // Initialize RGB LED pins + pinMode(LEDPIN_RGB_GREEN, OUTPUT); + pinMode(LEDPIN_RGB_RED, OUTPUT); + #endif + pinMode(LED_PIN, OUTPUT); - led_on(); - - delay(1000); - - // Initialize the AP - ap.setup(AP_BUTTON_PIN, &config, debugger); + pinMode(AP_BUTTON_PIN, INPUT_PULLUP); led_off(); - if (!ap.isActivated) - { - setupWiFi(); + WiFi.disconnect(true); + WiFi.softAPdisconnect(true); + WiFi.mode(WIFI_OFF); - // Configure uart for AMS data -#if defined SOFTWARE_SERIAL - if(config.meterType == 3) { - hanSerial->begin(2400, SWSERIAL_8N1); - } else { - hanSerial->begin(2400, SWSERIAL_8E1); + if(config.hasConfig()) { + if(debugger) config.print(debugger); + WiFi_connect(); + client = new WiFiClient(); + } else { + if(debugger) { + debugger->println("No configuration, booting AP"); } -#else - if(config.meterType == 3) { - hanSerial->begin(2400, SERIAL_8N1); - } else { - hanSerial->begin(2400, SERIAL_8E1); - } -#if defined UART2 - hanSerial->swap(); -#endif -#endif - while (!&hanSerial); - - hanReader.setup(hanSerial, debugger); - - // Compensate for the known Kaifa bug - hanReader.compensateFor09HeaderBug = (config.meterType == 1); + swapWifiMode(); } - ws.setup(&config, debugger); +#if SOFTWARE_SERIAL + if(debugger) debugger->println("HAN has software serial"); + if(config.getMeterType() == 3) { + hanSerial->begin(2400, SWSERIAL_8N1); + } else { + hanSerial->begin(2400, SWSERIAL_8E1); + } +#else + if(debugger) { + debugger->println("HAN has hardware serial"); + debugger->flush(); + } + if(config.getMeterType() == 3) { + hanSerial->begin(2400, SERIAL_8N1); + } else { + hanSerial->begin(2400, SERIAL_8E1); + } +#if UART2 + hanSerial->swap(); +#endif +#endif + + hanReader.setup(hanSerial, 0); + + // Compensate for the known Kaifa bug + hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1); + + // Empty buffer before starting + while (hanSerial->available() > 0) { + hanSerial->read(); + } + + ws.setup(&config, debugger, &mqtt); + +#if HAS_RGB_LED + //Signal startup by blinking red / green / yellow + rgb_led(RGB_RED, 2); + delay(250); + rgb_led(RGB_GREEN, 2); + delay(250); + rgb_led(RGB_YELLOW, 2); +#endif } -// the loop function runs over and over again until power down or reset -void loop() -{ - // Only do normal stuff if we're not booted as AP - if (!ap.loop()) - { - // Turn off the LED - led_off(); +int buttonTimer = 0; +bool buttonActive = false; +unsigned long longPressTime = 5000; +bool longPressActive = false; - // Reconnect to WiFi and MQTT as needed - if (WiFi.status() != WL_CONNECTED) { - WiFi_connect(); +bool wifiConnected = false; + +unsigned long lastTemperatureRead = 0; +double temperature = -127; + +bool even = true; +unsigned long lastRead = 0; +unsigned long lastSuccessfulRead = 0; + +unsigned long lastErrorBlink = 0; +int lastError = 0; + +void loop() { + unsigned long now = millis(); + if(AP_BUTTON_PIN != INVALID_BUTTON_PIN) { + if (digitalRead(AP_BUTTON_PIN) == LOW) { + if (buttonActive == false) { + buttonActive = true; + buttonTimer = now; + } + + if ((now - buttonTimer > longPressTime) && (longPressActive == false)) { + longPressActive = true; + swapWifiMode(); + } } else { - if (config.mqttHost) { - mqtt.loop(); - yield(); - if(!mqtt.connected()) { - MQTT_connect(); + if (buttonActive == true) { + if (longPressActive == true) { + longPressActive = false; + } else { + // Single press action } + buttonActive = false; } } - - readHanPort(); } - else - { + + if(now - lastTemperatureRead > 5000) { + temperature = hw.getTemperature(); + lastTemperatureRead = now; + } + + if(now > 10000 && now - lastErrorBlink > 3000) { + errorBlink(); + } + + // Only do normal stuff if we're not booted as AP + if (WiFi.getMode() != WIFI_AP) { + led_off(); + + if (WiFi.status() != WL_CONNECTED) { + wifiConnected = false; + WiFi_connect(); + } else { + if(!wifiConnected) { + wifiConnected = true; + if(debugger) { + debugger->println("Successfully connected to WiFi!"); + debugger->println(WiFi.localIP()); + } + } + if (!config.getMqttHost().isEmpty()) { + mqtt.loop(); + delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device + if(!mqtt.connected() || config.isMqttChanged()) { + MQTT_connect(); + } + } else if(mqtt.connected()) { + mqtt.disconnect(); + } + } + } else { + dnsServer.processNextRequest(); // Continously flash the LED when AP mode - if (millis() / 1000 % 2 == 0) led_on(); - else led_off(); + if (now / 50 % 64 == 0) led_on(); + else led_off(); + + } + if(now - lastRead > 100) { + yield(); + readHanPort(); + lastRead = now; } ws.loop(); delay(1); // Needed for auto modem sleep @@ -152,25 +265,56 @@ void led_off() #endif } - -void setupWiFi() -{ - // Turn off AP - WiFi.enableAP(false); - - // Connect to WiFi - WiFi.mode(WIFI_STA); - WiFi.begin(config.ssid, config.ssidPassword); - - // Wait for WiFi connection - if (debugger) debugger->print("\nWaiting for WiFi to connect..."); - while (WiFi.status() != WL_CONNECTED) { - if (debugger) debugger->print("."); - delay(500); +void errorBlink() { + if(lastError == 3) + lastError = 0; + lastErrorBlink = millis(); + for(;lastError < 3;lastError++) { + switch(lastError) { + case 0: + if(lastErrorBlink - lastSuccessfulRead > 30000) { + rgb_led(1, 2); // If no message received from AMS in 30 sec, blink once + return; + } + break; + case 1: + if(!config.getMqttHost().isEmpty() && mqtt.lastError() != 0) { + rgb_led(1, 3); // If MQTT error, blink twice + return; + } + break; + case 2: + if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) { + rgb_led(1, 4); // If WiFi not connected, blink three times + return; + } + break; + } } - if (debugger) debugger->println(" connected"); +} - client = new WiFiClient(); +void swapWifiMode() { + led_on(); + WiFiMode_t mode = WiFi.getMode(); + dnsServer.stop(); + WiFi.disconnect(true); + WiFi.softAPdisconnect(true); + WiFi.mode(WIFI_OFF); + yield(); + + if (mode != WIFI_AP || !config.hasConfig()) { + if(debugger) debugger->println("Swapping to AP mode"); + WiFi.softAP("AMS2MQTT"); + WiFi.mode(WIFI_AP); + + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", WiFi.softAPIP()); + } else { + if(debugger) debugger->println("Swapping to STA mode"); + WiFi_connect(); + } + delay(500); + led_off(); } void mqttMessageReceived(String &topic, String &payload) @@ -188,113 +332,140 @@ void mqttMessageReceived(String &topic, String &payload) // Ideas could be to query for values or to initiate OTA firmware update } -void readHanPort() -{ - if (hanReader.read() && config.hasConfig()) - { - // Flash LED on, this shows us that data is received - led_on(); +void readHanPort() { + if (hanReader.read()) { + // Empty serial buffer. For some reason this seems to make a difference. Some garbage on the wire after package? + while(hanSerial->available()) { + hanSerial->read(); + } - // Get the timestamp (as unix time) from the package - time_t time = hanReader.getPackageTime(); - if (debugger) debugger->print("Time of the package is: "); - if (debugger) debugger->println(time); + lastSuccessfulRead = millis(); - // Define a json object to keep the data - StaticJsonDocument<1024> json; + if(config.getMeterType() > 0) { + #if HAS_RGB_LED + rgb_led(RGB_GREEN, 1); + #else + led_on(); + #endif - // Any generic useful info here - json["id"] = WiFi.macAddress(); - json["up"] = millis(); - json["t"] = time; + AmsData data(config.getMeterType(), hanReader); + ws.setData(data); - // Add a sub-structure to the json object, - // to keep the data from the meter itself - JsonObject data = json.createNestedObject("data"); + if(!config.getMqttHost().isEmpty() && !config.getMqttPublishTopic().isEmpty()) { + StaticJsonDocument<512> json; + hanToJson(json, data, hw, temperature); + if (debugger) { + debugger->print("Sending data to MQTT: "); + serializeJsonPretty(json, *debugger); + debugger->println(); + } -#if defined TEMP_SENSOR_PIN - // Get the temperature too - tempSensor.requestTemperatures(); - data["temp"] = tempSensor.getTempCByIndex(0); -#endif - - hanToJson(data, config.meterType, hanReader); - - if(config.mqttHost != 0 && strlen(config.mqttHost) != 0 && config.mqttPublishTopic != 0 && strlen(config.mqttPublishTopic) != 0) { - // Write the json to the debug port - if (debugger) { - debugger->print("Sending data to MQTT: "); - serializeJsonPretty(json, *debugger); - debugger->println(); + String msg; + serializeJson(json, msg); + mqtt.publish(config.getMqttPublishTopic(), msg.c_str()); + mqtt.loop(); + delay(10); } - // Publish the json to the MQTT server - String msg; - serializeJson(json, msg); - - mqtt.publish(config.mqttPublishTopic, msg.c_str()); - mqtt.loop(); + #if HAS_RGB_LED + rgb_led(RGB_GREEN, 0); + #else + led_off(); + #endif + } else { + // Auto detect meter if not set + for(int i = 1; i <= 3; i++) { + String list; + switch(i) { + case 1: + list = hanReader.getString((int) Kaifa_List1Phase::ListVersionIdentifier); + break; + case 2: + list = hanReader.getString((int) Aidon_List1Phase::ListVersionIdentifier); + break; + case 3: + list = hanReader.getString((int) Kamstrup_List1Phase::ListVersionIdentifier); + break; + } + if(!list.isEmpty()) { + list.toLowerCase(); + if(list.startsWith("kfm")) { + config.setMeterType(1); + if(debugger) debugger->println("Detected Kaifa meter"); + break; + } else if(list.startsWith("aidon")) { + config.setMeterType(2); + if(debugger) debugger->println("Detected Aidon meter"); + break; + } else if(list.startsWith("kamstrup")) { + config.setMeterType(3); + if(debugger) debugger->println("Detected Kamstrup meter"); + break; + } + } + } + hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1); } - ws.setJson(json); + } - // Flash LED off - led_off(); + // Switch parity if meter is still not detected + if(config.getMeterType() == 0 && millis() - lastSuccessfulRead > 10000) { + lastSuccessfulRead = millis(); + if(debugger) debugger->println("No data for current setting, switching parity"); +#if SOFTWARE_SERIAL + if(even) { + hanSerial->begin(2400, SWSERIAL_8N1); + } else { + hanSerial->begin(2400, SWSERIAL_8E1); + } +#else + if(even) { + hanSerial->begin(2400, SERIAL_8N1); + } else { + hanSerial->begin(2400, SERIAL_8E1); + } +#endif + even = !even; } } +unsigned long wifiTimeout = WIFI_CONNECTION_TIMEOUT; +unsigned long lastWifiRetry = -WIFI_CONNECTION_TIMEOUT; void WiFi_connect() { - // Connect to WiFi access point. - if (debugger) - { - debugger->println(); - debugger->println(); - debugger->print("Connecting to WiFi network "); - debugger->println(config.ssid); - } - - if (WiFi.status() != WL_CONNECTED) - { - // Make one first attempt at connect, this seems to considerably speed up the first connection - WiFi.disconnect(); - WiFi.begin(config.ssid, config.ssidPassword); - delay(1000); - } - - // Wait for the WiFi connection to complete - long vTimeout = millis() + WIFI_CONNECTION_TIMEOUT; - while (WiFi.status() != WL_CONNECTED) { + if(millis() - lastWifiRetry < wifiTimeout) { delay(50); - if (debugger) debugger->print("."); - - // If we timed out, disconnect and try again - if (vTimeout < millis()) - { - if (debugger) - { - debugger->print("Timout during connect. WiFi status is: "); - debugger->println(WiFi.status()); - } - WiFi.disconnect(); - WiFi.begin(config.ssid, config.ssidPassword); - vTimeout = millis() + WIFI_CONNECTION_TIMEOUT; - } - yield(); + return; } + lastWifiRetry = millis(); if (debugger) { debugger->println(); - debugger->println("WiFi connected"); - debugger->println("IP address: "); - debugger->println(WiFi.localIP()); + debugger->println(); + debugger->print("Connecting to WiFi network "); + debugger->println(config.getWifiSsid()); + } + + if (WiFi.status() != WL_CONNECTED) { + WiFi.disconnect(); + yield(); + + WiFi.enableAP(false); + WiFi.mode(WIFI_STA); + if(!config.getWifiIp().isEmpty()) { + IPAddress ip, gw, sn(255,255,255,0); + ip.fromString(config.getWifiIp()); + gw.fromString(config.getWifiGw()); + sn.fromString(config.getWifiSubnet()); + WiFi.config(ip, gw, sn); + } + WiFi.begin(config.getWifiSsid().c_str(), config.getWifiPassword().c_str()); + yield(); } } -// 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. - unsigned long lastMqttRetry = -10000; void MQTT_connect() { - if(!config.mqttHost) { + if(config.getMqttHost().isEmpty()) { if(debugger) debugger->println("No MQTT config"); return; } @@ -305,25 +476,27 @@ void MQTT_connect() { lastMqttRetry = millis(); if(debugger) { debugger->print("Connecting to MQTT: "); - debugger->print(config.mqttHost); + debugger->print(config.getMqttHost()); debugger->print(", port: "); - debugger->print(config.mqttPort); + debugger->print(config.getMqttPort()); debugger->println(); } mqtt.disconnect(); + yield(); - mqtt.begin(config.mqttHost, config.mqttPort, *client); + mqtt.begin(config.getMqttHost().c_str(), config.getMqttPort(), *client); // 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 ((config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str())) || + (!config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str(), config.getMqttUser().c_str(), config.getMqttPassword().c_str()))) { if (debugger) debugger->println("\nSuccessfully connected to MQTT!"); + config.ackMqttChange(); // 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); + if (!config.getMqttSubscribeTopic().isEmpty()) { + mqtt.subscribe(config.getMqttSubscribeTopic()); + if (debugger) debugger->printf(" Subscribing to [%s]\r\n", config.getMqttSubscribeTopic().c_str()); } sendMqttData("Connected!"); @@ -340,22 +513,72 @@ void MQTT_connect() { void sendMqttData(String data) { // Make sure we have configured a publish topic - if (config.mqttPublishTopic == 0 || strlen(config.mqttPublishTopic) == 0) + if (config.getMqttPublishTopic().isEmpty()) return; // Build a json with the message in a "data" attribute - StaticJsonDocument<500> json; + StaticJsonDocument<128> json; json["id"] = WiFi.macAddress(); json["up"] = millis(); json["data"] = data; + double vcc = hw.getVcc(); + if(vcc > 0) { + json["vcc"] = vcc; + } + float rssi = WiFi.RSSI(); + rssi = isnan(rssi) ? -100.0 : rssi; + json["rssi"] = rssi; // Stringify the json String msg; serializeJson(json, msg); // Send the json over MQTT - mqtt.publish(config.mqttPublishTopic, msg.c_str()); + mqtt.publish(config.getMqttPublishTopic(), msg.c_str()); if (debugger) debugger->print("sendMqttData: "); if (debugger) debugger->println(data); } + +void rgb_led(int color, int mode) { +// Activate red and green LEDs if RGB LED is present (HAS_RGB_LED=1) +// If no RGB LED present (HAS_RGB_LED=0 or not defined), all output goes to ESP onboard LED +// color: 1=red, 2=green, 3=yellow +// mode: 0=OFF, 1=ON, >=2 -> Short blink(s), number of blinks: (mode - 1) +#ifndef HAS_RGB_LED +#define LEDPIN_RGB_RED LED_PIN +#define LEDPIN_RGB_GREEN LED_PIN +#endif + int blinkduration = 50; // milliseconds + switch (mode) { + case 0: //OFF + digitalWrite(LEDPIN_RGB_RED, HIGH); + digitalWrite(LEDPIN_RGB_GREEN, HIGH); + break; + case 1: //ON + switch (color) { + case 1: //Red + digitalWrite(LEDPIN_RGB_RED, LOW); + digitalWrite(LEDPIN_RGB_GREEN, HIGH); + break; + case 2: //Green + digitalWrite(LEDPIN_RGB_RED, HIGH); + digitalWrite(LEDPIN_RGB_GREEN, LOW); + break; + case 3: //Yellow + digitalWrite(LEDPIN_RGB_RED, LOW); + digitalWrite(LEDPIN_RGB_GREEN, LOW); + break; + } + break; + default: // Blink + for(int i = 1; i < mode; i++) { + rgb_led(color, 1); + delay(blinkduration); + rgb_led(color, 0); + if(i != mode) + delay(blinkduration); + } + break; + } +} diff --git a/src/Base64.cpp b/src/Base64.cpp deleted file mode 100644 index 5cf68987..00000000 --- a/src/Base64.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright (C) 2016 Arturo Guadalupi. All right reserved. - -This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -*/ - -#include "Base64.h" -#include -#if (defined(__AVR__)) -#include -#else -#include -#endif -const char PROGMEM _Base64AlphabetTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -int Base64Class::encode(char *output, char *input, int inputLength) { - int i = 0, j = 0; - int encodedLength = 0; - unsigned char A3[3]; - unsigned char A4[4]; - - while(inputLength--) { - A3[i++] = *(input++); - if(i == 3) { - fromA3ToA4(A4, A3); - - for(i = 0; i < 4; i++) { - output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[i]]); - } - - i = 0; - } - } - - if(i) { - for(j = i; j < 3; j++) { - A3[j] = '\0'; - } - - fromA3ToA4(A4, A3); - - for(j = 0; j < i + 1; j++) { - output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[j]]); - } - - while((i++ < 3)) { - output[encodedLength++] = '='; - } - } - output[encodedLength] = '\0'; - return encodedLength; -} - -int Base64Class::decode(char * output, char * input, int inputLength) { - int i = 0, j = 0; - int decodedLength = 0; - unsigned char A3[3]; - unsigned char A4[4]; - - - while (inputLength--) { - if(*input == '=') { - break; - } - - A4[i++] = *(input++); - if (i == 4) { - for (i = 0; i <4; i++) { - A4[i] = lookupTable(A4[i]); - } - - fromA4ToA3(A3,A4); - - for (i = 0; i < 3; i++) { - output[decodedLength++] = A3[i]; - } - i = 0; - } - } - - if (i) { - for (j = i; j < 4; j++) { - A4[j] = '\0'; - } - - for (j = 0; j <4; j++) { - A4[j] = lookupTable(A4[j]); - } - - fromA4ToA3(A3,A4); - - for (j = 0; j < i - 1; j++) { - output[decodedLength++] = A3[j]; - } - } - output[decodedLength] = '\0'; - return decodedLength; -} - -int Base64Class::encodedLength(int plainLength) { - int n = plainLength; - return (n + 2 - ((n + 2) % 3)) / 3 * 4; -} - -int Base64Class::decodedLength(char * input, int inputLength) { - int i = 0; - int numEq = 0; - for(i = inputLength - 1; input[i] == '='; i--) { - numEq++; - } - - return ((6 * inputLength) / 8) - numEq; -} - -//Private utility functions -inline void Base64Class::fromA3ToA4(unsigned char * A4, unsigned char * A3) { - A4[0] = (A3[0] & 0xfc) >> 2; - A4[1] = ((A3[0] & 0x03) << 4) + ((A3[1] & 0xf0) >> 4); - A4[2] = ((A3[1] & 0x0f) << 2) + ((A3[2] & 0xc0) >> 6); - A4[3] = (A3[2] & 0x3f); -} - -inline void Base64Class::fromA4ToA3(unsigned char * A3, unsigned char * A4) { - A3[0] = (A4[0] << 2) + ((A4[1] & 0x30) >> 4); - A3[1] = ((A4[1] & 0xf) << 4) + ((A4[2] & 0x3c) >> 2); - A3[2] = ((A4[2] & 0x3) << 6) + A4[3]; -} - -inline unsigned char Base64Class::lookupTable(char c) { - if(c >='A' && c <='Z') return c - 'A'; - if(c >='a' && c <='z') return c - 71; - if(c >='0' && c <='9') return c + 4; - if(c == '+') return 62; - if(c == '/') return 63; - return -1; -} - -Base64Class Base64; diff --git a/src/Base64.h b/src/Base64.h deleted file mode 100644 index 7330225e..00000000 --- a/src/Base64.h +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright (C) 2016 Arturo Guadalupi. All right reserved. - -This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -*/ - -#ifndef _BASE64_H -#define _BASE64_H - -class Base64Class{ - public: - int encode(char *output, char *input, int inputLength); - int decode(char * output, char * input, int inputLength); - int encodedLength(int plainLength); - int decodedLength(char * input, int inputLength); - - private: - inline void fromA3ToA4(unsigned char * A4, unsigned char * A3); - inline void fromA4ToA3(unsigned char * A3, unsigned char * A4); - inline unsigned char lookupTable(char c); -}; -extern Base64Class Base64; - -#endif // _BASE64_H diff --git a/src/HanToJson.cpp b/src/HanToJson.cpp new file mode 100644 index 00000000..721a0ecc --- /dev/null +++ b/src/HanToJson.cpp @@ -0,0 +1,46 @@ +#include "HanToJson.h" + +void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature) { + json["id"] = WiFi.macAddress(); + json["up"] = millis(); + json["t"] = data.getPackageTimestamp(); + + double vcc = hw.getVcc(); + if(vcc > 0) { + json["vcc"] = serialized(String(vcc, 3)); + } + + json["rssi"] = hw.getWifiRssi(); + + if(temperature != DEVICE_DISCONNECTED_C) { + json["temp"] = serialized(String(temperature, 2)); + } + + // Add a sub-structure to the json object, + // to keep the data from the meter itself + JsonObject jd = json.createNestedObject("data"); + + switch(data.getListType()) { + case 3: + jd["rtc"] = data.getMeterTimestamp(); + jd["tPI"] = data.getActiveImportCounter(); + jd["tPO"] = data.getActiveExportCounter(); + jd["tQI"] = data.getReactiveImportCounter(); + jd["tQO"] = data.getReactiveExportCounter(); + case 2: + jd["lv"] = data.getListId(); + jd["id"] = data.getMeterId(); + jd["type"] = data.getMeterType(); + jd["Q"] = data.getReactiveImportPower(); + jd["PO"] = data.getActiveExportPower(); + jd["QO"] = data.getReactiveExportPower(); + jd["I1"] = data.getL1Current(); + jd["I2"] = data.getL2Current(); + jd["I3"] = data.getL3Current(); + jd["U1"] = data.getL1Voltage(); + jd["U2"] = data.getL2Voltage(); + jd["U3"] = data.getL3Voltage(); + case 1: + jd["P"] = data.getActiveImportPower(); + } +} diff --git a/src/HanToJson.h b/src/HanToJson.h new file mode 100644 index 00000000..302768b1 --- /dev/null +++ b/src/HanToJson.h @@ -0,0 +1,16 @@ +#ifndef _HANTOJSON_h +#define _HANTOJSON_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#else + #include "WProgram.h" +#endif + +#include +#include "AmsData.h" +#include "HwTools.h" + +void hanToJson(JsonDocument& json, AmsData& data, HwTools& hw, double temperature); + +#endif diff --git a/src/HwTools.cpp b/src/HwTools.cpp new file mode 100644 index 00000000..a0a3ba65 --- /dev/null +++ b/src/HwTools.cpp @@ -0,0 +1,40 @@ +#include "HwTools.h" + +double HwTools::getVcc() { +#if defined(ARDUINO_ESP8266_WEMOS_D1MINI) + return (((double) ESP.getVcc()) / 900); // This board has a voltage divider on VCC. Yes, 900 is correct +#elif defined(ESP8266) + #if defined(ESP_VCC_CALIB_FACTOR) + return ((double) ESP.getVcc()) / 1024 * ESP_VCC_CALIB_FACTOR; + #else + return ((double) ESP.getVcc()) / 1024; + #endif +#endif + return -1; +} + +double HwTools::getTemperature() { + +#if defined TEMP_SENSOR_PIN + if(!tempSensorInit) { + tempSensor->begin(); + delay(50); + tempSensor->requestTemperatures(); + hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C; + tempSensorInit = true; + } + + if(hasTempSensor) { + tempSensor->requestTemperatures(); + return tempSensor->getTempCByIndex(0); + } else { + return DEVICE_DISCONNECTED_C; + } +#endif + return DEVICE_DISCONNECTED_C; +} + +int HwTools::getWifiRssi() { + int rssi = WiFi.RSSI(); + return isnan(rssi) ? -100.0 : rssi; +} \ No newline at end of file diff --git a/src/HwTools.h b/src/HwTools.h new file mode 100644 index 00000000..7e5302ee --- /dev/null +++ b/src/HwTools.h @@ -0,0 +1,43 @@ +#ifndef _HWTOOLS_H +#define _HWTOOLS_H + +#include "Arduino.h" + +#if defined(ESP8266) +#include +#elif defined(ESP32) +#include +#endif + +#include +#include + +#if HW_ROARFRED +#define TEMP_SENSOR_PIN 5 +#elif defined(ARDUINO_LOLIN_D32) +#define TEMP_SENSOR_PIN 14 +#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#define TEMP_SENSOR_PIN D5 +#else +#define TEMP_SENSOR_PIN 0xFFFFFFFF +#endif + + + +class HwTools { +public: + double getVcc(); + double getTemperature(); + int getWifiRssi(); + + HwTools() { + oneWire = new OneWire(TEMP_SENSOR_PIN); + tempSensor = new DallasTemperature(this->oneWire); + }; +private: + bool tempSensorInit, hasTempSensor; + OneWire *oneWire; + DallasTemperature *tempSensor; +}; + +#endif diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index ff52f5ba..63f7714a 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -2,24 +2,26 @@ #include "version.h" #include "root/index_html.h" -#include "root/configuration_html.h" +#include "root/configmeter_html.h" +#include "root/configwifi_html.h" +#include "root/configmqtt_html.h" +#include "root/configweb_html.h" #include "root/boot_css.h" #include "root/gaugemeter_js.h" #include "Base64.h" -#if defined(ESP8266) -ESP8266WebServer server(80); -#elif defined(ESP32) // ARDUINO_ARCH_ESP32 -WebServer server(80); -#endif -void AmsWebServer::setup(configuration* config, Stream* debugger) { +void AmsWebServer::setup(AmsConfiguration* config, Stream* debugger, MQTTClient* mqtt) { this->config = config; this->debugger = debugger; + this->mqtt = mqtt; server.on("/", std::bind(&AmsWebServer::indexHtml, this)); - server.on("/configuration", std::bind(&AmsWebServer::configurationHtml, this)); + server.on("/config-meter", std::bind(&AmsWebServer::configMeterHtml, this)); + server.on("/config-wifi", std::bind(&AmsWebServer::configWifiHtml, this)); + server.on("/config-mqtt", std::bind(&AmsWebServer::configMqttHtml, this)); + server.on("/config-web", std::bind(&AmsWebServer::configWebHtml, this)); server.on("/boot.css", std::bind(&AmsWebServer::bootCss, this)); server.on("/gaugemeter.js", std::bind(&AmsWebServer::gaugemeterJs, this)); server.on("/data.json", std::bind(&AmsWebServer::dataJson, this)); @@ -27,70 +29,31 @@ void AmsWebServer::setup(configuration* config, Stream* debugger) { server.on("/save", std::bind(&AmsWebServer::handleSave, this)); server.begin(); // Web server start - - print("Web server is ready for config at http://"); - if(WiFi.getMode() == WIFI_AP) { - print(WiFi.softAPIP()); - } else { - print(WiFi.localIP()); - } - println("/"); } void AmsWebServer::loop() { server.handleClient(); } -void AmsWebServer::setJson(StaticJsonDocument<1024> json) { - if(!json.isNull()) { - p = json["data"]["P"].as(); - if(json["data"].containsKey("U1")) { - u1 = json["data"]["U1"].as(); - i1 = json["data"]["I1"].as(); - - if(json["data"].containsKey("U2")) { - u2 = json["data"]["U2"].as(); - i2 = json["data"]["I2"].as(); +void AmsWebServer::setData(AmsData& data) { + this->data.apply(data); - if(json["data"].containsKey("U3")) { - u3 = json["data"]["U3"].as(); - i3 = json["data"]["I3"].as(); - } - } - - - if(maxPwr == 0 && config->hasConfig() && config->fuseSize > 0 && config->distSys > 0) { - int volt = config->distSys == 2 ? 400 : 230; - if(u2 > 0) { - maxPwr = config->fuseSize * sqrt(3) * volt; - } else { - maxPwr = config->fuseSize * 230; - } - } + if(maxPwr == 0 && data.getListType() > 1 && config->hasConfig() && config->getMainFuse() > 0 && config->getDistributionSystem() > 0) { + int volt = config->getDistributionSystem() == 2 ? 400 : 230; + if(data.isThreePhase()) { + maxPwr = config->getMainFuse() * sqrt(3) * volt; } else { - if(u1 > 0) { - json["data"]["U1"] = u1; - json["data"]["I1"] = i1; - } - if(u2 > 0) { - json["data"]["U2"] = u2; - json["data"]["I2"] = i2; - } - if(u3 > 0) { - json["data"]["U3"] = u3; - json["data"]["I3"] = i3; - } + maxPwr = config->getMainFuse() * 230; } - this->json = json; } } bool AmsWebServer::checkSecurity(byte level) { - bool access = WiFi.getMode() == WIFI_AP || !config->hasConfig() || config->authSecurity < level; - if(!access && config->authSecurity >= level && server.hasHeader("Authorization")) { + bool access = WiFi.getMode() == WIFI_AP || !config->hasConfig() || config->getAuthSecurity() < level; + if(!access && config->getAuthSecurity() >= level && server.hasHeader("Authorization")) { println(" forcing web security"); - String expectedAuth = String(config->authUser) + ":" + String(config->authPass); + String expectedAuth = String(config->getAuthUser()) + ":" + String(config->getAuthPassword()); String providedPwd = server.header("Authorization"); providedPwd.replace("Basic ", ""); @@ -109,6 +72,7 @@ bool AmsWebServer::checkSecurity(byte level) { if(!access) { println(" no access, requesting user/pass"); server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\""); + server.setContentLength(0); server.send(401, "text/html", ""); } return access; @@ -122,7 +86,26 @@ void AmsWebServer::indexHtml() { String html = String((const __FlashStringHelper*) INDEX_HTML); html.replace("${version}", VERSION); - html.replace("${data.P}", String(p)); + + if(WiFi.getMode() != WIFI_AP) { + html.replace("boot.css", BOOTSTRAP_URL); + } + + double u1 = data.getL1Voltage(); + double u2 = data.getL2Voltage(); + double u3 = data.getL3Voltage(); + double i1 = data.getL1Current(); + double i2 = data.getL2Current(); + double i3 = data.getL3Current(); + double tpi = data.getActiveImportCounter(); + double tpo = data.getActiveExportCounter(); + double tqi = data.getReactiveImportCounter(); + double tqo = data.getReactiveExportCounter(); + + html.replace("${data.P}", String(data.getActiveImportPower())); + html.replace("${data.PO}", String(data.getActiveExportPower())); + html.replace("${display.export}", config->getProductionCapacity() > 0 ? "" : "none"); + html.replace("${text.import}", config->getProductionCapacity() > 0 ? "Import" : "Consumption"); html.replace("${data.U1}", u1 > 0 ? String(u1, 1) : ""); html.replace("${data.I1}", u1 > 0 ? String(i1, 1) : ""); @@ -136,101 +119,169 @@ void AmsWebServer::indexHtml() { html.replace("${data.I3}", u3 > 0 ? String(i3, 1) : ""); html.replace("${display.P3}", u3 > 0 ? "" : "none"); + html.replace("${data.tPI}", tpi > 0 ? String(tpi, 1) : ""); + html.replace("${data.tPO}", tpi > 0 ? String(tpo, 1) : ""); + html.replace("${data.tQI}", tpi > 0 ? String(tqi, 1) : ""); + html.replace("${data.tQO}", tpi > 0 ? String(tqo, 1) : ""); + html.replace("${display.accumulative}", tpi > 0 ? "" : "none"); + + double vcc = hw.getVcc(); + html.replace("${vcc}", vcc > 0 ? String(vcc, 2) : ""); + + double temp = hw.getTemperature(); + html.replace("${temp}", temp > 0 ? String(temp, 1) : ""); + html.replace("${display.temp}", temp != DEVICE_DISCONNECTED_C ? "" : "none"); + + int rssi = hw.getWifiRssi(); + html.replace("${wifi.rssi}", vcc > 0 ? String(rssi) : ""); + html.replace("${wifi.channel}", WiFi.channel() > 0 ? String(WiFi.channel()) : ""); + html.replace("${wifi.ssid}", !WiFi.SSID().isEmpty() ? String(WiFi.SSID()) : ""); + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1"); + + server.setContentLength(html.length()); server.send(200, "text/html", html); } -void AmsWebServer::configurationHtml() { - println("Serving /configuration.html over http..."); +void AmsWebServer::configMeterHtml() { + println("Serving /config/meter.html over http..."); if(!checkSecurity(1)) return; - String html = String((const __FlashStringHelper*) CONFIGURATION_HTML); + String html = String((const __FlashStringHelper*) CONFIGMETER_HTML); html.replace("${version}", VERSION); + if(WiFi.getMode() != WIFI_AP) { + html.replace("boot.css", BOOTSTRAP_URL); + } + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - if(config->hasConfig()) { - html.replace("${config.ssid}", config->ssid); - html.replace("${config.ssidPassword}", config->ssidPassword); - html.replace("${config.meterType}", String(config->fuseSize)); - for(int i = 0; i<4; i++) { - html.replace("${config.meterType" + String(i) + "}", config->meterType == i ? "selected" : ""); - } - html.replace("${config.mqtt}", config->mqttHost == 0 ? "" : "checked"); - html.replace("${config.mqttHost}", config->mqttHost); - html.replace("${config.mqttPort}", String(config->mqttPort)); - html.replace("${config.mqttClientID}", config->mqttClientID); - html.replace("${config.mqttPublishTopic}", config->mqttPublishTopic); - html.replace("${config.mqttSubscribeTopic}", config->mqttSubscribeTopic); - html.replace("${config.mqttUser}", config->mqttUser); - html.replace("${config.mqttPass}", config->mqttPass); - html.replace("${config.authUser}", config->authUser); - html.replace("${config.authSecurity}", String(config->authSecurity)); - for(int i = 0; i<3; i++) { - html.replace("${config.authSecurity" + String(i) + "}", config->authSecurity == i ? "selected" : ""); - } - html.replace("${config.authPass}", config->authPass); - html.replace("${config.fuseSize}", String(config->fuseSize)); - for(int i = 0; i<64; i++) { - html.replace("${config.fuseSize" + String(i) + "}", config->fuseSize == i ? "selected" : ""); - } - for(int i = 0; i<3; i++) { - html.replace("${config.distSys" + String(i) + "}", config->distSys == i ? "selected" : ""); - } - } else { - html.replace("${config.ssid}", ""); - html.replace("${config.ssidPassword}", ""); - html.replace("${config.meterType}", ""); - for(int i = 0; i<4; i++) { - html.replace("${config.meterType" + String(i) + "}", i == 0 ? "selected" : ""); - } - html.replace("${config.mqtt}", ""); - html.replace("${config.mqttHost}", ""); - html.replace("${config.mqttPort}", "1883"); - html.replace("${config.mqttClientID}", ""); - html.replace("${config.mqttPublishTopic}", ""); - html.replace("${config.mqttSubscribeTopic}", ""); - html.replace("${config.mqttUser}", ""); - html.replace("${config.mqttPass}", ""); - html.replace("${config.authSecurity}", ""); - for(int i = 0; i<3; i++) { - html.replace("${config.authSecurity" + String(i) + "}", i == 0 ? "selected" : ""); - } - html.replace("${config.authUser}", ""); - html.replace("${config.authPass}", ""); - html.replace("${config.fuseSize}", ""); - for(int i = 0; i<64; i++) { - html.replace("${config.fuseSize" + String(i) + "}", i == 0 ? "selected" : ""); - } - for(int i = 0; i<3; i++) { - html.replace("${config.distSys" + String(i) + "}", i == 0 ? "selected" : ""); - } + html.replace("${config.meterType}", String(config->getMainFuse())); + for(int i = 0; i<4; i++) { + html.replace("${config.meterType" + String(i) + "}", config->getMeterType() == i ? "selected" : ""); } + html.replace("${config.distributionSystem}", String(config->getDistributionSystem())); + for(int i = 0; i<3; i++) { + html.replace("${config.distributionSystem" + String(i) + "}", config->getDistributionSystem() == i ? "selected" : ""); + } + html.replace("${config.mainFuse}", String(config->getMainFuse())); + for(int i = 0; i<64; i++) { + html.replace("${config.mainFuse" + String(i) + "}", config->getMainFuse() == i ? "selected" : ""); + } + html.replace("${config.productionCapacity}", String(config->getProductionCapacity())); + + server.setContentLength(html.length()); + server.send(200, "text/html", html); +} + +void AmsWebServer::configWifiHtml() { + println("Serving /config/wifi.html over http..."); + + if(!checkSecurity(1)) + return; + + String html = String((const __FlashStringHelper*) CONFIGWIFI_HTML); + html.replace("${version}", VERSION); + + if(WiFi.getMode() != WIFI_AP) { + html.replace("boot.css", BOOTSTRAP_URL); + } + + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + + html.replace("${config.wifiSsid}", config->getWifiSsid()); + html.replace("${config.wifiPassword}", config->getWifiPassword()); + html.replace("${config.wifiIpType1}", config->getWifiIp().isEmpty() ? "" : "selected"); + html.replace("${config.wifiIp}", config->getWifiIp()); + html.replace("${config.wifiGw}", config->getWifiGw()); + html.replace("${config.wifiSubnet}", config->getWifiSubnet()); + + server.setContentLength(html.length()); + server.send(200, "text/html", html); +} + +void AmsWebServer::configMqttHtml() { + println("Serving /config/mqtt.html over http..."); + + if(!checkSecurity(1)) + return; + + String html = String((const __FlashStringHelper*) CONFIGMQTT_HTML); + html.replace("${version}", VERSION); + + if(WiFi.getMode() != WIFI_AP) { + html.replace("boot.css", BOOTSTRAP_URL); + } + + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + + html.replace("${config.mqtt}", config->getMqttHost() == 0 ? "" : "checked"); + html.replace("${config.mqttHost}", config->getMqttHost()); + html.replace("${config.mqttPort}", String(config->getMqttPort())); + html.replace("${config.mqttClientId}", config->getMqttClientId()); + html.replace("${config.mqttPublishTopic}", config->getMqttPublishTopic()); + html.replace("${config.mqttSubscribeTopic}", config->getMqttSubscribeTopic()); + html.replace("${config.mqttUser}", config->getMqttUser()); + html.replace("${config.mqttPassword}", config->getMqttPassword()); + + server.setContentLength(html.length()); + server.send(200, "text/html", html); +} + +void AmsWebServer::configWebHtml() { + println("Serving /config/web.html over http..."); + + if(!checkSecurity(1)) + return; + + String html = String((const __FlashStringHelper*) CONFIGWEB_HTML); + html.replace("${version}", VERSION); + + if(WiFi.getMode() != WIFI_AP) { + html.replace("boot.css", BOOTSTRAP_URL); + } + + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + + html.replace("${config.authSecurity}", String(config->getAuthSecurity())); + for(int i = 0; i<3; i++) { + html.replace("${config.authSecurity" + String(i) + "}", config->getAuthSecurity() == i ? "selected" : ""); + } + html.replace("${config.authUser}", config->getAuthUser()); + html.replace("${config.authPassword}", config->getAuthPassword()); + + server.setContentLength(html.length()); server.send(200, "text/html", html); } void AmsWebServer::bootCss() { println("Serving /boot.css over http..."); - server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.send(200, "text/css", BOOT_CSS); + String css = String((const __FlashStringHelper*) BOOT_CSS); + + server.sendHeader("Cache-Control", "public, max-age=3600"); + + server.setContentLength(css.length()); + server.send(200, "text/css", css); } void AmsWebServer::gaugemeterJs() { println("Serving /gaugemeter.js over http..."); - server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.send(200, "application/javascript", GAUGEMETER_JS); + String js = String((const __FlashStringHelper*) GAUGEMETER_JS); + + server.sendHeader("Cache-Control", "public, max-age=3600"); + + server.setContentLength(js.length()); + server.send(200, "application/javascript", js); } void AmsWebServer::dataJson() { @@ -239,117 +290,217 @@ void AmsWebServer::dataJson() { if(!checkSecurity(2)) return; - String jsonStr; - if(!json.isNull()) { - println(" json has data"); + StaticJsonDocument<768> json; + String jsonStr; + if(data.getLastUpdateMillis() > 0) { int maxPwr = this->maxPwr; if(maxPwr == 0) { - if(u2 > 0) { + if(data.isThreePhase()) { maxPwr = 20000; } else { maxPwr = 10000; } } - json["maxPower"] = maxPwr; - json["pct"] = min(p*100/maxPwr, 100); - json["meterType"] = config->meterType; - json["currentMillis"] = millis(); + json["up"] = data.getLastUpdateMillis(); + json["t"] = data.getPackageTimestamp(); + json.createNestedObject("data"); + json["data"]["P"] = data.getActiveImportPower(); + json["data"]["PO"] = data.getActiveExportPower(); - serializeJson(json, jsonStr); + double u1 = data.getL1Voltage(); + double u2 = data.getL2Voltage(); + double u3 = data.getL3Voltage(); + double i1 = data.getL1Current(); + double i2 = data.getL2Current(); + double i3 = data.getL3Current(); + double tpi = data.getActiveImportCounter(); + double tpo = data.getActiveExportCounter(); + double tqi = data.getReactiveImportCounter(); + double tqo = data.getReactiveExportCounter(); + + if(u1 > 0) { + json["data"]["U1"] = u1; + json["data"]["I1"] = i1; + } + if(u2 > 0) { + json["data"]["U2"] = u2; + json["data"]["I2"] = i2; + } + if(u3 > 0) { + json["data"]["U3"] = u3; + json["data"]["I3"] = i3; + } + + if(tpi > 0) { + json["data"]["tPI"] = tpi; + json["data"]["tPO"] = tpo; + json["data"]["tQI"] = tqi; + json["data"]["tQO"] = tqo; + } + + json["p_pct"] = min(data.getActiveImportPower()*100/maxPwr, 100); + + if(config->getProductionCapacity() > 0) { + int maxPrd = config->getProductionCapacity() * 1000; + json["po_pct"] = min(data.getActiveExportPower()*100/maxPrd, 100); + } } else { - println(" json is empty"); - jsonStr = "{}"; + json["p_pct"] = -1; + json["po_pct"] = -1; } + unsigned long now = millis(); + json["id"] = WiFi.macAddress(); + json["maxPower"] = maxPwr; + json["meterType"] = config->getMeterType(); + json["currentMillis"] = now; + double vcc = hw.getVcc(); + json["vcc"] = serialized(String(vcc, 3)); + + double temp = hw.getTemperature(); + json["temp"] = serialized(String(temp, 2)); + + json.createNestedObject("wifi"); + float rssi = WiFi.RSSI(); + rssi = isnan(rssi) ? -100.0 : rssi; + json["wifi"]["ssid"] = WiFi.SSID(); + json["wifi"]["channel"] = (int) WiFi.channel(); + json["wifi"]["rssi"] = rssi; + + json.createNestedObject("status"); + + String espStatus; + if(vcc == 0) { + espStatus = "secondary"; + } else if(vcc > 3.1) { + espStatus = "success"; + } else if(vcc > 2.8) { + espStatus = "warning"; + } else { + espStatus = "danger"; + } + json["status"]["esp"] = espStatus; + + String hanStatus; + if(config->getMeterType() == 0) { + hanStatus = "secondary"; + } else if(now - data.getLastUpdateMillis() < 15000) { + hanStatus = "success"; + } else if(now - data.getLastUpdateMillis() < 30000) { + hanStatus = "warning"; + } else { + hanStatus = "danger"; + } + json["status"]["han"] = hanStatus; + + String wifiStatus; + if(config->getWifiSsid().isEmpty()) { + wifiStatus = "secondary"; + } else if(rssi > -75) { + wifiStatus = "success"; + } else if(rssi > -95) { + wifiStatus = "warning"; + } else { + wifiStatus = "danger"; + } + json["status"]["wifi"] = wifiStatus; + + String mqttStatus; + if(config->getMqttHost().isEmpty()) { + mqttStatus = "secondary"; + } else if(mqtt->connected()) { + mqttStatus = "success"; + } else if(mqtt->lastError() == 0) { + mqttStatus = "warning"; + } else { + mqttStatus = "danger"; + } + json["status"]["mqtt"] = mqttStatus; + + json.createNestedObject("mqtt"); + json["mqtt"]["lastError"] = (int) mqtt->lastError(); + + serializeJson(json, jsonStr); + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1"); + + server.setContentLength(jsonStr.length()); server.send(200, "application/json", jsonStr); } void AmsWebServer::handleSave() { String temp; - temp = server.arg("ssid"); - config->ssid = new char[temp.length() + 1]; - temp.toCharArray(config->ssid, temp.length() + 1, 0); - - temp = server.arg("ssidPassword"); - config->ssidPassword = new char[temp.length() + 1]; - temp.toCharArray(config->ssidPassword, temp.length() + 1, 0); - - config->meterType = (byte)server.arg("meterType").toInt(); - - if(server.hasArg("mqtt") && server.arg("mqtt") == "true") { - println("MQTT enabled"); - temp = server.arg("mqttHost"); - config->mqttHost = new char[temp.length() + 1]; - temp.toCharArray(config->mqttHost, temp.length() + 1, 0); - - config->mqttPort = (int)server.arg("mqttPort").toInt(); - - temp = server.arg("mqttClientID"); - config->mqttClientID = new char[temp.length() + 1]; - temp.toCharArray(config->mqttClientID, temp.length() + 1, 0); - - temp = server.arg("mqttPublishTopic"); - config->mqttPublishTopic = new char[temp.length() + 1]; - temp.toCharArray(config->mqttPublishTopic, temp.length() + 1, 0); - - temp = server.arg("mqttSubscribeTopic"); - config->mqttSubscribeTopic = new char[temp.length() + 1]; - temp.toCharArray(config->mqttSubscribeTopic, temp.length() + 1, 0); - - temp = server.arg("mqttUser"); - config->mqttUser = new char[temp.length() + 1]; - temp.toCharArray(config->mqttUser, temp.length() + 1, 0); - - temp = server.arg("mqttPass"); - config->mqttPass = new char[temp.length() + 1]; - temp.toCharArray(config->mqttPass, temp.length() + 1, 0); - } else { - println("MQTT disabled"); - config->mqttHost = NULL; - config->mqttUser = NULL; - config->mqttPass = NULL; + if(server.hasArg("meterConfig") && server.arg("meterConfig") == "true") { + config->setMeterType(server.arg("meterType").toInt()); + config->setDistributionSystem(server.arg("distributionSystem").toInt()); + config->setMainFuse(server.arg("mainFuse").toInt()); + config->setProductionCapacity(server.arg("productionCapacity").toInt()); } - config->authSecurity = (byte)server.arg("authSecurity").toInt(); - - if(config->authSecurity > 0) { - temp = server.arg("authUser"); - config->authUser = new char[temp.length() + 1]; - temp.toCharArray(config->authUser, temp.length() + 1, 0); - - temp = server.arg("authPass"); - config->authPass = new char[temp.length() + 1]; - temp.toCharArray(config->authPass, temp.length() + 1, 0); + if(server.hasArg("wifiConfig") && server.arg("wifiConfig") == "true") { + config->setWifiSsid(server.arg("wifiSsid")); + config->setWifiPassword(server.arg("wifiPassword")); + if(server.hasArg("wifiIpType") && server.arg("wifiIpType").toInt() == 1) { + config->setWifiIp(server.arg("wifiIp")); + config->setWifiGw(server.arg("wifiGw")); + config->setWifiSubnet(server.arg("wifiSubnet")); + } else { + config->clearWifiIp(); + } } - config->fuseSize = (int)server.arg("fuseSize").toInt(); + if(server.hasArg("mqttConfig") && server.arg("mqttConfig") == "true") { + if(server.hasArg("mqtt") && server.arg("mqtt") == "true") { + config->setMqttHost(server.arg("mqttHost")); + config->setMqttPort(server.arg("mqttPort").toInt()); + config->setMqttClientId(server.arg("mqttClientId")); + config->setMqttPublishTopic(server.arg("mqttPublishTopic")); + config->setMqttSubscribeTopic(server.arg("mqttSubscribeTopic")); + config->setMqttUser(server.arg("mqttUser")); + config->setMqttPassword(server.arg("mqttPassword")); + config->setAuthUser(server.arg("authUser")); + config->setAuthPassword(server.arg("authPassword")); + } else { + config->clearMqtt(); + } + } - config->distSys = (byte)server.arg("distSys").toInt(); + if(server.hasArg("authConfig") && server.arg("authConfig") == "true") { + config->setAuthSecurity((byte)server.arg("authSecurity").toInt()); + if(config->getAuthSecurity() > 0) { + config->setAuthUser(server.arg("authUser")); + config->setAuthPassword(server.arg("authPassword")); + } else { + config->clearAuth(); + } + } println("Saving configuration now..."); if (debugger) config->print(debugger); - if (config->save()) - { - println("Successfully saved. Will reboot now."); - String html = "

Successfully Saved!

Device is restarting now...

Go to index"; - server.send(200, "text/html", html); - yield(); - delay(1000); + if (config->save()) { + println("Successfully saved."); + if(config->isWifiChanged()) { + String html = "

Successfully Saved!

Go to index"; + server.send(200, "text/html", html); + yield(); + println("Wifi config changed, rebooting"); + delay(1000); #if defined(ESP8266) - ESP.reset(); + ESP.reset(); #elif defined(ESP32) - ESP.restart(); + ESP.restart(); #endif - } - else - { + } else { + server.sendHeader("Location", String("/"), true); + server.send ( 302, "text/plain", ""); + } + } else { println("Error saving configuration"); String html = "

Error saving configuration!

"; server.send(500, "text/html", html); diff --git a/src/web/AmsWebServer.h b/src/web/AmsWebServer.h index 317a78b9..888844c0 100644 --- a/src/web/AmsWebServer.h +++ b/src/web/AmsWebServer.h @@ -1,8 +1,13 @@ #ifndef _AMSWEBSERVER_h #define _AMSWEBSERVER_h +#define BOOTSTRAP_URL "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" + +#include #include -#include "configuration.h" +#include "AmsConfiguration.h" +#include "HwTools.h" +#include "AmsData.h" #if defined(ARDUINO) && ARDUINO >= 100 #include "Arduino.h" @@ -22,17 +27,18 @@ class AmsWebServer { public: - void setup(configuration* config, Stream* debugger); + void setup(AmsConfiguration* config, Stream* debugger, MQTTClient* mqtt); void loop(); - void setJson(StaticJsonDocument<1024> json); + + void setData(AmsData& data); private: - configuration* config; + int maxPwr; + HwTools hw; + AmsConfiguration* config; + AmsData data; Stream* debugger; - StaticJsonDocument<1024> json; - int maxPwr; - int p; - double u1, u2, u3, i1, i2, i3; + MQTTClient* mqtt; #if defined(ESP8266) ESP8266WebServer server; @@ -43,7 +49,10 @@ private: bool checkSecurity(byte level); void indexHtml(); - void configurationHtml(); + void configMeterHtml(); + void configWifiHtml(); + void configMqttHtml(); + void configWebHtml(); void bootCss(); void gaugemeterJs(); void dataJson(); diff --git a/web/boot.css b/web/boot.css index 5b9b1906..e0630a43 100644 --- a/web/boot.css +++ b/web/boot.css @@ -62,6 +62,9 @@ body { .mb-0, .my-0 { margin-bottom: 0!important; } +.m-2 { + margin: .5rem!important; +} .mb-2, .my-2 { margin-bottom: .5rem!important; } @@ -92,7 +95,8 @@ body { } .border-bottom { border-bottom: 1px solid #dee2e6!important; -}.rounded { +} +.rounded { border-radius: .25rem!important; } div { @@ -140,10 +144,6 @@ h1, h2, h3, h4, h5, h6 { margin-right: -15px; margin-left: -15px; } -.d-flex { - display: -ms-flexbox!important; - display: flex!important; -} .col, .col-1, .col-10, .col-11, .col-12, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-auto, .col-lg, .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-auto, .col-md, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-auto, .col-sm, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-auto, .col-xl, .col-xl-1, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-auto { position: relative; width: 100%; @@ -185,6 +185,22 @@ h1, h2, h3, h4, h5, h6 { flex: 0 0 75%; max-width: 75%; } +.d-none { + display: none!important; +} +.d-flex { + display: -ms-flexbox!important; + display: flex!important; +} +.flex-row { + -ms-flex-direction: row!important; + flex-direction: row!important; +} + +.flex-column { + -ms-flex-direction: column!important; + flex-direction: column!important; +} a { color: #007bff; text-decoration: none; @@ -218,6 +234,92 @@ a { background-color: #007bff; border-color: #007bff; } +.navbar { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: justify; + justify-content: space-between; + padding: .5rem 1rem; +} +.navbar-dark .navbar-brand { + color: #fff; +} +.navbar-expand { + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -ms-flex-pack: start; + justify-content: flex-start; +} +.navbar-nav { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.navbar-expand .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; +} +.navbar-brand { + display: inline-block; + padding-top: .3125rem; + padding-bottom: .3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} +.navbar-dark .navbar-nav .nav-link { + color: rgba(255,255,255,.5); +} +.navbar-expand .navbar-nav .nav-link { + padding-right: .5rem; + padding-left: .5rem; +} +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} +.nav-link { + display: block; + padding: .5rem 1rem; +} +.badge { + display: inline-block; + padding: .25em .4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25rem; + transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; +} +.badge-secondary { + color: #fff; + background-color: #6c757d; +} +.badge-success { + color: #fff; + background-color: #28a745; +} +.badge-warning { + color: #212529; + background-color: #ffc107; +} +.badge-danger { + color: #fff; + background-color: #dc3545; +} .form-group { margin-bottom: 1rem; } @@ -256,6 +358,55 @@ input[type="radio"], input[type="checkbox"] { box-sizing: border-box; padding: 0; } +.input-group { + position: relative; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-align: stretch; + align-items: stretch; + width: 100%; +} +.input-group-append { + margin-left: -1px; +} +.input-group-append, .input-group-prepend { + display: -ms-flexbox; + display: flex; +} +.input-group>.input-group-append>.btn, .input-group>.input-group-append>.input-group-text, .input-group>.input-group-prepend:first-child>.btn:not(:first-child), .input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child), .input-group>.input-group-prepend:not(:first-child)>.btn, .input-group>.input-group-prepend:not(:first-child)>.input-group-text { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-text { + display: -ms-flexbox; + display: flex; + -ms-flex-align: center; + align-items: center; + padding: .375rem .75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: .25rem; +} +.input-group>.custom-select:not(:last-child), .input-group>.form-control:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group>.custom-file, .input-group>.custom-select, .input-group>.form-control, .input-group>.form-control-plaintext { + position: relative; + -ms-flex: 1 1 0%; + flex: 1 1 0%; + min-width: 0; + margin-bottom: 0; +} hr { margin-top: 1rem; margin-bottom: 1rem; @@ -265,6 +416,23 @@ hr { height: 0; overflow: visible; } +ul { + display: block; + list-style-type: disc; + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 40px; +} +li { + display: list-item; + text-align: -webkit-match-parent; +} +dl, ol, ul { + margin-top: 0; + margin-bottom: 1rem; +} @media (min-width: 576px) { .container, .container-sm { @@ -275,6 +443,11 @@ hr { .container, .container-md, .container-sm { max-width: 720px; } + .col-md-3 { + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } .col-md-4 { -ms-flex: 0 0 33.333333%; flex: 0 0 33.333333%; @@ -285,6 +458,17 @@ hr { flex: 0 0 50%; max-width: 50%; } + .d-md-flex { + display: -ms-flexbox!important; + display: flex!important; + } + .flex-md-row { + -ms-flex-direction: row!important; + flex-direction: row!important; + } + .ml-md-auto, .mx-md-auto { + margin-left: auto!important; + } } @media (min-width: 992px) { .container, .container-lg, .container-md, .container-sm { diff --git a/web/configmeter.html b/web/configmeter.html new file mode 100644 index 00000000..80019693 --- /dev/null +++ b/web/configmeter.html @@ -0,0 +1,103 @@ + + + + + AMS reader - Meter configuration + + + + +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+
+ +
kWp
+
+
+
+
+
+
+
+
+
+ Back +
+
+ +
+
+
+
+ + diff --git a/web/configmqtt.html b/web/configmqtt.html new file mode 100644 index 00000000..e9607059 --- /dev/null +++ b/web/configmqtt.html @@ -0,0 +1,118 @@ + + + + + AMS reader - WiFi configuration + + + + + +
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ Back +
+
+ +
+
+
+
+ + + diff --git a/web/configuration.html b/web/configuration.html deleted file mode 100644 index d0faab58..00000000 --- a/web/configuration.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - - AMS reader - configuration - - - - - - - -
-
-
-
AMS reader - configuration
- ${version} -
-
-
-
-
-
-
WiFi
-
- -
- -
-
-
- -
- -
-
-
-
-
AMS meter
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-
-
MQTT
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-
-
Web server
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-
-
-
- Back -
-
- -
-
-
-
- - - diff --git a/web/configweb.html b/web/configweb.html new file mode 100644 index 00000000..61119311 --- /dev/null +++ b/web/configweb.html @@ -0,0 +1,94 @@ + + + + + AMS reader - Meter configuration + + + + + +
+ +
+ +
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ Back +
+
+ +
+
+
+
+ + + diff --git a/web/configwifi.html b/web/configwifi.html new file mode 100644 index 00000000..885051d9 --- /dev/null +++ b/web/configwifi.html @@ -0,0 +1,111 @@ + + + + + AMS reader - WiFi configuration + + + + + +
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+
+
+
+ Back +
+
+ +
+
+
+
+ + + diff --git a/web/index.html b/web/index.html index 609a2d71..56fa19c6 100644 --- a/web/index.html +++ b/web/index.html @@ -5,14 +5,10 @@ AMS reader - +