Compare commits

...

389 Commits

Author SHA1 Message Date
Gunnar Skjold
29524c2123 Reducing stack mem footprint 2022-02-10 10:35:57 +01:00
Gunnar Skjold
e2fb5e2673 Removed exception handling 2022-02-08 08:37:54 +01:00
Gunnar Skjold
751a0edca7 160mhz esp32 and some error handling in loop() 2022-02-07 21:18:27 +01:00
Gunnar Skjold
a8e62e086c Divide current another decimal point for Kamstrup CT meters 2022-02-06 19:33:59 +01:00
Gunnar Skjold
35eb69bebb Trying higher buffer for MQTT 2022-02-06 19:28:32 +01:00
Gunnar Skjold
5c7c0699b2 Added images 2022-02-06 11:29:19 +01:00
Gunnar Skjold
7e31a60000 Support null values in the middle of the payload 2022-02-05 19:46:41 +01:00
Gunnar Skjold
793bc877fc Support null values in the middle of the payload 2022-02-05 19:34:44 +01:00
Gunnar Skjold
5ab6de21dc Added some documentation 2022-02-05 19:18:07 +01:00
Gunnar Skjold
db5e242ad8 Various changes 2022-02-04 17:36:30 +01:00
Gunnar Skjold
5be921a342 Various changes 2022-02-04 17:35:47 +01:00
Gunnar Skjold
dd87d70876 Free value on fuse size and production capacity 2022-01-28 18:29:50 +01:00
Gunnar Skjold
52992f09ee Stability adjustments 2022-01-28 17:41:48 +01:00
Gunnar Skjold
6aa02d54c8 Improvements for ENTSO-E 2022-01-23 17:52:10 +01:00
Gunnar Skjold
9dbf9137c7 Merge branch 'master' of github.com:gskjold/AmsToMqttBridge 2022-01-21 17:57:35 +01:00
Gunnar Skjold
c0a9a01b41 Fixed uint problem with current 2022-01-21 17:57:01 +01:00
Gunnar Skjold
7b203b196f Fixed voltage and current labels for esp8266 2022-01-21 17:50:02 +01:00
Gunnar Skjold
50c06e2cfe Removed ap pin from generic ESP32 2022-01-16 19:38:35 +01:00
Gunnar Skjold
e4d4753181 Fixed incorrect labels for IT/TT current 2022-01-16 17:24:57 +01:00
Gunnar Skjold
04daf551fb Fixed incorrect time diff for price fetch 2022-01-16 17:04:58 +01:00
Gunnar Skjold
4b51f0f235 Only update exchange rate once per day 2022-01-16 10:50:08 +01:00
Gunnar Skjold
5e4ccca663 Fetch prices between 13:30 and 14:00 2022-01-16 10:37:08 +01:00
Gunnar Skjold
6b0ec39759 Various updates 2022-01-16 09:58:13 +01:00
Gunnar Skjold
1f7d845a32 Removed debugging 2022-01-14 07:13:43 +01:00
Gunnar Skjold
6de5b719f3 Various changes 2022-01-14 07:12:08 +01:00
Gunnar Skjold
90859ee6b8 Cleanup 2022-01-09 20:47:33 +01:00
Gunnar Skjold
78dd856a97 Various changes 2022-01-09 20:45:25 +01:00
Gunnar Skjold
9897ccc563 Some adjustments for chrome dark mode 2022-01-08 11:19:18 +01:00
Gunnar Skjold
701e5904c5 Fixed build error on ESP8266 2022-01-08 10:39:03 +01:00
Gunnar Skjold
54ea17d345 Re-organized documents 2022-01-08 10:21:19 +01:00
Gunnar Skjold
327fed6f08 Month plot fix and some other things 2022-01-08 10:13:02 +01:00
Gunnar Skjold
8fe1a1edf2 Fixed checksum error for encrypted meters 2022-01-05 19:25:52 +01:00
Gunnar Skjold
6fd8dc0432 Fixed package timestamp for Kaifa 2022-01-03 13:52:04 +01:00
Gunnar Skjold
f586336ae3 Voltage calculation for Kaifa and other three phase meters not reporting voltage for L2 2022-01-03 09:46:53 +01:00
Gunnar Skjold
1527ee7d45 Timezone conversion for Kaifa 2022-01-03 09:05:04 +01:00
Gunnar Skjold
c543b38829 Timezone conversion for Kaifa 2022-01-03 09:00:19 +01:00
Gunnar Skjold
6d12d7120c Increase accumulated values to 3 decimals 2022-01-03 08:41:58 +01:00
Gunnar Skjold
c1d4ba772e Added extra char for string terminator to wifi config 2022-01-03 08:24:10 +01:00
Gunnar Skjold
a03d4113e7 Added checksum verification for Mbus payload 2022-01-03 08:23:49 +01:00
Gunnar Skjold
8751b6325d Updated example ini 2021-12-28 17:30:19 +01:00
Gunnar Skjold
ff02dd43a9 Deleted internal Timezone lib 2021-12-27 14:15:06 +01:00
Gunnar Skjold
b772fb6323 Issue template config 2021-12-27 14:14:24 +01:00
Gunnar Skjold
f5acfce578 Fixed build error 2021-12-22 09:26:03 +01:00
Gunnar Skjold
4b902e2544 Added some defaults for NTP and DNS 2021-12-22 08:09:46 +01:00
Gunnar Skjold
217247b28e Temperature sensor graph sort 2021-12-19 10:23:18 +01:00
Gunnar Skjold
4828f5a727 Temperature graph 2021-12-19 09:57:51 +01:00
Gunnar Skjold
062a3634a9 Fixed entso-e debugging 2021-12-19 09:17:57 +01:00
Gunnar Skjold
376cd0cf90 Fixed NULL access 2021-12-19 09:15:06 +01:00
Gunnar Skjold
2a20893a58 Fixed real time calculation issues 2021-12-19 09:14:31 +01:00
Gunnar Skjold
f3dba112de Merge branch 'master' of github.com:gskjold/AmsToMqttBridge 2021-12-18 18:30:22 +01:00
Gunnar Skjold
b037d6bb64 Try at least twice before showing ESP as red 2021-12-18 18:25:31 +01:00
Gunnar Skjold
6f7eacddff Trying to get ESP32S2 to work 2021-12-18 18:24:58 +01:00
Gunnar Skjold
4185411315 Merge pull request #169 from kng/master
Change time.h to timelib.h for 32bit to compile
2021-12-18 17:21:19 +01:00
Daniel Ekman
2a1b5a5f6d Change time.h to timelib.h for 32bit to compile 2021-12-18 17:10:53 +01:00
Gunnar Skjold
246a4f96fe Merge pull request #168 from kng/master
Assignment error in meter types
2021-12-18 15:43:09 +01:00
Daniel Ekman
c7d235b367 Assignment error in meter types 2021-12-18 15:37:30 +01:00
Gunnar Skjold
c5c8fbc2a0 Trying timezone from repo again 2021-12-18 15:25:06 +01:00
Gunnar Skjold
8adf591c4e Fixed timelib 2021-12-18 15:20:15 +01:00
Gunnar Skjold
aa893f7ede Fixed bug in price fetch 2021-12-18 15:17:34 +01:00
Gunnar Skjold
f8b1725e94 Revert lib changes 2021-12-18 14:41:58 +01:00
Gunnar Skjold
ae7e3d11d5 Merge branch 'master' of github.com:gskjold/AmsToMqttBridge 2021-12-18 14:33:59 +01:00
Gunnar Skjold
73b20a0766 Merge pull request #167 from kng/master
Libdeps updated to avoid conflict with time.h
2021-12-18 14:29:58 +01:00
Daniel Ekman
d8cf961258 Libdeps updated to avoid conflict with time.h 2021-12-18 13:55:57 +01:00
Gunnar Skjold
bccd19812d Added power factor to MQTT JSON 2021-12-16 11:37:00 +01:00
Gunnar Skjold
6954ff5432 Merge pull request #163 from mikkle/pf-mqtt-cp-fix
Fix c/p error in mqtt powerfactor publishing
2021-12-16 11:26:01 +01:00
Mikkel Troest
6200f31b83 Fix c/p error in mqtt powerfactor publishing 2021-12-16 10:36:38 +01:00
Gunnar Skjold
5408b3c2a9 Make sure temperatures are read just after receiving HAN data 2021-12-13 20:57:17 +01:00
Gunnar Skjold
bd0b3ebb26 Removed debugging 2021-12-12 10:38:40 +01:00
Gunnar Skjold
895a9bc6b1 Fixed api key overwrite 2021-12-12 10:36:50 +01:00
Gunnar Skjold
ff935fc920 Specify that prices only work for ESP32 2021-12-12 08:36:23 +01:00
Gunnar Skjold
3833421e5f Fixed "new version" announcement 2021-12-10 19:09:24 +01:00
Gunnar Skjold
ebdc357a47 Check for scaling and use if available 2021-12-10 18:46:57 +01:00
Gunnar Skjold
7da617e8c2 Merge pull request #154 from gskjold/dev-1.6.0
v2.0
2021-12-10 08:52:10 +01:00
Gunnar Skjold
0b86761d2c Fixed a couple of things before release 2021-12-10 08:38:48 +01:00
Gunnar Skjold
d697f7e37f Final changes before 2.0 2021-12-10 08:19:48 +01:00
Gunnar Skjold
db859e3ff5 Fixed readme 2021-12-08 21:06:52 +01:00
Gunnar Skjold
94d22957bd Removed old screenshot 2021-12-08 21:06:00 +01:00
Gunnar Skjold
1ebaf443cc Updated readme 2021-12-08 21:05:42 +01:00
Gunnar Skjold
a336f711b0 Fixed day graph 2021-12-08 20:35:53 +01:00
Gunnar Skjold
e6d3b47d4f Some adjustment to data storage 2021-12-07 19:34:34 +01:00
Gunnar Skjold
eb479f8216 More adaptations for Austian meters 2021-12-07 19:07:43 +01:00
Gunnar Skjold
38cba4e8da Various fixes 2021-12-06 19:33:40 +01:00
Gunnar Skjold
bc6d45ecf2 Fixed WiFi instability? 2021-12-06 16:58:55 +01:00
Gunnar Skjold
097131d7fb Some changes 2021-12-06 10:47:42 +01:00
Gunnar Skjold
2107ca50e4 More changes for Austrian meters, untested 2021-12-06 10:47:08 +01:00
Gunnar Skjold
178f603937 Fix 2021-12-05 20:14:46 +01:00
Gunnar Skjold
21687368c6 Preparing for austrian meters 2021-12-05 20:09:34 +01:00
Gunnar Skjold
6054e900e6 Experimenting 2021-12-05 13:08:34 +01:00
Gunnar Skjold
3da5275624 Fixed merge problem 2021-12-05 12:21:21 +01:00
Gunnar Skjold
3fda2cfe7a Removed test code 2021-12-05 12:16:13 +01:00
Gunnar Skjold
2bb651f95c Merge branch 'master' into dev-1.6.0 2021-12-05 12:15:39 +01:00
Gunnar Skjold
e2442a26ee Trying to figure out esp8266 wifi reconnect 2021-12-05 12:07:34 +01:00
Gunnar Skjold
cbd2ab4a7a Trying to figure out esp8266 wifi reconnect 2021-12-05 12:07:20 +01:00
Gunnar Skjold
76f8e2c343 2.0 development 2021-12-04 15:41:13 +01:00
Gunnar Skjold
ab101c8622 Some changes after testing 2021-11-30 08:01:20 +01:00
Gunnar Skjold
f425abb52d Added missing file from last commit 2021-11-27 20:29:18 +01:00
Gunnar Skjold
dc5bfff655 Merge branch 'master' into dev-1.6.0 2021-11-27 20:23:35 +01:00
Gunnar Skjold
eb59245118 Added usage plots and ADC reading for Vcc 2021-11-27 20:16:26 +01:00
Gunnar Skjold
d18fd27a24 Fixed accumulated values from Kaifa 1p 2021-11-23 20:55:55 +01:00
Gunnar Skjold
6f09f523e4 Added possibility to upgrade from GUI if there is enough RAM for SSL 2021-11-18 20:42:13 +01:00
Gunnar Skjold
e78d59c31a Different menu for ESP8266 and ESP32 2021-11-18 20:28:29 +01:00
Gunnar Skjold
e7f3217d7b Added support for power factor 2021-11-18 20:20:51 +01:00
Gunnar Skjold
ab534ce60a Ignore firmware update if AP button is pressed 2021-11-18 19:43:48 +01:00
Gunnar Skjold
933246eae8 Changed from SPIFFS to LittleFS 2021-11-18 19:40:20 +01:00
Gunnar Skjold
5c38d1cf3e Fixes after testing 2021-11-18 19:06:33 +01:00
Gunnar Skjold
580085f717 Fixed kamstrup list version 2021-11-17 21:47:00 +01:00
Gunnar Skjold
9831d4aa78 Removed buffer size 2021-11-17 21:46:37 +01:00
Gunnar Skjold
a3561d5c58 Fixed free mem formatting bug 2021-11-17 20:50:30 +01:00
Gunnar Skjold
2a524cd0ac Fixed Kamstrup bug 2021-11-17 20:42:45 +01:00
Gunnar Skjold
254e010594 Fixed save crash 2021-11-16 20:40:21 +01:00
Gunnar Skjold
24025d6785 Added support for IEC62056-21 2021-11-16 20:07:47 +01:00
Gunnar Skjold
4c92e592d6 Added support for uart_swap for ESP8266 2021-11-08 12:05:48 +01:00
Gunnar Skjold
f192ddae81 Some changes while testing 2021-11-06 21:02:38 +01:00
Gunnar Skjold
1cd2446365 Some modifications to support timestamp from all meters 2021-11-06 19:44:50 +01:00
Gunnar Skjold
6d26102b8e New meter config 2021-11-06 19:30:58 +01:00
Gunnar Skjold
8e9da8f255 First step in implementing a new DLMS parser 2021-11-06 16:56:02 +01:00
Gunnar Skjold
72bdb6e363 Update issue templates 2021-11-01 07:14:54 +01:00
Gunnar Skjold
6df942f488 Revert time version update 2021-10-31 09:37:58 +01:00
Gunnar Skjold
1e323ac3b9 Various changes for version updates and some debugging for upload 2021-10-31 09:33:25 +01:00
Gunnar Skjold
05bdbaf1f5 Fixed error 2021-10-30 18:42:59 +02:00
Gunnar Skjold
6cca25788e Fixed copy/paste error 2021-10-23 10:45:57 +02:00
Gunnar Skjold
e929f87ea9 Fixed decimals in json 2021-10-23 10:38:53 +02:00
Gunnar Skjold
21a4102553 Changes for correct time conversion 2021-10-23 10:05:14 +02:00
Gunnar Skjold
768645fc0a Added more decimals in data.json 2021-10-23 10:04:52 +02:00
Gunnar Skjold
c670549dea Warning for BUS powered devices on firmware upgrade 2021-10-23 09:21:53 +02:00
Gunnar Skjold
147b2ca33e Fixed build error 2021-10-23 09:07:07 +02:00
Gunnar Skjold
c645b82ed3 Fixed voltage and amp meters 2021-10-23 09:04:00 +02:00
Gunnar Skjold
26bb8a0fea Support two phase power calculation 2021-10-23 08:59:16 +02:00
Gunnar Skjold
507ed13770 Added GPIO designation to UART in dropdown 2021-10-23 08:40:40 +02:00
Gunnar Skjold
a0f53a0c52 Partial fix of dead gauges 2021-10-21 21:17:03 +02:00
Gunnar Skjold
13bbc81b7f Clear debug config on setup if not already set 2021-09-23 20:56:24 +02:00
Gunnar Skjold
7412ba2697 Removed temp sensor update if no sensors were found 2021-09-23 20:30:22 +02:00
Gunnar Skjold
dce7b7e64b Support for long MQTT username and password 2021-09-23 19:28:55 +02:00
Gunnar Skjold
7627d6c369 Updated readme 2021-09-23 18:48:58 +02:00
Gunnar Skjold
9fc9adea1c Fixed crash when enabling substitute values in meter config 2021-09-23 18:30:15 +02:00
Gunnar Skjold
46df07fe40 Fixed build error on ESP32 2021-09-23 18:20:54 +02:00
Gunnar Skjold
0aeb5555e7 Fixed long web passwords 2021-09-23 18:10:16 +02:00
Gunnar Skjold
181fe3c909 Removed meter from setup 2021-09-23 17:36:43 +02:00
Gunnar Skjold
867d6a4ef8 Added profiles for Pow board 2021-09-23 17:34:21 +02:00
Gunnar Skjold
d52216a73e Moved hardware page to Wiki 2021-09-23 17:28:51 +02:00
Gunnar Skjold
d70b41a454 Added images for Pow 2021-09-23 17:25:52 +02:00
Gunnar Skjold
332c366561 Renamed kamstrup meters 2021-09-22 11:44:59 +02:00
Gunnar Skjold
b572ad97f8 Added Kamstrup document 2021-09-22 11:42:22 +02:00
Gunnar Skjold
aa307f8690 Fixed incorrect value for accumulated export 2021-09-22 11:40:48 +02:00
Gunnar Skjold
19223312b5 Fixed danish KAmstrup 2021-09-13 10:14:47 +02:00
Gunnar Skjold
74bc5aa7a0 Fixed build 2021-09-13 10:14:28 +02:00
Gunnar Skjold
e66e8a96ff Added some frames as documentation 2021-09-13 08:10:39 +02:00
Gunnar Skjold
ce95360a64 Minor fix 2021-09-13 08:08:39 +02:00
Gunnar Skjold
7802aeaab1 Removed unnecessary config for arduinojson 2021-09-13 08:07:38 +02:00
Gunnar Skjold
37ce3566bf Fixed config clear 2021-09-13 08:07:03 +02:00
Gunnar Skjold
606bac100a Cleanup 2021-03-24 09:37:46 +01:00
Gunnar Skjold
be569d1802 Fixed buffer for JSON generation 2021-03-17 20:36:40 +01:00
Gunnar Skjold
3e21105b2d Fixed prices in JSON handler 2021-03-17 20:33:11 +01:00
Gunnar Skjold
9946827431 Fixed cheapest price calculation 2021-03-17 20:17:36 +01:00
Gunnar Skjold
90d03ca77f Retain flag for prices 2021-03-16 11:44:01 +01:00
Gunnar Skjold
7727b17122 Fixed publishing of prices 2021-03-16 11:02:59 +01:00
Gunnar Skjold
fbc4c8c502 Merge pull request #101 from gskjold/dev-v1.5.0
Dev v1.5.0
2021-03-10 19:22:23 +01:00
Gunnar Skjold
5d6a4ea0a4 Updated readme 2021-03-10 19:15:00 +01:00
Gunnar Skjold
91ce486bde Updated readme 2021-03-10 19:14:22 +01:00
Gunnar Skjold
523139749e Auto install minifier 2021-03-10 19:14:12 +01:00
Gunnar Skjold
5b9f3b7aed Minor changes after testing 2021-03-10 18:33:45 +01:00
Gunnar Skjold
af76243761 Retain flag for accumulated values 2021-03-10 17:45:16 +01:00
Gunnar Skjold
605dc4901c Fixed incorrect check for 32/35 A main fuse 2021-03-10 17:45:02 +01:00
Gunnar Skjold
54fb950513 Changes after testing 2021-01-22 08:01:23 +01:00
Gunnar Skjold
ff84278edf Some changes after testing 2021-01-20 07:41:09 +01:00
Gunnar Skjold
f15cf5d75e Some changes after testing 2021-01-18 20:32:51 +01:00
Gunnar Skjold
33070af111 Refactored MQTT payload handling into separate classes 2021-01-17 20:11:04 +01:00
Gunnar Skjold
53573184f3 Simplified temperature.json 2021-01-17 15:53:56 +01:00
Gunnar Skjold
decc4788a7 Simplified code that generates data.json 2021-01-17 15:08:01 +01:00
Gunnar Skjold
f9597c786e Changed to BearSSL for meter decryption on ESP8266 + some minor changes 2021-01-17 12:34:57 +01:00
Gunnar Skjold
af8f5a7c24 Memory optimization and bugfix 2021-01-16 16:02:39 +01:00
Gunnar Skjold
a830a52863 Stripped HTML to improve stability 2021-01-14 20:03:49 +01:00
Gunnar Skjold
037bac24de Changes in user interface 2021-01-14 16:19:00 +01:00
Gunnar Skjold
837c3cf802 Taking timezone into account when calculating midnight 2021-01-11 20:21:36 +01:00
Gunnar Skjold
75956c087c Closing http connections after receiving data 2021-01-11 09:49:25 +01:00
Gunnar Skjold
88528b4099 Added reboot and cleaned up a bit in UI 2021-01-10 22:30:55 +01:00
Gunnar Skjold
6176d34e84 Link to documentation 2021-01-10 22:03:02 +01:00
Gunnar Skjold
f2dda26bbb Added support for retrieving energy price from ENTSO-E API 2021-01-10 20:54:25 +01:00
Gunnar Skjold
402ecf67d7 Merge branch 'master' into dev-v1.5.0 2021-01-09 11:25:31 +01:00
Gunnar Skjold
a83a6c1c53 Fixed release 2021-01-09 11:21:01 +01:00
Gunnar Skjold
5beb13894c Initial implementation of supporting timezone in timestamp 2021-01-09 11:11:49 +01:00
Gunnar Skjold
c3fa618ab2 Fixed build problem 2021-01-09 09:50:20 +01:00
Gunnar Skjold
7713ae8566 Trying to fix GitHub Actions build issue 2021-01-09 09:36:20 +01:00
Gunnar Skjold
7ae860ec72 Fixed incorrect reading of analog temperature 2021-01-09 09:33:09 +01:00
Gunnar Skjold
376008a735 Merge pull request #96 from kallemooo/fixFromHex
Changed fromHex() to use an supplied buffer
2021-01-04 09:58:32 +01:00
Karl Thorén
feed10184b Changed fromHex() to use an supplied buffer
Solves the problem with returning a pointer to local variable.

Signed-off-by: Karl Thorén <karl.h.thoren@gmail.com>
2020-12-27 16:29:53 +01:00
Gunnar Skjold
59ca29f6a8 Update README.md 2020-09-25 06:46:16 +02:00
Gunnar Skjold
644a3fa40b Fixed factory reset 2020-09-05 20:03:06 +02:00
Gunnar Skjold
a2c1c8fc61 Updated ESP32 flash procedure 2020-09-05 19:27:36 +02:00
Gunnar Skjold
b823f029ea Updated readme 2020-09-05 19:12:59 +02:00
Gunnar Skjold
fd907deec1 Merge pull request #87 from gskjold/dev-v1.4.0
Dev v1.4.0
2020-09-05 19:05:01 +02:00
Gunnar Skjold
c1b56e25ad Minor changes 2020-09-05 18:59:52 +02:00
Gunnar Skjold
5491088cec Cleared out some unnecessary files from mbedtls lib 2020-09-05 11:14:00 +02:00
Gunnar Skjold
04271accc1 Dynamic temperature page 2020-09-05 11:10:36 +02:00
Gunnar Skjold
a332e38b97 Some cleanup 2020-09-05 09:40:07 +02:00
Gunnar Skjold
17bd85ebd0 Added more debugging code. Also some changes after testing 2020-09-04 07:25:11 +02:00
Gunnar Skjold
4207216770 Some changes for mbedtls when building ESP32 2020-08-28 21:20:11 +02:00
Gunnar Skjold
02491e074b Added comment 2020-08-28 20:33:46 +02:00
Gunnar Skjold
e79a0585f0 Some changes during testing 2020-08-28 19:58:33 +02:00
Gunnar Skjold
859220d33f Added missing getter 2020-08-28 17:55:07 +02:00
Gunnar Skjold
5d47105951 Added support for TMP236 analog temp sensor 2020-08-28 17:46:34 +02:00
Gunnar Skjold
e71f937856 Some changes during testing 2020-08-06 13:11:31 +02:00
Gunnar Skjold
d730aac33b Send temperature updates to MQTT 2020-08-05 20:33:49 +02:00
Gunnar Skjold
e121ec75d8 Support multiple temperature sensors 2020-08-05 19:55:16 +02:00
Gunnar Skjold
6479fd6a63 Splitted system config into Web, NTP, GPIO and Debugging 2020-08-05 13:45:10 +02:00
Gunnar Skjold
603f2925ce Removing mbedtls code in HanReader lib 2020-07-31 11:25:45 +02:00
Gunnar Skjold
1284f3f848 Using internal mbedtls lib for encrypted meters. Next up, find out how to modify config.h when using external lib. 2020-07-31 11:19:23 +02:00
Gunnar Skjold
620e355a27 Support encrypted meters and added vcc offset 2020-07-31 10:15:11 +02:00
Gunnar Skjold
47ddf57547 Merge branch 'master' into dev-v1.4.0 2020-07-26 11:24:43 +02:00
Gunnar Skjold
fcbfe4d96f Changes to make substituted I2 correct when exporting power 2020-07-25 09:17:39 +02:00
Gunnar Skjold
d789d6ff3b Reduced number of build targets 2020-07-25 09:15:05 +02:00
Gunnar Skjold
b4f18de030 Made base64 work for both platforms 2020-07-25 08:48:51 +02:00
Gunnar Skjold
00d5d215cd Switched to internal base64 2020-07-25 08:45:31 +02:00
Gunnar Skjold
8de5a58a6b Changed dependency for Base64 2020-07-06 19:17:45 +02:00
Gunnar Skjold
012794e682 Make web content minifier optional 2020-07-06 19:14:37 +02:00
Gunnar Skjold
38eb2d8c19 Merge pull request #79 from gskjold/dev-v1.3.0
Dev v1.3.0
2020-06-06 20:30:47 +02:00
Gunnar Skjold
d95137adbc Support for Kamstrup IT meters 2020-05-28 18:44:38 +02:00
Gunnar Skjold
cde3f80fca Ensure that GPIO is correctly set after setup 2020-05-28 14:19:03 +02:00
Gunnar Skjold
cdc012743f Changes for building on GitHub actions for all branches 2020-05-27 08:38:59 +02:00
Gunnar Skjold
23c90315a5 Some changes before public testing 2020-05-24 17:14:16 +02:00
Gunnar Skjold
8d938f111c Fixed mDNS 2020-05-24 16:30:51 +02:00
Gunnar Skjold
cff6c02d57 Some cleanup and changes. Trying to get self-update to work, no luck 2020-05-22 22:26:31 +02:00
Gunnar Skjold
0c92f7401c Removed unused code 2020-05-22 17:24:17 +02:00
Gunnar Skjold
195a0d4e77 Merge branch 'dev-v1.3.0' of github.com:gskjold/AmsToMqttBridge into dev-v1.3.0 2020-05-22 17:23:41 +02:00
Gunnar Skjold
43f50e0e0a Check for updates from GitHub and minor changes during testing 2020-05-22 17:23:38 +02:00
Gunnar Skjold
f2f20afd9c Merge pull request #76 from atlej68/dev-1.3.0-test
dev-1.3.0-testing: Timezone for esp32 and minor domoticz fix.
2020-05-22 17:22:54 +02:00
Atle Johansen
f22cfbb223 Domoticz: make config save values, cleanup. 2020-05-16 18:39:23 +02:00
Atle Johansen
803c5116bd alt. to Tz.h for esp32 (Tz.h not avail. for esp32) 2020-05-16 17:04:28 +02:00
Gunnar Skjold
a542fbc931 Yellow flash for AP mode if RGB LED is configured 2020-05-16 10:20:27 +02:00
Gunnar Skjold
85a70016fa Checking for new version on GitHub 2020-05-14 21:13:13 +02:00
Gunnar Skjold
778daf8645 Fixed som HTML and JS problems. Removed external NTP library and using espressif internal NTP client instead 2020-05-10 12:29:00 +02:00
Gunnar Skjold
efa99f970c Factory reset and splitted HTML into head, content and footer files to reduce memory footprint while parsing HTML templates 2020-05-10 11:51:00 +02:00
Gunnar Skjold
953f2d4110 Some modifications to increase stability 2020-05-08 22:58:16 +02:00
Gunnar Skjold
c3c0ca0a1b Added option to substitute missing I2 for Aidon IT meters. Also cleaned up some more UI 2020-05-04 16:07:23 +02:00
Gunnar Skjold
dc83853d2e Fixed some GPIO and Vcc configuration issues 2020-05-03 21:27:19 +02:00
Gunnar Skjold
f5123e9aa1 UI cleanup and added devkit board to initial setup 2020-05-03 21:01:58 +02:00
Gunnar Skjold
2da69dd451 Merge branch 'dev-v1.3.0' of github.com:gskjold/AmsToMqttBridge into dev-v1.3.0 2020-05-03 16:49:28 +02:00
Gunnar Skjold
2858123c1b Added configuration of GPIO in UI. Added initial setup page in AP mode. Major changes in storing configuration. 2020-05-03 16:29:38 +02:00
Gunnar Skjold
9d4488f8df Merge pull request #70 from atlej68/add_domoticz
Add domoticz
2020-05-03 16:28:05 +02:00
Gunnar Skjold
f78c8e3582 Merge pull request #69 from atlej68/add_devkit
Add devkit boards
2020-05-03 16:27:45 +02:00
Atle Johansen
431d6714b5 fix typo 2020-05-01 15:46:16 +02:00
Atle Johansen
550a3c1a0b reverse local change 2020-05-01 15:24:17 +02:00
Atle Johansen
b6f5e72638 merge with 1..3.0 and implementation of comments 2020-05-01 15:22:39 +02:00
Atle Johansen
958ff37d7d Merge remote-tracking branch 'upstream/dev-v1.3.0' into add_domoticz 2020-05-01 14:21:55 +02:00
Gunnar Skjold
1ea9da22c7 Merge pull request #68 from stenjo/feature/fullreport
Implementation of MQTT RAW full format
2020-05-01 12:16:06 +02:00
Gunnar Skjold
0c93c52e3d Implemented support for MQTT SSL and cleaned up necessary code 2020-05-01 12:10:08 +02:00
Atle Johansen
41784511e9 Add config for direct MQTT messages to Domoticz 2020-04-30 18:56:21 +02:00
Atle Johansen
a78fdc0b59 Add devkit boards 2020-04-30 18:28:12 +02:00
Gunnar Skjold
cc032fdf29 Implemented upload of certificates for MQTT SSL 2020-04-29 21:04:42 +02:00
Sten Otto Johnsen
f696e0b59b Removed checksum change 2020-04-24 12:15:25 +02:00
Sten Otto Johnsen
f67f12a188 Adding full report on raw data 2020-04-24 00:05:12 +02:00
Gunnar Skjold
719ed56e21 Merge pull request #66 from gskjold/dev-v1.2.3
Dev v1.2.3
2020-04-19 20:23:05 +02:00
Gunnar Skjold
cc72d0e0b3 Uncommited changes 2020-04-18 19:18:39 +02:00
Gunnar Skjold
ab175ec9ec Added correct conversion for int8, int16 and uint32 2020-04-18 11:04:43 +02:00
Gunnar Skjold
398407350c Fixed typ-o in MQTT topic for temperature and moved voltage bootup check into compiler option SELF_POWERED 2020-04-10 08:52:56 +02:00
Gunnar Skjold
5e33a15e85 Fixed build file 2020-04-09 08:58:56 +02:00
Gunnar Skjold
7f51534e91 Fixed page title on reboot page 2020-04-09 08:56:50 +02:00
Gunnar Skjold
0f5af6b274 Fixed ESP32 crash with RemoteDebug and unformatted SPIFFS. Added battery voltage for Lolin D32 and moved ESP_VCC_CALIB_FACTOR to cover all boards 2020-04-09 08:44:25 +02:00
Gunnar Skjold
7886ce668e Fixed build file? 2020-04-07 21:16:31 +02:00
Gunnar Skjold
0f4848c872 Added missing dep 2020-04-07 20:56:49 +02:00
Gunnar Skjold
6911d203ca Merge pull request #56 from gskjold/dev-v1.2
Version 1.2
2020-04-07 20:53:39 +02:00
Gunnar Skjold
e96b5bbf1b Changes during testing 2020-03-30 21:14:58 +02:00
Gunnar Skjold
8457db50f6 Merge branch 'master' into dev-v1.2 2020-03-30 20:12:13 +02:00
Gunnar Skjold
279c5f8321 Fixed actions script 2020-03-30 07:28:02 +02:00
Gunnar Skjold
889b9153fa Increased limit for ArduinoJson positive exponentiation 2020-03-30 07:05:34 +02:00
Gunnar Skjold
0de1d0bb6f Changed accumulated values for Kaifa 2020-03-30 07:02:42 +02:00
Gunnar Skjold
bf13965a9e Various changes during testing 2020-03-26 20:55:23 +01:00
Gunnar Skjold
7b52efd332 Fix missing release version on files 2020-03-25 20:37:15 +01:00
Gunnar Skjold
c936f605d6 Fixes #45 2020-03-25 20:21:39 +01:00
Gunnar Skjold
9d0ceb9ca8 Fixes problem with release 2020-03-25 20:07:23 +01:00
Gunnar Skjold
b33273e3cc Fixes #47 2020-03-25 20:06:42 +01:00
Gunnar Skjold
d9b45d037b Some changes during testing 2020-03-24 21:19:09 +01:00
Gunnar Skjold
8c75a9530e Default config for ESP32 2020-03-24 20:26:13 +01:00
Gunnar Skjold
73d00f786a Added remote debugging 2020-03-24 20:17:44 +01:00
Gunnar Skjold
ab016fff93 Merge pull request #54 from Allram/master
Increased MQTT buffer to work with Aidon 6525 meters
2020-03-24 20:11:00 +01:00
Vegard Fladby
a215aa7766 Increased MQTT buffer to work with Aidon 6525 meters 2020-03-24 19:49:40 +01:00
Gunnar Skjold
d747c84a14 Added DNS to static IP config. Added hostname to config. Added mDNS 2020-03-22 15:08:17 +01:00
Gunnar Skjold
26634f96b0 Added firmware upload from UI 2020-03-22 11:54:27 +01:00
Gunnar Skjold
97669cf4ad When using raw payload with MQTT, only update if changed 2020-03-10 18:57:25 +01:00
Gunnar Skjold
e12980db11 Added option to send data as raw values to MQTT 2020-03-10 18:43:18 +01:00
Gunnar Skjold
732639814b Reduce resolution of "up" in JSON from millis to seconds 2020-03-10 17:46:18 +01:00
Gunnar Skjold
c07c2e66d6 Fixed uptime rollover in UI 2020-03-08 19:47:47 +01:00
Gunnar Skjold
9b70802450 Fixed conversion of date and time from meter to account for CET/CEST 2020-03-08 18:37:10 +01:00
Gunnar Skjold
30d73c3a6e Updated documentation 2020-03-01 13:24:06 +01:00
Gunnar Skjold
bd905c3595 Merge pull request #44 from gskjold/dev-v1.1.0
Dev v1.1.0
2020-03-01 12:56:12 +01:00
Gunnar Skjold
1a2e70b1fb Merge pull request #42 from ArnieO/dev-v1.1.0
Dev v1.1.0 Optional ESP8266 Vcc calibration factor in *.ini file
2020-03-01 12:53:56 +01:00
Gunnar Skjold
d6da7b2715 Merge branch 'dev-v1.1.0' of github.com:gskjold/AmsToMqttBridge into dev-v1.1.0 2020-03-01 09:41:18 +01:00
Gunnar Skjold
059a430f9a Fixed problem with AP mode with INVALID_BUTTON_PIN set 2020-03-01 09:39:58 +01:00
Gunnar Skjold
8ade50c2a6 Initialize all meter data to 0. Removed calculations for i2 2020-02-26 09:06:29 +01:00
Gunnar Skjold
5b94d8ff61 Added calculated value for L2 current when missing from Aidon 2020-02-25 14:08:04 +01:00
ArnieO
232b9c279d Optional Vcc calibration factor defined in *.ino 2020-02-24 20:05:34 +01:00
ArnieO
9b6a6af6ec Merge remote-tracking branch 'upstream/dev-v1.1.0' into dev-v1.1.0 2020-02-24 13:32:26 +01:00
Gunnar Skjold
9671e1eba3 Fixed bug where UI showed stalled HAN when exporting power. Also, maybe fixed issue with Vcc precision in UI? 2020-02-24 12:01:51 +01:00
Gunnar Skjold
78e531a7e9 Changed term for production and consumption when local production is defined 2020-02-24 07:47:39 +01:00
Gunnar Skjold
478d63505d Allow compiling when RGB is not defined 2020-02-24 07:47:12 +01:00
Gunnar Skjold
d8dfaaa730 Version hardening 2020-02-24 07:46:48 +01:00
Gunnar Skjold
f311b70b37 Merge branch 'master' into dev-v1.1.0 2020-02-24 07:17:48 +01:00
Gunnar Skjold
52bc2b14c9 Some changes after testing 2020-02-23 20:31:10 +01:00
ArnieO
2ddfc16d6a Updated comment for rgbled() function 2020-02-23 17:12:26 +01:00
Gunnar Skjold
51ab4a7f07 Reducing precision on temperature and vcc in json to reduce length 2020-02-23 11:34:25 +01:00
Gunnar Skjold
e90a30bfde Implemented blinking for red LED on error 2020-02-22 17:23:56 +01:00
Gunnar Skjold
a63e6962d3 Merge pull request #38 from ArnieO/dev-v1.1.0
Added code for optional RGB LED
2020-02-22 16:42:19 +01:00
Gunnar Skjold
951ccf7de5 Merge branch 'dev-v1.1.0' into dev-v1.1.0 2020-02-22 16:42:04 +01:00
Gunnar Skjold
001e3376df Merge pull request #39 from gskjold/dev-v1.1.0-mqttraw
Merged code preparations for other MQTT payload formats
2020-02-22 16:37:29 +01:00
Gunnar Skjold
9e4bf93f0a Some changes to reduce dlms read errors 2020-02-22 12:29:04 +01:00
ArnieO
e5ac823f69 Changed mqtt buffer to 384, JSON buffer to <512> 2020-02-21 23:29:01 +01:00
Gunnar Skjold
73fd228e2f Merge branch 'dev-v1.1.0' into dev-v1.1.0-mqttraw 2020-02-21 20:58:17 +01:00
Gunnar Skjold
fdac8f88af Minor optimization 2020-02-21 20:52:55 +01:00
Gunnar Skjold
9c8552869c Preparations for selectable MQTT payload format 2020-02-21 20:51:55 +01:00
ArnieO
be75b013c0 Added code for RGB LED 2020-02-21 19:13:44 +01:00
ArnieO
79274e490d Added code for RGB LED 2020-02-21 18:53:05 +01:00
ArnieO
47293eebe4 Added code for RGB LED 2020-02-21 18:38:28 +01:00
Gunnar Skjold
90b62638ed Dependency hardening 2020-02-20 20:36:12 +01:00
Gunnar Skjold
78e766156d Minimizing calls to web server 2020-02-18 21:33:41 +01:00
Gunnar Skjold
4b32af69a7 Fixed typo 2020-02-18 07:37:42 +01:00
Gunnar Skjold
916b5550d9 Fixed typo 2020-02-18 07:25:10 +01:00
Gunnar Skjold
48016240f9 Some cleanup after testing with ESP32 2020-02-17 20:06:52 +01:00
Gunnar Skjold
518b408205 Minor changes to fix build 2020-02-17 17:23:18 +01:00
Gunnar Skjold
73a0359597 Minor changes to fix build 2020-02-17 17:21:34 +01:00
Gunnar Skjold
bbe82f9cdc Added a small delay that helped smooth the voltage on a HAN powered device. No more sudden reboots 2020-02-16 21:45:58 +01:00
Gunnar Skjold
4786735d4c Support older config versions. Fixed css for AP mode. Some cleanup and changes to preserve power 2020-02-16 20:09:29 +01:00
Gunnar Skjold
227eb7b6ff Now using external base64 lib. Also updated some previously commited errors 2020-02-16 16:03:01 +01:00
Gunnar Skjold
00edd94033 Fixed some bugs from late night coding 2020-02-16 15:46:01 +01:00
Gunnar Skjold
ccd818e1ab Merge branch 'master' into dev-v1.1.0 2020-02-16 15:28:47 +01:00
Gunnar Skjold
fc42fb22d8 Merge pull request #33 from gskjold/dev-v1.0.1
Dev v1.0.1
2020-02-16 14:49:36 +01:00
Gunnar Skjold
f6066fbbf3 Cleanup and fix bugs found during testing 2020-02-15 22:13:04 +01:00
Gunnar Skjold
9c09740a41 Production graph and fixed vcc value 2020-02-15 21:04:18 +01:00
Gunnar Skjold
4e24e2949a Updated featheresp32 config to use Serial2 for Mbus 2020-02-15 18:01:10 +01:00
Gunnar Skjold
eff00f1fe0 Only show temp if sensor is attached. Added common functions to get temp and vcc 2020-02-14 21:48:03 +01:00
Gunnar Skjold
f484f3eb0e Merge branch 'dev-v1.0.1' into dev-v1.1.0 2020-02-14 20:19:15 +01:00
Gunnar Skjold
b2174dd521 If debug mode and HW_ROARFRED, use software serial to set upp RX for HAN with 2400 and TX for debugging with 115200 2020-02-14 20:14:30 +01:00
Gunnar Skjold
fe79a96827 Added change to allow auto modem sleep 2020-02-14 19:52:15 +01:00
Gunnar Skjold
517a40b0a6 Finalized new configuration menu and moved AP button trigger from setup to loop 2020-02-14 19:49:25 +01:00
Gunnar Skjold
75f3c8c592 Merge branch 'dev-v1.0.1' into dev-v1.1.0 2020-02-14 16:07:54 +01:00
Gunnar Skjold
554cc7c023 Added date and time from meter as rtc in json 2020-02-14 16:03:52 +01:00
Gunnar Skjold
6d0c722c98 Using kwh and kvarh as standard for accumulated numbers from kamstrup 2020-02-14 12:25:40 +01:00
Gunnar Skjold
d2f93c07bd Not sure if this does anything, but increased json and mqtt limits to see if Kamstrup list 3 is received 2020-02-13 20:50:13 +01:00
Gunnar Skjold
d63516fd49 Deleted autosave 2020-02-13 20:41:08 +01:00
Gunnar Skjold
c114b777c7 Minor changes to UI 2020-02-13 20:40:33 +01:00
Gunnar Skjold
a06729b535 Bugfix for previous commit 2020-02-12 20:11:55 +01:00
Gunnar Skjold
0ea21991ea Continued work with v1.1.0
- Merge branch 'low_power' into dev-v1.1.0
- Corrected accumulated import/export from Aidon
- Added VCC and RSSI to MQTT messages

Changes in UI:
- New top navbar
- Show VCC and WiFi information
- Show MQTT error messages
- Show ESP, HAN, WiFi and MQTT status badge
- Show accumulated Import/export
2020-02-12 19:55:06 +01:00
Gunnar Skjold
1994f16b82 Merge branch 'dev-v1.0.1' into dev-v1.1.0 2020-02-12 16:41:04 +01:00
Gunnar Skjold
b6efae656f Expose export power in JSON 2020-02-12 16:40:51 +01:00
Gunnar Skjold
fa0f93cefe Switched to reactive import for Q 2020-02-12 13:25:36 +01:00
Gunnar Skjold
058dc658c1 Switched to reactive import for Q 2020-02-12 13:16:49 +01:00
Gunnar Skjold
f6df84bf9a Auto detect meter type 2020-02-11 18:40:38 +01:00
Gunnar Skjold
4b9cb27e58 Disable web security if AP mode is triggered 2020-02-11 17:32:18 +01:00
Gunnar Skjold
5f14517258 Minor changes after testing MQTT reconnect 2020-02-11 17:25:52 +01:00
Gunnar Skjold
ad820fff01 Allow other services to get resources while trying to connect to mqtt 2020-02-11 11:04:37 +01:00
Gunnar Skjold
6d3d4adc7e Merge branch 'master' into low_power 2020-02-09 13:45:27 +01:00
Gunnar Skjold
99a23b3bf7 Cropped webui image 2020-02-09 13:19:48 +01:00
Gunnar Skjold
15e861ce70 Added AP mode pin for both Wemos D1 and D32 2020-02-09 12:18:08 +01:00
Gunnar Skjold
fdfadb9bf9 Updated Aidon parser with support for 3 phase IT/TT system 2020-02-08 18:23:56 +01:00
Gunnar Skjold
388d35c390 Updated documentation 2020-02-07 23:12:27 +01:00
Gunnar Skjold
e466a45b5c Fixed missing check in checkbox when configuration opened in firefox 2020-02-07 22:33:55 +01:00
Gunnar Skjold
2e7b9e8e43 Fixed missing check in checkbox when configuration opened in firefox 2020-02-07 22:26:17 +01:00
Gunnar Skjold
deb2148e9a Software serial for development boards. Added a few extra boards 2020-02-07 17:27:58 +01:00
Gunnar Skjold
27cb5dd833 Trying a large sleep when cap is not charged 2020-02-06 19:15:06 +01:00
Gunnar Skjold
7e6ac860fd Merge branch 'master' into low_power 2020-02-06 19:02:14 +01:00
Gunnar Skjold
3a7f6078f4 Use builtin LED for Lolin D32 2020-02-06 19:02:05 +01:00
Gunnar Skjold
dbc551a41d Moved configuration object to main file and load the config before start. Fixed issue where partiy is switched for Kamstrup half way when in debug mode 2020-02-05 20:43:37 +01:00
Gunnar Skjold
9bb596aab1 Reverted what was intended as support for earlier configuration versions. 2020-02-02 08:15:49 +01:00
Gunnar Skjold
4a3d22e75c Fixed script to generate webroot header files 2020-02-01 09:57:43 +01:00
Gunnar Skjold
d68ffc827d Trigger build on changes in scripts and web 2020-02-01 09:56:14 +01:00
Gunnar Skjold
20c62a63cf Fixed script to generate webroot header files 2020-02-01 09:54:58 +01:00
Gunnar Skjold
8f85b43fc3 Made MQTT optional 2020-02-01 09:52:37 +01:00
Gunnar Skjold
57d8603790 Added option for electric distribution system (230/400) 2020-01-31 21:30:20 +01:00
Gunnar Skjold
fc1f1554d8 Extracted webroot into usable files 2020-01-31 21:08:27 +01:00
Gunnar Skjold
70ed0b538a Merge branch 'master' into low_power 2020-01-29 21:00:10 +01:00
Gunnar Skjold
ced6f125fd Trigger build on changing ini file 2020-01-29 20:58:09 +01:00
Gunnar Skjold
f76cacf835 Trigger build on changing ini file 2020-01-29 20:56:31 +01:00
Gunnar Skjold
a8205edae9 Fixed build after merge 2020-01-29 20:53:52 +01:00
Gunnar Skjold
0a317450f6 Merge branch 'master' into low_power 2020-01-29 20:51:28 +01:00
Gunnar Skjold
e9ed6d825a Merge pull request #14 from gskjold/issue-9
New web interface that is always available
2020-01-29 20:50:26 +01:00
Gunnar Skjold
a6751c787c Merge branch 'issue-9' into low_power 2020-01-29 20:39:07 +01:00
Gunnar Skjold
378dbbcc99 Only show phases with power, and always deliver volts and amps to frontend 2020-01-29 20:38:53 +01:00
Gunnar Skjold
aed56b0b56 Merge branch 'issue-9' into low_power 2020-01-28 21:19:01 +01:00
Gunnar Skjold
36b37eb2a6 Calculate max power when receiving json from reader 2020-01-28 21:18:48 +01:00
Gunnar Skjold
550b216ba6 Merge branch 'issue-9' into low_power 2020-01-28 21:12:00 +01:00
Gunnar Skjold
7e4047096b Stripped away bootstrap css and created a smaller boot.css as replacement. Load real bootstrap from CDN. Added HTML only (non-js) view of data 2020-01-28 21:10:54 +01:00
Gunnar Skjold
7fdedd8bcf Forgot some changes 2020-01-28 19:20:50 +01:00
Gunnar Skjold
f693869bd4 Merge branch 'master' into issue-9 2020-01-28 19:18:13 +01:00
Gunnar Skjold
32d58fdd71 Stripped jquery and full bootstrap from image 2020-01-23 19:45:29 +01:00
Gunnar Skjold
aef78962fb Some modifications to reduce power consumption i AP mode 2020-01-23 18:47:02 +01:00
Gunnar Skjold
8ee713b616 Included some code from @dakarym to support the self powered board for testing 2020-01-22 15:26:07 +01:00
Gunnar Skjold
0fa3e3585a Finalized live update of meter data in web gui 2020-01-19 21:37:56 +01:00
Gunnar Skjold
a88291c0f0 Implemented new user interface and prepared for live view 2020-01-17 21:49:36 +01:00
Gunnar Skjold
ac1609bfac Merge branch 'issue-6' into issue-9 2020-01-17 19:17:44 +01:00
Gunnar Skjold
22d5839d60 Added HTML, CSS and JS for new user interface 2020-01-17 14:41:02 +01:00
171 changed files with 12725 additions and 3250 deletions

39
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,39 @@
---
name: Bug report
about: Report a bug
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Hardware information:**
- Meter: [e.g. Aidon]
- AMS reader: [e.g. Pow-U, ESP32 etc]
- M-bus adapter (if applicable):
**Relevant firmware information:**
- Version: [e.g. 1.5.0]
- MQTT: [yes/no]
- HAN GPIO: [e.g. GPIO5]
- HAN baud and parity: [e.g. 2400 8E1]
- Temperature sensors [e.g. 3xDS18B20]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Meter configuration
url: https://github.com/gskjold/AmsToMqttBridge/wiki/Known-hardware-configurations
about: Please check your meter configuration here first.
- name: Frequently asked questions
url: https://github.com/gskjold/AmsToMqttBridge/wiki/FAQ
about: Please check frequently asked questions first.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

22
.github/ISSUE_TEMPLATE/support.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Support
about: Request support
title: ''
labels: ''
assignees: ''
---
**Describe your problem**
A clear and concise description of what the problem is.
**Hardware information:**
- Meter: [e.g. Aidon]
- AMS reader: [e.g. Pow-U, ESP32 etc]
- M-bus adapter (if applicable):
**Relevant firmware information:**
- Version: [e.g. 1.5.0]
- MQTT: [yes/no]
- HAN GPIO: [e.g. GPIO5]
- Temperature sensors [e.g. 3xDS18B20]

View File

@@ -5,8 +5,11 @@ on:
paths:
- src/**
- lib/**
- scripts/**
- web/**
- platformio.ini
branches:
- master
- '*'
tags:
- '*'
- '!v*.*.*'
@@ -36,7 +39,9 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
pip install -U platformio css_html_js_minify
- name: Configure build targets
run: echo "[platformio]\ndefault_envs = esp8266, esp32" > platformio-user.ini
- name: PlatformIO lib install
run: pio lib install
- name: PlatformIO run

View File

@@ -14,11 +14,15 @@ jobs:
steps:
- name: Check out code from repo
uses: actions/checkout@v1
- name: Get release version from tag
- name: Get release version for filenames
id: release_tag
env:
GITHUB_REF: ${{ github.ref }}
run: echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
- name: Get release version for code
env:
GITHUB_REF: ${{ github.ref }}
run: echo "GITHUB_TAG=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV
- name: Cache Python dependencies
uses: actions/cache@v1
with:
@@ -36,7 +40,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -U platformio
pip install -U platformio css_html_js_minify
- name: PlatformIO lib install
run: pio lib install
- name: PlatformIO run
@@ -51,30 +55,30 @@ jobs:
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload esp12e binary to release
uses: actions/upload-release-asset@v1.0.1
- name: Upload esp8266 binary to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/esp12e/firmware.bin
asset_name: ams2mqtt-esp12e-${{ steps.release_tag.outputs.tag }}.bin
asset_path: .pio/build/esp8266/firmware.bin
asset_name: ams2mqtt-esp8266-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream
- name: Upload hw1esp12e binary to release
uses: actions/upload-release-asset@v1.0.1
- name: Upload esp32 binary to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/hw1esp12e/firmware.bin
asset_name: ams2mqtt-hw1esp12e-${{ steps.release_tag.outputs.tag }}.bin
asset_path: .pio/build/esp32/firmware.bin
asset_name: ams2mqtt-esp32-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream
- name: Upload featheresp32 binary to release
uses: actions/upload-release-asset@v1.0.1
- name: Upload esp32 partitions to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: .pio/build/featheresp32/firmware.bin
asset_name: ams2mqtt-featheresp32-${{ steps.release_tag.outputs.tag }}.bin
asset_path: .pio/build/esp32/partitions.bin
asset_name: ams2mqtt-esp32-partitions-${{ steps.release_tag.outputs.tag }}.bin
asset_content_type: application/octet-stream

8
.gitignore vendored
View File

@@ -7,4 +7,10 @@
.vscode
.pio
platformio-user.ini
src/version.h
/src/version.h
/src/web/root
/src/AmsToMqttBridge.ino.cpp
/test
/web/test.html
/sdkconfig
/.tmp

View File

@@ -1,20 +1,20 @@
# AMS <-> MQTT Bridge
Orignally designed and coded by [@roarfred](https://github.com/roarfred), see the original repo at [roarfred/AmsToMqttBridge](https://github.com/roarfred/AmsToMqttBridge)
# AMS MQTT Bridge
This code is designed to decode data from electric smart meters installed in many countries in Europe these days. The data is presented in a graphical web interface and can also send the data to a MQTT broker which makes it suitable for home automation project. Originally it was only designed to work with Norwegian meters, but has since been adapter to read any IEC-62056-7-5 or IEC-62056-21 compliant meters.
This repository contains the code and schematics necessary to build a device to receive and convert data from AMS electrical meters installed in Norway. The code can be used on both ESP8266 and ESP32, both as custom build devices or built from readily available modules. It reads data from the HAN port of the meter and sends this to a configured MQTT bus.
Later development have added Energy usage graph for both day and month, as well as future energy price (Prices only available for ESP32). The code can run on any ESP8266 or ESP32 hardware which you can read more about in the [WiKi](https://github.com/gskjold/AmsToMqttBridge/wiki). If you don't have the knowledge to set up a ESP device yourself, have a look at the shop at [amsleser.no](https://amsleser.no/).
## Release binaries
In the [Release section](https://github.com/gskjold/AmsToMqttBridge/releases) of this repository, you will find precompiled binaries for some common boards.
<img src="webui.png">
- _esp12e_ :: General ESP8266 board with 12E or 12F chip, ex NodeMCU board.
- _hw1esp12e_ :: First version hardware with ESP 12E of 12F chip.
- _featheresp32_ :: Adafruit ESP32 feather
Go to the [WiKi](https://github.com/gskjold/AmsToMqttBridge/wiki) for information on how to get your own device! And find the latest prebuilt firmware file at the [release section](https://github.com/gskjold/AmsToMqttBridge/releases).
### Flashing binaries with [esptool.py](https://github.com/espressif/esptool)
## Building this project with PlatformIO
To build this project, you need [PlatformIO](https://platformio.org/) installed.
Linux:
```esptool.py --port /dev/ttyUSB0 write_flash 0x0 binary-file.bin```
It is recommended to use Visual Studio Code with the PlatformIO plugin for development.
Windows:
```esptool.py --port COM1 write_flash 0x0 binary-file.bin```
[Visual Studio Code](https://code.visualstudio.com/download)
[PlatformIO vscode plugin](https://platformio.org/install/ide?install=vscode)
For development purposes, copy the ```platformio-user.ini-example``` to ```platformio-user.ini``` and customize to your preference. The code will adapt to the platform and board set in your profile.

Binary file not shown.

View File

@@ -0,0 +1,32 @@
1.1.1.8.0.255 - Active+ Energy
1.1.2.8.0.255 - Active- Energy
1.1.3.8.0.255 - Reactive+ Energy
1.1.4.8.0.255 - Reactive- Energy
1.1.0.0.1.255 - Meter number 1
1.1.1.7.0.255 - Active+ Instantaneous value
1.1.2.7.0.255 - Active- Instantaneous value
1.1.3.7.0.255 - Reactive+ Instantaneous value
1.1.4.7.0.255 - Reactive- Instantaneous value
0.1.1.0.0.255 - Current date/time
1.1.32.7.0.255 - L1 Voltage Instantaneous value
1.1.52.7.0.255 - L2 Voltage Instantaneous value
1.1.72.7.0.255 - L3 Voltage Instantaneous value
1.1.31.7.0.255 - L1 Current Instantaneous value
1.1.51.7.0.255 - L2 Current Instantaneous value
1.1.71.7.0.255 - L3 Current Instantaneous value
1.1.21.7.0.255 - L1 Active+ Instantaneous value
1.1.41.7.0.255 - L2 Active+ Instantaneous value
1.1.61.7.0.255 - L3 Active+ Instantaneous value
1.1.33.7.0.255 - L1 (cos.phi) (PF) Instantaneous value
1.1.53.7.0.255 - L2 (cos.phi) (PF) Instantaneous value
1.1.73.7.0.255 - L3 (cos.phi) (PF) Instantaneous value
1.1.13.7.0.255 - Avegage (cos.phi) (PF) Inst. value
1.1.22.7.0.255 - L1 Active- Instantaneous value
1.1.42.7.0.255 - L2 Active- Instantaneous value
1.1.62.7.0.255 - L3 Active- Instantaneous value
1.1.22.8.0.255 - L1 Active- Energy
1.1.42.8.0.255 - L2 Active- Energy
1.1.62.8.0.255 - L3 Active- Energy
1.1.21.8.0.255 - L1 Active+ Energy
1.1.41.8.0.255 - L2 Active+ Energy
1.1.61.8.0.255 - L3 Active+ Energy

View File

@@ -0,0 +1,112 @@
<GatewayRequest>
<NetworkId Value="231" />
<PhysicalDeviceAddress Value="" />
<DataNotification>
<LongInvokeIdAndPriority Value="40000000" />
<DateTime Value="" />
<NotificationBody>
<DataValue>
<Structure Qty="41" >
<String Value="Kamstrup_V0001" />
<!--1.1.1.8.0.255-->
<OctetString Value="0101010800FF" />
<UInt32 Value="001194CA" />
<!--1.1.2.8.0.255-->
<OctetString Value="0101020800FF" />
<UInt32 Value="00000000" />
<!--1.1.3.8.0.255-->
<OctetString Value="0101030800FF" />
<UInt32 Value="0000127E" />
<!--1.1.4.8.0.255-->
<OctetString Value="0101040800FF" />
<UInt32 Value="0009550E" />
<!--1.1.0.0.1.255-->
<OctetString Value="0101000001FF" />
<UInt32 Value="0144ADE1" />
<!--1.1.1.7.0.255-->
<OctetString Value="0101010700FF" />
<UInt32 Value="00000531" />
<!--1.1.2.7.0.255-->
<OctetString Value="0101020700FF" />
<UInt32 Value="00000000" />
<!--1.1.3.7.0.255-->
<OctetString Value="0101030700FF" />
<UInt32 Value="00000000" />
<!--1.1.4.7.0.255-->
<OctetString Value="0101040700FF" />
<UInt32 Value="00000054" />
<!--0.1.1.0.0.255-->
<OctetString Value="0001010000FF" />
<!--2020-05-12 10:24:50-->
<OctetString Value="07E4050C020A1832FF800080" />
<!--1.1.32.7.0.255-->
<OctetString Value="0101200700FF" />
<UInt16 Value="00E4" />
<!--1.1.52.7.0.255-->
<OctetString Value="0101340700FF" />
<UInt16 Value="00E5" />
<!--1.1.72.7.0.255-->
<OctetString Value="0101480700FF" />
<UInt16 Value="00E3" />
<!--1.1.31.7.0.255-->
<OctetString Value="01011F0700FF" />
<UInt32 Value="0000004B" />
<!--1.1.51.7.0.255-->
<OctetString Value="0101330700FF" />
<UInt32 Value="00000070" />
<!--1.1.71.7.0.255-->
<OctetString Value="0101470700FF" />
<UInt32 Value="000001E4" />
<!--1.1.21.7.0.255-->
<OctetString Value="0101150700FF" />
<UInt32 Value="00000070" />
<!--1.1.41.7.0.255-->
<OctetString Value="0101290700FF" />
<UInt32 Value="000000B5" />
<!--1.1.61.7.0.255-->
<OctetString Value="01013D0700FF" />
<UInt32 Value="0000040C" />
<!--1.1.33.7.0.255-->
<OctetString Value="0101210700FF" />
<UInt16 Value="004D" />
<!--1.1.53.7.0.255-->
<OctetString Value="0101350700FF" />
<UInt16 Value="004E" />
<!--1.1.73.7.0.255-->
<OctetString Value="0101490700FF" />
<UInt16 Value="0062" />
<!--1.1.13.7.0.255-->
<OctetString Value="01010D0700FF" />
<UInt16 Value="0063" />
<!--1.1.22.7.0.255-->
<OctetString Value="0101160700FF" />
<UInt32 Value="00000000" />
<!--1.1.42.7.0.255-->
<OctetString Value="01012A0700FF" />
<UInt32 Value="00000000" />
<!--1.1.62.7.0.255-->
<OctetString Value="01013E0700FF" />
<UInt32 Value="00000000" />
<!--1.1.22.8.0.255-->
<OctetString Value="0101160800FF" />
<UInt32 Value="00000000" />
<!--1.1.42.8.0.255-->
<OctetString Value="01012A0800FF" />
<UInt32 Value="00000000" />
<!--1.1.62.8.0.255-->
<OctetString Value="01013E0800FF" />
<UInt32 Value="00000000" />
<!--1.1.21.8.0.255-->
<OctetString Value="0101150800FF" />
<UInt32 Value="000A8F97" />
<!--1.1.41.8.0.255-->
<OctetString Value="0101290800FF" />
<UInt32 Value="0004C152" />
<!--1.1.61.8.0.255-->
<OctetString Value="01013D0800FF" />
<UInt32 Value="000243DF" />
</Structure>
</DataValue>
</NotificationBody>
</DataNotification>
</GatewayRequest>

BIN
doc/Encrypted payload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Binary file not shown.

BIN
doc/M-Bus_DOC48.PDF Normal file

Binary file not shown.

Binary file not shown.

18
doc/Norway/Aidon_OBIS.txt Normal file
View File

@@ -0,0 +1,18 @@
1.1.0.2.129.255 - List version identifier
0.0.96.1.0.255 - Meter ID
0.0.96.1.7.255 - Meter Model
1.0.1.7.0.255 - Active+ Instantaneous value
1.0.2.7.0.255 - Active- Instantaneous value
1.0.3.7.0.255 - Reactive+ Instantaneous value
1.0.4.7.0.255 - Reactive- Instantaneous value
1.0.31.7.0.255 - L1 Current Instantaneous value
1.0.51.7.0.255 - L2 Current Instantaneous value
1.0.71.7.0.255 - L3 Current Instantaneous value
1.0.32.7.0.255 - L1 Voltage Instantaneous value
1.0.52.7.0.255 - L2 Voltage Instantaneous value
1.0.72.7.0.255 - L3 Voltage Instantaneous value
0.0.1.0.0.255 - Current date/time
1.0.1.8.0.255 - Active+ Energy
1.0.2.8.0.255 - Active- Energy
1.0.3.8.0.255 - Reactive+ Energy
1.0.4.8.0.255 - Reactive- Energy

119
doc/Norway/Aidon_data.xml Normal file
View File

@@ -0,0 +1,119 @@
<GatewayRequest>
<NetworkId Value="231" />
<PhysicalDeviceAddress Value="" />
<DataNotification>
<LongInvokeIdAndPriority Value="40000000" />
<DateTime Value="" />
<NotificationBody>
<DataValue>
<Array Qty="0D" >
<Structure Qty="02" >
<!--1.1.0.2.129.255-->
<OctetString Value="0101000281FF" />
<String Value="AIDON_V0001" />
</Structure>
<Structure Qty="02" >
<!--0.0.96.1.0.255-->
<OctetString Value="0000600100FF" />
<String Value="0000000000000000" />
</Structure>
<Structure Qty="02" >
<!--0.0.96.1.7.255-->
<OctetString Value="0000600107FF" />
<String Value="6534" />
</Structure>
<Structure Qty="03" >
<!--1.0.1.7.0.255-->
<OctetString Value="0100010700FF" />
<UInt32 Value="00000339" />
<Structure Qty="02" >
<Int8 Value="00" />
<Enum Value="1B" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.2.7.0.255-->
<OctetString Value="0100020700FF" />
<UInt32 Value="00000000" />
<Structure Qty="02" >
<Int8 Value="00" />
<Enum Value="1B" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.3.7.0.255-->
<OctetString Value="0100030700FF" />
<UInt32 Value="00000000" />
<Structure Qty="02" >
<Int8 Value="00" />
<Enum Value="1D" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.4.7.0.255-->
<OctetString Value="0100040700FF" />
<UInt32 Value="00000251" />
<Structure Qty="02" >
<Int8 Value="00" />
<Enum Value="1D" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.31.7.0.255-->
<OctetString Value="01001F0700FF" />
<Int16 Value="0012" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="21" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.51.7.0.255-->
<OctetString Value="0100330700FF" />
<Int16 Value="0003" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="21" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.71.7.0.255-->
<OctetString Value="0100470700FF" />
<Int16 Value="0016" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="21" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.32.7.0.255-->
<OctetString Value="0100200700FF" />
<UInt16 Value="08FE" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="23" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.52.7.0.255-->
<OctetString Value="0100340700FF" />
<UInt16 Value="08F8" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="23" />
</Structure>
</Structure>
<Structure Qty="03" >
<!--1.0.72.7.0.255-->
<OctetString Value="0100480700FF" />
<UInt16 Value="08F7" />
<Structure Qty="02" >
<Int8 Value="FF" />
<Enum Value="23" />
</Structure>
</Structure>
</Array>
</DataValue>
</NotificationBody>
</DataNotification>
</GatewayRequest>

BIN
doc/Norway/Kaifa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

BIN
doc/Sweden/Aidon-RJ12.pdf Normal file

Binary file not shown.

Binary file not shown.

30
frames/Aidon-Sweden.raw Normal file
View File

@@ -0,0 +1,30 @@
7E A2 43 41 08 83 13 85 EB E6 E7 00 0F 40 00 00 00 00
01 1B
02 02 09 06 00 00 01 00 00 FF 09 0C 07 E5 0C 0A 05 10 39 00 FF 80 00 FF
02 03 09 06 01 00 01 07 00 FF 06 00 00 07 E5 02 02 0F 00 16 1B
02 03 09 06 01 00 02 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
02 03 09 06 01 00 03 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D
02 03 09 06 01 00 04 07 00 FF 06 00 00 02 48 02 02 0F 00 16 1D
02 03 09 06 01 00 1F 07 00 FF 10 00 09 02 02 0F FF 16 21
02 03 09 06 01 00 33 07 00 FF 10 00 25 02 02 0F FF 16 21
02 03 09 06 01 00 47 07 00 FF 10 00 2E 02 02 0F FF 16 21
02 03 09 06 01 00 20 07 00 FF 12 08 E3 02 02 0F FF 16 23
02 03 09 06 01 00 34 07 00 FF 12 08 D8 02 02 0F FF 16 23
02 03 09 06 01 00 48 07 00 FF 12 08 DF 02 02 0F FF 16 23
02 03 09 06 01 00 15 07 00 FF 06 00 00 00 D5 02 02 0F 00 16 1B
02 03 09 06 01 00 16 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
02 03 09 06 01 00 17 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D
02 03 09 06 01 00 18 07 00 FF 06 00 00 00 36 02 02 0F 00 16 1D
02 03 09 06 01 00 29 07 00 FF 06 00 00 03 0C 02 02 0F 00 16 1B
02 03 09 06 01 00 2A 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
02 03 09 06 01 00 2B 07 00 FF 06 00 00 01 21 02 02 0F 00 16 1D
02 03 09 06 01 00 2C 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D
02 03 09 06 01 00 3D 07 00 FF 06 00 00 03 F9 02 02 0F 00 16 1B
02 03 09 06 01 00 3E 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
02 03 09 06 01 00 3F 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D
02 03 09 06 01 00 40 07 00 FF 06 00 00 00 E9 02 02 0F 00 16 1D
02 03 09 06 01 00 01 08 00 FF 06 03 C2 5A 64 02 02 0F 00 16 1E
02 03 09 06 01 00 02 08 00 FF 06 00 00 00 00 02 02 0F 00 16 1E
02 03 09 06 01 00 03 08 00 FF 06 00 04 5D 06 02 02 0F 00 16 20
02 03 09 06 01 00 04 08 00 FF 06 00 B4 9D 89 02 02 0F 00 16 20
1C 90 7E

33
frames/Aidon-TN-3p.raw Normal file
View File

@@ -0,0 +1,33 @@
T FF FF DA SA SA C HC HC LD LS LQ AT AI AI AI AI AD
7E A0 2A 41 08 83 13 04 13 E6 E7 00 0F 40 00 00 00 00 01 01 02 03 09 06 01 00 01 07 00 FF 06 00 00 08 64 02 02 0F 00 16 1B E1
7E A1 1E 41 08 83 13 EE EE E6 E7 00 0F 40 00 00 00 00 01 0D 02 02 09 06 01 01 00 02 81 FF 0A 0B 41 49 44 4F 4E 5F 56 30 30 30 31 02 02 09 06 00 00 60 01 00 FF 0A 10 37 33 35 39 39 39 32 38 39 30 34 39 37 39 39 37 02 02 09 06 00 00 60 01 07 FF 0A 04 36 35 33 34 02 03 09 06 01 00 01 07 00 FF 06 00 00 08 6C 02 02 0F 00 16 1B 02 03 09 06 01 00 02 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B 02 03 09 06 01 00 03 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D 02 03 09 06 01 00 04 07 00 FF 06 00 00 02 09 02 02 0F 00 16 1D 02 03 09 06 01 00 1F 07 00 FF 10 00 41 02 02 0F FF 16 21 02 03 09 06 01 00 33 07 00 FF 10 00 13 02 02 0F FF 16 21 02 03 09 06 01 00 47 07 00 FF 10 00 0E 02 02 0F FF 16 21 02 03 09 06 01 00 20 07 00 FF 12 08 F2 02 02 0F FF 16 23 02 03 09 06 01 00 34 07 00 FF 12 08 D1 02 02 0F FF 16 23 02 03 09 06 01 00 48 07 00 FF 12 08 E8 02 02 0F FF 16 23 8B
7E A1 8A 41 08 83 13 EB FD E6 E7 00 0F 40 00 00 00 00 01 12 02 02 09 06 01 01 00 02 81 FF 0A 0B 41 49 44 4F 4E 5F 56 30 30 30 31 02 02 09 06 00 00 60 01 00 FF 0A 10 37 33 35 39 39 39 32 38 39 30 34 39 37 39 39 37 02 02 09 06 00 00 60 01 07 FF 0A 04 36 35 33 34 02 03 09 06 01 00 01 07 00 FF 06 00 00 03 9A 02 02 0F 00 16 1B 02 03 09 06 01 00 02 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B 02 03 09 06 01 00 03 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D 02 03 09 06 01 00 04 07 00 FF 06 00 00 02 0E 02 02 0F 00 16 1D 02 03 09 06 01 00 1F 07 00 FF 10 00 11 02 02 0F FF 16 21 02 03 09 06 01 00 33 07 00 FF 10 00 10 02 02 0F FF 16 21 02 03 09 06 01 00 47 07 00 FF 10 00 0E 02 02 0F FF 16 21 02 03 09 06 01 00 20 07 00 FF 12 08 F4 02 02 0F FF 16 23 02 03 09 06 01 00 34 07 00 FF 12 08 CD 02 02 0F FF 16 23 02 03 09 06 01 00 48 07 00 FF 12 08 DC 02 02 0F FF 16 23 02 02 09 06 00 00 01 00 00 FF 09 0C 07 E5 03 18 03 08 00 00 FF 00 00 00 02 03 09 06 01 00 01 08 00 FF 06 00 47 F0 34 02 02 0F 01 16 1E 02 03 09 06 01 00 02 08 00 FF 06 00 00 00 00 02 02 0F 01 16 1E 02 03 09 06 01 00 03 08 00 FF 06 00 00 21 9E 02 02 0F 01 16 20 02 03 09 06 01 00 04 08 00 FF 06 00 08 E0 21 02 02 0F 01 16 20 57
7E A1 8A 41 08 83 13 EB FD E6 E7 00
0F
40 00 00 00 00
01 12
02 02 09 06 01 01 00 02 81 FF 0A 0B 41 49 44 4F 4E 5F 56 30 30 30 31
02 02 09 06 00 00 60 01 00 FF 0A 10 37 33 35 39 39 39 32 38 39 30 34 39 37 39 39 37
02 02 09 06 00 00 60 01 07 FF 0A 04 36 35 33 34
02 03 09 06 01 00 01 07 00 FF 06 00 00 09 6D 02 02 0F 00 16 1B
02 03 09 06 01 00 02 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
02 03 09 06 01 00 03 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1D
02 03 09 06 01 00 04 07 00 FF 06 00 00 02 5B 02 02 0F 00 16 1D
Object with three values Value Object with two values
| Obis code | | Scaling
| | | | | Unit
02 03 09 06 01 00 1F 07 00 FF 10 00 11 02 02 0F FF 16 21
02 03 09 06 01 00 33 07 00 FF 10 00 03 02 02 0F FF 16 21
02 03 09 06 01 00 47 07 00 FF 10 00 5A 02 02 0F FF 16 21
02 03 09 06 01 00 20 07 00 FF 12 09 04 02 02 0F FF 16 23
02 03 09 06 01 00 34 07 00 FF 12 09 02 02 02 0F FF 16 23
02 03 09 06 01 00 48 07 00 FF 12 08 EC 02 02 0F FF 16 23
02 02 09 06 00 00 01 00 00 FF 09 0C 07 E5 0A 1F 00 14 00 00 FF 00 00 00
02 03 09 06 01 00 01 08 00 FF 06 00 56 9F 52 02 02 0F 01 16 1E
02 03 09 06 01 00 02 08 00 FF 06 00 00 00 00 02 02 0F 01 16 1E
02 03 09 06 01 00 03 08 00 FF 06 00 00 22 D0 02 02 0F 01 16 20
02 03 09 06 01 00 04 08 00 FF 06 00 0A F5 EC 02 02 0F 01 16 20
51 D7
7E

34
frames/Kaifa-TN-3p.raw Normal file
View File

@@ -0,0 +1,34 @@
T FF FF DA SA SA C HC HC LD LS LQ AT AI AI AI AI AD
7E A0 27 01 02 01 10 5A 87 E6 E7 00 0F 40 00 00 00 09 0C 07 E5 03 17 02 13 1A 3A FF 80 00 00
02 01 // Frame type and size
06 00 00 0B F3 // Active power
5B 05 7E // CRC and end tag
T FF FF DA SA SA C HC HC LD LS LQ AT AI AI AI AI AD
7E A0 78 01 02 01 10 C4 98 E6 E7 00 0F 40 00 00 00 09 0C 07 E5 03 17 02 13 1B 00 FF 80 00 00
02 0D // Frame type and size
09 07 4B 46 4D 5F 30 30 31 // List version
09 10 XX XX XX XX XX XX XX XX XX XX 35 33 34 34 39 33 // Meter ID
09 07 4D 41 33 30 34 48 34 // Meter type
06 00 00 0C 21 // Active import
06 00 00 00 00 // Active export
06 00 00 00 00 // Reactive import
06 00 00 01 9F // Reactive export
06 00 00 0B F3 // I1
06 00 00 05 0B // I2
06 00 00 25 11 // I3
06 00 00 09 44 // U1
06 00 00 09 49 // U2
06 00 00 09 39 // U3
C9 95 7E // CRC and end tag
7E A0 9A 01 02 01 10 AA A5 E6 E7 00 0F 40 00 00 00 09 0C 07 E5 03 17 02 13 00 0A FF 80 00 00
02 12 09 07 4B 46 4D 5F 30 30 31
09 10 XX XX XX XX XX XX XX XX XX XX 35 33 34 34 39 33
09 07 4D 41 33 30 34 48 34
06 00 00 09 99
06 00 00 00 00 06 00 00 00 00 06 00 00 01 67 06 00 00 03 BF 06 00 00 05 05
06 00 00 24 34 06 00 00 09 45 06 00 00 09 4F 06 00 00 09 3B
09 0C 07 E5 03 17 02 13 00 0A FF 80 00 00 06 01 34 3B 5D 06 00 00 00 00 06 00 00 09 36 06 00 3C 7A 98 DA 15 7E
7E A0 79 01 02 01 10 80 93 E6 E7 00 0F 40 00 00 00 09 0C 07 E1 09 0E 04 15 1F 14 FF 80 00 00 02 0D 09 07 4B 46 4D 5F 30 30 31 09 10 36 39 37 30 36 33 31 34 30 31 37 35 33 39 38 35 09 08 4D 41 33 30 34 48 33 45 06 00 00 04 0C 06 00 00 00 00 06 00 00 00 00 06 00 00 00 4E 06 00 00 07 C1 06 00 00 0C 9E 06 00 00 0D 7E 06 00 00 09 5F 06 00 00 00 00 06 00 00 09 66 87 96 7E

51
frames/Kamstrup-1p.raw Normal file
View File

@@ -0,0 +1,51 @@
7E A0 BA 2B 21 13 ED AA E6 E7 00 0F 00 00 00 00
0C 07 E6 02 05 06 0D 00 0A FF 80 00 00
02 19
0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31
09 06 01 01 00 00 05 FF 0A 10 35 37 30 36 35 36 37 32 37 31 35 33 33 32 30 37
09 06 01 01 60 01 01 FF 0A 12 36 38 36 31 31 31 31 42 4E 32 34 32 31 30 31 30 34 30
09 06 01 01 01 07 00 FF 06 00 00 02 68
09 06 01 01 02 07 00 FF 06 00 00 00 00
09 06 01 01 03 07 00 FF 06 00 00 00 53
09 06 01 01 04 07 00 FF 06 00 00 00 00
09 06 01 01 1F 07 00 FF 06 00 00 01 22
00 00 00 00
09 06 01 01 20 07 00 FF 12 00 E2
00 00 00 00
05 D8 7E
7E A0 BA 2B 21 13 ED AA E6 E7 00 0F 00 00 00 00
0C 07 E6 02 05 06 0D 00 14 FF 80 00 00
02 19
0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31
09 06 01 01 00 00 05 FF 0A 10 35 37 30 36 35 36 37 32 37 31 35 33 33 32 30 37
09 06 01 01 60 01 01 FF 0A 12 36 38 36 31 31 31 31 42 4E 32 34 32 31 30 31 30 34 30
09 06 01 01 01 07 00 FF 06 00 00 02 68
09 06 01 01 02 07 00 FF 06 00 00 00 00
09 06 01 01 03 07 00 FF 06 00 00 00 53
09 06 01 01 04 07 00 FF 06 00 00 00 00
09 06 01 01 1F 07 00 FF 06 00 00 01 23
00 00 00 00
09 06 01 01 20 07 00 FF 12 00 E1
00 00 00 00
8E 5E 7E
7E A1 04 2B 21 13 77 6E E6 E7 00 0F 00 00 00 00
0C 07 E6 02 05 06 0D 00 19 FF 80 00 00
02 23 0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31
09 06 01 01 00 00 05 FF 0A 10 35 37 30 36 35 36 37 32 37 31 35 33 33 32 30 37
09 06 01 01 60 01 01 FF 0A 12 36 38 36 31 31 31 31 42 4E 32 34 32 31 30 31 30 34 30
09 06 01 01 01 07 00 FF 06 00 00 02 6B
09 06 01 01 02 07 00 FF 06 00 00 00 00
09 06 01 01 03 07 00 FF 06 00 00 00 54
09 06 01 01 04 07 00 FF 06 00 00 00 00
09 06 01 01 1F 07 00 FF 06 00 00 01 25
00 00 00 00
09 06 01 01 20 07 00 FF 12 00 E1
00 00 00 00
09 06 00 01 01 00 00 FF 09 0C 07 E6 02 05 06 0D 00 19 FF 80 00 00
09 06 01 01 01 08 00 FF 06 00 12 CF 93
09 06 01 01 02 08 00 FF 06 00 00 00 00
09 06 01 01 03 08 00 FF 06 00 00 8C CE
09 06 01 01 04 08 00 FF 06 00 05 E5 04
7F E9 7E

View File

@@ -0,0 +1,51 @@
# After decode:
7E
A1 E9 // Frame type and size
41 03 13 C6 37 E6 E7 00
DB // Encrypted
08 4B 41 4D 45 01 AC 4D 6E // System title
82 // Prefix for 2-byte length
01 D0 // Length 464
30 // Security tag 0011 0000, 0=Compression off, 0=Unicast, 1=Encryption, 0=Authentication, 0000= Security Suite ID
00 00 A3 2F // Frame counter
// Decrypted frame below
0F 00 00 00 00
0C 07 E4 05 0C 02 0A 19 00 FF 80 00 80 // Package timestamp
02 41
0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31 - List ID
09 06 01 01 01 08 00 FF 06 00 11 94 CA - Active+ Energy
09 06 01 01 02 08 00 FF 06 00 00 00 00 - Active- Energy
09 06 01 01 03 08 00 FF 06 00 00 12 7E - Reactive+ Energy
09 06 01 01 04 08 00 FF 06 00 09 55 0E - Reactive- Energy
09 06 01 01 00 00 01 FF 06 01 44 AD E1 - Electricity ID?
09 06 01 01 01 07 00 FF 06 00 00 05 CC - Active+ Instantaneous value
09 06 01 01 02 07 00 FF 06 00 00 00 00 - Active- Instantaneous value
09 06 01 01 03 07 00 FF 06 00 00 00 00 - Reactive+ Instantaneous value
09 06 01 01 04 07 00 FF 06 00 00 00 17 - Reactive- Instantaneous value
09 06 00 01 01 00 00 FF 09 0C 07 E4 05 0C 02 0A 19 00 FF 80 00 80 - Current date/time
09 06 01 01 20 07 00 FF 12 00 E5 - L1 Voltage Instantaneous value
09 06 01 01 34 07 00 FF 12 00 E5 - L2 Voltage Instantaneous value
09 06 01 01 48 07 00 FF 12 00 E3 - L3 Voltage Instantaneous value
09 06 01 01 1F 07 00 FF 06 00 00 00 4B - L1 Current Instantaneous value
09 06 01 01 33 07 00 FF 06 00 00 00 AA - L2 Current Instantaneous value
09 06 01 01 47 07 00 FF 06 00 00 01 E4 - L3 Current Instantaneous value
09 06 01 01 15 07 00 FF 06 00 00 00 71 - L1 Active+ Instantaneous value
09 06 01 01 29 07 00 FF 06 00 00 01 54 - L2 Active+ Instantaneous value
09 06 01 01 3D 07 00 FF 06 00 00 04 07 - L3 Active+ Instantaneous value
09 06 01 01 21 07 00 FF 12 00 4D - L1 (cos.phi) (PF) Instantaneous value
09 06 01 01 35 07 00 FF 12 00 5F - L2 (cos.phi) (PF) Instantaneous value
09 06 01 01 49 07 00 FF 12 00 62 - L3 (cos.phi) (PF) Instantaneous value
09 06 01 01 0D 07 00 FF 12 00 63 - Avegage (cos.phi) (PF) Inst. value
09 06 01 01 16 07 00 FF 06 00 00 00 00 - L1 Active- Instantaneous value
09 06 01 01 2A 07 00 FF 06 00 00 00 00 - L2 Active- Instantaneous value
09 06 01 01 3E 07 00 FF 06 00 00 00 00 - L3 Active- Instantaneous value
09 06 01 01 16 08 00 FF 06 00 00 00 00 - L1 Active- Energy
09 06 01 01 2A 08 00 FF 06 00 00 00 00 - L2 Active- Energy
09 06 01 01 3E 08 00 FF 06 00 00 00 00 - L3 Active- Energy
09 06 01 01 15 08 00 FF 06 00 0A 8F 97 - L1 Active+ Energy
09 06 01 01 29 08 00 FF 06 00 04 C1 53 - L2 Active+ Energy
09 06 01 01 3D 08 00 FF 06 00 02 43 E0 - L3 Active+ Energy
5B C3 CD 5E 79 18 18 DA 9F 97 85 FF 5A 84 7E

18
frames/Kamstup-TN-3p.raw Normal file
View File

@@ -0,0 +1,18 @@
7E A0 E2 2B 21 13 23 9A E6 E7 00 0F 00 00 00 00
0C 07 E5 0B 11 03 0B 32 00 FF 80 00 00
02 19
0A 0E 4B 61 6D 73 74 72 75 70 5F 56 30 30 30 31 - List ID
09 06 01 01 00 00 05 FF 0A 10 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX - Meter ID
09 06 01 01 60 01 01 FF 0A 12 36 38 34 31 31 33 31 42 4E 32 34 33 31 30 31 30 34 30 - Meter model
09 06 01 01 01 07 00 FF 06 00 00 05 E6 - Active+
09 06 01 01 02 07 00 FF 06 00 00 00 00 - Active-
09 06 01 01 03 07 00 FF 06 00 00 00 00 - Reactive+
09 06 01 01 04 07 00 FF 06 00 00 01 92 - Reactive-
09 06 01 01 1F 07 00 FF 06 00 00 00 A1 - L1 current
09 06 01 01 33 07 00 FF 06 00 00 00 C1 - L2 current
09 06 01 01 47 07 00 FF 06 00 00 01 8E - L3 current
09 06 01 01 20 07 00 FF 12 00 EB - L1 voltage
09 06 01 01 34 07 00 FF 12 00 EC - L2 voltage
09 06 01 01 48 07 00 FF 12 00 EC - L3 voltage
EF 5F 7E

45
frames/austria.raw Normal file
View File

@@ -0,0 +1,45 @@
// HDLC header
68
01 01 // Format (0x00) and total length (257)
68 // Start
53 // Control field
FF // Address (Broadcast address)
// LLC
00 // Control information field
01 // Source SAP
67 // Destination SAP
DB // Encrypted
08 53 41 47 59 05 E6 D9 FD // System title
81 // Prefix for 1-byte length
F8 // Length (248), starting from 0xDB and including end byte
20 // Security tag 0010 0000, 0=Compression off, 0=Unicast, 1=Encryption, 0=No auth, 0000= Security Suite ID
00 72 00 76 // Frame counter
68 01 01 68
53 FF 10 01 67
DB
08 53 41 47 59 05 E6 D9 FD
81 F8
20
00 01 A0 E0
0F 80 3E 37 71
0C 07 E5 0C 1B 01 0E 00 2D 00 FF C4 02 // Frame timestamp
02 23 // 35 items
09 0C 07 E5 0C 1B 01 0E 00 2D 00 FF C4 02 // Meter timestamp
09 06 01 00 01 08 00 FF 06 00 43 3D 0A 02 02 0F 00 16 1E
09 06 01 00 02 08 00 FF 06 00 00 01 03 02 02 0F 00 16 1E
09 06 01 00 01 07 00 FF 06 00 00 01 FE 02 02 0F 00 16 1B
09 06 01 00 02 07 00 FF 06 00 00 00 00 02 02 0F 00 16 1B
09 06 01 00 20 07 00 FF 12 09 34 02 02 0F FF 16 23
09 06 01 00 34 07 00 FF 12 09 34 02 02 0F FF 16 23
09 06 01 00 48 07 00 FF 12 09 2D 02 02 0F FF 16 23
09 06 01 00 1F 07 00 FF 12 00 63 02 02 0F FE 16 21
09 06 01 00 33 07 00 FF 12 00 3F 02 02 0F FE 16 21
09 06 01 00 47 07 00 FF 12 00 54 02 02 0F FE 16 21
09 06 01 00 0D 07 00 FF 10 03 CF 02 02 0F FD 16 FF // Power factor
09 0C 31 37 38 32 31 30 30 31 35 31 36 35 // Meter ID
01 67

32
frames/decoding.txt Normal file
View File

@@ -0,0 +1,32 @@
T = Tag
FF = Frame format (4 bit type, 1 bit segmentation, 11 bit frame length)
DA = Destination Address (1-4 bytes, LSB=1 terminates)
SA = Source address (1-4 bytes, LSB=1 terminates)
C = Control (1 byte)
HC = HCS (2 bytes)
LD = LLC Destination
LS = LLC Remote
LQ = LLC Quality
AT = Tag (0x0F = unencrypted, 0xDB = encrypted) Not really documented that well...
AI = Invoke ID and priority (4 bytes)
AD = Date and time
AS = System title
CT = Cipher frame tag ? Undocumented
CL = Lenght of Cipher frame. Length of payload will be this number - 5 (control and counter in start) - 12 (GCM tag appended after payload)
CC = Security control
CO = Invocation counter
Security control bits
01234567
00110000 (0x30)
0 = read acces
1 = write access
2 = Authenticated req
3 = Encrypted req
4 = Digitally signed req
5 = Authenticated res
6 = Encrypted res
7 = Digitally signed res

View File

@@ -1,21 +1 @@
# Hardware options
There are currently two possible hardware options for this project, both in need of external power supply. A self powered board is under development by a member of the community and is currently being tested.
## Hardware v1 by [@roarfred](https://github.com/roarfred)
Composed from a ESP12E (or F) chip, this ESP8266 based board is designed specifically for this project with an on board M-bus chip.
Building this project will require some skills in ordering and assembling electronic circuits as well as programming. No detailed instructions are available.
![The HAN Reader Hardware](v1/img/HanReaderInEnclosure.PNG)
*The completed board mounted in a [3D printed enclosure](/Enclosure)*
## Assembly of readily available modules
Use a ESP based developmentboard and a M-bus module.
[Adafruit Feather M0 WiFi w/ATWINC1500](https://www.adafruit.com/product/3010)
[TSS721 M-BUS module board](https://www.aliexpress.com/item/TSS721/32751482255.html)
![FeatherMbus](img/feather_3010-00_mbus_slave.jpg)
[See Hardware page in Wiki](https://github.com/gskjold/AmsToMqttBridge/wiki)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,3 @@
EESchema-DOCLIB Version 2.0
#
#End Doc Library

View File

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

View File

@@ -1,2 +1,3 @@
(sym_lib_table
(lib (name HAN_ESP_TSS721-rescue)(type Legacy)(uri ${KIPRJMOD}/HAN_ESP_TSS721-rescue.lib)(options "")(descr ""))
)

BIN
images/dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
images/dayplot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images/main-header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
images/monthplot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
images/sensor-displays.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
images/status-bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/tempsensors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,9 +0,0 @@
name=HanConfigAp
version=1.0.0
author=roarfred
maintainer=roarfred <not@important.com>
sentence=HAN Configuraiton accesspoint
paragraph=HAN Configuraiton accesspoint
category=Sensors
url=https://github.com/roarfred/AmsToMqttBridge
architectures=*

View File

@@ -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 <Arduino.h>
#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#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;

View File

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

View File

@@ -1,298 +0,0 @@
#include "HanConfigAp.h"
#include "config_html.h"
#include "style_css.h"
#include "Base64.h"
#if defined(ESP8266)
ESP8266WebServer HanConfigAp::server(80);
#elif defined(ESP32) // ARDUINO_ARCH_ESP32
WebServer HanConfigAp::server(80);
#endif
Stream* HanConfigAp::debugger;
bool HanConfigAp::hasConfig() {
return config.hasConfig();
}
void HanConfigAp::setup(int accessPointButtonPin, Stream* debugger)
{
this->debugger = debugger;
// 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
config.load();
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());
}
}
void HanConfigAp::enableWeb() {
server.on("/", handleRoot);
server.on("/style.css", handleStyle);
server.on("/save", handleSave);
server.begin(); // Web server start
print("Web server is ready for config at http://");
if(isActivated) {
print(WiFi.softAPIP());
} else {
print(WiFi.localIP());
}
println("/");
}
bool HanConfigAp::loop() {
if(isActivated) {
//DNS
dnsServer.processNextRequest();
}
//HTTP
server.handleClient();
return isActivated;
}
/** Handle root or redirect to captive portal */
void HanConfigAp::handleRoot() {
println("Serving / over http...");
configuration *config = new configuration();
config->load();
String html = String((const __FlashStringHelper*) CONFIG_HTML);
if(config->hasConfig()) {
bool access = !config->isAuth();
if(config->isAuth() && server.hasHeader("Authorization")) {
String expectedAuth = String(config->authUser) + ":" + String(config->authPass);
String providedPwd = server.header("Authorization");
providedPwd.replace("Basic ", "");
char inputString[providedPwd.length()];
providedPwd.toCharArray(inputString, providedPwd.length()+1);
int inputStringLength = sizeof(inputString);
int decodedLength = Base64.decodedLength(inputString, inputStringLength);
char decodedString[decodedLength];
Base64.decode(decodedString, inputString, inputStringLength);
print("Received auth: ");
println(decodedString);
access = String(decodedString).equals(expectedAuth);
}
if(!access) {
server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
server.send(401, "text/html", "");
} else {
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
html.replace("${config.ssid}", config->ssid);
html.replace("${config.ssidPassword}", config->ssidPassword);
switch (config->meterType) {
case 1:
html.replace("${config.meterType0}", "");
html.replace("${config.meterType1}", "selected");
html.replace("${config.meterType2}", "");
html.replace("${config.meterType3}", "");
break;
case 2:
html.replace("${config.meterType0}", "");
html.replace("${config.meterType1}", "");
html.replace("${config.meterType2}", "selected");
html.replace("${config.meterType3}", "");
break;
case 3:
html.replace("${config.meterType0}", "");
html.replace("${config.meterType1}", "");
html.replace("${config.meterType2}", "");
html.replace("${config.meterType3}", "selected");
break;
default:
html.replace("${config.meterType0}", "selected");
html.replace("${config.meterType1}", "");
html.replace("${config.meterType2}", "");
html.replace("${config.meterType3}", "");
}
html.replace("${config.mqtt}", config->mqtt);
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.authPass}", config->authPass);
}
} else {
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
html.replace("${config.ssid}", "");
html.replace("${config.ssidPassword}", "");
html.replace("${config.meterType0}", "selected");
html.replace("${config.meterType1}", "");
html.replace("${config.meterType2}", "");
html.replace("${config.meterType3}", "");
html.replace("${config.mqtt}", "");
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.authUser}", "");
html.replace("${config.authPass}", "");
}
server.send(200, "text/html", html);
}
void HanConfigAp::handleStyle() {
println("Serving /style.css over http...");
String css = String((const __FlashStringHelper*) STYLE_CSS);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "text/css", css);
}
void HanConfigAp::handleSave() {
configuration *config = new configuration();
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();
temp = server.arg("mqtt");
config->mqtt = new char[temp.length() + 1];
temp.toCharArray(config->mqtt, 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);
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);
println("Saving configuration now...");
if (HanConfigAp::debugger) config->print(HanConfigAp::debugger);
if (config->save())
{
println("Successfully saved. Will reboot now.");
String html = "<html><body><h1>Successfully Saved!</h1><h3>Device is restarting now...</h3></form>";
server.send(200, "text/html", html);
#if defined(ESP8266)
ESP.reset();
#elif defined(ESP32)
ESP.restart();
#endif
}
else
{
println("Error saving configuration");
String html = "<html><body><h1>Error saving configuration!</h1></form>";
server.send(500, "text/html", html);
}
}
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);
}

View File

@@ -1,62 +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 <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#elif defined(ESP32) // ARDUINO_ARCH_ESP32
#include <WiFi.h>
#include <WebServer.h>
#else
#warning "Unsupported board type"
#endif
#include <DNSServer.h>
#include "configuration.h"
#define INVALID_BUTTON_PIN 0xFFFFFFFF
class HanConfigAp {
public:
void setup(int accessPointButtonPin, Stream* debugger);
void enableWeb();
bool loop();
bool hasConfig();
configuration config;
bool isActivated = false;
private:
const char* AP_SSID = "AMS2MQTT";
// 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);
// Web server
static void handleRoot();
static void handleStyle();
static void handleSave();
#if defined(ESP8266)
static ESP8266WebServer server;
#elif defined(ESP32) // ARDUINO_ARCH_ESP32
static WebServer server;
#endif
static Stream* debugger;
};
#endif

View File

@@ -1,85 +0,0 @@
const char CONFIG_HTML[] PROGMEM = R"=="==(
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="/style.css">
<title>AMS2MQTT - configuration</title>
</head>
<body>
<form method='post' action='/save'>
<div class="wrapper">
<div class="inner-wrapper">
<div>
<h2>WiFi</h2>
</div>
<div>
<input type='text' name='ssid' value="${config.ssid}" placeholder="SSID">
</div>
<div>
<input type='password' name='ssidPassword' value="${config.ssidPassword}" placeholder="Password">
</div>
</div>
<div class="inner-wrapper">
<div>
<h2>Meter Type</h2>
</div>
<div class="select-style">
<select name="meterType">
<option value="0" ${config.meterType0} disabled class="disabled-option"> SELECT TYPE </option>
<option value="1" ${config.meterType1}>Kaifa</option>
<option value="2" ${config.meterType2}>Aidon</option>
<option value="3" ${config.meterType3}>Kamstrup</option>
</select>
</div>
</div>
<div class="inner-wrapper">
<div>
<h2>MQTT</h2>
</div>
<div>
<label>Server & port:</label>
<input type='text' name='mqtt' value="${config.mqtt}" placeholder="server">
<input type='number' name='mqttPort' value="${config.mqttPort}" placeholder="port">
</div>
<div>
<label>Client ID:</label>
<input type='text' name='mqttClientID' value="${config.mqttClientID}" placeholder="client id">
</div>
<div>
<label>Publish topic: </label>
<input type='text' name='mqttPublishTopic' value="${config.mqttPublishTopic}" placeholder="topic">
</div>
<div>
<label>Username:</label>
<input type='text' name='mqttUser' value="${config.mqttUser}" placeholder="Blank for insecure">
</div>
<div>
<label>Password:</label>
<input type='password' name='mqttPass' value="${config.mqttPass}" placeholder="Blank for insecure">
</div>
<div>
<input class="submit-button" type='submit' value='save'>
</div>
</div>
<div class="inner-wrapper">
<div>
<h2>Webserver</h2>
</div>
<div>
<label>Username:</label>
<input type='text' name='authUser' value="${config.authUser}" placeholder="Blank for insecure">
</div>
<div>
<label>Password:</label>
<input type='password' name='authPass' value="${config.authPass}" placeholder="Blank for insecure">
</div>
</div>
</div>
</form>
<body>
</html>
)=="==";

View File

@@ -1,248 +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);
address += saveString(address, mqtt);
address += saveInt(address, mqttPort);
address += saveString(address, mqttClientID);
address += saveString(address, mqttPublishTopic);
address += saveString(address, mqttSubscribeTopic);
if (isSecure()) {
address += saveBool(address, true);
address += saveString(address, mqttUser);
address += saveString(address, mqttPass);
}
else
address += saveBool(address, false);
address += saveBool(address, isAuth());
if (isAuth()) {
address += saveString(address, authUser);
address += saveString(address, authPass);
}
bool success = EEPROM.commit();
EEPROM.end();
return success;
}
bool configuration::load()
{
int address = EEPROM_CONFIG_ADDRESS;
bool success = false;
ssid = (char*)String("").c_str();
ssidPassword = (char*)String("").c_str();
meterType = (byte)0;
mqtt = (char*)String("").c_str();
mqttClientID = (char*)String("").c_str();
mqttPublishTopic = (char*)String("").c_str();
mqttSubscribeTopic = (char*)String("").c_str();
mqttUser = 0;
mqttPass = 0;
mqttPort = 1883;
authUser = 0;
authPass = 0;
EEPROM.begin(EEPROM_SIZE);
int cs = EEPROM.read(address);
if (cs >= 71)
{
address++;
address += readString(address, &ssid);
address += readString(address, &ssidPassword);
address += readByte(address, &meterType);
address += readString(address, &mqtt);
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;
}
success = true;
}
if(cs >= 72) {
bool auth = false;
address += readBool(address, &auth);
if (auth) {
address += readString(address, &authUser);
address += readString(address, &authPass);
} else {
authUser = 0;
authPass = 0;
}
success = true;
}
EEPROM.end();
return success;
}
bool configuration::isSecure()
{
return (mqttUser != 0) && (String(mqttUser).length() > 0);
}
bool configuration::isAuth() {
return (authUser != 0) && (String(authUser).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);
debugger->printf("mqtt: %s\r\n", this->mqtt);
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->isAuth()) {
debugger->printf("WEB AUTH:\r\n");
debugger->printf("authUser: %s\r\n", this->authUser);
debugger->printf("authPass: %s\r\n", this->authPass);
}
debugger->println("-----------------------------------------------");
}
template <class T> 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 <class T> 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<length; i++)
{
buffer[i] = EEPROM.read(pAddress + address++);
}
*pString = buffer;
return address;
}
int configuration::saveString(int pAddress, char* pString)
{
int address = 0;
int length = strlen(pString) + 1;
EEPROM.put(pAddress + address, length);
address++;
for (int i = 0; i < length; i++)
{
EEPROM.put(pAddress + address, pString[i]);
address++;
}
return address;
}

View File

@@ -1,60 +0,0 @@
// config.h
#ifndef _CONFIGURATION_h
#define _CONFIGURATION_h
#include <EEPROM.h>
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
class configuration {
public:
char* ssid;
char* ssidPassword;
char* mqtt;
int mqttPort;
char* mqttClientID;
char* mqttPublishTopic;
char* mqttSubscribeTopic;
char* mqttUser;
char* mqttPass;
byte meterType;
char* authUser;
char* authPass;
bool hasConfig();
bool isSecure();
bool isAuth();
bool save();
bool load();
void print(Stream* debugger);
protected:
private:
const int EEPROM_SIZE = 512;
const byte EEPROM_CHECK_SUM = 72; // 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 <class T> int writeAnything(int ee, const T& value);
template <class T> int readAnything(int ee, T& value);
};
#endif

View File

@@ -1,135 +0,0 @@
const char STYLE_CSS[] PROGMEM = R"=="==(
body,div,input {
font-family: "Roboto", Arial, Lucida Grande;
}
.wrapper {
width: 500px;
position: absolute;
padding: 30px;
background-color: #FFF;
border-radius: 1px;
color: #333;
border-color: rgba(0, 0, 0, 0.03);
box-shadow: 0 2px 2px rgba(0, 0, 0, .24), 0 0 2px rgba(0, 0, 0, .12);
margin-left: 20px;
margin-top: 20px;
}
div {
padding-bottom: 5px;
}
label {
font-family: "Roboto", "Helvetica Neue", sans-serif;
font-size: 14px;
line-height: 16px;
width: 100px;
display: inline-block;
}
input {
font-family: "Roboto", "Helvetica Neue", sans-serif;
font-size: 14px;
line-height: 16px;
bottom: 30px;
border: none;
border-bottom: 1px solid #d4d4d4;
padding: 10px;
background: transparent;
transition: all .25s ease;
}
input[type=number] {
width: 70px;
margin-left: 5px;
}
input:focus {
outline: none;
border-bottom: 1px solid #3f51b5;
}
h2 {
text-align: left;
font-size: 20px;
font-weight: bold;
letter-spacing: 3px;
line-height: 28px;
}
.submit-button {
position: absolute;
text-align: right;
border-radius: 20px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
background-color: #3f51b5;
color: #FFF;
padding: 12px 25px;
display: inline-block;
font-size: 12px;
font-weight: bold;
letter-spacing: 2px;
right: 0px;
bottom: 10px;
cursor: pointer;
transition: all .25s ease;
box-shadow: 0 2px 2px rgba(0, 0, 0, .24), 0 0 2px rgba(0, 0, 0, .12);
width: 100px;
}
.select-style {
border-top: 10px solid white;
border-bottom: 1px solid #d4d4d4;
color: #ffffff;
cursor: pointer;
display: block;
font-family: Roboto, "Helvetica Neue", sans-serif;
font-size: 14px;
font-weight: 400;
height: 16px;
line-height: 14px;
min-width: 200px;
padding-bottom: 7px;
padding-left: 0px;
padding-right: 0px;
position: relative;
text-align: left;
width: 80%;
-webkit-box-direction: normal;
overflow: hidden;
background: #ffffff url("data:image/png;base64,R0lGODlhDwAUAIABAAAAAP///yH5BAEAAAEALAAAAAAPABQAAAIXjI+py+0Po5wH2HsXzmw//lHiSJZmUAAAOw==") no-repeat 98% 50%;
}
.disabled-option {
color: #d4d4d4;
}
.select-style select {
padding: 5px 8px;
width: 100%;
border: none;
box-shadow: none;
background: transparent;
background-image: none;
-webkit-appearance: none;
}
.select-style select:focus {
outline: none;
border: none;
}
@media only screen and (max-width: 1000px) {
.wrapper {
width: 80%;
}
}
@media only screen and (max-width: 300px) {
.wrapper {
width: 75%;
}
}
@media only screen and (max-width: 600px) {
.wrapper {
width: 80%;
margin-left: 0px;
margin-top: 0px;
}
.submit-button {
bottom: 0px;
width: 70px;
}
input {
width: 100%;
}
}
)=="==";

View File

@@ -1,197 +0,0 @@
/*
* Simple sketch to simulate reading data from a Kamstrup
* AMS Meter.
*
* Created 24. October 2017 by Roar Fredriksen
* Modified 06. November 2017 by Ruben Andreassen
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <HanReader.h>
#include <Kamstrup.h>
// The HAN Port reader
HanReader hanReader;
// WiFi and MQTT endpoints
const char* ssid = "ssid";
const char* password = "password";
const char* mqtt_server = "ip or dns";
const char* mqtt_topic = "sensors/out/espams";
const char* device_name = "espams";
bool enableDebug = false;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
//setupDebugPort(); //Comment out this line if you dont need debugging on Serial1
setupWiFi();
setupMqtt();
// initialize the HanReader
// (passing no han port, as we are feeding data manually, but provide Serial for debugging)
if (enableDebug) {
hanReader.setup(&Serial, 2400, SERIAL_8N1, &Serial1);
} else {
hanReader.setup(&Serial, 2400, SERIAL_8N1, NULL);
}
}
void setupMqtt()
{
client.setServer(mqtt_server, 1883);
}
void setupDebugPort()
{
enableDebug = true;
// Initialize the Serial port for debugging
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void setupWiFi()
{
// Initialize wifi
if (enableDebug) {
Serial1.print("Connecting to ");
Serial1.println(ssid);
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if (enableDebug) Serial1.print(".");
}
if (enableDebug) {
Serial1.println("");
Serial1.println("WiFi connected");
Serial1.println("IP address: ");
Serial1.println(WiFi.localIP());
}
}
void loop() {
loopMqtt();
// Read one byte from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
if (enableDebug) {
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
}
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kamstrup::List1 || listSize == (int)Kamstrup::List2)
{
// Define a json object to keep the data
StaticJsonBuffer<MQTT_MAX_PACKET_SIZE> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// Any generic useful info here
root["dn"] = device_name;
root["up"] = millis();
// Add a sub-structure to the json object,
// to keep the data from the meter itself
JsonObject& data = root.createNestedObject("data");
data["ls"] = listSize;
data["lvi"] = hanReader.getString((int)Kamstrup_List1::ListVersionIdentifier);
data["mid"] = hanReader.getString((int)Kamstrup_List1::MeterID);
data["mt"] = hanReader.getString((int)Kamstrup_List1::MeterType);
data["t"] = hanReader.getPackageTime();
data["aip"] = hanReader.getInt((int)Kamstrup_List1::ActiveImportPower); //power
data["aep"] = hanReader.getInt((int)Kamstrup_List1::ActiveExportPower);
data["rip"] = hanReader.getInt((int)Kamstrup_List1::ReactiveImportPower);
data["rep"] = hanReader.getInt((int)Kamstrup_List1::ReactiveExportPower);
data["al1"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL1) / 100.0;
data["al2"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL2) / 100.0;
data["al3"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL3) / 100.0;
data["vl1"] = hanReader.getInt((int)Kamstrup_List1::VoltageL1);
data["vl2"] = hanReader.getInt((int)Kamstrup_List1::VoltageL2);
data["vl3"] = hanReader.getInt((int)Kamstrup_List1::VoltageL3);
if (listSize == (int)Kamstrup::List2)
{
data["cl"] = hanReader.getTime((int)Kamstrup_List2::MeterClock);
data["caie"] = hanReader.getInt((int)Kamstrup_List2::CumulativeActiveImportEnergy);
data["caee"] = hanReader.getInt((int)Kamstrup_List2::CumulativeActiveExportEnergy);
data["crie"] = hanReader.getInt((int)Kamstrup_List2::CumulativeReactiveImportEnergy);
data["cree"] = hanReader.getInt((int)Kamstrup_List2::CumulativeReactiveExportEnergy);
}
if (enableDebug) {
root.printTo(Serial1);
Serial1.println("JSON length");
Serial1.println(root.measureLength());
Serial1.println("");
}
// Publish the json to the MQTT server
char msg[MQTT_MAX_PACKET_SIZE];
root.printTo(msg, MQTT_MAX_PACKET_SIZE);
bool result = client.publish(mqtt_topic, msg);
if (enableDebug) {
Serial1.println("MQTT publish result:");
Serial1.println(result);
}
}
}
}
// Ensure the MQTT lirary gets some attention too
void loopMqtt()
{
if (!client.connected()) {
reconnectMqtt();
}
client.loop();
}
void reconnectMqtt() {
// Loop until we're reconnected
while (!client.connected()) {
if (enableDebug) Serial1.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
if (enableDebug) Serial1.println("connected");
// Once connected, publish an announcement...
// client.publish("sensors", "hello world");
// ... and resubscribe
// client.subscribe("inTopic");
}
else {
if (enableDebug) {
Serial1.print("failed, rc=");
Serial1.print(client.state());
Serial1.println(" try again in 5 seconds");
}
// Wait 5 seconds before retrying
delay(5000);
}
}
}

View File

@@ -1,95 +0,0 @@
# Setup
1. Copy AmsToMqttBridge\Code\Arduino\HanReader\src to Arduino\libraries
2. Download the following libraries and put them in Arduino\libraries
- ESP8266WiFi
- PubSubClient
- ArduinoJson
3. **Set MQTT_MAX_PACKET_SIZE in PubSubClient.h to at least 512 (i used 1024)**
4. Edit the following variables in the project:
- ssid
- password
- mqtt_server
- mqtt_topic
- device_name
## Output example:
### List 1
```
{
"dn": "espams",
"up": 1475902,
"data": {
"ls": 25,
"lvi": "Kamstrup_V0001",
"mid": "5706567274389702",
"mt": "6841121BN243101040",
"t": 1510088840,
"aip": 3499,
"aep": 0,
"rip": 0,
"rep": 424,
"al1": 10.27,
"al2": 6.37,
"al3": 11.79,
"vl1": 231,
"vl2": 226,
"vl3": 231
}
}
```
### List 2
```
{
"dn": "espams",
"up": 1041212,
"data": {
"ls": 35,
"lvi": "Kamstrup_V0001",
"mid": "5706567274389702",
"mt": "6841121BN243101040",
"t": 1510088405,
"aip": 4459,
"aep": 0,
"rip": 0,
"rep": 207,
"al1": 14.72,
"al2": 6.39,
"al3": 15.02,
"vl1": 231,
"vl2": 227,
"vl3": 231,
"cl": 1510088405,
"caie": 588500,
"caee": 0,
"crie": 93,
"cree": 80831
}
}
```
### List 1 and 2 fields
- dn = Device Name
- up = MS since last reboot
- ls = List Size
- lvi = List Version Identifier
- mid = Meter ID
- mt = Meter Type
- t = Time
- aie = Active Import Power
- aep = Active Export Power
- rip = Reactive Import Power
- rep = Reactive Export Power
- al1 = Current L1
- al2 = Current L2
- al3 = Current L3
- cl1 = Voltage L1
- cl2 = Voltage L2
- cl3 = Voltage L3
### List 2 additional fields
- cl = Meter Clock
- caie = Cumulative Active Import Energy
- caee = Cumulative Active Export Energy
- crie = Cumulative Reactive Import Energy
- cree = Cumulative Reactive Export Energy

View File

@@ -1,61 +0,0 @@
/*
* Simple sketch to read MBus data from electrical meter
* As the protocol requires "Even" parity, and this is
* only supported on the hardware port of the ESP8266,
* we'll have to use Serial1 for debugging.
*
* This means you'll have to program the ESP using the
* regular RX/TX port, and then you must remove the FTDI
* and connect the MBus signal from the meter to the
* RS pin. The FTDI/RX can be moved to Pin2 for debugging
*
* Created 14. september 2017 by Roar Fredriksen
*/
#include "HanReader.h"
#include "Kaifa.h"
// The HAN Port reader
HanReader hanReader;
void setup() {
setupDebugPort();
// initialize the HanReader
// (passing Serial as the HAN port and Serial1 for debugging)
hanReader.setup(&Serial, &Serial1);
}
void setupDebugPort()
{
// Initialize the Serial1 port for debugging
// (This port is fixed to Pin2 of the ESP8266)
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void loop() {
// Read one byte from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kaifa::List1)
{
int power = hanReader.getInt((int)Kaifa_List1::ActivePowerImported);
Serial1.print("Power consumtion is right now: ");
Serial1.print(power);
Serial1.println(" W");
}
}
}

View File

@@ -1,202 +0,0 @@
/*
* Simple sketch to read MBus data from electrical meter
* As the protocol requires "Even" parity, and this is
* only supported on the hardware port of the ESP8266,
* we'll have to use Serial1 for debugging.
*
* This means you'll have to program the ESP using the
* regular RX/TX port, and then you must remove the FTDI
* and connect the MBus signal from the meter to the
* RS pin. The FTDI/RX can be moved to Pin2 for debugging
*
* Created 14. september 2017 by Roar Fredriksen
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "HanReader.h"
#include "Kaifa.h"
// The HAN Port reader
HanReader hanReader;
// WiFi and MQTT endpoints
const char* ssid = "Roar_Etne";
const char* password = "**********";
const char* mqtt_server = "192.168.10.203";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
setupDebugPort();
setupWiFi();
setupMqtt();
// initialize the HanReader
// (passing Serial as the HAN port and Serial1 for debugging)
hanReader.setup(&Serial, &Serial1);
}
void setupMqtt()
{
client.setServer(mqtt_server, 1883);
}
void setupDebugPort()
{
// Initialize the Serial1 port for debugging
// (This port is fixed to Pin2 of the ESP8266)
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void setupWiFi()
{
// Initialize wifi
Serial1.print("Connecting to ");
Serial1.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial1.print(".");
}
Serial1.println("");
Serial1.println("WiFi connected");
Serial1.println("IP address: ");
Serial1.println(WiFi.localIP());
}
void loop() {
loopMqtt();
// Read one byt from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kaifa::List1 || listSize == (int)Kaifa::List2 || listSize == (int)Kaifa::List3)
{
if (listSize == (int)Kaifa::List1)
{
Serial1.println(" (list #1 has no ID)");
}
else
{
String id = hanReader.getString((int)Kaifa_List2::ListVersionIdentifier);
Serial1.println(id);
}
// Get the timestamp (as unix time) from the package
time_t time = hanReader.getPackageTime();
Serial.print("Time of the package is: ");
Serial.println(time);
// Define a json object to keep the data
StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// Any generic useful info here
root["id"] = "espdebugger";
root["up"] = millis();
root["t"] = time;
// Add a sub-structure to the json object,
// to keep the data from the meter itself
JsonObject& data = root.createNestedObject("data");
// Based on the list number, get all details
// according to OBIS specifications for the meter
if (listSize == (int)Kaifa::List1)
{
data["P"] = hanReader.getInt((int)Kaifa_List1::ActivePowerImported);
}
else if (listSize == (int)Kaifa::List2)
{
data["lv"] = hanReader.getString((int)Kaifa_List2::ListVersionIdentifier);
data["id"] = hanReader.getString((int)Kaifa_List2::MeterID);
data["type"] = hanReader.getString((int)Kaifa_List2::MeterType);
data["P"] = hanReader.getInt((int)Kaifa_List2::ActiveImportPower);
data["Q"] = hanReader.getInt((int)Kaifa_List2::ReactiveImportPower);
data["I1"] = hanReader.getInt((int)Kaifa_List2::CurrentL1);
data["I2"] = hanReader.getInt((int)Kaifa_List2::CurrentL2);
data["I3"] = hanReader.getInt((int)Kaifa_List2::CurrentL3);
data["U1"] = hanReader.getInt((int)Kaifa_List2::VoltageL1);
data["U2"] = hanReader.getInt((int)Kaifa_List2::VoltageL2);
data["U3"] = hanReader.getInt((int)Kaifa_List2::VoltageL3);
}
else if (listSize == (int)Kaifa::List3)
{
data["lv"] = hanReader.getString((int)Kaifa_List3::ListVersionIdentifier);;
data["id"] = hanReader.getString((int)Kaifa_List3::MeterID);
data["type"] = hanReader.getString((int)Kaifa_List3::MeterType);
data["P"] = hanReader.getInt((int)Kaifa_List3::ActiveImportPower);
data["Q"] = hanReader.getInt((int)Kaifa_List3::ReactiveImportPower);
data["I1"] = hanReader.getInt((int)Kaifa_List3::CurrentL1);
data["I2"] = hanReader.getInt((int)Kaifa_List3::CurrentL2);
data["I3"] = hanReader.getInt((int)Kaifa_List3::CurrentL3);
data["U1"] = hanReader.getInt((int)Kaifa_List3::VoltageL1);
data["U2"] = hanReader.getInt((int)Kaifa_List3::VoltageL2);
data["U3"] = hanReader.getInt((int)Kaifa_List3::VoltageL3);
data["tPI"] = hanReader.getInt((int)Kaifa_List3::CumulativeActiveImportEnergy);
data["tPO"] = hanReader.getInt((int)Kaifa_List3::CumulativeActiveExportEnergy);
data["tQI"] = hanReader.getInt((int)Kaifa_List3::CumulativeReactiveImportEnergy);
data["tQO"] = hanReader.getInt((int)Kaifa_List3::CumulativeReactiveExportEnergy);
}
// Write the json to the debug port
root.printTo(Serial1);
Serial1.println();
// Publish the json to the MQTT server
char msg[1024];
root.printTo(msg, 1024);
client.publish("sensors/out/espdebugger", msg);
}
}
}
// Ensure the MQTT lirary gets some attention too
void loopMqtt()
{
if (!client.connected()) {
reconnectMqtt();
}
client.loop();
}
void reconnectMqtt() {
// Loop until we're reconnected
while (!client.connected()) {
Serial1.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial1.println("connected");
// Once connected, publish an announcement...
// client.publish("sensors", "hello world");
// ... and resubscribe
// client.subscribe("inTopic");
}
else {
Serial1.print("failed, rc=");
Serial1.print(client.state());
Serial1.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}

View File

@@ -1,9 +0,0 @@
name=HANreader
version=1.0.0
author=roarfred
maintainer=roarfred <not@important.com>
sentence=HAN support
paragraph=HAN support
category=Sensors
url=https://github.com/roarfred/AmsToMqttBridge
architectures=*

View File

@@ -1,19 +0,0 @@
Arduino Compatible Cross Platform C++ Library Project : For more information see http://www.visualmicro.com
This project works exactly the same way as an Arduino library.
Add this project to any solution that contains an Arduino project and #include <headers.h> in code as you would any normal Arduino library headers.
To enable intellisense and to support live build discovery outside of the "standard" Arduino library locations, ensure that the library is added as a shared project reference to the master Arduino project. To do this, right click the master project "References" node and then click "Add Reference". A window will open and the library will appear on the "Shared Projects" tab. Click the checkbox next to the library name to add the reference. If this library is moved the shared referencemust be removed and re-added.
VS2017 has a bug, workround: After moving existing source code within a "library or shared project", close and re-open the solution.
Visual Studio will display intellisense for libraries based on the platform/board that has been specified for the currently active "Startup Project" of the current solution.
IMPORTANT: The arduino.cc Library Rules must be followed when adding code or restructing libraries.
blog: http://www.visualmicro.com/post/2017/01/16/Arduino-Cross-Platform-Library-Development.aspx

View File

@@ -1,181 +0,0 @@
// Aidon.h
#ifndef _AIDON_h
#define _AIDON_h
enum class Aidon
{
List1 = 0x01,
List1PhaseShort = 0x09,
List1PhaseLong = 0xff, // TODO: Need sample
List3PhaseShort = 0x0D,
List3PhaseLong = 0x12
};
enum class Aidon_List1
{
ListSize,
IGN_0,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_1,
ActiveImportPowerInt8,
ActiveImportPowerEnum
};
enum class Aidon_List1Phase
{
ListSize,
IGN_0,
ListVersionIdentifier_OBIS,
ListVersionIdentifier,
IGN_1,
MeterID_OBIS,
MeterID,
IGN_2,
MeterType_OBIS,
MeterType,
IGN_3,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_4,
ActiveImportPowerInt8,
ActiveImportPowerEnum,
IGN_5,
ActiveExportPower_OBIS,
ActiveExportPower,
IGN_6,
ActiveExportPowerInt8,
ActiveExportPowerEnum,
IGN_7,
ReactiveImportPower_OBIS,
ReactiveImportPower,
IGN_8,
ReactiveImportPowerInt8,
ReactiveImportPowerEnum,
IGN_9,
ReactiveExportPower_OBIS,
ReactiveExportPower,
IGN_10,
ReactiveExportPowerInt8,
ReactiveExportPowerEnum,
IGN_11,
CurrentL1_OBIS,
CurrentL1,
IGN_12,
CurrentL1Int8,
CurrentL1Enum,
IGN_13,
VoltageL1_OBIS,
VoltageL1,
IGN_14,
VoltageL1Int8,
VoltageL1Enum,
};
enum class Aidon_List3Phase
{
ListSize,
IGN_0,
ListVersionIdentifier_OBIS,
ListVersionIdentifier,
IGN_1,
MeterID_OBIS,
MeterID,
IGN_2,
MeterType_OBIS,
MeterType,
IGN_3,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_4,
ActiveImportPowerInt8,
ActiveImportPowerEnum,
IGN_5,
ActiveExportPower_OBIS,
ActiveExportPower,
IGN_6,
ActiveExportPowerInt8,
ActiveExportPowerEnum,
IGN_7,
ReactiveImportPower_OBIS,
ReactiveImportPower,
IGN_8,
ReactiveImportPowerInt8,
ReactiveImportPowerEnum,
IGN_9,
ReactiveExportPower_OBIS,
ReactiveExportPower,
IGN_10,
ReactiveExportPowerInt8,
ReactiveExportPowerEnum,
IGN_11,
CurrentL1_OBIS,
CurrentL1,
IGN_12,
CurrentL1Int8,
CurrentL1Enum,
IGN_13,
CurrentL2_OBIS,
CurrentL2,
IGN_14,
CurrentL2Int8,
CurrentL2Enum,
IGN_15,
CurrentL3_OBIS,
CurrentL3,
IGN_16,
CurrentL3Int8,
CurrentL3Enum,
IGN_17,
VoltageL1_OBIS,
VoltageL1,
IGN_18,
VoltageL1Int8,
VoltageL1Enum,
IGN_19,
VoltageL2_OBIS,
VoltageL2,
IGN_20,
VoltageL2Int8,
VoltageL2Enum,
IGN_21,
VoltageL3_OBIS,
VoltageL3,
IGN_22,
VoltageL3Int8,
VoltageL3Enum,
IGN_23,
Timestamp_OBIS,
Timestamp,
IGN_24,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
IGN_25,
CumulativeActiveImportEnergyInt8,
CumulativeActiveImportEnergyEnum,
IGN_26,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
IGN_27,
CumulativeActiveExportEnergyInt8,
CumulativeActiveExportEnergyEnum,
IGN_28,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
IGN_29,
CumulativeReactiveImportEnergyInt8,
CumulativeReactiveImportEnergyEnum,
IGN_30,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy,
IGN_31,
CumulativeReactiveExportEnergyInt8,
CumulativeReactiveExportEnergyEnum
};
#endif

View File

@@ -1,37 +0,0 @@
#include "Crc16.h"
Crc16Class::Crc16Class()
{
unsigned short value;
unsigned short temp;
for (unsigned short i = 0; i < 256; ++i)
{
value = 0;
temp = i;
for (byte j = 0; j < 8; ++j)
{
if (((value ^ temp) & 0x0001) != 0)
{
value = (ushort)((value >> 1) ^ polynomial);
}
else
{
value >>= 1;
}
temp >>= 1;
}
table[i] = value;
}
}
unsigned short Crc16Class::ComputeChecksum(byte *data, int start, int length)
{
ushort fcs = 0xffff;
for (int i = start; i < (start + length); i++)
{
byte index = (fcs ^ data[i]) & 0xff;
fcs = (ushort)((fcs >> 8) ^ table[index]);
}
fcs ^= 0xffff;
return fcs;
}

View File

@@ -1,23 +0,0 @@
#ifndef _CRC16_h
#define _CRC16_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
class Crc16Class
{
public:
Crc16Class();
unsigned short ComputeChecksum(byte *data, int start, int length);
protected:
private:
const unsigned short polynomial = 0x8408;
unsigned short table[256];
};
#endif

View File

@@ -1,154 +0,0 @@
#include "DlmsReader.h"
DlmsReader::DlmsReader()
{
//this->Clear();
}
void DlmsReader::Clear()
{
this->position = 0;
this->dataLength = 0;
this->destinationAddressLength = 0;
this->sourceAddressLength = 0;
this->frameFormatType = 0;
}
bool DlmsReader::Read(byte data)
{
if (position == 0 && data != 0x7E)
{
// we haven't started yet, wait for the start flag (no need to capture any data yet)
return false;
}
else
{
// We have completed reading of one package, so clear and be ready for the next
if (dataLength > 0 && position >= dataLength + 2)
Clear();
// Check if we're about to run into a buffer overflow
if (position >= DLMS_READER_BUFFER_SIZE)
Clear();
// Check if this is a second start flag, which indicates the previous one was a stop from the last package
if (position == 1 && data == 0x7E)
{
// just return, we can keep the one byte we had in the buffer
return false;
}
// We have started, so capture every byte
buffer[position++] = data;
if (position == 1)
{
// This was the start flag, we're not done yet
return false;
}
else if (position == 2)
{
// Capture the Frame Format Type
frameFormatType = (byte)(data & 0xF0);
if (!IsValidFrameFormat(frameFormatType))
Clear();
return false;
}
else if (position == 3)
{
// Capture the length of the data package
dataLength = ((buffer[1] & 0x0F) << 8) | buffer[2];
return false;
}
else if (destinationAddressLength == 0)
{
// Capture the destination address
destinationAddressLength = GetAddress(3, destinationAddress, 0, DLMS_READER_MAX_ADDRESS_SIZE);
if (destinationAddressLength > 3)
Clear();
return false;
}
else if (sourceAddressLength == 0)
{
// Capture the source address
sourceAddressLength = GetAddress(3 + destinationAddressLength, sourceAddress, 0, DLMS_READER_MAX_ADDRESS_SIZE);
if (sourceAddressLength > 3)
Clear();
return false;
}
else if (position == 4 + destinationAddressLength + sourceAddressLength + 2)
{
// Verify the header checksum
ushort headerChecksum = GetChecksum(position - 3);
if (headerChecksum != Crc16.ComputeChecksum(buffer, 1, position - 3))
Clear();
return false;
}
else if (position == dataLength + 1)
{
// Verify the data package checksum
ushort checksum = this->GetChecksum(position - 3);
if (checksum != Crc16.ComputeChecksum(buffer, 1, position - 3))
Clear();
return false;
}
else if (position == dataLength + 2)
{
// We're done, check the stop flag and signal we're done
if (data == 0x7E)
return true;
else
{
Clear();
return false;
}
}
}
return false;
}
bool DlmsReader::IsValidFrameFormat(byte frameFormatType)
{
return frameFormatType == 0xA0;
}
int DlmsReader::GetRawData(byte *dataBuffer, int start, int length)
{
if (dataLength > 0 && position == dataLength + 2)
{
int headerLength = 3 + destinationAddressLength + sourceAddressLength + 2;
int bytesWritten = 0;
for (int i = headerLength + 1; i < dataLength - 1; i++)
{
dataBuffer[i + start - headerLength - 1] = buffer[i];
bytesWritten++;
}
return bytesWritten;
}
else
return 0;
}
int DlmsReader::GetAddress(int addressPosition, byte* addressBuffer, int start, int length)
{
int addressBufferPos = start;
for (int i = addressPosition; i < position; i++)
{
addressBuffer[addressBufferPos++] = buffer[i];
// LSB=1 means this was the last address byte
if ((buffer[i] & 0x01) == 0x01)
break;
// See if we've reached last byte, try again when we've got more data
else if (i == position - 1)
return 0;
}
return addressBufferPos - start;
}
ushort DlmsReader::GetChecksum(int checksumPosition)
{
return (ushort)(buffer[checksumPosition + 2] << 8 |
buffer[checksumPosition + 1]);
}

View File

@@ -1,42 +0,0 @@
#ifndef _DLMSREADER_h
#define _DLMSREADER_h
#include "Crc16.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define DLMS_READER_BUFFER_SIZE 512
#define DLMS_READER_MAX_ADDRESS_SIZE 5
class DlmsReader
{
public:
DlmsReader();
bool Read(byte data);
int GetRawData(byte *buffer, int start, int length);
protected:
Crc16Class Crc16;
private:
byte buffer[DLMS_READER_BUFFER_SIZE];
int position;
int dataLength;
byte frameFormatType;
byte destinationAddress[DLMS_READER_MAX_ADDRESS_SIZE];
byte destinationAddressLength;
byte sourceAddress[DLMS_READER_MAX_ADDRESS_SIZE];
byte sourceAddressLength;
void Clear();
int GetAddress(int addressPosition, byte* buffer, int start, int length);
unsigned short GetChecksum(int checksumPosition);
bool IsValidFrameFormat(byte frameFormatType);
void WriteBuffer();
};
#endif

View File

@@ -1,288 +0,0 @@
#include "HanReader.h"
HanReader::HanReader()
{
}
void HanReader::setup(HardwareSerial *hanPort, Stream *debugPort)
{
han = hanPort;
bytesRead = 0;
debug = debugPort;
if (debug) debug->println("MBUS serial setup complete");
}
void HanReader::setup(HardwareSerial *hanPort)
{
setup(hanPort, NULL);
}
bool HanReader::read(byte data)
{
if (reader.Read(data))
{
bytesRead = reader.GetRawData(buffer, 0, 512);
if (debug)
{
debug->print("Got valid DLMS data (");
debug->print(bytesRead);
debug->println(" bytes):");
debugPrint(buffer, 0, bytesRead);
}
/*
Data should start with E6 E7 00 0F
and continue with four bytes for the InvokeId
*/
if (bytesRead < 9)
{
if (debug) debug->println("Invalid HAN data: Less than 9 bytes received");
return false;
}
else if (
buffer[0] != 0xE6 ||
buffer[1] != 0xE7 ||
buffer[2] != 0x00 ||
buffer[3] != 0x0F
)
{
if (debug) debug->println("Invalid HAN data: Start should be E6 E7 00 0F");
return false;
}
listSize = getInt(0, buffer, 0, bytesRead);
if (debug) debug->print("HAN data is valid, listSize: ");
if (debug) debug->println(listSize);
return true;
}
return false;
}
void HanReader::debugPrint(byte *buffer, int start, int length)
{
for (int i = start; i < start + length; i++)
{
if (buffer[i] < 0x10)
debug->print("0");
debug->print(buffer[i], HEX);
debug->print(" ");
if ((i - start + 1) % 16 == 0)
debug->println("");
else if ((i - start + 1) % 4 == 0)
debug->print(" ");
yield(); // Let other get some resources too
}
debug->println("");
}
bool HanReader::read()
{
if (han->available())
{
byte newByte = han->read();
return read(newByte);
}
return false;
}
int HanReader::getListSize()
{
return listSize;
}
time_t HanReader::getPackageTime()
{
int packageTimePosition = dataHeader
+ (compensateFor09HeaderBug ? 1 : 0);
return getTime(buffer, packageTimePosition, bytesRead);
}
time_t HanReader::getTime(int objectId)
{
return getTime(objectId, buffer, 0, bytesRead);
}
int HanReader::getInt(int objectId)
{
return getInt(objectId, buffer, 0, bytesRead);
}
String HanReader::getString(int objectId)
{
return getString(objectId, buffer, 0, bytesRead);
}
int HanReader::findValuePosition(int dataPosition, byte *buffer, int start, int length)
{
// The first byte after the header gives the length
// of the extended header information (variable)
int headerSize = dataHeader + (compensateFor09HeaderBug ? 1 : 0);
int firstData = headerSize + buffer[headerSize] + 1;
for (int i = start + firstData; i<length; i++)
{
if (dataPosition-- == 0)
return i;
else if (buffer[i] == 0x00) // null
i += 0;
else if (buffer[i] == 0x0A) // String
i += buffer[i + 1] + 1;
else if (buffer[i] == 0x09) // byte array
i += buffer[i + 1] + 1;
else if (buffer[i] == 0x01) // array (1 byte for reading size)
i += 1;
else if (buffer[i] == 0x02) // struct (1 byte for reading size)
i += 1;
else if (buffer[i] == 0x10) // int16 value (2 bytes)
i += 2;
else if (buffer[i] == 0x12) // uint16 value (2 bytes)
i += 2;
else if (buffer[i] == 0x06) // uint32 value (4 bytes)
i += 4;
else if (buffer[i] == 0x0F) // int8 value (1 bytes)
i += 1;
else if (buffer[i] == 0x16) // enum (1 bytes)
i += 1;
else
{
if (debug)
{
debug->print("Unknown data type found: 0x");
debug->println(buffer[i], HEX);
}
return 0; // unknown data type found
}
}
if (debug)
{
debug->print("Passed the end of the data. Length was: ");
debug->println(length);
}
return 0;
}
time_t HanReader::getTime(int dataPosition, byte *buffer, int start, int length)
{
// TODO: check if the time is represented always as a 12 byte string (0x09 0x0C)
int timeStart = findValuePosition(dataPosition, buffer, start, length);
timeStart += 1;
return getTime(buffer, start + timeStart, length - timeStart);
}
time_t HanReader::getTime(byte *buffer, int start, int length)
{
int pos = start;
int dataLength = buffer[pos++];
if (dataLength == 0x0C)
{
int year = buffer[pos] << 8 |
buffer[pos + 1];
int month = buffer[pos + 2];
int day = buffer[pos + 3];
int hour = buffer[pos + 5];
int minute = buffer[pos + 6];
int second = buffer[pos + 7];
return toUnixTime(year, month, day, hour, minute, second);
}
else
{
// Date format not supported
return (time_t)0L;
}
}
int HanReader::getInt(int dataPosition, byte *buffer, int start, int length)
{
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0)
{
int value = 0;
int bytes = 0;
switch (buffer[valuePosition++])
{
case 0x10:
bytes = 2;
break;
case 0x12:
bytes = 2;
break;
case 0x06:
bytes = 4;
break;
case 0x02:
bytes = 1;
break;
case 0x01:
bytes = 1;
break;
case 0x0F:
bytes = 1;
break;
case 0x16:
bytes = 1;
break;
}
for (int i = valuePosition; i < valuePosition + bytes; i++)
{
value = value << 8 | buffer[i];
}
return value;
}
return 0;
}
String HanReader::getString(int dataPosition, byte *buffer, int start, int length)
{
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0)
{
String value = String("");
for (int i = valuePosition + 2; i < valuePosition + buffer[valuePosition + 1] + 2; i++)
{
value += String((char)buffer[i]);
}
return value;
}
return String("");
}
time_t HanReader::toUnixTime(int year, int month, int day, int hour, int minute, int second)
{
byte daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
long secondsPerMinute = 60;
long secondsPerHour = secondsPerMinute * 60;
long secondsPerDay = secondsPerHour * 24;
long time = (year - 1970) * secondsPerDay * 365L;
for (int yearCounter = 1970; yearCounter<year; yearCounter++)
if ((yearCounter % 4 == 0) && ((yearCounter % 100 != 0) || (yearCounter % 400 == 0)))
time += secondsPerDay;
if (month > 2 && (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))
time += secondsPerDay;
for (int monthCounter = 1; monthCounter<month; monthCounter++)
time += daysInMonth[monthCounter - 1] * secondsPerDay;
time += (day - 1) * secondsPerDay;
time += hour * secondsPerHour;
time += minute * secondsPerMinute;
time += second;
return (time_t)time;
}

View File

@@ -1,52 +0,0 @@
#ifndef _HANREADER_h
#define _HANREADER_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "DlmsReader.h"
class HanReader
{
public:
const uint dataHeader = 8;
bool compensateFor09HeaderBug = false;
HanReader();
void setup(HardwareSerial *hanPort);
void setup(HardwareSerial *hanPort, Stream *debugPort);
bool read();
bool read(byte data);
int getListSize();
time_t getPackageTime();
int getInt(int objectId);
String getString(int objectId);
time_t getTime(int objectId);
private:
Stream *debug;
HardwareSerial *han;
byte buffer[512];
int bytesRead;
DlmsReader reader;
int listSize;
int findValuePosition(int dataPosition, byte *buffer, int start, int length);
time_t getTime(int dataPosition, byte *buffer, int start, int length);
time_t getTime(byte *buffer, int start, int length);
int getInt(int dataPosition, byte *buffer, int start, int length);
String getString(int dataPosition, byte *buffer, int start, int length);
time_t toUnixTime(int year, int month, int day, int hour, int minute, int second);
void debugPrint(byte *buffer, int start, int length);
};
#endif

View File

@@ -1,57 +0,0 @@
#ifndef _KAIFA_h
#define _KAIFA_h
enum class Kaifa : byte {
List1 = 0x01,
List1PhaseShort = 0x09,
List3PhaseShort = 0x0D,
List1PhaseLong = 0x0E,
List3PhaseLong = 0x12
};
enum class Kaifa_List1 {
ListSize,
ActivePowerImported
};
enum class Kaifa_List3Phase {
ListSize,
ListVersionIdentifier,
MeterID,
MeterType,
ActiveImportPower,
ActiveExportPower,
ReactiveImportPower,
ReactiveExportPower,
CurrentL1,
CurrentL2,
CurrentL3,
VoltageL1,
VoltageL2,
VoltageL3,
MeterClock,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy
};
enum class Kaifa_List1Phase {
ListSize,
ListVersionIdentifier,
MeterID,
MeterType,
ActiveImportPower,
ActiveExportPower,
ReactiveImportPower,
ReactiveExportPower,
CurrentL1,
VoltageL1,
MeterClock,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy
};
#endif

View File

@@ -1,88 +0,0 @@
// Kamstrup.h
#ifndef _KAMSTRUP_h
#define _KAMSTRUP_h
enum class Kamstrup
{
List3PhaseShort = 0x19,
List3PhaseLong = 0x23,
List1PhaseShort = 0x11,
List1PhaseLong = 0x1B
};
enum class Kamstrup_List3Phase
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
CurrentL2_OBIS,
CurrentL2,
CurrentL3_OBIS,
CurrentL3,
VoltageL1_OBIS,
VoltageL1,
VoltageL2_OBIS,
VoltageL2,
VoltageL3_OBIS,
VoltageL3,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
enum class Kamstrup_List1Phase
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
VoltageL1_OBIS,
VoltageL1,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
#endif

View File

@@ -1,9 +0,0 @@
name=HanToJson
version=1.0.0
author=roarfred
maintainer=roarfred <not@important.com>
sentence=HAN reader data to Json
paragraph=HAN reader data to Json
category=Sensors
url=https://github.com/roarfred/AmsToMqttBridge
architectures=*

View File

@@ -1,240 +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["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["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["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["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::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["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);
}
// TODO: Do not divide Aidon values by 10!?
}
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::ReactiveExportPower);
data["I1"] = ((double) hanReader.getInt( (int)Aidon_List1Phase::CurrentL1)) / 10;
data["U1"] = ((double) hanReader.getInt( (int)Aidon_List1Phase::VoltageL1)) / 10;
}
// TODO Aidon::List1PhaseLong
}
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);
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["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["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["I1"] = ((double) hanReader.getInt((int)Kamstrup_List1Phase::CurrentL1)) / 100;
data["U1"] = hanReader.getInt( (int)Kamstrup_List1Phase::VoltageL1);
}
if (listSize >= (int)Kamstrup::List1PhaseLong)
{
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);
}

View File

@@ -1,17 +0,0 @@
#ifndef _HANTOJSON_h
#define _HANTOJSON_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <ArduinoJson.h>
#include "HanReader.h"
void hanToJson(JsonObject& data, byte meterType, HanReader& hanReader);
void hanToJson(JsonObject& root, byte meterType, HanReader& hanReader, Stream *debugPort);
#endif

View File

@@ -4,10 +4,18 @@ default_envs = dev
[env:dev]
platform = espressif8266
board = esp12e
framework = ${common.framework}
board_build.ldscript = eagle.flash.4m2m.ld
framework = arduino
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
lib_compat_mode = off
build_flags =
-D HAS_DALLAS_TEMP_SENSOR=0
-D IS_CUSTOM_AMS_BOARD=0
monitor_speed = 2400
monitor_flags = --parity E
-D WEBSOCKET_DISABLED=1
-D DEBUG_MODE=1
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py
monitor_speed = 115200 ; If serial port is shared with HAN, use baud and parity configured for meter
monitor_flags =
--parity
N

View File

@@ -2,32 +2,32 @@
extra_configs = platformio-user.ini
[common]
lib_deps = Timezone@1.2.4, 256dpi/MQTT@2.5.0, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1
lib_ignore = OneWire
[env:esp8266]
platform = espressif8266@3.2.0
board = esp12e
board_build.ldscript = eagle.flash.4m2m.ld
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
[env:esp12e]
platform = espressif8266
board = esp12e
framework = ${common.framework}
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py
build_flags =
-D HAS_DALLAS_TEMP_SENSOR=0
-D IS_CUSTOM_AMS_BOARD=0
-D WEBSOCKET_DISABLED=1
[env:hw1esp12e]
platform = espressif8266
board = esp12e
framework = ${common.framework}
lib_deps = ${common.lib_deps}
build_flags =
-D HAS_DALLAS_TEMP_SENSOR=1
-D IS_CUSTOM_AMS_BOARD=1
[env:featheresp32]
platform = espressif32
board = featheresp32
framework = ${common.framework}
[env:esp32]
platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
board = esp32dev
framework = arduino
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
extra_scripts =
pre:scripts/addversion.py
scripts/makeweb.py
build_flags =
-D HAS_DALLAS_TEMP_SENSOR=0
-D IS_CUSTOM_AMS_BOARD=0
-D WEBSOCKET_DISABLED=1
board_build.f_cpu = 160000000L

16
scripts/addversion.py Normal file
View File

@@ -0,0 +1,16 @@
import os
FILENAME_VERSION_H = 'src/version.h'
version = os.environ.get('GITHUB_TAG')
if version == None:
version = "SNAPSHOT"
import datetime
hf = """
#ifndef VERSION
#define VERSION "{}"
#endif
""".format(version)
with open(FILENAME_VERSION_H, 'w+') as f:
f.write(hf)

72
scripts/makeweb.py Normal file
View File

@@ -0,0 +1,72 @@
import os
import re
import shutil
try:
from css_html_js_minify import html_minify, js_minify, css_minify
except:
from SCons.Script import (
ARGUMENTS,
COMMAND_LINE_TARGETS,
DefaultEnvironment,
)
env = DefaultEnvironment()
env.Execute(
env.VerboseAction(
'$PYTHONEXE -m pip install "css_html_js_minify" ',
"Installing Python dependencies",
)
)
try:
from css_html_js_minify import html_minify, js_minify, css_minify
except:
print("WARN: Unable to load minifier")
webroot = "web"
srcroot = "src/web/root"
version = os.environ.get('GITHUB_TAG')
if version == None:
version = "SNAPSHOT"
if os.path.exists(srcroot):
shutil.rmtree(srcroot)
os.mkdir(srcroot)
else:
os.mkdir(srcroot)
for filename in os.listdir(webroot):
basename = re.sub("[^0-9a-zA-Z]+", "_", filename)
srcfile = webroot + "/" + filename
dstfile = srcroot + "/" + basename + ".h"
varname = basename.upper()
with open(srcfile, encoding="utf-8") as f:
content = f.read().replace("${version}", version)
try:
if filename.endswith(".html"):
content = html_minify(content)
elif filename.endswith(".css"):
content = css_minify(content)
elif (filename.endswith(".js") and filename != 'gaugemeter.js') or filename.endswith(".json"):
content = js_minify(content)
except:
print("WARN: Unable to minify")
with open(dstfile, "w") as dst:
dst.write("const char ")
dst.write(varname)
dst.write("[] PROGMEM = R\"==\"==(")
dst.write(content)
dst.write(")==\"==\";\n")
dst.write("const int ");
dst.write(varname)
dst.write("_LEN PROGMEM = ");
dst.write(str(len(content)))
dst.write(";");

1068
src/AmsConfiguration.cpp Normal file

File diff suppressed because it is too large Load Diff

322
src/AmsConfiguration.h Normal file
View File

@@ -0,0 +1,322 @@
#ifndef _AMSCONFIGURATION_h
#define _AMSCONFIGURATION_h
#include <EEPROM.h>
#include "Arduino.h"
#define EEPROM_SIZE 1024*3
#define EEPROM_CHECK_SUM 92 // Used to check if config is stored. Change if structure changes
#define EEPROM_CONFIG_ADDRESS 0
#define EEPROM_TEMP_CONFIG_ADDRESS 2048
#define CONFIG_SYSTEM_START 8
#define CONFIG_METER_START 224
#define CONFIG_GPIO_START 266
#define CONFIG_ENTSOE_START 290
#define CONFIG_WIFI_START 360
#define CONFIG_WEB_START 648
#define CONFIG_DEBUG_START 824
#define CONFIG_DOMOTICZ_START 856
#define CONFIG_NTP_START 872
#define CONFIG_MQTT_START 1004
#define CONFIG_MQTT_START_86 224
#define CONFIG_METER_START_87 784
#define CONFIG_ENTSOE_START_90 286
#define CONFIG_WIFI_START_91 16
struct SystemConfig {
uint8_t boardType;
}; // 1
struct WiFiConfig91 {
char ssid[32];
char psk[64];
char ip[15];
char gateway[15];
char subnet[15];
char dns1[15];
char dns2[15];
char hostname[32];
bool mdns;
}; // 204
struct WiFiConfig {
char ssid[32];
char psk[64];
char ip[16];
char gateway[16];
char subnet[16];
char dns1[16];
char dns2[16];
char hostname[32];
bool mdns;
}; // 209
struct MqttConfig86 {
char host[128];
uint16_t port;
char clientId[32];
char publishTopic[64];
char subscribeTopic[64];
char username[64];
char password[64];
uint8_t payloadFormat;
bool ssl;
}; // 420
struct MqttConfig {
char host[128];
uint16_t port;
char clientId[32];
char publishTopic[64];
char subscribeTopic[64];
char username[128];
char password[256];
uint8_t payloadFormat;
bool ssl;
}; // 676
struct WebConfig {
uint8_t security;
char username[64];
char password[64];
}; // 129
struct MeterConfig {
uint32_t baud;
uint8_t parity;
bool invert;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
uint8_t encryptionKey[16];
uint8_t authenticationKey[16];
}; // 41
struct MeterConfig87 {
uint8_t type;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
uint8_t encryptionKey[16];
uint8_t authenticationKey[16];
bool substituteMissing;
}; // 37
struct DebugConfig {
bool telnet;
bool serial;
uint8_t level;
}; // 3
struct GpioConfig {
uint8_t hanPin;
uint8_t apPin;
uint8_t ledPin;
bool ledInverted;
uint8_t ledPinRed;
uint8_t ledPinGreen;
uint8_t ledPinBlue;
bool ledRgbInverted;
uint8_t tempSensorPin;
uint8_t tempAnalogSensorPin;
uint8_t vccPin;
int16_t vccOffset;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
uint16_t vccResistorGnd;
uint16_t vccResistorVcc;
}; // 20
struct DomoticzConfig {
uint16_t elidx;
uint16_t vl1idx;
uint16_t vl2idx;
uint16_t vl3idx;
uint16_t cl1idx;
}; // 10
struct NtpConfig {
bool enable;
bool dhcp;
int16_t offset;
int16_t summerOffset;
char server[64];
}; // 70
struct EntsoeConfig {
char token[37];
char area[17];
char currency[4];
uint32_t multiplier;
}; // 62
struct ConfigObject83 {
uint8_t boardType;
char wifiSsid[32];
char wifiPassword[64];
char wifiIp[15];
char wifiGw[15];
char wifiSubnet[15];
char wifiDns1[15];
char wifiDns2[15];
char wifiHostname[32];
char mqttHost[128];
uint16_t mqttPort;
char mqttClientId[32];
char mqttPublishTopic[64];
char mqttSubscribeTopic[64];
char mqttUser[64];
char mqttPassword[64];
uint8_t mqttPayloadFormat;
bool mqttSsl;
uint8_t authSecurity;
char authUser[64];
char authPassword[64];
uint8_t meterType;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
uint8_t meterEncryptionKey[16];
uint8_t meterAuthenticationKey[16];
bool substituteMissing;
bool sendUnknown;
bool debugTelnet;
bool debugSerial;
uint8_t debugLevel;
uint8_t hanPin;
uint8_t apPin;
uint8_t ledPin;
bool ledInverted;
uint8_t ledPinRed;
uint8_t ledPinGreen;
uint8_t ledPinBlue;
bool ledRgbInverted;
uint8_t tempSensorPin;
uint8_t vccPin;
int16_t vccOffset;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
uint16_t domoELIDX;
uint16_t domoVL1IDX;
uint16_t domoVL2IDX;
uint16_t domoVL3IDX;
uint16_t domoCL1IDX;
bool mDnsEnable;
bool ntpEnable;
bool ntpDhcp;
int16_t ntpOffset;
int16_t ntpSummerOffset;
char ntpServer[64];
uint8_t tempAnalogSensorPin;
};
struct TempSensorConfig {
uint8_t address[8];
char name[16];
bool common;
};
class AmsConfiguration {
public:
bool hasConfig();
int getConfigVersion();
bool save();
bool getSystemConfig(SystemConfig&);
bool setSystemConfig(SystemConfig&);
bool getWiFiConfig(WiFiConfig&);
bool setWiFiConfig(WiFiConfig&);
void clearWifi(WiFiConfig&);
void clearWifiIp(WiFiConfig&);
bool isWifiChanged();
void ackWifiChange();
bool getMqttConfig(MqttConfig&);
bool setMqttConfig(MqttConfig&);
void clearMqtt(MqttConfig&);
void setMqttChanged();
bool isMqttChanged();
void ackMqttChange();
bool getWebConfig(WebConfig&);
bool setWebConfig(WebConfig&);
void clearAuth(WebConfig&);
bool getMeterConfig(MeterConfig&);
bool setMeterConfig(MeterConfig&);
void clearMeter(MeterConfig&);
bool isMeterChanged();
void ackMeterChanged();
bool getDebugConfig(DebugConfig&);
bool setDebugConfig(DebugConfig&);
void clearDebug(DebugConfig&);
bool pinUsed(uint8_t, GpioConfig&);
bool getGpioConfig(GpioConfig&);
bool setGpioConfig(GpioConfig&);
void clearGpio(GpioConfig&);
void print(Print* debugger);
bool getDomoticzConfig(DomoticzConfig&);
bool setDomoticzConfig(DomoticzConfig&);
void clearDomo(DomoticzConfig&);
bool isDomoChanged();
void ackDomoChange();
bool getNtpConfig(NtpConfig&);
bool setNtpConfig(NtpConfig&);
void clearNtp(NtpConfig&);
bool isNtpChanged();
void ackNtpChange();
bool getEntsoeConfig(EntsoeConfig&);
bool setEntsoeConfig(EntsoeConfig&);
void clearEntsoe(EntsoeConfig&);
bool isEntsoeChanged();
void ackEntsoeChange();
void loadTempSensors();
void saveTempSensors();
uint8_t getTempSensorCount();
TempSensorConfig* getTempSensorConfig(uint8_t address[8]);
void updateTempSensorConfig(uint8_t address[8], const char name[32], bool common);
bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]);
void clear();
protected:
private:
uint8_t configVersion = 0;
bool wifiChanged, mqttChanged, meterChanged = true, domoChanged, ntpChanged = true, entsoeChanged = false;
uint8_t tempSensorCount = 0;
TempSensorConfig** tempSensors = NULL;
bool loadConfig83(int address);
bool relocateConfig86();
bool relocateConfig87();
bool relocateConfig90(); // 2.0.0
bool relocateConfig91(); // 2.0.2
int readString(int pAddress, char* pString[]);
int readInt(int pAddress, int *pValue);
int readBool(int pAddress, bool *pValue);
int readByte(int pAddress, byte *pValue);
};
#endif

185
src/AmsData.cpp Normal file
View File

@@ -0,0 +1,185 @@
#include "AmsData.h"
AmsData::AmsData() {}
void AmsData::apply(AmsData& other) {
if(other.getListType() < 3) {
unsigned long ms = this->lastUpdateMillis > other.getLastUpdateMillis() ? 0 : other.getLastUpdateMillis() - this->lastUpdateMillis;
if(ms > 0) {
if(other.getActiveImportPower() > 0) {
float add = other.getActiveImportPower() * (((float) ms) / 3600000.0);
activeImportCounter += add / 1000.0;
//Serial.printf("%dW, %dms, %.6fkWh added\n", other.getActiveImportPower(), ms, add);
}
if(other.getListType() > 1) {
ms = this->lastUpdateMillis > other.getLastUpdateMillis() ? 0 : other.getLastUpdateMillis() - this->lastList2;
if(other.getActiveExportPower() > 0) {
float add = other.getActiveExportPower() * (((float) ms) / 3600000.0);
activeExportCounter += add / 1000.0;
}
if(other.getReactiveImportPower() > 0) {
float add = other.getReactiveImportPower() * (((float) ms) / 3600000.0);
reactiveImportCounter += add / 1000.0;
}
if(other.getReactiveExportPower() > 0) {
float add = other.getReactiveExportPower() * (((float) ms) / 3600000.0);
reactiveExportCounter += add / 1000.0;
}
}
counterEstimated = true;
}
}
this->lastUpdateMillis = other.getLastUpdateMillis();
if(other.getListType() > 1) {
this->lastList2 = this->lastUpdateMillis;
}
this->packageTimestamp = other.getPackageTimestamp();
if(other.getListType() > this->listType)
this->listType = other.getListType();
switch(other.getListType()) {
case 3:
this->powerFactor = other.getPowerFactor();
this->l1PowerFactor = other.getL1PowerFactor();
this->l2PowerFactor = other.getL2PowerFactor();
this->l3PowerFactor = other.getL3PowerFactor();
this->meterTimestamp = other.getMeterTimestamp();
this->activeImportCounter = other.getActiveImportCounter();
this->activeExportCounter = other.getActiveExportCounter();
this->reactiveImportCounter = other.getReactiveImportCounter();
this->reactiveExportCounter = other.getReactiveExportCounter();
this->counterEstimated = false;
case 2:
this->listId = other.getListId();
this->meterId = other.getMeterId();
this->meterType = other.getMeterType();
this->meterModel = other.getMeterModel();
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();
this->twoPhase = other.isTwoPhase();
case 1:
this->activeImportPower = other.getActiveImportPower();
}
}
unsigned long AmsData::getLastUpdateMillis() {
return this->lastUpdateMillis;
}
time_t AmsData::getPackageTimestamp() {
return this->packageTimestamp;
}
uint8_t AmsData::getListType() {
return this->listType;
}
String AmsData::getListId() {
return this->listId;
}
String AmsData::getMeterId() {
return this->meterId;
}
uint8_t AmsData::getMeterType() {
return this->meterType;
}
String AmsData::getMeterModel() {
return this->meterModel;
}
time_t AmsData::getMeterTimestamp() {
return this->meterTimestamp;
}
uint16_t AmsData::getActiveImportPower() {
return this->activeImportPower;
}
uint16_t AmsData::getReactiveImportPower() {
return this->reactiveImportPower;
}
uint16_t AmsData::getActiveExportPower() {
return this->activeExportPower;
}
uint16_t AmsData::getReactiveExportPower() {
return this->reactiveExportPower;
}
float AmsData::getL1Voltage() {
return this->l1voltage;
}
float AmsData::getL2Voltage() {
return this->l2voltage;
}
float AmsData::getL3Voltage() {
return this->l3voltage;
}
float AmsData::getL1Current() {
return this->l1current;
}
float AmsData::getL2Current() {
return this->l2current;
}
float AmsData::getL3Current() {
return this->l3current;
}
float AmsData::getPowerFactor() {
return this->powerFactor;
}
float AmsData::getL1PowerFactor() {
return this->l1PowerFactor;
}
float AmsData::getL2PowerFactor() {
return this->l2PowerFactor;
}
float AmsData::getL3PowerFactor() {
return this->l3PowerFactor;
}
float AmsData::getActiveImportCounter() {
return this->activeImportCounter;
}
float AmsData::getReactiveImportCounter() {
return this->reactiveImportCounter;
}
float AmsData::getActiveExportCounter() {
return this->activeExportCounter;
}
float AmsData::getReactiveExportCounter() {
return this->reactiveExportCounter;
}
bool AmsData::isThreePhase() {
return this->threePhase;
}
bool AmsData::isTwoPhase() {
return this->twoPhase;
}

78
src/AmsData.h Normal file
View File

@@ -0,0 +1,78 @@
#ifndef _AMSDATA_H
#define _AMSDATA_H
#include "Arduino.h"
#include <Timezone.h>
enum AmsType {
AmsTypeAutodetect = 0x00,
AmsTypeAidon = 0x01,
AmsTypeKaifa = 0x02,
AmsTypeKamstrup = 0x03,
AmsTypeIskra = 0x08,
AmsTypeLandis = 0x09,
AmsTypeSagemcom = 0x0A,
AmsTypeCustom = 0x88,
AmsTypeUnknown = 0xFF
};
class AmsData {
public:
AmsData();
void apply(AmsData& other);
unsigned long getLastUpdateMillis();
time_t getPackageTimestamp();
uint8_t getListType();
String getListId();
String getMeterId();
uint8_t getMeterType();
String getMeterModel();
time_t getMeterTimestamp();
uint16_t getActiveImportPower();
uint16_t getReactiveImportPower();
uint16_t getActiveExportPower();
uint16_t getReactiveExportPower();
float getL1Voltage();
float getL2Voltage();
float getL3Voltage();
float getL1Current();
float getL2Current();
float getL3Current();
float getPowerFactor();
float getL1PowerFactor();
float getL2PowerFactor();
float getL3PowerFactor();
float getActiveImportCounter();
float getReactiveImportCounter();
float getActiveExportCounter();
float getReactiveExportCounter();
bool isThreePhase();
bool isTwoPhase();
protected:
unsigned long lastUpdateMillis = 0;
unsigned long lastList2 = 0;
uint8_t listType = 0, meterType = AmsTypeUnknown;
time_t packageTimestamp = 0;
String listId, meterId, meterModel;
time_t meterTimestamp = 0;
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
float activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
bool threePhase = false, twoPhase = false, counterEstimated = false;
};
#endif

343
src/AmsDataStorage.cpp Normal file
View File

@@ -0,0 +1,343 @@
#include "AmsDataStorage.h"
#include <lwip/apps/sntp.h>
#include "EEPROM.h"
#include "LittleFS.h"
#include "AmsStorage.h"
AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) {
day.version = 3;
month.version = 4;
this->debugger = debugger;
}
void AmsDataStorage::setTimezone(Timezone* tz) {
this->tz = tz;
}
bool AmsDataStorage::update(AmsData* data) {
time_t now = time(nullptr);
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Time is: %d\n", now);
}
if(now < EPOCH_2021_01_01) {
if(data->getMeterTimestamp() > 0) {
now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using meter timestamp, which is: %d\n", now);
}
} else if(data->getPackageTimestamp() > 0) {
now = data->getPackageTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using package timestamp, which is: %d\n", now);
}
}
}
if(now < EPOCH_2021_01_01) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Invalid time: %d\n", now);
}
return false;
}
if(now-day.lastMeterReadTime < 3595) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) It is only %d seconds since last update, ignoring\n", (now-day.lastMeterReadTime));
}
return false;
}
tmElements_t tm, last;
breakTime(now, tm);
if(day.lastMeterReadTime > EPOCH_2021_01_01) {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last day update: %d\n", day.lastMeterReadTime);
}
breakTime(day.lastMeterReadTime, last);
for(int i = last.Hour; i < tm.Hour; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Clearing hour: %d\n", i);
}
setHour(i, 0);
}
}
if(month.lastMeterReadTime > EPOCH_2021_01_01) {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month update: %d\n", month.lastMeterReadTime);
}
if(tz != NULL) {
breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(month.lastMeterReadTime), last);
} else {
breakTime(now, tm);
breakTime(month.lastMeterReadTime, last);
}
for(int i = last.Day; i < tm.Day; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Clearing day: %d\n", i);
}
setDay(i, 0);
}
}
if(day.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for day plot, resetting\n");
}
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
}
if(data->getListType() != 3) return false;
else if(tm.Minute > 5) return false;
// Update day plot
if(day.activeImport == 0 || now - day.lastMeterReadTime > 86400) {
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last day update, clearing data\n");
}
for(int i = 0; i<24; i++) {
setHour(i, 0);
}
} else if(now - day.lastMeterReadTime < 4000) {
breakTime(now - 3600, tm);
int16_t val = (((data->getActiveImportCounter() * 1000) - day.activeImport) - ((data->getActiveExportCounter() * 1000) - day.activeExport));
setHour(tm.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Usage for hour %d: %d\n", tm.Hour, val);
}
day.activeImport = data->getActiveImportCounter() * 1000;
day.activeExport = data->getActiveExportCounter() * 1000;
day.lastMeterReadTime = now;
} else {
float mins = (now - day.lastMeterReadTime) / 60.0;
uint16_t im = ((data->getActiveImportCounter() * 1000) - day.activeImport);
uint16_t ex = ((data->getActiveExportCounter() * 1000) - day.activeExport);
float ipm = im / mins;
float epm = ex / mins;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last day update, minutes: %.1f, import: %d (%.2f/min), export: %d (%.2f/min)\n", mins, im, ipm, ex, epm);
}
breakTime(day.lastMeterReadTime, tm);
day.lastMeterReadTime = day.lastMeterReadTime - (tm.Minute * 60) - tm.Second;
breakTime(now, tm);
time_t stopAt = now - (tm.Minute * 60) - tm.Second;
while(day.lastMeterReadTime < stopAt) {
time_t cur = min(day.lastMeterReadTime + 3600, stopAt);
uint8_t minutes = round((cur - day.lastMeterReadTime) / 60.0);
if(minutes < 1) break;
breakTime(day.lastMeterReadTime, last);
float val = ((ipm * minutes) - (epm * minutes));
setHour(last.Hour, val);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f (%lu)\n", last.Hour, val, cur);
}
day.activeImport += ipm * minutes;
day.activeExport += epm * minutes;
day.lastMeterReadTime = cur;
}
}
// Update month plot
if(tz != NULL) {
breakTime(tz->toLocal(now), tm);
} else {
breakTime(now, tm);
}
if(month.lastMeterReadTime > now) {
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Invalid future timestamp for month plot, resetting\n");
}
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
month.lastMeterReadTime = now;
}
if(tm.Hour == 0 && now - month.lastMeterReadTime > 86300) {
if(month.activeImport == 0 || now - month.lastMeterReadTime > 2678400) {
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
month.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf("(AmsDataStorage) Too long since last month update, clearing data\n");
}
for(int i = 1; i<=31; i++) {
setDay(i, 0);
}
} else if(now - month.lastMeterReadTime < 87000) {
int32_t val = (month.activeImport == 0 ? 0 : ((data->getActiveImportCounter() * 1000) - month.activeImport) - ((data->getActiveExportCounter() * 1000) - month.activeExport));
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Usage for day %d: %d\n", tm.Day, val);
}
time_t yesterday = now - 3600;
breakTime(yesterday, tm);
setDay(tm.Day, val);
month.activeImport = data->getActiveImportCounter() * 1000;
month.activeExport = data->getActiveExportCounter() * 1000;
month.lastMeterReadTime = now;
} else {
float hrs = (now - month.lastMeterReadTime) / 3600.0;
uint16_t im = ((data->getActiveImportCounter() * 1000) - month.activeImport);
uint16_t ex = ((data->getActiveExportCounter() * 1000) - month.activeExport);
float iph = im / hrs;
float eph = ex / hrs;
// There is something wacky going on when it ends up here. The total value (im) is way way lower than it should be, which in
// turn causes low values for all estimates. And then when it returns to the normal case above, the value is waaay higher.
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Since last month update, hours: %.1f, import: %d (%.2f/hr), export: %d (%.2f/hr)\n", hrs, im, iph, ex, eph);
}
// Make sure last month read is at midnight
if(tz != NULL) {
breakTime(tz->toLocal(month.lastMeterReadTime), tm);
} else {
breakTime(month.lastMeterReadTime, tm);
}
month.lastMeterReadTime = month.lastMeterReadTime - (tm.Hour * 3600) - (tm.Minute * 60) - tm.Second;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month read after resetting to midnight: %lu\n", month.lastMeterReadTime);
}
if(tz != NULL) {
breakTime(tz->toLocal(now), tm);
} else {
breakTime(now, tm);
}
time_t stopAt = now - (tm.Hour * 3600) - (tm.Minute * 60) - tm.Second;
while(month.lastMeterReadTime < stopAt) {
time_t cur = min(month.lastMeterReadTime + 86400, stopAt);
uint8_t hours = round((cur - month.lastMeterReadTime) / 3600.0);
if(tz != NULL) {
breakTime(tz->toLocal(month.lastMeterReadTime), last);
} else {
breakTime(month.lastMeterReadTime, last);
}
float val = ((iph * hours) - (eph * hours));
setDay(last.Day, val);
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f (%lu)\n", last.Day, val, cur);
}
month.activeImport += iph * hours;
month.activeExport += eph * hours;
month.lastMeterReadTime = cur;
}
}
}
return true;
}
void AmsDataStorage::setHour(uint8_t hour, int32_t val) {
if(hour < 0 || hour > 24) return;
day.points[hour] = val / 10;
}
int32_t AmsDataStorage::getHour(uint8_t hour) {
if(hour < 0 || hour > 24) return 0;
return day.points[hour] * 10;
}
void AmsDataStorage::setDay(uint8_t day, int32_t val) {
if(day < 1 || day > 31) return;
month.points[day-1] = val / 10;
}
int32_t AmsDataStorage::getDay(uint8_t day) {
if(day < 1 || day > 31) return 0;
return (month.points[day-1] * 10);
}
bool AmsDataStorage::load() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(AmsDataStorage) Unable to load LittleFS\n");
}
return false;
}
bool ret = false;
if(LittleFS.exists(FILE_DAYPLOT)) {
File file = LittleFS.open(FILE_DAYPLOT, "r");
char buf[file.size()];
file.readBytes(buf, file.size());
DayDataPoints* day = (DayDataPoints*) buf;
file.close();
if(day->version == 3) {
memcpy(&this->day, day, sizeof(this->day));
ret = true;
} else {
ret = false;
}
}
if(LittleFS.exists(FILE_MONTHPLOT)) {
File file = LittleFS.open(FILE_MONTHPLOT, "r");
char buf[file.size()];
file.readBytes(buf, file.size());
MonthDataPoints* month = (MonthDataPoints*) buf;
file.close();
if(month->version == 4) {
memcpy(&this->month, month, sizeof(this->month));
ret = ret && true;
} else {
ret = false;
}
}
LittleFS.end();
return ret;
}
bool AmsDataStorage::save() {
if(!LittleFS.begin()) {
if(debugger->isActive(RemoteDebug::ERROR)) {
debugger->printf("(AmsDataStorage) Unable to load LittleFS\n");
}
return false;
}
{
File file = LittleFS.open(FILE_DAYPLOT, "w");
char buf[sizeof(day)];
memcpy(buf, &day, sizeof(day));
for(int i = 0; i < sizeof(day); i++) {
file.write(buf[i]);
}
file.close();
}
{
File file = LittleFS.open(FILE_MONTHPLOT, "w");
char buf[sizeof(month)];
memcpy(buf, &month, sizeof(month));
for(int i = 0; i < sizeof(month); i++) {
file.write(buf[i]);
}
file.close();
}
LittleFS.end();
return true;
}

49
src/AmsDataStorage.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef _AMSDATASTORAGE_H
#define _AMSDATASTORAGE_H
#include "Arduino.h"
#include "AmsData.h"
#include "RemoteDebug.h"
#include "Timezone.h"
#define EPOCH_2021_01_01 1609459200
struct DayDataPoints {
uint8_t version;
int16_t points[24];
time_t lastMeterReadTime;
uint32_t activeImport;
uint32_t activeExport;
}; // 37 bytes
struct MonthDataPoints {
uint8_t version;
int16_t points[31];
time_t lastMeterReadTime;
uint32_t activeImport;
uint32_t activeExport;
}; // 75 bytes
class AmsDataStorage {
public:
AmsDataStorage(RemoteDebug*);
void setTimezone(Timezone*);
bool update(AmsData*);
int32_t getHour(uint8_t);
int32_t getDay(uint8_t);
bool load();
bool save();
private:
Timezone* tz;
DayDataPoints day = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
MonthDataPoints month = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
RemoteDebug* debugger;
void setHour(uint8_t, int32_t);
void setDay(uint8_t, int32_t);
};
#endif

13
src/AmsStorage.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef _AMSSTORAGE_H
#define _AMSSTORAGE_H
#define FILE_FIRMWARE "/firmware.bin"
#define FILE_MQTT_CA "/mqtt-ca.pem"
#define FILE_MQTT_CERT "/mqtt-cert.pem"
#define FILE_MQTT_KEY "/mqtt-key.pem"
#define FILE_DAYPLOT "/dayplot.bin"
#define FILE_MONTHPLOT "/monthplot.bin"
#endif

24
src/AmsToMqttBridge.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef _AMSTOMQTTBRIDGE_H
#define _AMSTOMQTTBRIDGE_H
#define WIFI_CONNECTION_TIMEOUT 60000;
#define INVALID_BUTTON_PIN 0xFFFFFFFF
#define MAX_PEM_SIZE 4096
#include <SoftwareSerial.h>
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ESPmDNS.h>
#include "Update.h"
#endif
#include "LittleFS.h"
#endif

File diff suppressed because it is too large Load Diff

399
src/HwTools.cpp Normal file
View File

@@ -0,0 +1,399 @@
#include "HwTools.h"
void HwTools::setup(GpioConfig* config, AmsConfiguration* amsConf) {
this->config = config;
this->amsConf = amsConf;
this->tempSensorInit = false;
if(sensorApi != NULL)
delete sensorApi;
if(oneWire != NULL)
delete oneWire;
if(config->tempSensorPin > 0 && config->tempSensorPin < 40) {
pinMode(config->tempSensorPin, INPUT);
} else {
config->tempSensorPin = 0xFF;
}
if(config->vccPin > 0 && config->vccPin < 40) {
getAdcChannel(config->vccPin, voltAdc);
if(voltAdc.unit != 0xFF) {
#if defined(CONFIG_IDF_TARGET_ESP32)
if(voltAdc.unit == ADC_UNIT_1) {
voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar);
adc1_config_channel_atten((adc1_channel_t) voltAdc.channel, ADC_ATTEN_DB_6);
} else if(voltAdc.unit == ADC_UNIT_2) {
voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar);
adc2_config_channel_atten((adc2_channel_t) voltAdc.channel, ADC_ATTEN_DB_6);
}
#endif
} else {
pinMode(config->vccPin, INPUT);
}
} else {
voltAdc.unit = 0xFF;
voltAdc.channel = 0xFF;
config->vccPin = 0xFF;
}
if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) {
pinMode(config->tempAnalogSensorPin, INPUT);
} else {
config->tempAnalogSensorPin = 0xFF;
}
if(config->ledPin > 0 && config->ledPin < 40) {
pinMode(config->ledPin, OUTPUT);
ledOff(LED_INTERNAL);
} else {
config->ledPin = 0xFF;
}
if(config->ledPinRed > 0 && config->ledPinRed < 40) {
pinMode(config->ledPinRed, OUTPUT);
ledOff(LED_RED);
} else {
config->ledPinRed = 0xFF;
}
if(config->ledPinGreen > 0 && config->ledPinGreen < 40) {
pinMode(config->ledPinGreen, OUTPUT);
ledOff(LED_GREEN);
} else {
config->ledPinGreen = 0xFF;
}
if(config->ledPinBlue > 0 && config->ledPinBlue < 40) {
pinMode(config->ledPinBlue, OUTPUT);
ledOff(LED_BLUE);
} else {
config->ledPinBlue = 0xFF;
}
}
void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
config.unit = 0xFF;
config.channel = 0xFF;
#if defined(ESP32)
switch(pin) {
case ADC1_CHANNEL_0_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_0;
break;
case ADC1_CHANNEL_1_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_1;
break;
case ADC1_CHANNEL_2_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_2;
break;
case ADC1_CHANNEL_3_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_3;
break;
case ADC1_CHANNEL_4_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_4;
break;
case ADC1_CHANNEL_5_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_5;
break;
case ADC1_CHANNEL_6_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_6;
break;
case ADC1_CHANNEL_7_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_7;
break;
case ADC2_CHANNEL_0_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_0;
break;
case ADC2_CHANNEL_1_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_1;
break;
case ADC2_CHANNEL_2_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_2;
break;
case ADC2_CHANNEL_3_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_3;
break;
case ADC2_CHANNEL_4_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_4;
break;
case ADC2_CHANNEL_5_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_5;
break;
case ADC2_CHANNEL_6_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_6;
break;
case ADC2_CHANNEL_7_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_7;
break;
case ADC2_CHANNEL_8_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_8;
break;
case ADC2_CHANNEL_9_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_9;
break;
}
#endif
}
double HwTools::getVcc() {
double volts = 0.0;
if(config->vccPin != 0xFF) {
#if defined(CONFIG_IDF_TARGET_ESP32)
if(voltAdc.unit != 0xFF) {
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
if(voltAdc.unit == ADC_UNIT_1) {
x += adc1_get_raw((adc1_channel_t) voltAdc.channel);
} else if(voltAdc.unit == ADC_UNIT_2) {
int v = 0;
adc2_get_raw((adc2_channel_t) voltAdc.channel, ADC_WIDTH_BIT_12, &v);
x += v;
}
}
x = x / 10;
uint32_t voltage = esp_adc_cal_raw_to_voltage(x, voltAdcChar);
volts = voltage / 1000.0;
} else {
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
x += analogRead(config->vccPin);
}
volts = x / 40950;
}
#else
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
x += analogRead(config->vccPin);
}
volts = x / 10240;
#endif
} else {
#if defined(ESP8266)
volts = ESP.getVcc() / 1024.0;
#endif
}
if(config->vccResistorGnd > 0 && config->vccResistorVcc > 0) {
volts *= ((double) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
}
float vccOffset = config->vccOffset / 100.0;
float vccMultiplier = config->vccMultiplier / 1000.0;
return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0);
}
uint8_t HwTools::getTempSensorCount() {
return sensorCount;
}
TempSensorData* HwTools::getTempSensorData(uint8_t i) {
if(i < sensorCount) {
return tempSensors[i];
}
return NULL;
}
bool HwTools::updateTemperatures() {
if(config->tempSensorPin != 0xFF) {
if(!tempSensorInit) {
oneWire = new OneWire(config->tempSensorPin);
sensorApi = new DallasTemperature(this->oneWire);
sensorApi->begin();
delay(100);
tempSensorInit = true;
DeviceAddress addr;
sensorApi->requestTemperatures();
int c = sensorApi->getDeviceCount();
if(this->tempSensors != NULL) {
delete this->tempSensors;
}
this->tempSensors = new TempSensorData*[c];
for(int i = 0; i < c; i++) {
bool found = false;
sensorApi->getAddress(addr, i);
float t = sensorApi->getTempC(addr);
for(int x = 0; x < sensorCount; x++) {
TempSensorData *data = tempSensors[x];
if(isSensorAddressEqual(data->address, addr)) {
found = true;
data->lastRead = t;
if(t > -85) {
data->changed = data->lastValidRead != t;
data->lastValidRead = t;
}
}
}
if(!found) {
TempSensorData *data = new TempSensorData();
memcpy(data->address, addr, 8);
data->lastRead = t;
if(t > -85) {
data->changed = data->lastValidRead != t;
data->lastValidRead = t;
}
tempSensors[sensorCount++] = data;
}
delay(10);
}
} else {
if(sensorCount > 0) {
sensorApi->requestTemperatures();
for(int x = 0; x < sensorCount; x++) {
TempSensorData *data = tempSensors[x];
float t = sensorApi->getTempC(data->address);
data->lastRead = t;
if(t > -85) {
data->changed = data->lastValidRead != t;
data->lastValidRead = t;
}
}
}
}
return true;
}
return false;
}
bool HwTools::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) {
for(int i = 0; i < 8; i++) {
if(a[i] != b[i]) return false;
}
return true;
}
double HwTools::getTemperature() {
uint8_t c = 0;
double ret = 0;
double analogTemp = getTemperatureAnalog();
if(analogTemp != DEVICE_DISCONNECTED_C) {
ret += analogTemp;
c++;
}
for(int x = 0; x < sensorCount; x++) {
TempSensorData data = *tempSensors[x];
TempSensorConfig* conf = amsConf->getTempSensorConfig(data.address);
if((conf == NULL || conf->common) && data.lastValidRead > -85) {
ret += data.lastValidRead;
c++;
}
}
return c == 0 ? DEVICE_DISCONNECTED_C : ret/c;
}
double HwTools::getTemperatureAnalog() {
if(config->tempAnalogSensorPin != 0xFF) {
float adcCalibrationFactor = 1.06587;
int volts;
#if defined(ESP8266)
volts = (analogRead(config->tempAnalogSensorPin) / 1024.0) * 3.3;
#elif defined(ESP32)
volts = (analogRead(config->tempAnalogSensorPin) / 4095.0) * 3.3;
#endif
return ((volts * adcCalibrationFactor) - 0.4) / 0.0195;
}
return DEVICE_DISCONNECTED_C;
}
int HwTools::getWifiRssi() {
int rssi = WiFi.RSSI();
return isnan(rssi) ? -100.0 : rssi;
}
bool HwTools::ledOn(uint8_t color) {
if(color == LED_INTERNAL) {
return writeLedPin(color, config->ledInverted ? LOW : HIGH);
} else {
return writeLedPin(color, config->ledRgbInverted ? LOW : HIGH);
}
}
bool HwTools::ledOff(uint8_t color) {
if(color == LED_INTERNAL) {
return writeLedPin(color, config->ledInverted ? HIGH : LOW);
} else {
return writeLedPin(color, config->ledRgbInverted ? HIGH : LOW);
}
}
bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
for(int i = 0; i < blink; i++) {
if(!ledOn(color)) return false;
delay(50);
ledOff(color);
if(i != blink)
delay(50);
}
return true;
}
bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
switch(color) {
case LED_INTERNAL: {
if(config->ledPin != 0xFF) {
digitalWrite(config->ledPin, state);
return true;
} else {
return false;
}
break;
}
case LED_RED: {
if(config->ledPinRed != 0xFF) {
digitalWrite(config->ledPinRed, state);
return true;
} else {
return false;
}
break;
}
case LED_GREEN: {
if(config->ledPinGreen != 0xFF) {
digitalWrite(config->ledPinGreen, state);
return true;
} else {
return false;
}
break;
}
case LED_BLUE: {
if(config->ledPinBlue != 0xFF) {
digitalWrite(config->ledPinBlue, state);
return true;
} else {
return false;
}
break;
}
case LED_YELLOW: {
if(config->ledPinRed != 0xFF && config->ledPinGreen != 0xFF) {
digitalWrite(config->ledPinRed, state);
digitalWrite(config->ledPinGreen, state);
return true;
} else {
return false;
}
break;
}
}
return false;
}

Some files were not shown because too many files have changed in this diff Show More