From 15fb7307fc363c2e7d8f478183dc3c0af63ad58f Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Fri, 14 Sep 2018 16:39:06 +0200 Subject: [PATCH 01/34] Add login parms for tools/decode-status.py --- tools/decode-status.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tools/decode-status.py b/tools/decode-status.py index 154b6e719b04..74b10a57c61d 100644 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -28,12 +28,13 @@ and store it in file status.json Usage: - ./decode-status.py -d + ./decode-status.py -d [-u username] [-p password] or ./decode-status.py -f Example: ./decode-status.py -d sonoff1 + ./decode-status.py -d sonoff1 -p 12345678 or ./decode-status.py -f status.json """ @@ -42,6 +43,7 @@ import os.path import json import pycurl +import urllib2 from sys import exit from optparse import OptionParser from StringIO import StringIO @@ -136,13 +138,20 @@ parser = OptionParser(usage) parser.add_option("-d", "--dev", action="store", type="string", dest="device", help="device to retrieve status from") +parser.add_option("-u", "--username", action="store", type="string", + dest="username", help="username for login", default="admin") +parser.add_option("-p", "--password", action="store", type="string", + dest="password", help="password for login", default=None) parser.add_option("-f", "--file", metavar="FILE", dest="jsonfile", default="status.json", help="status json file (default: status.json)") (options, args) = parser.parse_args() if (options.device): buffer = StringIO() - url = str("http://{}/cm?cmnd=status%200".format(options.device)) + loginstr = "" + if options.password is not None: + loginstr = "user={}&password={}&".format(urllib2.quote(options.username), urllib2.quote(options.password)) + url = str("http://{}/cm?{}cmnd=status%200".format(options.device, loginstr)) c = pycurl.Curl() c.setopt(c.URL, url) c.setopt(c.WRITEDATA, buffer) @@ -217,4 +226,4 @@ def StartDecode(): try: StartDecode() except Exception as e: - print("E: {}".format(e)) \ No newline at end of file + print("E: {}".format(e)) From 9fc5d7162965e1d3415cee133404f961e85c9e99 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 15 Sep 2018 12:00:22 +0200 Subject: [PATCH 02/34] Update Czech translation Update Czech translation --- sonoff/language/cs-CZ.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 9a0b11062ff8..e90797725c4f 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -94,7 +94,7 @@ #define D_FALSE "Nepravda" #define D_FILE "Soubor" #define D_FREE_MEMORY "Volná paměť" -#define D_FREQUENCY "Frequency" +#define D_FREQUENCY "Kmitočet" #define D_GAS "Plyn" #define D_GATEWAY "Výchozí brána" #define D_GROUP "Skupina" @@ -128,7 +128,7 @@ #define D_POWERUSAGE_APPARENT "Apparent Power" #define D_POWERUSAGE_REACTIVE "Reactive Power" #define D_PRESSURE "Tlak" -#define D_PRESSUREATSEALEVEL "Tlak na úrovni hladiny moře" +#define D_PRESSUREATSEALEVEL "Tlak na hladině moře" #define D_PROGRAM_FLASH_SIZE "Velikost paměti flash" #define D_PROGRAM_SIZE "Velikost programu" #define D_PROJECT "Projekt" From d0fa749c306327f4b37c873fd18b986d163c397e Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sat, 15 Sep 2018 15:56:56 +0200 Subject: [PATCH 03/34] MCP230xx - Fix support for setoption4 --- sonoff/xsns_29_mcp230xx.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 058d6c25c4a9..586b8da8e013 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -297,7 +297,7 @@ void MCP230xx_CheckForInterrupt(void) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}"), mqtt_data, intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); } if (int_event) { char command[19]; // Theoretical max = 'event MCPINT_D16=1' so 18 + 1 (for the \n) From 98cfb0080382df4f3a8a0cb1a77297fdf70595e6 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sat, 15 Sep 2018 18:59:45 +0200 Subject: [PATCH 04/34] MCP230xx - Fix millisecond counter reset placement --- sonoff/xsns_29_mcp230xx.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 586b8da8e013..80aa76b7afdc 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -280,7 +280,9 @@ void MCP230xx_CheckForInterrupt(void) { if (report_int) { bool int_tele = false; bool int_event = false; - unsigned long millis_since_last_int = millis() - int_millis[intp+(mcp230xx_port*8)]; + unsigned long millis_now = millis(); + unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; + int_millis[intp+(mcp230xx_port*8)]=millis_now; switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { case 0: int_tele=true; @@ -304,7 +306,6 @@ void MCP230xx_CheckForInterrupt(void) { sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); ExecuteCommand(command, SRC_RULE); } - int_millis[intp+(mcp230xx_port*8)]=millis(); } } } From bf7dcb8eec8c898e869b04e7313e1fcd1073726c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 16 Sep 2018 16:09:00 +0200 Subject: [PATCH 05/34] 6.2.1.3 Add SerialSend5 6.2.1.4 20180916 * Add command SerialSend5 to send raw serial data like "A5074100545293" * Update MCP230xx driver * Update Czech translation * Update MP3 driver (#3800) * Add userid/password option to decode-status.py (#3796) * Fix syslog when emulation is selected (#2109, #3784) * Fix Pzem2 compilation error (#3766, #3767) --- sonoff/_changelog.ino | 13 +++++++++++-- sonoff/sonoff.ino | 13 ++++++++----- sonoff/sonoff_version.h | 2 +- sonoff/support.ino | 15 +++++++++++++++ sonoff/user_config.h | 2 +- sonoff/xdrv_06_snfbridge.ino | 19 ++----------------- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index eed6ffcc747e..bd4ee9b7c61c 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,10 +1,19 @@ -/* 6.2.1.3 20180907 +/* 6.2.1.4 20180916 + * Add command SerialSend5 to send raw serial data like "A5074100545293" + * Update MCP230xx driver + * Update Czech translation + * Update MP3 driver (#3800) + * Add userid/password option to decode-status.py (#3796) + * Fix syslog when emulation is selected (#2109, #3784) + * Fix Pzem2 compilation error (#3766, #3767) + * + * 6.2.1.3 20180907 * Change web Configure Module GPIO drop down list order for better readability * Fix showing Period Power in energy threshold messages * Fix ButtonRetain to not use default topic for clearing retain messages (#3737) * Add sleep to Nova Fitness SDS01X sensor (#2841, #3724, #3749) * Add Analog input AD0 enabled to sonoff-sensors.bin (#3756, #3757) - * Add Support to Xiaomi-Phillips Bulbs + * Add Support for Xiaomi-Philips Bulbs (#3787) * * 6.2.1.2 20180906 * Fix KNX PA exception. Regression from 6.2.1 buffer overflow caused by subStr() (#3700, #3710) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index a55e896c4d3b..4903fe80f213 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -969,22 +969,25 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200); } - else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 4)) { + else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) { SetSeriallog(LOG_LEVEL_NONE); Settings.flag.mqtt_serial = 1; - Settings.flag.mqtt_serial_raw = (4 == index) ? 1 : 0; + Settings.flag.mqtt_serial_raw = (index > 3) ? 1 : 0; if (data_len > 0) { if (1 == index) { - Serial.printf("%s\n", dataBuf); + Serial.printf("%s\n", dataBuf); // "Hello Tiger\n" } else if (2 == index || 4 == index) { for (int i = 0; i < data_len; i++) { - Serial.write(dataBuf[i]); + Serial.write(dataBuf[i]); // "Hello Tiger" or "A0" } } else if (3 == index) { uint16_t dat_len = data_len; - Serial.printf("%s", Unescape(dataBuf, &dat_len)); + Serial.printf("%s", Unescape(dataBuf, &dat_len)); // "Hello\f" + } + else if (5 == index) { + SerialSendRaw(dataBuf, data_len); // "AA004566" } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index f068473ab5f4..f5f94dad156b 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020103 +#define VERSION 0x06020104 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/support.ino b/sonoff/support.ino index af93fa1486ba..fb2b5eae323e 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -704,6 +704,21 @@ void ClaimSerial() Settings.baudrate = baudrate / 1200; } +void SerialSendRaw(char *codes, int size) +{ + char *p; + char stemp[3]; + uint8_t code; + + while (size > 0) { + snprintf(stemp, sizeof(stemp), codes); + code = strtol(stemp, &p, 16); + Serial.write(code); + size -= 2; + codes += 2; + } +} + uint32_t GetHash(const char *buffer, size_t size) { uint32_t hash = 0; diff --git a/sonoff/user_config.h b/sonoff/user_config.h index a07c87ec3c2b..5a2b909de0ef 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -342,7 +342,7 @@ //#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop -// #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) + #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) diff --git a/sonoff/xdrv_06_snfbridge.ino b/sonoff/xdrv_06_snfbridge.ino index b0518f133b72..379d29455000 100644 --- a/sonoff/xdrv_06_snfbridge.ino +++ b/sonoff/xdrv_06_snfbridge.ino @@ -199,21 +199,6 @@ uint8_t SnfBrUpdateInit() /********************************************************************************************/ -void SonoffBridgeSendRaw(char *codes, int size) -{ - char *p; - char stemp[3]; - uint8_t code; - - while (size > 0) { - snprintf(stemp, sizeof(stemp), codes); - code = strtol(stemp, &p, 16); - Serial.write(code); - size -= 2; - codes += 2; - } -} - void SonoffBridgeReceivedRaw() { // Decoding according to https://github.com/Portisch/RF-Bridge-EFM8BB1 @@ -552,11 +537,11 @@ boolean SonoffBridgeCommand() break; case 192: // 0xC0 - Beep char beep[] = "AAC000C055"; - SonoffBridgeSendRaw(beep, sizeof(beep)); + SerialSendRaw(beep, sizeof(beep)); break; } } else { - SonoffBridgeSendRaw(XdrvMailbox.data, XdrvMailbox.data_len); + SerialSendRaw(XdrvMailbox.data, XdrvMailbox.data_len); sonoff_bridge_receive_raw_flag = 1; } } From 29b3e6ec27ae9395764e19545246f7fb76f254d3 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sun, 16 Sep 2018 21:36:56 +0200 Subject: [PATCH 06/34] MCP230xx - Change address selection from auto to user defined --- sonoff/user_config.h | 3 +- sonoff/xsns_29_mcp230xx.ino | 73 +++++++++++++++---------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 5a2b909de0ef..20339b5f6c77 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -294,7 +294,8 @@ // #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code) #define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code) // #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code) -// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+4k7 code) +// #define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) +// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) // #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) // #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) // #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 80aa76b7afdc..50798eb8fadd 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -31,15 +31,6 @@ #define XSNS_29 29 -#define MCP230xx_ADDRESS1 0x20 -#define MCP230xx_ADDRESS2 0x21 -#define MCP230xx_ADDRESS3 0x22 -#define MCP230xx_ADDRESS4 0x23 -#define MCP230xx_ADDRESS5 0x24 -#define MCP230xx_ADDRESS6 0x25 -#define MCP230xx_ADDRESS7 0x26 -#define MCP230xx_ADDRESS8 0x27 - /* Default register locations for MCP23008 - They change for MCP23017 in default bank mode */ @@ -53,8 +44,6 @@ uint8_t MCP230xx_INTCAP = 0x08; uint8_t MCP230xx_GPIO = 0x09; uint8_t mcp230xx_type = 0; -uint8_t mcp230xx_address; -uint8_t mcp230xx_addresses[] = { MCP230xx_ADDRESS1, MCP230xx_ADDRESS2, MCP230xx_ADDRESS3, MCP230xx_ADDRESS4, MCP230xx_ADDRESS5, MCP230xx_ADDRESS6, MCP230xx_ADDRESS7, MCP230xx_ADDRESS8 }; uint8_t mcp230xx_pincount = 0; uint8_t mcp230xx_int_en = 0; uint8_t mcp230xx_int_prio_counter = 0; @@ -131,7 +120,7 @@ const char* IntModeTxt(uint8_t intmo) { } uint8_t MCP230xx_readGPIO(uint8_t port) { - return I2cRead8(mcp230xx_address, MCP230xx_GPIO + port); + return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); } void MCP230xx_ApplySettings(void) { @@ -178,11 +167,11 @@ void MCP230xx_ApplySettings(void) { } #endif // USE_MCP230xx_OUTPUT } - I2cWrite8(mcp230xx_address, MCP230xx_GPPU+mcp230xx_port, reg_gppu); - I2cWrite8(mcp230xx_address, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); - I2cWrite8(mcp230xx_address, MCP230xx_IODIR+mcp230xx_port, reg_iodir); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); #ifdef USE_MCP230xx_OUTPUT - I2cWrite8(mcp230xx_address, MCP230xx_GPIO+mcp230xx_port, reg_portpins); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); #endif // USE_MCP230xx_OUTPUT } for (uint8_t idx=0;idx 0) { - if (I2cValidRead8(&mcp230xx_intcap, mcp230xx_address, MCP230xx_INTCAP+mcp230xx_port)) { + if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { for (uint8_t intp = 0; intp < 8; intp++) { if ((intf >> intp) & 0x01) { // we know which pin caused interrupt report_int = 0; @@ -367,7 +352,7 @@ void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { portpins ^= (1 << (pin-(port*8))); } } - I2cWrite8(mcp230xx_address, MCP230xx_GPIO + port, portpins); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); if (Settings.flag.save_state) { // Firmware configured to save last known state in settings Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; From bffc820fb05bae7582934d27e76b6fd1bd86206b Mon Sep 17 00:00:00 2001 From: andrethomas Date: Mon, 17 Sep 2018 08:23:39 +0200 Subject: [PATCH 07/34] Update Bug_report.md --- .github/ISSUE_TEMPLATE/Bug_report.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 2616af48fed0..f97db3ad082b 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -4,6 +4,9 @@ about: Create a report to help us improve --- +**IMPORTANT NOTICE** +If you do not complete the template below your it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. + **Describe the bug** _A clear and concise description of what the bug is._ @@ -11,6 +14,7 @@ _A clear and concise description of what the bug is._ _Also, make sure these boxes are checked [x] before submitting your issue - Thank you!_ - [ ] _Searched the problem in issues and in the wiki_ - [ ] _Hardware used_ : +- [ ] _Development/Compiler/Upload tools used_ : - [ ] _Provide the output of command_``status 0`` : ``` STATUS 0 OUTPUT HERE From 2f0cd60cb3a6ee1cab85c1b3b73e9a13d41e1a8a Mon Sep 17 00:00:00 2001 From: andrethomas Date: Mon, 17 Sep 2018 08:26:43 +0200 Subject: [PATCH 08/34] Update Custom.md --- .github/ISSUE_TEMPLATE/Custom.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index 159549128389..bc769a38d2db 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -4,12 +4,16 @@ about: Users Troubleshooting Help --- +**IMPORTANT NOTICE** +If you do not complete the template below your it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. + Make sure these boxes are checked [x] before submitting your issue - Thank you! - [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues) - [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting) - [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers) - [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4) +- [ ] Development/Compiler/Upload tools used : - [ ] Hardware used : - [ ] Provide the output of command ``status 0`` : ``` From 3d74ed9964b9b40ba4fc6bce18aa561d25554f58 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 17 Sep 2018 09:08:37 +0200 Subject: [PATCH 09/34] Update Bug_report.md --- .github/ISSUE_TEMPLATE/Bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index f97db3ad082b..bf55e130ecb0 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -5,7 +5,7 @@ about: Create a report to help us improve --- **IMPORTANT NOTICE** -If you do not complete the template below your it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. +If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. **Describe the bug** _A clear and concise description of what the bug is._ From de406b7868f0c6a0d7ab75a3270ef9971dec30a8 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 17 Sep 2018 09:11:18 +0200 Subject: [PATCH 10/34] Update Custom.md --- .github/ISSUE_TEMPLATE/Custom.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index bc769a38d2db..77fc7a3e7b5e 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -5,7 +5,7 @@ about: Users Troubleshooting Help --- **IMPORTANT NOTICE** -If you do not complete the template below your it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. +If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses. Make sure these boxes are checked [x] before submitting your issue - Thank you! From ff4f8f75c17aa21db912775d947d31caf9a2616b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 17 Sep 2018 20:32:38 +0200 Subject: [PATCH 11/34] Add uncalibrated energy monitor Add uncalibrated energy monitoring to Shelly2 (#2789) --- sonoff/_changelog.ino | 1 + sonoff/xdrv_03_energy.ino | 4 +- sonoff/xnrg_04_mcp39f501.ino | 301 +++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 sonoff/xnrg_04_mcp39f501.ino diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index bd4ee9b7c61c..fdf73fdc782b 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -6,6 +6,7 @@ * Add userid/password option to decode-status.py (#3796) * Fix syslog when emulation is selected (#2109, #3784) * Fix Pzem2 compilation error (#3766, #3767) + * Add uncalibrated energy monitoring to Shelly2 (#2789) * * 6.2.1.3 20180907 * Change web Configure Module GPIO drop down list order for better readability diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index ac26a18da6dd..aa589efab211 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -98,6 +98,8 @@ void EnergyUpdateToday() void Energy200ms() { + energy_power_on = (power != 0) | Settings.flag.no_power_on_check; + energy_fifth_second++; if (5 == energy_fifth_second) { energy_fifth_second = 0; @@ -121,8 +123,6 @@ void Energy200ms() } } - energy_power_on = (power &1) | Settings.flag.no_power_on_check; - XnrgCall(FUNC_EVERY_200_MSECOND); if (energy_calc_power_factor) { diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino new file mode 100644 index 000000000000..f74111f9d22c --- /dev/null +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -0,0 +1,301 @@ +/* + xnrg_04_mcp39f501.ino - MCP39F501 energy sensor support for Sonoff-Tasmota + + Copyright (C) 2018 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_ENERGY_SENSOR +#ifdef USE_MCP39F501 +/*********************************************************************************************\ + * MCP39F501 - Energy (Shelly 2) + * + * Based on datasheet from https://www.microchip.com/wwwproducts/en/MCP39F501 + * and https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c +\*********************************************************************************************/ + +#define XNRG_04 4 + +#define MCP_START_FRAME 0xA5 +#define MCP_ACK_FRAME 0x06 +#define MCP_ERROR_NAK 0x15 +#define MCP_ERROR_CRC 0x51 + +#define MCP_SINGLE_WIRE 0xAB + +#define MCP_SET_ADDRESS 0x41 + +#define MCP_READ 0x4E +#define MCP_READ_16 0x52 +#define MCP_READ_32 0x44 + +#define MCP_WRITE 0x4D +#define MCP_WRITE_16 0x57 +#define MCP_WRITE_32 0x45 + +#define MCP_SAVE_REGISTERS 0x53 + +#define MCP_FLASH_READ 0x42 +#define MCP_FLASH_WRITE 0x50 + +uint32 mcp_system_configuration = 0x03000000; +uint8_t mcp_single_wire_active = 0; + +/*********************************************************************************************\ + * Olimex tools + * https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c +\*********************************************************************************************/ + + +unsigned long McpExtractInt(uint8_t *data, uint8_t offset, uint8_t size) +{ + unsigned long result = 0; + unsigned long pow = 1; + + for (byte i = 0; i < size; i++) { + result = result + data[offset + i] * pow; + pow = pow * 256; + } + return result; +} + +void McpSetSystemConfiguration(uint16 interval) +{ + uint8_t data[17]; + + data[ 0] = MCP_START_FRAME; + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; // Set address pointer + data[ 3] = 0x00; // address + data[ 4] = 0x42; // address + data[ 5] = MCP_WRITE_32; // Write 4 bytes + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration + data[10] = MCP_SET_ADDRESS; // Set address pointer + data[11] = 0x00; // address + data[12] = 0x5A; // address + data[13] = MCP_WRITE_16; // Write 2 bytes + data[14] = (interval >> 8) & 0xFF; // interval + data[15] = (interval >> 0) & 0xFF; // interval + uint8_t checksum = 0; + for (byte i = 0; i < sizeof(data) -1; i++) { checksum += (uint8_t)data[i]; } + data[16] = checksum; + + // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A + AddLogSerial(LOG_LEVEL_DEBUG, data, sizeof(data)); + + for (byte i = 0; i < sizeof(data); i++) { Serial.write(data[i]); } +} + +void McpSingleWireStart() +{ + if ((mcp_system_configuration & (1 << 8)) != 0) { return; } + mcp_system_configuration = mcp_system_configuration | (1 << 8); + McpSetSystemConfiguration(6); // 64 + mcp_single_wire_active = 1; +} + +void McpSingleWireStop() +{ + if ((mcp_system_configuration & (1 << 8)) == 0) { return; } + mcp_system_configuration = mcp_system_configuration & (~(1 << 8)); + McpSetSystemConfiguration(2); // 4 + mcp_single_wire_active = 0; +} + +/********************************************************************************************/ + +unsigned long mcp_current = 0; +unsigned long mcp_voltage = 0; +unsigned long mcp_power = 0; +unsigned long mcp_frequency = 0; + +void McpParseData(uint8_t single_wire) +{ + if (single_wire) { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + // AB CD EF 51 06 00 00 B8 08 FC 0D 00 00 0A C4 11 + // Header-- Current---- Volt- Power------ Freq- Ck + + mcp_current = McpExtractInt((uint8_t*)serial_in_buffer, 3, 4); + mcp_voltage = McpExtractInt((uint8_t*)serial_in_buffer, 7, 2); + mcp_power = McpExtractInt((uint8_t*)serial_in_buffer, 9, 4); + mcp_frequency = McpExtractInt((uint8_t*)serial_in_buffer, 13, 2); + } else { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + // 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35 + // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck + + mcp_current = McpExtractInt((uint8_t*)serial_in_buffer, 2, 4); + mcp_voltage = McpExtractInt((uint8_t*)serial_in_buffer, 6, 2); + mcp_power = McpExtractInt((uint8_t*)serial_in_buffer, 8, 4); + mcp_frequency = McpExtractInt((uint8_t*)serial_in_buffer, 22, 2); + } + + if (energy_power_on) { // Powered on + energy_frequency = (float)mcp_frequency / 1000; + energy_voltage = (float)mcp_voltage / 10; + energy_power = (float)mcp_power / 100; + if (0 == energy_power) { + energy_current = 0; + } else { + energy_current = (float)mcp_current / 10000; + } + } else { // Powered off + energy_frequency = 0; + energy_voltage = 0; + energy_power = 0; + energy_current = 0; + } +} + +bool McpSerialInput() +{ + Settings.flag.mqtt_serial = 0; // Disable possible SerialReceive handling + + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (MCP_ERROR_CRC == serial_in_buffer[0]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); + return 1; + } + else if (MCP_ACK_FRAME == serial_in_buffer[0]) { + if ((serial_in_byte_counter > 1) && (serial_in_byte_counter == serial_in_buffer[1])) { + + AddLogSerial(LOG_LEVEL_DEBUG_MORE); + + uint8_t checksum = 0; + for (byte i = 0; i < serial_in_byte_counter -1; i++) { checksum += (uint8_t)serial_in_buffer[i]; } + if (checksum != serial_in_buffer[serial_in_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { + if (25 == serial_in_buffer[1]) { McpParseData(0); } + } + return 1; + } + } + else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) { + if (serial_in_byte_counter == 16) { + + AddLogSerial(LOG_LEVEL_DEBUG_MORE); + + uint8_t checksum = 0; + for (byte i = 3; i < serial_in_byte_counter -1; i++) { checksum += (uint8_t)serial_in_buffer[i]; } +// if (~checksum != serial_in_buffer[serial_in_byte_counter -1]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); +// } else { + McpParseData(1); +// } + return 1; + } + } + else { + return 1; + } + serial_in_byte = 0; // Discard + return 0; +} + +/********************************************************************************************/ + +void McpEverySecond() +{ + if (!mcp_single_wire_active) { + char get_state[] = "A5084100044E1656"; + SerialSendRaw(get_state, sizeof(get_state)); + } + + energy_kWhtoday += (energy_power / 36); + EnergyUpdateToday(); +} + +void McpSnsInit() +{ + digitalWrite(15, 1); // GPIO15 - MCP enable +} + +void McpDrvInit() +{ + if (!energy_flg) { + if (SHELLY2 == Settings.module) { + pinMode(15, OUTPUT); + digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's + baudrate = 4800; + energy_calc_power_factor = 1; // Calculate power factor from data + energy_flg = XNRG_04; + } + } +} + +boolean McpCommand() +{ + boolean serviced = true; + + if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { + + } + else if (CMND_POWERSET == energy_command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && power_cycle) { +// Settings.energy_power_calibration = (XdrvMailbox.payload * power_cycle) / CSE_PREF; + } + } + else if (CMND_VOLTAGESET == energy_command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && voltage_cycle) { +// Settings.energy_voltage_calibration = (XdrvMailbox.payload * voltage_cycle) / CSE_UREF; + } + } + else if (CMND_CURRENTSET == energy_command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && current_cycle) { +// Settings.energy_current_calibration = (XdrvMailbox.payload * current_cycle) / 1000; + } + } + else serviced = false; // Unknown command + + return serviced; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +int Xnrg04(byte function) +{ + int result = 0; + + if (FUNC_PRE_INIT == function) { + McpDrvInit(); + } + else if (XNRG_04 == energy_flg) { + switch (function) { + case FUNC_INIT: + McpSnsInit(); + break; + case FUNC_EVERY_SECOND: + McpEverySecond(); + break; + case FUNC_COMMAND: + result = McpCommand(); + break; + case FUNC_SERIAL: + result = McpSerialInput(); + break; + } + } + return result; +} + +#endif // USE_MCP39F501 +#endif // USE_ENERGY_SENSOR From c9f0c184661b7838536448823d3035a157223222 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Thu, 20 Sep 2018 20:44:17 +0200 Subject: [PATCH 12/34] LM75AD - Force function parameters to be C and C++ compliant --- sonoff/xsns_26_lm75ad.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonoff/xsns_26_lm75ad.ino b/sonoff/xsns_26_lm75ad.ino index 092c8d153d3d..1fd0f444861e 100644 --- a/sonoff/xsns_26_lm75ad.ino +++ b/sonoff/xsns_26_lm75ad.ino @@ -46,7 +46,7 @@ uint8_t lm75ad_type = 0; uint8_t lm75ad_address; uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; -void LM75ADDetect() +void LM75ADDetect(void) { if (lm75ad_type) { return; } @@ -64,7 +64,7 @@ void LM75ADDetect() } } -float LM75ADGetTemp() { +float LM75ADGetTemp(void) { int16_t sign = 1; uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); From 363e19d681584f09268f49fd4ed540d421f79753 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Thu, 20 Sep 2018 21:03:33 +0200 Subject: [PATCH 13/34] MCP230xx - Force C/C++ compliance --- sonoff/xsns_29_mcp230xx.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 50798eb8fadd..1ca59f8ed335 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -181,7 +181,7 @@ void MCP230xx_ApplySettings(void) { MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts } -void MCP230xx_Detect() +void MCP230xx_Detect(void) { if (mcp230xx_type) { return; From 846d7a911f0571f209109a39585e7abad4e50cac Mon Sep 17 00:00:00 2001 From: andrethomas Date: Thu, 20 Sep 2018 22:56:07 +0200 Subject: [PATCH 14/34] Add driverXX support for FUNC_COMMAND callback --- sonoff/i18n.h | 4 ++++ sonoff/sonoff.ino | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index da47371d8411..32cb458f1b4f 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -174,6 +174,7 @@ #define D_CMND_BLINKTIME "BlinkTime" #define D_CMND_BLINKCOUNT "BlinkCount" #define D_CMND_SENSOR "Sensor" +#define D_CMND_DRIVER "Driver" #define D_CMND_SAVEDATA "SaveData" #define D_CMND_SETOPTION "SetOption" #define D_CMND_TEMPERATURE_RESOLUTION "TempRes" @@ -467,6 +468,9 @@ const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}"; const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}"; +const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}"; +const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}"; + const char JSON_SNS_TEMP[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"; const char JSON_SNS_TEMPHUM[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 4903fe80f213..84293f5bcff9 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -83,7 +83,7 @@ enum TasmotaCommands { CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME, CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE, CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, - CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER }; + CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER }; const char kTasmotaCommands[] PROGMEM = D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" @@ -93,7 +93,7 @@ const char kTasmotaCommands[] PROGMEM = D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" - D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER; + D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER; const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; @@ -690,6 +690,16 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) XsnsCall(FUNC_COMMAND); // if (!XsnsCall(FUNC_COMMAND)) type = NULL; } + else if (CMND_DRIVER == command_code) { + XdrvMailbox.index = index; + XdrvMailbox.data_len = data_len; + XdrvMailbox.payload16 = payload16; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.topic = command; + XdrvMailbox.data = dataBuf; + XdrvCall(FUNC_COMMAND); + } else if ((CMND_SETOPTION == command_code) && (index < 82)) { byte ptype; byte pindex; From 415ed97dab55c02fd1a6b4536bfa40befdaa47c4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 21 Sep 2018 11:15:42 +0200 Subject: [PATCH 15/34] 6.2.1.5 Add web authentication Add authentication to HTTP web pages --- sonoff/_changelog.ino | 5 +- sonoff/sonoff_version.h | 2 +- sonoff/xdrv_02_webserver.ino | 107 ++++++++++++++--------------------- sonoff/xdrv_07_domoticz.ino | 6 +- sonoff/xdrv_09_timers.ino | 6 +- sonoff/xdrv_11_knx.ino | 10 ++-- 6 files changed, 56 insertions(+), 80 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index fdf73fdc782b..aa6e8bfae62e 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,7 @@ -/* 6.2.1.4 20180916 +/* 6.2.1.5 20180921 + * Add authentication to HTTP web pages + * + * 6.2.1.4 20180916 * Add command SerialSend5 to send raw serial data like "A5074100545293" * Update MCP230xx driver * Update Czech translation diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index f5f94dad156b..43253e40ff03 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020104 +#define VERSION 0x06020105 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/xdrv_02_webserver.ino b/sonoff/xdrv_02_webserver.ino index 6cadb952fe91..5182b7051808 100644 --- a/sonoff/xdrv_02_webserver.ino +++ b/sonoff/xdrv_02_webserver.ino @@ -29,8 +29,6 @@ uint8_t *efm8bb1_update = NULL; #endif // USE_RF_FLASH -#define D_TASMOTA_TOKEN "Tasmota-Token" - enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 }; const char HTTP_HEAD[] PROGMEM = @@ -58,17 +56,13 @@ const char HTTP_HEAD[] PROGMEM = "eb('s1').value=l.innerText||l.textContent;" "eb('p1').focus();" "}" - "function lx(){" - "if(to==1){" - "if(tp<30){" - "tp++;" - "lt=setTimeout(lx,33);" // Wait for token from server - "}else{" - "lt=setTimeout(la,1355);" // Discard action and retry - "}" - "return;" + "function la(p){" + "var a='';" + "if(la.arguments.length==1){" + "a=p;" + "clearTimeout(lt);" "}" - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) + "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) "x=new XMLHttpRequest();" "x.onreadystatechange=function(){" "if(x.readyState==4&&x.status==200){" @@ -76,32 +70,15 @@ const char HTTP_HEAD[] PROGMEM = "eb('l1').innerHTML=s;" "}" "};" - "x.open('GET','ay'+pc,true);" // Async request - "x.setRequestHeader('" D_TASMOTA_TOKEN "',to);" - "x.send();" // Perform command if available and get updated information - "pc='';" - "lt=setTimeout(la,2345-(tp*33));" - "}" - "function la(p){" - "if(la.arguments.length==1){" - "pc='?'+p;" - "clearTimeout(lt);" - "}else{pc='';}" - "to=1;tp=0;" - "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1) - "x=new XMLHttpRequest();" - "x.onreadystatechange=function(){" - "if(x.readyState==4&&x.status==200){to=x.getResponseHeader('" D_TASMOTA_TOKEN "');}else{to=1;}" - "};" - "x.open('GET','az',true);" // Async request - "x.send();" // Get token from server - "lx();" + "x.open('GET','ay'+a,true);" + "x.send();" + "lt=setTimeout(la,2345);" "}" "function lb(p){" - "la('d='+p);" + "la('?d='+p);" "}" "function lc(p){" - "la('c='+p);" + "la('?t='+p);" "}"; const char HTTP_HEAD_STYLE[] PROGMEM = @@ -340,7 +317,7 @@ const char HTTP_END[] PROGMEM = "" ""; -const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; +const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; const char HTTP_DEVICE_STATE[] PROGMEM = "%s%s"; // {c} = %'>
"), idx, idx); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(""), idx, idx); page += mqtt_data; } } @@ -634,34 +616,13 @@ void HandleRoot() } } -void HandleToken() -{ - char token[11]; - - ajax_token = random(2, 0x7FFFFFFF); - snprintf_P(token, sizeof(token), PSTR("%u"), ajax_token); - SetHeader(); - WebServer->sendHeader(FPSTR(HDR_TASMOTA_TOKEN), token); - snprintf_P(token, sizeof(token), PSTR("%u"), random(0x7FFFFFFF)); - WebServer->send(200, FPSTR(HDR_CTYPE_HTML), token); - - const char* header_key[] = { D_TASMOTA_TOKEN }; - WebServer->collectHeaders(header_key, 1); -} - void HandleAjaxStatusRefresh() { + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + char svalue[80]; char tmp[100]; - if (WebServer->header(FPSTR(HDR_TASMOTA_TOKEN)).toInt() != ajax_token) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_FILE_NOT_FOUND)); - SetHeader(); - WebServer->send(404, FPSTR(HDR_CTYPE_PLAIN), mqtt_data); - return; - } - ajax_token = 1; - WebGetArg("o", tmp, sizeof(tmp)); if (strlen(tmp)) { ShowWebSource(SRC_WEBGUI); @@ -736,6 +697,7 @@ boolean HttpUser() void HandleConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); String page = FPSTR(HTTP_HEAD); @@ -759,6 +721,7 @@ void HandleConfiguration() void HandleModuleConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char stemp[20]; uint8_t midx; @@ -829,6 +792,7 @@ void HandleWifiConfiguration() void HandleWifi(boolean scan) { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); @@ -925,6 +889,7 @@ void HandleWifi(boolean scan) void HandleMqttConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); String page = FPSTR(HTTP_HEAD); @@ -948,6 +913,7 @@ void HandleMqttConfiguration() void HandleLoggingConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); String page = FPSTR(HTTP_HEAD); @@ -995,6 +961,7 @@ void HandleLoggingConfiguration() void HandleOtherConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); char stemp[40]; @@ -1032,6 +999,7 @@ void HandleOtherConfiguration() void HandleBackupConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); if (!SettingsBufferAlloc()) { return; } @@ -1067,6 +1035,7 @@ void HandleBackupConfiguration() void HandleSaveSettings() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char stemp[TOPSZ]; char stemp2[TOPSZ]; @@ -1232,6 +1201,7 @@ void HandleSaveSettings() void HandleResetConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char svalue[33]; @@ -1252,6 +1222,7 @@ void HandleResetConfiguration() void HandleRestoreConfiguration() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); String page = FPSTR(HTTP_HEAD); @@ -1270,6 +1241,7 @@ void HandleRestoreConfiguration() void HandleInformation() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); char stopic[TOPSZ]; @@ -1386,6 +1358,7 @@ void HandleInformation() void HandleUpgradeFirmware() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); String page = FPSTR(HTTP_HEAD); @@ -1405,6 +1378,7 @@ void HandleUpgradeFirmware() void HandleUpgradeFirmwareStart() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char svalue[100]; AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); @@ -1432,6 +1406,7 @@ void HandleUpgradeFirmwareStart() void HandleUploadDone() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); char error[100]; @@ -1684,6 +1659,7 @@ void HandlePreflightRequest() void HandleHttpCommand() { if (HttpUser()) { return; } +// if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); @@ -1741,6 +1717,7 @@ void HandleHttpCommand() void HandleConsole() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); String page = FPSTR(HTTP_HEAD); @@ -1756,6 +1733,7 @@ void HandleConsole() void HandleAjaxConsoleRefresh() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog byte cflg = 1; byte counter = 0; // Initial start, should never be 0 again @@ -1812,6 +1790,7 @@ void HandleAjaxConsoleRefresh() void HandleRestart() { if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); String page = FPSTR(HTTP_HEAD); diff --git a/sonoff/xdrv_07_domoticz.ino b/sonoff/xdrv_07_domoticz.ino index a7eb7708f1a4..a30f79439063 100644 --- a/sonoff/xdrv_07_domoticz.ino +++ b/sonoff/xdrv_07_domoticz.ino @@ -375,10 +375,8 @@ const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; void HandleDomoticzConfiguration() { - if (HTTP_USER == webserver_state) { - HandleRoot(); - return; - } + if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); char stemp[32]; diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 0652f3b537a6..66bf9361493f 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -676,10 +676,8 @@ const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; void HandleTimerConfiguration() { - if (HTTP_USER == webserver_state) { - HandleRoot(); - return; - } + if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); String page = FPSTR(HTTP_HEAD); diff --git a/sonoff/xdrv_11_knx.ino b/sonoff/xdrv_11_knx.ino index 3897caac7f51..88260b23ce1f 100644 --- a/sonoff/xdrv_11_knx.ino +++ b/sonoff/xdrv_11_knx.ino @@ -803,15 +803,13 @@ const char HTTP_FORM_KNX_ADD_TABLE_ROW2[] PROGMEM = void HandleKNXConfiguration() { + if (HttpUser()) { return; } + if (!WebAuthenticate()) { return WebServer->requestAuthentication(); } + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX); + char tmp[100]; String stmp; - if (HTTP_USER == webserver_state) { - HandleRoot(); - return; - } - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX); - if ( WebServer->hasArg("save") ) { KNX_Save_Settings(); HandleConfiguration(); From 4065a215f05173230ce3f5c2e8a56f0b5c2984e0 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 21 Sep 2018 15:22:17 +0200 Subject: [PATCH 16/34] Add Shelly2 Energy Monitoring Add energy monitoring to Shelly2 (#2789) --- sonoff/_changelog.ino | 1 + sonoff/i18n.h | 7 +- sonoff/settings.h | 8 +- sonoff/user_config.h | 1 + sonoff/xdrv_03_energy.ino | 8 +- sonoff/xnrg_04_mcp39f501.ino | 608 +++++++++++++++++++++++++++++------ 6 files changed, 537 insertions(+), 96 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index aa6e8bfae62e..72293c30d8e5 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,5 +1,6 @@ /* 6.2.1.5 20180921 * Add authentication to HTTP web pages + * Add energy monitoring to Shelly2 (#2789) * * 6.2.1.4 20180916 * Add command SerialSend5 to send raw serial data like "A5074100545293" diff --git a/sonoff/i18n.h b/sonoff/i18n.h index da47371d8411..4ea5ab3fea3f 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -292,6 +292,7 @@ #define D_CMND_VOLTAGESET "VoltageSet" #define D_CMND_CURRENTCAL "CurrentCal" #define D_CMND_CURRENTSET "CurrentSet" +#define D_CMND_FREQUENCYSET "FrequencySet" #define D_CMND_MAXPOWER "MaxPower" #define D_CMND_MAXPOWERHOLD "MaxPowerHold" #define D_CMND_MAXPOWERWINDOW "MaxPowerWindow" @@ -419,7 +420,8 @@ enum UnitNames { UNIT_SECTORS, UNIT_VOLT, UNIT_WATT, - UNIT_WATTHOUR }; + UNIT_WATTHOUR, + UNIT_HERTZ }; const char kUnitNames[] PROGMEM = D_UNIT_AMPERE "|" D_UNIT_HOUR "|" @@ -439,7 +441,8 @@ const char kUnitNames[] PROGMEM = D_UNIT_SECTORS "|" D_UNIT_VOLT "|" D_UNIT_WATT "|" - D_UNIT_WATTHOUR ; + D_UNIT_WATTHOUR "|" + "d" D_UNIT_HERTZ ; const char S_JSON_COMMAND_NVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%d %s\"}"; const char S_JSON_COMMAND_LVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%lu %s\"}"; diff --git a/sonoff/settings.h b/sonoff/settings.h index a1fd218ac63e..7da6108910aa 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -322,9 +322,13 @@ struct SYSCFG { uint16_t mcp230xx_int_timer; // 718 - byte free_71A[180]; // 71A + byte free_71A[174]; // 71A - char mems[MAX_RULE_MEMS][10]; // 7CE + unsigned long energy_frequency_calibration; // 7C8 + + byte free_7CC[2]; // 7CC + + char mems[MAX_RULE_MEMS][10]; // 7CE // 800 Full - no more free locations char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 20339b5f6c77..ef3e66fc21b6 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -348,6 +348,7 @@ // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM2 // Add support for PZEM003,014,016,017 Energy monitor (+1k1 code) +#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k2 code) // -- Low level interface devices ----------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index aa589efab211..06826197129f 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -29,14 +29,14 @@ enum EnergyCommands { CMND_POWERDELTA, CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH, - CMND_POWERCAL, CMND_POWERSET, CMND_VOLTAGECAL, CMND_VOLTAGESET, CMND_CURRENTCAL, CMND_CURRENTSET, + CMND_POWERCAL, CMND_POWERSET, CMND_VOLTAGECAL, CMND_VOLTAGESET, CMND_CURRENTCAL, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART, CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW, CMND_SAFEPOWER, CMND_SAFEPOWERHOLD, CMND_SAFEPOWERWINDOW }; const char kEnergyCommands[] PROGMEM = D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" - D_CMND_POWERCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGECAL "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTCAL "|" D_CMND_CURRENTSET "|" + D_CMND_POWERCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGECAL "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTCAL "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; @@ -451,6 +451,10 @@ boolean EnergyCommand() nvalue = Settings.energy_current_calibration; unit = UNIT_MICROSECOND; } + else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // dHz + nvalue = Settings.energy_frequency_calibration; + unit = UNIT_HERTZ; + } #if FEATURE_POWER_LIMIT else if (CMND_MAXPOWER == command_code) { diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index f74111f9d22c..a9620ce508f3 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -26,56 +26,383 @@ * and https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c \*********************************************************************************************/ -#define XNRG_04 4 - -#define MCP_START_FRAME 0xA5 -#define MCP_ACK_FRAME 0x06 -#define MCP_ERROR_NAK 0x15 -#define MCP_ERROR_CRC 0x51 - -#define MCP_SINGLE_WIRE 0xAB - -#define MCP_SET_ADDRESS 0x41 - -#define MCP_READ 0x4E -#define MCP_READ_16 0x52 -#define MCP_READ_32 0x44 - -#define MCP_WRITE 0x4D -#define MCP_WRITE_16 0x57 -#define MCP_WRITE_32 0x45 - -#define MCP_SAVE_REGISTERS 0x53 - -#define MCP_FLASH_READ 0x42 -#define MCP_FLASH_WRITE 0x50 - -uint32 mcp_system_configuration = 0x03000000; +#define XNRG_04 4 + +#define MCP_TIMEOUT 4 + +#define MCP_START_FRAME 0xA5 +#define MCP_ACK_FRAME 0x06 +#define MCP_ERROR_NAK 0x15 +#define MCP_ERROR_CRC 0x51 + +#define MCP_SINGLE_WIRE 0xAB + +#define MCP_SET_ADDRESS 0x41 + +#define MCP_READ 0x4E +#define MCP_READ_16 0x52 +#define MCP_READ_32 0x44 + +#define MCP_WRITE 0x4D +#define MCP_WRITE_16 0x57 +#define MCP_WRITE_32 0x45 + +#define MCP_SAVE_REGISTERS 0x53 + +#define MCP_CALIBRATION_BASE 0x0028 +#define MCP_CALIBRATION_LEN 52 + +#define MCP_FREQUENCY_REF_BASE 0x0094 +#define MCP_FREQUENCY_GAIN_BASE 0x00AE +#define MCP_FREQUENCY_LEN 4 + +#define EMTR_OUT_BASE 0x0004 +#define EMTR_OUT_LEN 28 + +#define MCP_FLASH_READ 0x42 +#define MCP_FLASH_WRITE 0x50 + +typedef struct mcp_calibration_registers_type { + uint16_t gain_current_rms; + uint16_t gain_voltage_rms; + uint16_t gain_active_power; + uint16_t gain_reactive_power; + sint32_t offset_current_rms; + sint32_t offset_active_power; + sint32_t offset_reactive_power; + sint16_t dc_offset_current; + sint16_t phase_compensation; + uint16_t apparent_power_divisor; + + uint32_t system_configuration; + uint16_t dio_configuration; + uint32_t range; + + uint32_t calibration_current; + uint16_t calibration_voltage; + uint32_t calibration_active_power; + uint32_t calibration_reactive_power; + uint16_t accumulation_interval; +} mcp_calibration_registers_type; +mcp_calibration_registers_type mcp_calibration_registers; + +typedef struct mcp_calibration_setpoint_type { + uint32_t calibration_current; + uint16_t calibration_voltage; + uint32_t calibration_active_power; + uint32_t calibration_reactive_power; + uint16_t line_frequency_ref; +} mcp_calibration_setpoint_type; +mcp_calibration_setpoint_type mcp_calibration_setpoint; + +typedef struct mcp_frequency_registers_type { + uint16_t line_frequency_ref; + uint16_t gain_line_frequency; +} mcp_frequency_registers_type; +mcp_frequency_registers_type mcp_frequency_registers; + +typedef struct mcp_output_registers_type { + uint32_t current_rms; + uint16_t voltage_rms; + uint32_t active_power; + uint32_t reactive_power; + uint32_t apparent_power; + sint16_t power_factor; + uint16_t line_frequency; + uint16_t thermistor_voltage; + uint16_t event_flag; + uint16_t system_status; +} mcp_output_registers_type; +mcp_output_registers_type mcp_output_registers; + +uint32_t mcp_system_configuration = 0x03000000; +uint16_t mcp_address = 0; uint8_t mcp_single_wire_active = 0; +uint8_t mcp_calibration_active = 0; +uint8_t mcp_init = 0; +uint8_t mcp_timeout = 0; +unsigned long mcp_kWhcounter = 0; /*********************************************************************************************\ * Olimex tools * https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c \*********************************************************************************************/ +uint8_t McpChecksum(uint8_t *data) +{ + uint8_t checksum = 0; + uint8_t offset = 0; + uint8_t len = data[1] -1; + + if (MCP_SINGLE_WIRE == data[0]) { + offset = 3; + len = 15; + } + for (byte i = offset; i < len; i++) { checksum += data[i]; } + return (MCP_SINGLE_WIRE == data[0]) ? ~checksum : checksum; +} -unsigned long McpExtractInt(uint8_t *data, uint8_t offset, uint8_t size) +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) { unsigned long result = 0; unsigned long pow = 1; for (byte i = 0; i < size; i++) { - result = result + data[offset + i] * pow; + result = result + (uint8_t)data[offset + i] * pow; pow = pow * 256; } return result; } +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) +{ + for (byte i = 0; i < size; i++) { + data[offset + i] = ((value >> (i * 8)) & 0xFF); + } +} + +void AddLogSerialSend(byte loglevel, uint8_t *buffer, int count) +{ + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL "Send")); + for (int i = 0; i < count; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++)); + } + AddLog(loglevel); +} + + +void McpSend(uint8_t *data) +{ + if (mcp_timeout) { return; } + mcp_timeout = MCP_TIMEOUT; + + data[0] = MCP_START_FRAME; + data[data[1] -1] = McpChecksum(data); + +// AddLogSerialSend(LOG_LEVEL_DEBUG_MORE, data, data[1]); + + for (byte i = 0; i < data[1]; i++) { + Serial.write(data[i]); + } +} + +uint32_t McpGetRange(uint8_t shift) +{ + return (mcp_calibration_registers.range >> shift) & 0xFF; +} + +void McpSetRange(uint8_t shift, uint32_t range) +{ + uint32_t old_range = McpGetRange(shift); + mcp_calibration_registers.range = mcp_calibration_registers.range ^ (old_range << shift); + mcp_calibration_registers.range = mcp_calibration_registers.range | (range << shift); +} + +bool McpCalibrationCalc(uint8_t range_shift) +{ + uint32_t measured; + uint32_t expected; + uint16_t *gain; + uint32_t new_gain; + + if (range_shift == 0) { + measured = mcp_output_registers.voltage_rms; + expected = mcp_calibration_registers.calibration_voltage; + gain = &(mcp_calibration_registers.gain_voltage_rms); + } else if (range_shift == 8) { + measured = mcp_output_registers.current_rms; + expected = mcp_calibration_registers.calibration_current; + gain = &(mcp_calibration_registers.gain_current_rms); + } else if (range_shift == 16) { + measured = mcp_output_registers.active_power; + expected = mcp_calibration_registers.calibration_active_power; + gain = &(mcp_calibration_registers.gain_active_power); + } else { + return false; + } + + if (measured == 0) { + return false; + } + + uint32_t range = McpGetRange(range_shift); + +calc: + new_gain = (*gain) * expected / measured; + + if (new_gain < 25000) { + range++; + if (measured > 6) { + measured = measured / 2; + goto calc; + } + } + + if (new_gain > 55000) { + range--; + measured = measured * 2; + goto calc; + } + + *gain = new_gain; + McpSetRange(range_shift, range); + + return true; +} + +void McpCalibrationReactivePower() +{ + mcp_calibration_registers.gain_reactive_power = mcp_calibration_registers.gain_reactive_power * mcp_calibration_setpoint.calibration_reactive_power / mcp_output_registers.reactive_power; +} + +void McpCalibrationLineFreqency() +{ + mcp_frequency_registers.gain_line_frequency = mcp_frequency_registers.gain_line_frequency * mcp_frequency_registers.line_frequency_ref / mcp_output_registers.line_frequency; +} + +void McpResetSetpoints() +{ + mcp_calibration_setpoint.calibration_active_power = 0; + mcp_calibration_setpoint.calibration_voltage = 0; + mcp_calibration_setpoint.calibration_current = 0; + mcp_calibration_setpoint.calibration_reactive_power = 0; + mcp_calibration_setpoint.line_frequency_ref = 0; +} + +/********************************************************************************************/ + +void McpGetAddress() +{ + // A5 07 41 00 26 52 65 + uint8_t data[7]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; // Set address pointer + data[3] = 0x00; // address + data[4] = 0x26; // address + data[5] = MCP_READ_16; // Read 2 bytes + + McpSend(data); + + // Receives 06 05 004D 58 +} + +void McpGetCalibration() +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = 4; + + // A5 08 41 00 28 4E 34 98 + uint8_t data[8]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; // Set address pointer + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address + data[5] = MCP_READ; // Read N bytes + data[6] = MCP_CALIBRATION_LEN; + + McpSend(data); + + // Receives 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 +} + +void McpSetCalibration() +{ + uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; // Set address pointer + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address + + data[5] = MCP_WRITE; // Write N bytes + data[6] = MCP_CALIBRATION_LEN; + + McpSetInt(mcp_calibration_registers.gain_current_rms, data, 0+7, 2); + McpSetInt(mcp_calibration_registers.gain_voltage_rms, data, 2+7, 2); + McpSetInt(mcp_calibration_registers.gain_active_power, data, 4+7, 2); + McpSetInt(mcp_calibration_registers.gain_reactive_power, data, 6+7, 2); + McpSetInt(mcp_calibration_registers.offset_current_rms, data, 8+7, 4); + McpSetInt(mcp_calibration_registers.offset_active_power, data, 12+7, 4); + McpSetInt(mcp_calibration_registers.offset_reactive_power, data, 16+7, 4); + McpSetInt(mcp_calibration_registers.dc_offset_current, data, 20+7, 2); + McpSetInt(mcp_calibration_registers.phase_compensation, data, 22+7, 2); + McpSetInt(mcp_calibration_registers.apparent_power_divisor, data, 24+7, 2); + + McpSetInt(mcp_calibration_registers.system_configuration, data, 26+7, 4); + McpSetInt(mcp_calibration_registers.dio_configuration, data, 30+7, 2); + McpSetInt(mcp_calibration_registers.range, data, 32+7, 4); + + McpSetInt(mcp_calibration_registers.calibration_current, data, 36+7, 4); + McpSetInt(mcp_calibration_registers.calibration_voltage, data, 40+7, 2); + McpSetInt(mcp_calibration_registers.calibration_active_power, data, 42+7, 4); + McpSetInt(mcp_calibration_registers.calibration_reactive_power, data, 46+7, 4); + McpSetInt(mcp_calibration_registers.accumulation_interval, data, 50+7, 2); + + data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; // Save registers to flash + data[MCP_CALIBRATION_LEN+8] = mcp_address; // Device address + + McpSend(data); +} + +void McpGetFrequency() +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = 4; + + // A5 0B 41 00 94 52 41 00 AE 52 18 + uint8_t data[11]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; // Set address pointer + data[3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address + data[4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address + + data[5] = MCP_READ_16; // Read register + + data[6] = MCP_SET_ADDRESS; // Set address pointer + data[7] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address + data[8] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address + + data[9] = MCP_READ_16; // Read register + + McpSend(data); +} + +void McpSetFrequency() +{ + // A5 11 41 00 94 57 C3 B4 41 00 AE 57 7E 46 53 4D 03 + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; // Set address pointer + data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address + data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address + + data[ 5] = MCP_WRITE_16; // Write register + data[ 6] = (mcp_frequency_registers.line_frequency_ref >> 8) & 0xFF; // line_frequency_ref high + data[ 7] = (mcp_frequency_registers.line_frequency_ref >> 0) & 0xFF; // line_frequency_ref low + + data[ 8] = MCP_SET_ADDRESS; // Set address pointer + data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address + data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address + + data[11] = MCP_WRITE_16; // Write register + data[12] = (mcp_frequency_registers.gain_line_frequency >> 8) & 0xFF; // gain_line_frequency high + data[13] = (mcp_frequency_registers.gain_line_frequency >> 0) & 0xFF; // gain_line_frequency low + + data[14] = MCP_SAVE_REGISTERS; // Save registers to flash + data[15] = mcp_address; // Device address + + McpSend(data); +} + void McpSetSystemConfiguration(uint16 interval) { + // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A uint8_t data[17]; - data[ 0] = MCP_START_FRAME; data[ 1] = sizeof(data); data[ 2] = MCP_SET_ADDRESS; // Set address pointer data[ 3] = 0x00; // address @@ -91,14 +418,8 @@ void McpSetSystemConfiguration(uint16 interval) data[13] = MCP_WRITE_16; // Write 2 bytes data[14] = (interval >> 8) & 0xFF; // interval data[15] = (interval >> 0) & 0xFF; // interval - uint8_t checksum = 0; - for (byte i = 0; i < sizeof(data) -1; i++) { checksum += (uint8_t)data[i]; } - data[16] = checksum; - // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A - AddLogSerial(LOG_LEVEL_DEBUG, data, sizeof(data)); - - for (byte i = 0; i < sizeof(data); i++) { Serial.write(data[i]); } + McpSend(data); } void McpSingleWireStart() @@ -109,9 +430,9 @@ void McpSingleWireStart() mcp_single_wire_active = 1; } -void McpSingleWireStop() +void McpSingleWireStop(uint8_t force) { - if ((mcp_system_configuration & (1 << 8)) == 0) { return; } + if (!force && ((mcp_system_configuration & (1 << 8)) == 0)) { return; } mcp_system_configuration = mcp_system_configuration & (~(1 << 8)); McpSetSystemConfiguration(2); // 4 mcp_single_wire_active = 0; @@ -119,10 +440,69 @@ void McpSingleWireStop() /********************************************************************************************/ -unsigned long mcp_current = 0; -unsigned long mcp_voltage = 0; -unsigned long mcp_power = 0; -unsigned long mcp_frequency = 0; +void McpAddressReceive() +{ + // 06 05 004D 58 + mcp_address = serial_in_buffer[2] * 256 + serial_in_buffer[3]; +} + +void McpParseCalibration() +{ + bool action = false; + + // 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 + mcp_calibration_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2); + mcp_calibration_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2); + mcp_calibration_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2); + mcp_calibration_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2); + mcp_calibration_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4); + mcp_calibration_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4); + mcp_calibration_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4); + mcp_calibration_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2); + mcp_calibration_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2); + mcp_calibration_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2); + + mcp_calibration_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4); + mcp_calibration_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2); + mcp_calibration_registers.range = McpExtractInt(serial_in_buffer, 34, 4); + + mcp_calibration_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4); + mcp_calibration_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2); + mcp_calibration_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4); + mcp_calibration_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4); + mcp_calibration_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2); + + if (mcp_calibration_setpoint.calibration_active_power) { + mcp_calibration_registers.calibration_active_power = mcp_calibration_setpoint.calibration_active_power; + if (McpCalibrationCalc(16)) { action = true; } + } + if (mcp_calibration_setpoint.calibration_voltage) { + mcp_calibration_registers.calibration_voltage = mcp_calibration_setpoint.calibration_voltage; + if (McpCalibrationCalc(0)) { action = true; } + } + if (mcp_calibration_setpoint.calibration_current) { + mcp_calibration_registers.calibration_current = mcp_calibration_setpoint.calibration_current; + if (McpCalibrationCalc(8)) { action = true; } + } + mcp_timeout = 0; + if (action) { McpSetCalibration(); } + McpResetSetpoints(); +} + +void McpParseFrequency() +{ + // 06 07 C350 8000 A0 + mcp_frequency_registers.line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3]; + mcp_frequency_registers.gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5]; + + if (mcp_calibration_setpoint.line_frequency_ref) { + mcp_frequency_registers.line_frequency_ref = mcp_calibration_setpoint.line_frequency_ref; + McpCalibrationLineFreqency(); + mcp_timeout = 0; + McpSetFrequency(); + } + McpResetSetpoints(); +} void McpParseData(uint8_t single_wire) { @@ -131,29 +511,31 @@ void McpParseData(uint8_t single_wire) // AB CD EF 51 06 00 00 B8 08 FC 0D 00 00 0A C4 11 // Header-- Current---- Volt- Power------ Freq- Ck - mcp_current = McpExtractInt((uint8_t*)serial_in_buffer, 3, 4); - mcp_voltage = McpExtractInt((uint8_t*)serial_in_buffer, 7, 2); - mcp_power = McpExtractInt((uint8_t*)serial_in_buffer, 9, 4); - mcp_frequency = McpExtractInt((uint8_t*)serial_in_buffer, 13, 2); + mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 3, 4); + mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 7, 2); + mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 9, 4); + mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 13, 2); } else { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35 + // 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7 // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck - mcp_current = McpExtractInt((uint8_t*)serial_in_buffer, 2, 4); - mcp_voltage = McpExtractInt((uint8_t*)serial_in_buffer, 6, 2); - mcp_power = McpExtractInt((uint8_t*)serial_in_buffer, 8, 4); - mcp_frequency = McpExtractInt((uint8_t*)serial_in_buffer, 22, 2); + mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 2, 4); + mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); + mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 8, 4); + mcp_output_registers.reactive_power = McpExtractInt(serial_in_buffer, 12, 4); + mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 22, 2); } if (energy_power_on) { // Powered on - energy_frequency = (float)mcp_frequency / 1000; - energy_voltage = (float)mcp_voltage / 10; - energy_power = (float)mcp_power / 100; + energy_frequency = (float)mcp_output_registers.line_frequency / 1000; + energy_voltage = (float)mcp_output_registers.voltage_rms / 10; + energy_power = (float)mcp_output_registers.active_power / 100; if (0 == energy_power) { energy_current = 0; } else { - energy_current = (float)mcp_current / 10000; + energy_current = (float)mcp_output_registers.current_rms / 10000; } } else { // Powered off energy_frequency = 0; @@ -165,61 +547,85 @@ void McpParseData(uint8_t single_wire) bool McpSerialInput() { - Settings.flag.mqtt_serial = 0; // Disable possible SerialReceive handling - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (MCP_ERROR_CRC == serial_in_buffer[0]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); - return 1; + unsigned long start = millis(); + while (millis() - start < 20) { + yield(); + if (Serial.available()) { + serial_in_buffer[serial_in_byte_counter++] = Serial.read(); + start = millis(); + } } - else if (MCP_ACK_FRAME == serial_in_buffer[0]) { - if ((serial_in_byte_counter > 1) && (serial_in_byte_counter == serial_in_buffer[1])) { - AddLogSerial(LOG_LEVEL_DEBUG_MORE); + AddLogSerial(LOG_LEVEL_DEBUG_MORE); + + if (1 == serial_in_byte_counter) { + if (MCP_ERROR_CRC == serial_in_buffer[0]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE)); + mcp_timeout = 0; + } + else if (MCP_ERROR_NAK == serial_in_buffer[0]) { +// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck")); + mcp_timeout = 0; + } + } + else if (MCP_ACK_FRAME == serial_in_buffer[0]) { + if (serial_in_byte_counter == serial_in_buffer[1]) { - uint8_t checksum = 0; - for (byte i = 0; i < serial_in_byte_counter -1; i++) { checksum += (uint8_t)serial_in_buffer[i]; } - if (checksum != serial_in_buffer[serial_in_byte_counter -1]) { + if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) { AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); } else { + if (5 == serial_in_buffer[1]) { McpAddressReceive(); } if (25 == serial_in_buffer[1]) { McpParseData(0); } + if (MCP_CALIBRATION_LEN + 3 == serial_in_buffer[1]) { McpParseCalibration(); } + if (MCP_FREQUENCY_LEN + 3 == serial_in_buffer[1]) { McpParseFrequency(); } } - return 1; + } + mcp_timeout = 0; } else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) { if (serial_in_byte_counter == 16) { - AddLogSerial(LOG_LEVEL_DEBUG_MORE); - - uint8_t checksum = 0; - for (byte i = 3; i < serial_in_byte_counter -1; i++) { checksum += (uint8_t)serial_in_buffer[i]; } -// if (~checksum != serial_in_buffer[serial_in_byte_counter -1]) { -// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); -// } else { + if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { McpParseData(1); -// } - return 1; + } + } + mcp_timeout = 0; } - else { - return 1; - } - serial_in_byte = 0; // Discard - return 0; + return 1; } /********************************************************************************************/ void McpEverySecond() { - if (!mcp_single_wire_active) { - char get_state[] = "A5084100044E1656"; - SerialSendRaw(get_state, sizeof(get_state)); + uint8_t get_state[] = { 0xA5, 0x08, 0x41, 0x00, 0x04, 0x4E, 0x16, 0x00 }; + + if (mcp_timeout) { + mcp_timeout--; + } + else if (mcp_calibration_active) { + mcp_calibration_active--; + } + else if (mcp_init) { + McpSingleWireStop(1); + mcp_init = 0; + } + else if (!mcp_address) { + McpGetAddress(); + } + else if (!mcp_single_wire_active) { + McpSend(get_state); } - energy_kWhtoday += (energy_power / 36); - EnergyUpdateToday(); + if (mcp_output_registers.active_power) { + energy_kWhtoday_delta += ((mcp_output_registers.active_power * 10) / 36); + EnergyUpdateToday(); + } } void McpSnsInit() @@ -235,6 +641,9 @@ void McpDrvInit() digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's baudrate = 4800; energy_calc_power_factor = 1; // Calculate power factor from data + mcp_timeout = 4; // Wait for initialization + mcp_init = 1; // Execute initial setup + McpResetSetpoints(); energy_flg = XNRG_04; } } @@ -246,20 +655,39 @@ boolean McpCommand() if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { + // MCP Debug commands - PowerCal + if (1 == XdrvMailbox.payload) { McpSingleWireStart(); } + if (2 == XdrvMailbox.payload) { McpSingleWireStop(0); } + if (3 == XdrvMailbox.payload) { McpGetAddress(); } + + serviced = false; } else if (CMND_POWERSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && power_cycle) { -// Settings.energy_power_calibration = (XdrvMailbox.payload * power_cycle) / CSE_PREF; + if (XdrvMailbox.data_len && mcp_output_registers.active_power) { + Settings.energy_power_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); + mcp_calibration_setpoint.calibration_active_power = Settings.energy_power_calibration; + McpGetCalibration(); } } else if (CMND_VOLTAGESET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && voltage_cycle) { -// Settings.energy_voltage_calibration = (XdrvMailbox.payload * voltage_cycle) / CSE_UREF; + if (XdrvMailbox.data_len && mcp_output_registers.voltage_rms) { + Settings.energy_voltage_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + mcp_calibration_setpoint.calibration_voltage = Settings.energy_voltage_calibration; + McpGetCalibration(); } } else if (CMND_CURRENTSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && current_cycle) { -// Settings.energy_current_calibration = (XdrvMailbox.payload * current_cycle) / 1000; + if (XdrvMailbox.data_len && mcp_output_registers.current_rms) { + Settings.energy_current_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + mcp_calibration_setpoint.calibration_current = Settings.energy_current_calibration; + McpGetCalibration(); + } + } + else if (CMND_FREQUENCYSET == energy_command_code) { + if (XdrvMailbox.data_len && mcp_output_registers.line_frequency) { + Settings.energy_frequency_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + mcp_calibration_setpoint.line_frequency_ref = Settings.energy_frequency_calibration; + McpGetFrequency(); } } else serviced = false; // Unknown command From f7a8b1f9234ee628596b0b00d3736753016cae3b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 21 Sep 2018 16:02:56 +0200 Subject: [PATCH 17/34] Shelly2 clean up Shelly2 clean up --- sonoff/i18n.h | 2 +- sonoff/user_config.h | 2 +- sonoff/xdrv_03_energy.ino | 2 +- sonoff/xnrg_04_mcp39f501.ino | 36 ++++++++++-------------------------- 4 files changed, 13 insertions(+), 29 deletions(-) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 4ea5ab3fea3f..b0a7ec0718c3 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -442,7 +442,7 @@ const char kUnitNames[] PROGMEM = D_UNIT_VOLT "|" D_UNIT_WATT "|" D_UNIT_WATTHOUR "|" - "d" D_UNIT_HERTZ ; + D_UNIT_HERTZ ; const char S_JSON_COMMAND_NVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%d %s\"}"; const char S_JSON_COMMAND_LVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%lu %s\"}"; diff --git a/sonoff/user_config.h b/sonoff/user_config.h index ef3e66fc21b6..438983a66c8f 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -348,7 +348,7 @@ // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM2 // Add support for PZEM003,014,016,017 Energy monitor (+1k1 code) -#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k2 code) +#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) // -- Low level interface devices ----------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 06826197129f..860183f49ae4 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -451,7 +451,7 @@ boolean EnergyCommand() nvalue = Settings.energy_current_calibration; unit = UNIT_MICROSECOND; } - else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // dHz + else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz nvalue = Settings.energy_frequency_calibration; unit = UNIT_HERTZ; } diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index a9620ce508f3..c34846edf064 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -56,12 +56,6 @@ #define MCP_FREQUENCY_GAIN_BASE 0x00AE #define MCP_FREQUENCY_LEN 4 -#define EMTR_OUT_BASE 0x0004 -#define EMTR_OUT_LEN 28 - -#define MCP_FLASH_READ 0x42 -#define MCP_FLASH_WRITE 0x50 - typedef struct mcp_calibration_registers_type { uint16_t gain_current_rms; uint16_t gain_voltage_rms; @@ -161,16 +155,6 @@ void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) } } -void AddLogSerialSend(byte loglevel, uint8_t *buffer, int count) -{ - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SERIAL "Send")); - for (int i = 0; i < count; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer++)); - } - AddLog(loglevel); -} - - void McpSend(uint8_t *data) { if (mcp_timeout) { return; } @@ -179,7 +163,7 @@ void McpSend(uint8_t *data) data[0] = MCP_START_FRAME; data[data[1] -1] = McpChecksum(data); -// AddLogSerialSend(LOG_LEVEL_DEBUG_MORE, data, data[1]); +// AddLogSerial(LOG_LEVEL_DEBUG_MORE, data, data[1]); for (byte i = 0; i < data[1]; i++) { Serial.write(data[i]); @@ -252,7 +236,7 @@ calc: void McpCalibrationReactivePower() { - mcp_calibration_registers.gain_reactive_power = mcp_calibration_registers.gain_reactive_power * mcp_calibration_setpoint.calibration_reactive_power / mcp_output_registers.reactive_power; + mcp_calibration_registers.gain_reactive_power = mcp_calibration_registers.gain_reactive_power * mcp_calibration_registers.calibration_reactive_power / mcp_output_registers.reactive_power; } void McpCalibrationLineFreqency() @@ -605,6 +589,11 @@ void McpEverySecond() { uint8_t get_state[] = { 0xA5, 0x08, 0x41, 0x00, 0x04, 0x4E, 0x16, 0x00 }; + if (mcp_output_registers.active_power) { + energy_kWhtoday_delta += ((mcp_output_registers.active_power * 10) / 36); + EnergyUpdateToday(); + } + if (mcp_timeout) { mcp_timeout--; } @@ -621,11 +610,6 @@ void McpEverySecond() else if (!mcp_single_wire_active) { McpSend(get_state); } - - if (mcp_output_registers.active_power) { - energy_kWhtoday_delta += ((mcp_output_registers.active_power * 10) / 36); - EnergyUpdateToday(); - } } void McpSnsInit() @@ -656,9 +640,9 @@ boolean McpCommand() if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { // MCP Debug commands - PowerCal - if (1 == XdrvMailbox.payload) { McpSingleWireStart(); } - if (2 == XdrvMailbox.payload) { McpSingleWireStop(0); } - if (3 == XdrvMailbox.payload) { McpGetAddress(); } +// if (1 == XdrvMailbox.payload) { McpSingleWireStart(); } +// if (2 == XdrvMailbox.payload) { McpSingleWireStop(0); } +// if (3 == XdrvMailbox.payload) { McpGetAddress(); } serviced = false; } From 557545cb56d774bfdfdeb32874a779b649a0fa2a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 21 Sep 2018 16:19:31 +0200 Subject: [PATCH 18/34] Cleanup Driver Cleanup Driver --- sonoff/sonoff.ino | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 84293f5bcff9..b5d752a3bd3f 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -679,7 +679,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); } - else if (CMND_SENSOR == command_code) { + else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) { XdrvMailbox.index = index; XdrvMailbox.data_len = data_len; XdrvMailbox.payload16 = payload16; @@ -687,18 +687,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) XdrvMailbox.grpflg = grpflg; XdrvMailbox.topic = command; XdrvMailbox.data = dataBuf; - XsnsCall(FUNC_COMMAND); -// if (!XsnsCall(FUNC_COMMAND)) type = NULL; - } - else if (CMND_DRIVER == command_code) { - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload16 = payload16; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.topic = command; - XdrvMailbox.data = dataBuf; - XdrvCall(FUNC_COMMAND); + if (CMND_SENSOR == command_code) { + XsnsCall(FUNC_COMMAND); + } else { + XdrvCall(FUNC_COMMAND); + } } else if ((CMND_SETOPTION == command_code) && (index < 82)) { byte ptype; From 4992b4368916e7a8cdb8a35b08fee75071197379 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Fri, 21 Sep 2018 23:43:58 +0200 Subject: [PATCH 19/34] PCA9685 I2C Hardware PWM - Experimental --- sonoff/user_config.h | 2 + sonoff/xdrv_15_pca9685.ino | 184 +++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 sonoff/xdrv_15_pca9685.ino diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 438983a66c8f..ee374d41ca28 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -298,6 +298,8 @@ // #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) // #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) // #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) +// #define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) +// #define USE_PCA9685_ADDR 0x40 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) // #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) // #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code) // #define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino new file mode 100644 index 000000000000..e53b8d17f542 --- /dev/null +++ b/sonoff/xdrv_15_pca9685.ino @@ -0,0 +1,184 @@ +/* + xdrv_15_pca9685.ino - Support for I2C PCA9685 12bit 16 pin hardware PWM driver + + Copyright (C) 2018 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_PCA9685 + +#define XDRV_15 15 + +#define PCA9685_REG_MODE1 0x00 +#define PCA9685_REG_LED0_ON_L 0x06 +#define PCA9685_REG_PRE_SCALE 0xFE + +uint8_t pca9685_detected = 0; +uint16_t pca9685_freq = 50; + +void PCA9685_Detect(void) +{ + if (pca9685_detected) { return; } + + uint8_t buffer; + + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + if (0x20 == buffer) { + pca9685_detected = 1; + snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "PCA9685", USE_PCA9685_ADDR); + AddLog(LOG_LEVEL_DEBUG); + PCA9685_Reset(); // Reset the controller + } + } + } +} + +void PCA9685_Reset(void) +{ + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); + PCA9685_SetPWMfreq(50); + for (uint8_t pin=0;pin<16;pin++) { + PCA9685_SetPWM(pin,0,false); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); +} + +void PCA9685_SetPWMfreq(double freq) { +/* + 7.3.5 from datasheet + prescale value = round(25000000/(4096*freq))-1; + */ + pca9685_freq=freq; + uint8_t pre_scale_osc = round(25000000/(4096*freq))-1; + if (1526 == freq) pre_scale_osc=0xFF; // force setting for 24hz because rounding causes 1526 to be 254 + uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); // read current value of MODE1 register + uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; // Determine register value to put PCA to sleep + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); // Let's sleep a little + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); // Set the pre-scaler + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); // Reset MODE1 register to original state and enable auto increment +} + +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { + uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; + uint32_t led_data = 0; + I2cWrite8(USE_PCA9685_ADDR, led_reg, on); + I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); + I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); + I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); +} + +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { + if (4096 == pwm) { + PCA9685_SetPWM_Reg(pin, 4096, 0); // Special use additional bit causes channel to turn on completely without PWM + } else { + PCA9685_SetPWM_Reg(pin, 0, pwm); + } +} + +bool PCA9685_Command(void) +{ + boolean serviced = true; + boolean validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint8_t ca=0;ca 1) { + uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((new_freq >= 24) && (new_freq <= 1526)) { + PCA9685_SetPWMfreq(new_freq); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}")); + return serviced; + } + } else { // No parameter was given for setfreq, so we return current setting + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); + return serviced; + } + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (paramcount > 2) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { + PCA9685_SetPWM(pin, 4096, false); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); + serviced = true; + return serviced; + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { + PCA9685_SetPWM(pin, 0, false); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); + serviced = true; + return serviced; + } + uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { + PCA9685_SetPWM(pin, pwm, false); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); + serviced = true; + return serviced; + } + } + } + } + return serviced; +} + +boolean Xdrv15(byte function) +{ + boolean result = false; + + if (i2c_flg) { + switch (function) { + case FUNC_MQTT_DATA: + break; + case FUNC_EVERY_SECOND: + PCA9685_Detect(); + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_JSON_APPEND: + break; + case FUNC_COMMAND: + if (XDRV_15 == XdrvMailbox.index) { + PCA9685_Command(); + } + break; + case FUNC_WEB_APPEND: + break; + default: + break; + } + } + return result; +} + +#endif // USE_PCA9685 +#endif // USE_IC2 From 4ff5667c2adeac42078c57d4dec38522d9ab7109 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sat, 22 Sep 2018 00:17:08 +0200 Subject: [PATCH 20/34] PCA9685 I2C Hardware PWM - Experimental --- sonoff/user_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index ee374d41ca28..b93e4421115f 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -299,7 +299,7 @@ // #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) // #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) // #define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) -// #define USE_PCA9685_ADDR 0x40 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) +// #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup) // #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) // #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code) // #define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) From 20e8d76877123ffd0584e929c35d087e092d410c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Sep 2018 12:04:38 +0200 Subject: [PATCH 21/34] TSL2561 driver fixes Rewrite TSL2561 driver to fix some issues (#3681) --- sonoff/_changelog.ino | 1 + sonoff/xsns_16_tsl2561.ino | 91 ++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 72293c30d8e5..908fd262a158 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /* 6.2.1.5 20180921 * Add authentication to HTTP web pages * Add energy monitoring to Shelly2 (#2789) + * Rewrite TSL2561 driver to fix some issues (#3681) * * 6.2.1.4 20180916 * Add command SerialSend5 to send raw serial data like "A5074100545293" diff --git a/sonoff/xsns_16_tsl2561.ino b/sonoff/xsns_16_tsl2561.ino index ac0536c6aaf3..9f785e3e406a 100644 --- a/sonoff/xsns_16_tsl2561.ino +++ b/sonoff/xsns_16_tsl2561.ino @@ -31,17 +31,67 @@ Tsl2561 Tsl(Wire); +uint8_t tsl2561_type = 0; +uint8_t tsl2561_valid = 0; +uint32_t tsl2561_milliLux = 0; +char tsl2561_types[] = "TSL2561"; + +bool Tsl2561Read() +{ + if (tsl2561_valid) { tsl2561_valid--; } + + uint8_t id; + bool gain; + Tsl2561::exposure_t exposure; + uint16_t scaledFull, scaledIr; + uint32_t full, ir; + + if (Tsl.available()) { + if (Tsl.on()) { + if (Tsl.id(id) + && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) + && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) + && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + } else{ + tsl2561_milliLux = 0; + } + } + } + tsl2561_valid = SENSOR_MAX_MISS; + return true; +} + void Tsl2561Detect() { + if (tsl2561_type) { return; } + if (!Tsl.available()) { Tsl.begin(); if (Tsl.available()) { - snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "TSL2561", Tsl.address()); + tsl2561_type = 1; + snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, tsl2561_types, Tsl.address()); AddLog(LOG_LEVEL_DEBUG); } } } +void Tsl2561EverySecond() +{ + if (90 == (uptime %100)) { + // 1mS + Tsl2561Detect(); + } + else if (uptime &3) { + // 1mS + if (tsl2561_type) { + if (!Tsl2561Read()) { + AddLogMissed(tsl2561_types, tsl2561_valid); +// if (!tsl2561_valid) { tsl2561_type = 0; } + } + } + } +} + #ifdef USE_WEBSERVER const char HTTP_SNS_TSL2561[] PROGMEM = "%s{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; // {s} = , {m} = , {e} = @@ -49,37 +99,17 @@ const char HTTP_SNS_TSL2561[] PROGMEM = void Tsl2561Show(boolean json) { - uint8_t id; - bool gain; - Tsl2561::exposure_t exposure; - uint16_t scaledFull, scaledIr; - uint32_t full, ir; - uint32_t milliLux; - - if (Tsl.available()) { - if (Tsl.on()) { - if( Tsl.id(id) - && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) - && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) - && Tsl2561Util::milliLux(full, ir, milliLux, Tsl2561::packageCS(id))) { - -// snprintf_P(log_data, sizeof(log_data), PSTR(D_ILLUMINANCE " g:%d, e:%d, f:%u, i:%u -> %u.%03u " D_UNIT_LUX), -// gain, exposure, full, ir, milliLux/1000, milliLux%1000); -// AddLog(LOG_LEVEL_DEBUG); - - if (json) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), - mqtt_data, milliLux/1000, milliLux%1000); + if (tsl2561_valid) { + if (json) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), + mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); #ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, (milliLux+500)/1000); + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } #endif // USE_DOMOTICZ #ifdef USE_WEBSERVER - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, milliLux/1000, milliLux%1000); + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); #endif // USE_WEBSERVER - } - } - Tsl.off(); } } } @@ -96,9 +126,12 @@ boolean Xsns16(byte function) if (i2c_flg) { switch (function) { - case FUNC_PREP_BEFORE_TELEPERIOD: + case FUNC_INIT: Tsl2561Detect(); break; + case FUNC_EVERY_SECOND: + Tsl2561EverySecond(); + break; case FUNC_JSON_APPEND: Tsl2561Show(1); break; From 57e7005b4898aaeb722444182123bff2651d86ca Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Sep 2018 12:18:13 +0200 Subject: [PATCH 22/34] Fix TSL2561 timeslot --- sonoff/xsns_16_tsl2561.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonoff/xsns_16_tsl2561.ino b/sonoff/xsns_16_tsl2561.ino index 9f785e3e406a..61308aa206c5 100644 --- a/sonoff/xsns_16_tsl2561.ino +++ b/sonoff/xsns_16_tsl2561.ino @@ -81,8 +81,8 @@ void Tsl2561EverySecond() // 1mS Tsl2561Detect(); } - else if (uptime &3) { - // 1mS + else if (!(uptime %2)) { // Update every 2 seconds + // ?mS - 4Sec if (tsl2561_type) { if (!Tsl2561Read()) { AddLogMissed(tsl2561_types, tsl2561_valid); From 7a767f104cd2a9e1626bbcd1ee079f541086367f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Sep 2018 15:09:53 +0200 Subject: [PATCH 23/34] 6.2.1.6 Remove some commands 6.2.1.6 20180922 * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet * Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) --- sonoff/_changelog.ino | 7 ++++++- sonoff/i18n.h | 3 --- sonoff/sonoff_version.h | 2 +- sonoff/xdrv_03_energy.ino | 35 ++++++++--------------------------- sonoff/xnrg_01_hlw8012.ino | 17 +++++++---------- sonoff/xnrg_02_cse7766.ino | 17 +++++++---------- sonoff/xnrg_04_mcp39f501.ino | 11 +---------- 7 files changed, 30 insertions(+), 62 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 908fd262a158..2b954e151a17 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,9 @@ -/* 6.2.1.5 20180921 +/* 6.2.1.6 20180922 + * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet + * Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet + * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) + * + * 6.2.1.5 20180921 * Add authentication to HTTP web pages * Add energy monitoring to Shelly2 (#2789) * Rewrite TSL2561 driver to fix some issues (#3681) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index a45d7687d22a..80f757447f28 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -287,11 +287,8 @@ #define D_CMND_CURRENTLOW "CurrentLow" #define D_CMND_CURRENTHIGH "CurrentHigh" #define D_CMND_ENERGYRESET "EnergyReset" -#define D_CMND_POWERCAL "PowerCal" #define D_CMND_POWERSET "PowerSet" -#define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_VOLTAGESET "VoltageSet" -#define D_CMND_CURRENTCAL "CurrentCal" #define D_CMND_CURRENTSET "CurrentSet" #define D_CMND_FREQUENCYSET "FrequencySet" #define D_CMND_MAXPOWER "MaxPower" diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 43253e40ff03..228a48d92459 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020105 +#define VERSION 0x06020106 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 860183f49ae4..3ccad7df4ae3 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -29,14 +29,14 @@ enum EnergyCommands { CMND_POWERDELTA, CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH, - CMND_POWERCAL, CMND_POWERSET, CMND_VOLTAGECAL, CMND_VOLTAGESET, CMND_CURRENTCAL, CMND_CURRENTSET, CMND_FREQUENCYSET, + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART, CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW, CMND_SAFEPOWER, CMND_SAFEPOWERHOLD, CMND_SAFEPOWERWINDOW }; const char kEnergyCommands[] PROGMEM = D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" - D_CMND_POWERCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGECAL "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTCAL "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ; @@ -415,45 +415,21 @@ boolean EnergyCommand() command, energy_total_chr, energy_yesterday_chr, energy_daily_chr); status_flag = 1; } - else if ((CMND_POWERCAL == command_code) && XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) { - Settings.energy_power_calibration = (XdrvMailbox.payload > 4000) ? XdrvMailbox.payload : HLW_PREF_PULSE; // HLW = 12530, CSE = 5364 - } - nvalue = Settings.energy_power_calibration; - unit = UNIT_MICROSECOND; - } - else if ((CMND_VOLTAGECAL == command_code) && XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) { - Settings.energy_voltage_calibration = (XdrvMailbox.payload > 999) ? XdrvMailbox.payload : HLW_UREF_PULSE; // HLW = 1950, CSE = 1912 - } - nvalue = Settings.energy_voltage_calibration; - unit = UNIT_MICROSECOND; - } - else if ((CMND_CURRENTCAL == command_code) && XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) { - Settings.energy_current_calibration = (XdrvMailbox.payload > 1100) ? XdrvMailbox.payload : HLW_IREF_PULSE; // HLW = 3500, CSE = 16140 - } - nvalue = Settings.energy_current_calibration; - unit = UNIT_MICROSECOND; - } else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt - snprintf_P(command, sizeof(command), PSTR(D_CMND_POWERCAL)); nvalue = Settings.energy_power_calibration; unit = UNIT_MICROSECOND; } else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt - snprintf_P(command, sizeof(command), PSTR(D_CMND_VOLTAGECAL)); nvalue = Settings.energy_voltage_calibration; unit = UNIT_MICROSECOND; } else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere - snprintf_P(command, sizeof(command), PSTR(D_CMND_CURRENTCAL)); nvalue = Settings.energy_current_calibration; unit = UNIT_MICROSECOND; } else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz nvalue = Settings.energy_frequency_calibration; - unit = UNIT_HERTZ; + unit = UNIT_MICROSECOND; } #if FEATURE_POWER_LIMIT @@ -518,6 +494,11 @@ boolean EnergyCommand() else serviced = false; // Unknown command if (serviced && !status_flag) { + + if (UNIT_MICROSECOND == unit) { + snprintf_P(command, sizeof(command), PSTR("%sCal"), command); + } + if (Settings.flag.value_units) { snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_LVALUE_SPACE_UNIT, command, nvalue, GetTextIndexed(sunit, sizeof(sunit), unit, kUnitNames)); } else { diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 6ef196fb1eeb..5140d65969ec 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -227,22 +227,19 @@ boolean HlwCommand() { boolean serviced = true; - if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { - - } - else if (CMND_POWERSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && hlw_cf_pulse_length) { - Settings.energy_power_calibration = (XdrvMailbox.payload * 10 * hlw_cf_pulse_length) / hlw_power_ratio; + if (CMND_POWERSET == energy_command_code) { + if (XdrvMailbox.data_len && hlw_cf_pulse_length) { + Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_pulse_length) / hlw_power_ratio; } } else if (CMND_VOLTAGESET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && hlw_cf1_voltage_pulse_length) { - Settings.energy_voltage_calibration = (XdrvMailbox.payload * 10 * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio; + if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) { + Settings.energy_voltage_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio; } } else if (CMND_CURRENTSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && hlw_cf1_current_pulse_length) { - Settings.energy_current_calibration = (XdrvMailbox.payload * hlw_cf1_current_pulse_length) / hlw_current_ratio; + if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) { + Settings.energy_current_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio; } } else serviced = false; // Unknown command diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index 84535cf96dcf..a433bc4a45e4 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -195,22 +195,19 @@ boolean CseCommand() { boolean serviced = true; - if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { - - } - else if (CMND_POWERSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && power_cycle) { - Settings.energy_power_calibration = (XdrvMailbox.payload * power_cycle) / CSE_PREF; + if (CMND_POWERSET == energy_command_code) { + if (XdrvMailbox.data_len && power_cycle) { + Settings.energy_power_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF; } } else if (CMND_VOLTAGESET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && voltage_cycle) { - Settings.energy_voltage_calibration = (XdrvMailbox.payload * voltage_cycle) / CSE_UREF; + if (XdrvMailbox.data_len && voltage_cycle) { + Settings.energy_voltage_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF; } } else if (CMND_CURRENTSET == energy_command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && current_cycle) { - Settings.energy_current_calibration = (XdrvMailbox.payload * current_cycle) / 1000; + if (XdrvMailbox.data_len && current_cycle) { + Settings.energy_current_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * current_cycle) / 1000; } } else serviced = false; // Unknown command diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index c34846edf064..05d55a425c68 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -637,16 +637,7 @@ boolean McpCommand() { boolean serviced = true; - if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) { - - // MCP Debug commands - PowerCal -// if (1 == XdrvMailbox.payload) { McpSingleWireStart(); } -// if (2 == XdrvMailbox.payload) { McpSingleWireStop(0); } -// if (3 == XdrvMailbox.payload) { McpGetAddress(); } - - serviced = false; - } - else if (CMND_POWERSET == energy_command_code) { + if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_output_registers.active_power) { Settings.energy_power_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); mcp_calibration_setpoint.calibration_active_power = Settings.energy_power_calibration; From 544abec7bf836ce945b2b65770d1895d07017951 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 22 Sep 2018 15:42:18 +0200 Subject: [PATCH 24/34] Set force_update to true for buttons --- sonoff/xdrv_12_home_assistant.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index 5c8a2a7cf1e4..b085947ae723 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -19,7 +19,7 @@ #ifdef USE_HOME_ASSISTANT -const char HASS_DISCOVER_SWITCH[] PROGMEM = +const char HASS_DISCOVER_RELAY[] PROGMEM = "{\"name\":\"%s\"," // dualr2 1 "\"command_topic\":\"%s\"," // cmnd/dualr2/POWER2 "\"state_topic\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",") @@ -39,7 +39,8 @@ const char HASS_DISCOVER_BUTTON[] PROGMEM = // "\"optimistic\":\"false\"," // false is Hass default when state_topic is set "\"availability_topic\":\"%s\"," // tele/dualr2/LWT "\"payload_available\":\"" D_ONLINE "\"," // Online - "\"payload_not_available\":\"" D_OFFLINE "\""; // Offline + "\"payload_not_available\":\"" D_OFFLINE "\"," // Offline + "\"force_update\":true"; const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = "%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer @@ -102,7 +103,7 @@ void HAssDiscoverRelay() GetTopic_P(command_topic, CMND, mqtt_topic, value_template); GetTopic_P(state_topic, STAT, mqtt_topic, S_RSLT_RESULT); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SWITCH, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); + snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic); if (is_light) { char brightness_command_topic[TOPSZ]; From 665a4abc47cbee81eb32cf5bcd23782ee044561b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Sep 2018 16:09:13 +0200 Subject: [PATCH 25/34] Fix Pow R2 and S31 low power * Add power value below 5W to Sonoff Pow R2 and S31 (#3745) * Add force_update to Home Assistant discovery (#3873) --- sonoff/_changelog.ino | 2 ++ sonoff/xnrg_02_cse7766.ino | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 2b954e151a17..cb72ff407bd0 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -2,6 +2,8 @@ * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet * Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) + * Add power value below 5W to Sonoff Pow R2 and S31 (#3745) + * Add force_update to Home Assistant discovery (#3873) * * 6.2.1.5 20180921 * Add authentication to HTTP web pages diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index a433bc4a45e4..8ba1b146efcc 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -27,6 +27,8 @@ #define XNRG_02 2 +#define CSE_MAX_INVALID_POWER 128 // Number of invalid power receipts before deciding active power is zero + #define CSE_NOT_CALIBRATED 0xAA #define CSE_PULSES_NOT_INITIALIZED -1 @@ -42,10 +44,12 @@ long power_cycle = 0; unsigned long power_cycle_first = 0; long cf_pulses = 0; long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; +uint8_t cse_power_invalid = CSE_MAX_INVALID_POWER; void CseReceived() { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - Power not valid (load below 5W) // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck @@ -89,10 +93,11 @@ void CseReceived() energy_voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)voltage_cycle; } if (adjustement & 0x10) { // Power valid + cse_power_invalid = 0; if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range energy_power = 0; } else { - if (0 == power_cycle_first) power_cycle_first = power_cycle; // Skip first incomplete power_cycle + if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle if (power_cycle_first != power_cycle) { power_cycle_first = -1; energy_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle; @@ -101,8 +106,12 @@ void CseReceived() } } } else { - power_cycle_first = 0; - energy_power = 0; // Powered on but no load + if (cse_power_invalid < CSE_MAX_INVALID_POWER) { // Allow measurements down to about 1W + cse_power_invalid++; + } else { + power_cycle_first = 0; + energy_power = 0; // Powered on but no load + } } if (adjustement & 0x20) { // Current valid if (0 == energy_power) { From f05fc71d675101572a9fbd7ac90ead0542537528 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Sep 2018 17:37:49 +0200 Subject: [PATCH 26/34] Fix rule trigger POWER1#STATE Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856) --- sonoff/_changelog.ino | 1 + sonoff/sonoff.h | 2 +- sonoff/sonoff.ino | 6 +++++- sonoff/xdrv_10_rules.ino | 1 + sonoff/xdrv_interface.ino | 8 -------- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index cb72ff407bd0..b220dc4b3148 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -4,6 +4,7 @@ * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) * Add power value below 5W to Sonoff Pow R2 and S31 (#3745) * Add force_update to Home Assistant discovery (#3873) + * Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856) * * 6.2.1.5 20180921 * Add authentication to HTTP web pages diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 80356d475b27..c3d514f0cb76 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -203,7 +203,7 @@ enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_R enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; enum XsnsFunctions {FUNC_PRE_INIT, FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, FUNC_PREP_BEFORE_TELEPERIOD, - FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SHOW_SENSOR, + FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_RULE_INIT_POWER, FUNC_SHOW_SENSOR, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM}; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index b5d752a3bd3f..a405fe47ba7f 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -335,7 +335,8 @@ void SetDevicePower(power_t rpower, int source) } } - XdrvSetPower(rpower); + XdrvMailbox.index = rpower; + XdrvCall(FUNC_SET_POWER); if ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) { Serial.write(0xA0); @@ -2613,6 +2614,9 @@ void setup() } } + XdrvMailbox.index = power; + XdrvCall(FUNC_RULE_INIT_POWER); + blink_powersave = power; snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s (" D_CMND_TOPIC " %s, " D_FALLBACK " %s, " D_CMND_GROUPTOPIC " %s) " D_VERSION " %s-" ARDUINO_ESP8266_RELEASE), diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 930bde7d5e6e..5ef59c037b0e 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -628,6 +628,7 @@ boolean Xdrv10(byte function) case FUNC_EVERY_SECOND: RulesEverySecond(); break; + case FUNC_RULE_INIT_POWER: case FUNC_SET_POWER: RulesSetPower(); break; diff --git a/sonoff/xdrv_interface.ino b/sonoff/xdrv_interface.ino index 43a391076062..a88d3b5f850e 100644 --- a/sonoff/xdrv_interface.ino +++ b/sonoff/xdrv_interface.ino @@ -153,14 +153,6 @@ boolean XdrvCommand(uint8_t grpflg, char *type, uint16_t index, char *dataBuf, u return XdrvCall(FUNC_COMMAND); } -void XdrvSetPower(power_t mpower) -{ -// XdrvMailbox.valid = 1; - XdrvMailbox.index = mpower; - - XdrvCall(FUNC_SET_POWER); -} - boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf) { XdrvMailbox.index = stopicBuf; From 01ce1b0c91a72d46a87c517626038b11497521da Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Sep 2018 13:55:42 +0200 Subject: [PATCH 27/34] Fix Shelly2 monitoring Disable serial logging on Shelly2 as serial is being used by energy monitoring (#3878) --- sonoff/_changelog.ino | 1 + sonoff/sonoff.h | 2 +- sonoff/sonoff.ino | 4 ---- sonoff/xdrv_10_rules.ino | 2 +- sonoff/xnrg_04_mcp39f501.ino | 1 + 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index b220dc4b3148..f38b681533de 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -5,6 +5,7 @@ * Add power value below 5W to Sonoff Pow R2 and S31 (#3745) * Add force_update to Home Assistant discovery (#3873) * Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856) + * Disable serial logging on Shelly2 as serial is being used by energy monitoring (#3878) * * 6.2.1.5 20180921 * Add authentication to HTTP web pages diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index c3d514f0cb76..80356d475b27 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -203,7 +203,7 @@ enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_R enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; enum XsnsFunctions {FUNC_PRE_INIT, FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_SECOND, FUNC_PREP_BEFORE_TELEPERIOD, - FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_RULE_INIT_POWER, FUNC_SHOW_SENSOR, + FUNC_JSON_APPEND, FUNC_WEB_APPEND, FUNC_SAVE_BEFORE_RESTART, FUNC_COMMAND, FUNC_MQTT_SUBSCRIBE, FUNC_MQTT_INIT, FUNC_MQTT_DATA, FUNC_SET_POWER, FUNC_SHOW_SENSOR, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM}; const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 }; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index a405fe47ba7f..dc1445fc48bc 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -2613,10 +2613,6 @@ void setup() SetPulseTimer(i, Settings.pulse_timer[i]); } } - - XdrvMailbox.index = power; - XdrvCall(FUNC_RULE_INIT_POWER); - blink_powersave = power; snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s (" D_CMND_TOPIC " %s, " D_FALLBACK " %s, " D_CMND_GROUPTOPIC " %s) " D_VERSION " %s-" ARDUINO_ESP8266_RELEASE), diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 5ef59c037b0e..303207c10be3 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -366,6 +366,7 @@ void RulesEvery50ms() if (Settings.rule_enabled) { // Any rule enabled char json_event[120]; + if (-1 == rules_new_power) { rules_new_power = power; } if (rules_new_power != rules_old_power) { if (rules_old_power != -1) { for (byte i = 0; i < devices_present; i++) { @@ -628,7 +629,6 @@ boolean Xdrv10(byte function) case FUNC_EVERY_SECOND: RulesEverySecond(); break; - case FUNC_RULE_INIT_POWER: case FUNC_SET_POWER: RulesSetPower(); break; diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 05d55a425c68..408e0ca26863 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -614,6 +614,7 @@ void McpEverySecond() void McpSnsInit() { + SetSeriallog(LOG_LEVEL_NONE); // Free serial interface from logging interference digitalWrite(15, 1); // GPIO15 - MCP enable } From 683c21124114a25f9d0bdfd3a9712a9dc7095fb9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Sep 2018 16:41:22 +0200 Subject: [PATCH 28/34] Fix Shelly2 wrong FrequencySet Fix Shelly2 wrong FrequencySet calculation and add input range checks (#3882) --- sonoff/_changelog.ino | 1 + sonoff/xnrg_04_mcp39f501.ino | 45 +++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index f38b681533de..b7988c69c1e5 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -6,6 +6,7 @@ * Add force_update to Home Assistant discovery (#3873) * Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856) * Disable serial logging on Shelly2 as serial is being used by energy monitoring (#3878) + * Fix Shelly2 wrong FrequencySet calculation and add input range checks (#3882) * * 6.2.1.5 20180921 * Add authentication to HTTP web pages diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 408e0ca26863..3fa12da2699e 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -239,8 +239,12 @@ void McpCalibrationReactivePower() mcp_calibration_registers.gain_reactive_power = mcp_calibration_registers.gain_reactive_power * mcp_calibration_registers.calibration_reactive_power / mcp_output_registers.reactive_power; } -void McpCalibrationLineFreqency() +void McpCalibrationLineFrequency() { + if ((0xFFFF == mcp_output_registers.line_frequency) || (0 == mcp_frequency_registers.gain_line_frequency)) { // Reset values to 50Hz + mcp_output_registers.line_frequency = 50000; + mcp_frequency_registers.gain_line_frequency = 0x8000; + } mcp_frequency_registers.gain_line_frequency = mcp_frequency_registers.gain_line_frequency * mcp_frequency_registers.line_frequency_ref / mcp_output_registers.line_frequency; } @@ -481,7 +485,7 @@ void McpParseFrequency() if (mcp_calibration_setpoint.line_frequency_ref) { mcp_frequency_registers.line_frequency_ref = mcp_calibration_setpoint.line_frequency_ref; - McpCalibrationLineFreqency(); + McpCalibrationLineFrequency(); mcp_timeout = 0; McpSetFrequency(); } @@ -637,33 +641,46 @@ void McpDrvInit() boolean McpCommand() { boolean serviced = true; + unsigned long value = 0; if (CMND_POWERSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_output_registers.active_power) { - Settings.energy_power_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); - mcp_calibration_setpoint.calibration_active_power = Settings.energy_power_calibration; - McpGetCalibration(); + value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); + if ((value > 100) && (value < 200000)) { // Between 1W and 2000W + Settings.energy_power_calibration = value; + mcp_calibration_setpoint.calibration_active_power = value; + McpGetCalibration(); + } } } else if (CMND_VOLTAGESET == energy_command_code) { if (XdrvMailbox.data_len && mcp_output_registers.voltage_rms) { - Settings.energy_voltage_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); - mcp_calibration_setpoint.calibration_voltage = Settings.energy_voltage_calibration; - McpGetCalibration(); + value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + if ((value > 1000) && (value < 2600)) { // Between 100V and 260V + Settings.energy_voltage_calibration = value; + mcp_calibration_setpoint.calibration_voltage = value; + McpGetCalibration(); + } } } else if (CMND_CURRENTSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_output_registers.current_rms) { - Settings.energy_current_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); - mcp_calibration_setpoint.calibration_current = Settings.energy_current_calibration; - McpGetCalibration(); + value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); + if ((value > 100) && (value < 80000)) { // Between 10mA and 8A + Settings.energy_current_calibration = value; + mcp_calibration_setpoint.calibration_current = value; + McpGetCalibration(); + } } } else if (CMND_FREQUENCYSET == energy_command_code) { if (XdrvMailbox.data_len && mcp_output_registers.line_frequency) { - Settings.energy_frequency_calibration = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); - mcp_calibration_setpoint.line_frequency_ref = Settings.energy_frequency_calibration; - McpGetFrequency(); + value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 1000); + if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz + Settings.energy_frequency_calibration = value; + mcp_calibration_setpoint.line_frequency_ref = value; + McpGetFrequency(); + } } } else serviced = false; // Unknown command From 5fa47c3fdce4b8842f095d8be1c8492685176909 Mon Sep 17 00:00:00 2001 From: andrethomas Date: Mon, 24 Sep 2018 00:30:07 +0200 Subject: [PATCH 29/34] MCP230xx - Add missing (void) --- sonoff/xsns_29_mcp230xx.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xsns_29_mcp230xx.ino b/sonoff/xsns_29_mcp230xx.ino index 1ca59f8ed335..9dbaf406a257 100644 --- a/sonoff/xsns_29_mcp230xx.ino +++ b/sonoff/xsns_29_mcp230xx.ino @@ -691,7 +691,7 @@ void MCP230xx_OutputTelemetry(void) { #endif // USE_MCP230xx_OUTPUT -void MCP230xx_Interrupt_Counter_Report() { +void MCP230xx_Interrupt_Counter_Report(void) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230_INTTIMER\": {"), GetDateAndTime(DT_LOCAL).c_str()); for (uint8_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report From f65d8c0cbf72ad6f68502df771fb0c91a6c3185c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 24 Sep 2018 11:44:40 +0200 Subject: [PATCH 30/34] Fix possible array overflow Fix possible array overflow (#3887 ) --- sonoff/sonoff.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index dc1445fc48bc..91c1fbb9b41e 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -462,10 +462,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL); fallback_topic_flag = (strstr(topicBuf, mqtt_client) != NULL); - type = strrchr(topicBuf, '/') +1; // Last part of received topic is always the command (type) + type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type) index = 1; if (type != NULL) { + type++; for (i = 0; i < strlen(type); i++) { type[i] = toupper(type[i]); } From 43c3cf57d596a14c18313e55ef1ab447f377dde4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 24 Sep 2018 18:16:35 +0200 Subject: [PATCH 31/34] Shelly2 Cleanup Cleanup and debug updates --- sonoff/settings.h | 24 +- sonoff/settings.ino | 4 +- sonoff/xdrv_99_debug.ino | 186 ++++++++++---- sonoff/xnrg_04_mcp39f501.ino | 462 +++++++++++++++-------------------- 4 files changed, 348 insertions(+), 328 deletions(-) diff --git a/sonoff/settings.h b/sonoff/settings.h index 7da6108910aa..bf862dd51f91 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -337,21 +337,21 @@ struct SYSCFG { } Settings; struct RTCRBT { - uint16_t valid; // 000 - uint8_t fast_reboot_count; // 002 - uint8_t free_003[1]; // 003 + uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) + uint8_t fast_reboot_count; // 282 + uint8_t free_003[1]; // 283 } RtcReboot; struct RTCMEM { - uint16_t valid; // 000 - byte oswatch_blocked_loop; // 002 - uint8_t ota_loader; // 003 - unsigned long energy_kWhtoday; // 004 - unsigned long energy_kWhtotal; // 008 - unsigned long pulse_counter[MAX_COUNTERS]; // 00C - power_t power; // 01C - uint8_t free_020[60]; // 020 - // 05C next free location (64 (=core) + 100 (=tasmota offset) + 92 (=0x5C RTCMEM struct) = 256 bytes (max = 512)) + uint16_t valid; // 290 (RTC memory offset 100) + byte oswatch_blocked_loop; // 292 + uint8_t ota_loader; // 293 + unsigned long energy_kWhtoday; // 294 + unsigned long energy_kWhtotal; // 298 + unsigned long pulse_counter[MAX_COUNTERS]; // 29C + power_t power; // 2AC + uint8_t free_020[60]; // 2B0 + // 2EC - 2FF free locations } RtcSettings; struct TIME_T { diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 5626f862bd12..0d8fa451a39e 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -95,7 +95,7 @@ void RtcSettingsSave() void RtcSettingsLoad() { - ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); + ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); // 0x290 #ifdef DEBUG_THEO AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Load")); RtcSettingsDump(); @@ -145,7 +145,7 @@ void RtcRebootSave() void RtcRebootLoad() { - ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); + ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); // 0x280 if (RtcReboot.valid != RTC_MEM_VALID) { memset(&RtcReboot, 0, sizeof(RTCRBT)); RtcReboot.valid = RTC_MEM_VALID; diff --git a/sonoff/xdrv_99_debug.ino b/sonoff/xdrv_99_debug.ino index 3cd2e69c5d30..abd209b82688 100644 --- a/sonoff/xdrv_99_debug.ino +++ b/sonoff/xdrv_99_debug.ino @@ -35,20 +35,25 @@ * Debug commands \*********************************************************************************************/ -#define D_CMND_CFGDUMP "CfgDump" -#define D_CMND_CFGPOKE "CfgPoke" -#define D_CMND_CFGPEEK "CfgPeek" -#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CFGDUMP "CfgDump" +#define D_CMND_CFGPOKE "CfgPoke" +#define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGSHOW "CfgShow" +#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CPUCHECK "CpuChk" #define D_CMND_EXCEPTION "Exception" -#define D_CMND_CPUCHECK "CpuChk" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_HELP "Help" -enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGXOR, CMND_EXCEPTION, CMND_CPUCHECK }; -const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGXOR "|" D_CMND_EXCEPTION "|" D_CMND_CPUCHECK; +enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGSHOW, CMND_CFGXOR, CMND_CPUCHECK, CMND_EXCEPTION, CMND_FREEMEM, CMND_RTCDUMP, CMND_HELP }; +const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGSHOW "|" D_CMND_CFGXOR "|" D_CMND_CPUCHECK "|" D_CMND_EXCEPTION "|" D_CMND_FREEMEM "|" D_CMND_RTCDUMP "|" D_CMND_HELP; uint32_t CPU_loops = 0; uint32_t CPU_last_millis = 0; uint32_t CPU_last_loop_time = 0; -uint8_t CPU_load_check = CPU_LOAD_CHECK; +uint8_t CPU_load_check = 0; +uint8_t CPU_show_freemem = 0; /*******************************************************************************************/ @@ -117,41 +122,6 @@ Decoding 14 results } } -/*******************************************************************************************/ - -void RtcSettingsDump() -{ - #define CFG_COLS 16 - - uint16_t idx; - uint16_t maxrow; - uint16_t row; - uint16_t col; - - uint8_t *buffer = (uint8_t *) &RtcSettings; - maxrow = ((sizeof(RTCMEM)+CFG_COLS)/CFG_COLS); - - for (row = 0; row < maxrow; row++) { - idx = row * CFG_COLS; - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); - for (col = 0; col < CFG_COLS; col++) { - if (!(col%4)) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (col = 0; col < CFG_COLS; col++) { -// if (!(col%4)) { -// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); -// } - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); - AddLog(LOG_LEVEL_INFO); - } -} - #endif // DEBUG_THEO /*******************************************************************************************/ @@ -224,6 +194,68 @@ void DebugFreeMem() /*******************************************************************************************/ +void DebugRtcDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + // |<--SDK data (256 bytes)-->|<--User data (512 bytes)-->| + // 000 - 0FF: SDK + // 000 - 01B: SDK rst_info + // 100 - 2FF: User + // 280 - 283: Tasmota RtcReboot (Offset 100 (x 4bytes) - sizeof(RTCRBT) (x 4bytes)) + // 290 - 2EB: Tasmota RtcSettings (Offset 100 (x 4bytes)) + + uint8_t buffer[768]; +// ESP.rtcUserMemoryRead(0, (uint32_t*)&buffer, sizeof(buffer)); + system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); + + maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + +// snprintf_P(log_data, sizeof(log_data), PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow); +// AddLog(LOG_LEVEL_DEBUG); + + if (0 == mrow) { // Default only 8 lines + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { +// if (!(col%4)) { +// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); +// } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + } +} + +/*******************************************************************************************/ + void DebugCfgDump(char* parms) { #define CFG_COLS 16 @@ -322,6 +354,53 @@ void DebugCfgPoke(char* parms) AddLog(LOG_LEVEL_INFO); } +void DebugCfgShow(uint8_t more) +{ + uint8_t *SetAddr; + SetAddr = (uint8_t *)&Settings; + + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]); + AddLog(LOG_LEVEL_INFO); + if (17 == more) { + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd); + AddLog(LOG_LEVEL_INFO); + snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password); + AddLog(LOG_LEVEL_INFO); + } +} + /*******************************************************************************************/ boolean DebugCommand() @@ -333,6 +412,15 @@ boolean DebugCommand() if (-1 == command_code) { serviced = false; // Unknown command } + else if (CMND_HELP == command_code) { + snprintf_P(log_data, sizeof(log_data), kDebugCommands); + AddLog(LOG_LEVEL_INFO); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } + else if (CMND_RTCDUMP == command_code) { + DebugRtcDump(XdrvMailbox.data); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } else if (CMND_CFGDUMP == command_code) { DebugCfgDump(XdrvMailbox.data); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); @@ -345,6 +433,10 @@ boolean DebugCommand() DebugCfgPoke(XdrvMailbox.data); snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); } + else if (CMND_CFGSHOW == command_code) { + DebugCfgShow(XdrvMailbox.payload); + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); + } #ifdef USE_WEBSERVER else if (CMND_CFGXOR == command_code) { if (XdrvMailbox.data_len > 0) { @@ -366,6 +458,12 @@ boolean DebugCommand() } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_load_check); } + else if (CMND_FREEMEM == command_code) { + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_show_freemem); + } else serviced = false; // Unknown command return serviced; @@ -392,7 +490,7 @@ boolean Xdrv99(byte function) result = DebugCommand(); break; case FUNC_FREE_MEM: - DebugFreeMem(); + if (CPU_show_freemem) { DebugFreeMem(); } break; } return result; diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 3fa12da2699e..abf11b71faf0 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -56,7 +56,7 @@ #define MCP_FREQUENCY_GAIN_BASE 0x00AE #define MCP_FREQUENCY_LEN 4 -typedef struct mcp_calibration_registers_type { +typedef struct mcp_cal_registers_type { uint16_t gain_current_rms; uint16_t gain_voltage_rms; uint16_t gain_active_power; @@ -77,24 +77,17 @@ typedef struct mcp_calibration_registers_type { uint32_t calibration_active_power; uint32_t calibration_reactive_power; uint16_t accumulation_interval; -} mcp_calibration_registers_type; -mcp_calibration_registers_type mcp_calibration_registers; +} mcp_cal_registers_type; typedef struct mcp_calibration_setpoint_type { uint32_t calibration_current; uint16_t calibration_voltage; uint32_t calibration_active_power; - uint32_t calibration_reactive_power; +// uint32_t calibration_reactive_power; uint16_t line_frequency_ref; } mcp_calibration_setpoint_type; mcp_calibration_setpoint_type mcp_calibration_setpoint; -typedef struct mcp_frequency_registers_type { - uint16_t line_frequency_ref; - uint16_t gain_line_frequency; -} mcp_frequency_registers_type; -mcp_frequency_registers_type mcp_frequency_registers; - typedef struct mcp_output_registers_type { uint32_t current_rms; uint16_t voltage_rms; @@ -103,15 +96,11 @@ typedef struct mcp_output_registers_type { uint32_t apparent_power; sint16_t power_factor; uint16_t line_frequency; - uint16_t thermistor_voltage; - uint16_t event_flag; - uint16_t system_status; } mcp_output_registers_type; mcp_output_registers_type mcp_output_registers; uint32_t mcp_system_configuration = 0x03000000; -uint16_t mcp_address = 0; -uint8_t mcp_single_wire_active = 0; +uint8_t mcp_address = 0; uint8_t mcp_calibration_active = 0; uint8_t mcp_init = 0; uint8_t mcp_timeout = 0; @@ -128,12 +117,8 @@ uint8_t McpChecksum(uint8_t *data) uint8_t offset = 0; uint8_t len = data[1] -1; - if (MCP_SINGLE_WIRE == data[0]) { - offset = 3; - len = 15; - } for (byte i = offset; i < len; i++) { checksum += data[i]; } - return (MCP_SINGLE_WIRE == data[0]) ? ~checksum : checksum; + return checksum; } unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) @@ -170,19 +155,83 @@ void McpSend(uint8_t *data) } } -uint32_t McpGetRange(uint8_t shift) +void McpResetSetpoints(void) +{ + memset(&mcp_calibration_setpoint, 0, sizeof(mcp_calibration_setpoint)); +} + +/********************************************************************************************/ + +void McpGetAddress(void) +{ + uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpAddressReceive(void) { - return (mcp_calibration_registers.range >> shift) & 0xFF; + // 06 05 004D 58 + mcp_address = serial_in_buffer[3]; } -void McpSetRange(uint8_t shift, uint32_t range) +/********************************************************************************************/ + +void McpGetCalibration(void) { - uint32_t old_range = McpGetRange(shift); - mcp_calibration_registers.range = mcp_calibration_registers.range ^ (old_range << shift); - mcp_calibration_registers.range = mcp_calibration_registers.range | (range << shift); + if (mcp_calibration_active) { return; } + mcp_calibration_active = 4; + + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; + + McpSend(data); } -bool McpCalibrationCalc(uint8_t range_shift) +void McpParseCalibration(void) +{ + bool action = false; + mcp_cal_registers_type cal_registers; + + // 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 + cal_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2); + cal_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2); + cal_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2); + cal_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2); + cal_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4); + cal_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4); + cal_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4); + cal_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2); + cal_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2); + cal_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2); + + cal_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4); + cal_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2); + cal_registers.range = McpExtractInt(serial_in_buffer, 34, 4); + + cal_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4); + cal_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2); + cal_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4); + cal_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4); + cal_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2); + + if (mcp_calibration_setpoint.calibration_active_power) { + cal_registers.calibration_active_power = mcp_calibration_setpoint.calibration_active_power; + if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } + } + if (mcp_calibration_setpoint.calibration_voltage) { + cal_registers.calibration_voltage = mcp_calibration_setpoint.calibration_voltage; + if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } + } + if (mcp_calibration_setpoint.calibration_current) { + cal_registers.calibration_current = mcp_calibration_setpoint.calibration_current; + if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } + } + mcp_timeout = 0; + if (action) { McpSetCalibration(&cal_registers); } + McpResetSetpoints(); +} + +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) { uint32_t measured; uint32_t expected; @@ -191,16 +240,16 @@ bool McpCalibrationCalc(uint8_t range_shift) if (range_shift == 0) { measured = mcp_output_registers.voltage_rms; - expected = mcp_calibration_registers.calibration_voltage; - gain = &(mcp_calibration_registers.gain_voltage_rms); + expected = cal_registers->calibration_voltage; + gain = &(cal_registers->gain_voltage_rms); } else if (range_shift == 8) { measured = mcp_output_registers.current_rms; - expected = mcp_calibration_registers.calibration_current; - gain = &(mcp_calibration_registers.gain_current_rms); + expected = cal_registers->calibration_current; + gain = &(cal_registers->gain_current_rms); } else if (range_shift == 16) { measured = mcp_output_registers.active_power; - expected = mcp_calibration_registers.calibration_active_power; - gain = &(mcp_calibration_registers.gain_active_power); + expected = cal_registers->calibration_active_power; + gain = &(cal_registers->gain_active_power); } else { return false; } @@ -209,7 +258,7 @@ bool McpCalibrationCalc(uint8_t range_shift) return false; } - uint32_t range = McpGetRange(range_shift); + uint32_t range = (cal_registers->range >> range_shift) & 0xFF; calc: new_gain = (*gain) * expected / measured; @@ -229,104 +278,50 @@ calc: } *gain = new_gain; - McpSetRange(range_shift, range); + uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; + cal_registers->range = cal_registers->range ^ (old_range << range_shift); + cal_registers->range = cal_registers->range | (range << range_shift); return true; } - -void McpCalibrationReactivePower() -{ - mcp_calibration_registers.gain_reactive_power = mcp_calibration_registers.gain_reactive_power * mcp_calibration_registers.calibration_reactive_power / mcp_output_registers.reactive_power; -} - -void McpCalibrationLineFrequency() -{ - if ((0xFFFF == mcp_output_registers.line_frequency) || (0 == mcp_frequency_registers.gain_line_frequency)) { // Reset values to 50Hz - mcp_output_registers.line_frequency = 50000; - mcp_frequency_registers.gain_line_frequency = 0x8000; - } - mcp_frequency_registers.gain_line_frequency = mcp_frequency_registers.gain_line_frequency * mcp_frequency_registers.line_frequency_ref / mcp_output_registers.line_frequency; -} - -void McpResetSetpoints() -{ - mcp_calibration_setpoint.calibration_active_power = 0; - mcp_calibration_setpoint.calibration_voltage = 0; - mcp_calibration_setpoint.calibration_current = 0; - mcp_calibration_setpoint.calibration_reactive_power = 0; - mcp_calibration_setpoint.line_frequency_ref = 0; -} - -/********************************************************************************************/ - -void McpGetAddress() +/* +void McpCalibrationReactivePower(void) { - // A5 07 41 00 26 52 65 - uint8_t data[7]; - - data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; // Set address pointer - data[3] = 0x00; // address - data[4] = 0x26; // address - data[5] = MCP_READ_16; // Read 2 bytes - - McpSend(data); - - // Receives 06 05 004D 58 + cal_registers.gain_reactive_power = cal_registers.gain_reactive_power * cal_registers.calibration_reactive_power / mcp_output_registers.reactive_power; } - -void McpGetCalibration() -{ - if (mcp_calibration_active) { return; } - mcp_calibration_active = 4; - - // A5 08 41 00 28 4E 34 98 - uint8_t data[8]; - - data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; // Set address pointer - data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address - data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address - data[5] = MCP_READ; // Read N bytes - data[6] = MCP_CALIBRATION_LEN; - - McpSend(data); - - // Receives 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 -} - -void McpSetCalibration() +*/ +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) { uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; // Set address pointer - data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address - data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address + data[2] = MCP_SET_ADDRESS; // Set address pointer + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address - data[5] = MCP_WRITE; // Write N bytes + data[5] = MCP_WRITE; // Write N bytes data[6] = MCP_CALIBRATION_LEN; - McpSetInt(mcp_calibration_registers.gain_current_rms, data, 0+7, 2); - McpSetInt(mcp_calibration_registers.gain_voltage_rms, data, 2+7, 2); - McpSetInt(mcp_calibration_registers.gain_active_power, data, 4+7, 2); - McpSetInt(mcp_calibration_registers.gain_reactive_power, data, 6+7, 2); - McpSetInt(mcp_calibration_registers.offset_current_rms, data, 8+7, 4); - McpSetInt(mcp_calibration_registers.offset_active_power, data, 12+7, 4); - McpSetInt(mcp_calibration_registers.offset_reactive_power, data, 16+7, 4); - McpSetInt(mcp_calibration_registers.dc_offset_current, data, 20+7, 2); - McpSetInt(mcp_calibration_registers.phase_compensation, data, 22+7, 2); - McpSetInt(mcp_calibration_registers.apparent_power_divisor, data, 24+7, 2); - - McpSetInt(mcp_calibration_registers.system_configuration, data, 26+7, 4); - McpSetInt(mcp_calibration_registers.dio_configuration, data, 30+7, 2); - McpSetInt(mcp_calibration_registers.range, data, 32+7, 4); - - McpSetInt(mcp_calibration_registers.calibration_current, data, 36+7, 4); - McpSetInt(mcp_calibration_registers.calibration_voltage, data, 40+7, 2); - McpSetInt(mcp_calibration_registers.calibration_active_power, data, 42+7, 4); - McpSetInt(mcp_calibration_registers.calibration_reactive_power, data, 46+7, 4); - McpSetInt(mcp_calibration_registers.accumulation_interval, data, 50+7, 2); + McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); + McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); + McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); + McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); + McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); + McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); + McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); + McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); + McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); + McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); + + McpSetInt(cal_registers->system_configuration, data, 26+7, 4); + McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); + McpSetInt(cal_registers->range, data, 32+7, 4); + + McpSetInt(cal_registers->calibration_current, data, 36+7, 4); + McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); + McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); + McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); + McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; // Save registers to flash data[MCP_CALIBRATION_LEN+8] = mcp_address; // Device address @@ -334,187 +329,123 @@ void McpSetCalibration() McpSend(data); } -void McpGetFrequency() +/********************************************************************************************/ + +void McpGetFrequency(void) { if (mcp_calibration_active) { return; } mcp_calibration_active = 4; - // A5 0B 41 00 94 52 41 00 AE 52 18 - uint8_t data[11]; + uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, + MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; - data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; // Set address pointer - data[3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address - data[4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address + McpSend(data); +} - data[5] = MCP_READ_16; // Read register +void McpParseFrequency(void) +{ + // 06 07 C350 8000 A0 + uint16_t line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3]; + uint16_t gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5]; - data[6] = MCP_SET_ADDRESS; // Set address pointer - data[7] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address - data[8] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address + if (mcp_calibration_setpoint.line_frequency_ref) { + line_frequency_ref = mcp_calibration_setpoint.line_frequency_ref; - data[9] = MCP_READ_16; // Read register + if ((0xFFFF == mcp_output_registers.line_frequency) || (0 == gain_line_frequency)) { // Reset values to 50Hz + mcp_output_registers.line_frequency = 50000; + gain_line_frequency = 0x8000; + } + gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_output_registers.line_frequency; - McpSend(data); + mcp_timeout = 0; + McpSetFrequency(line_frequency_ref, gain_line_frequency); + } + McpResetSetpoints(); } -void McpSetFrequency() +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) { // A5 11 41 00 94 57 C3 B4 41 00 AE 57 7E 46 53 4D 03 uint8_t data[17]; data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; // Set address pointer + data[ 2] = MCP_SET_ADDRESS; // Set address pointer data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address - data[ 5] = MCP_WRITE_16; // Write register - data[ 6] = (mcp_frequency_registers.line_frequency_ref >> 8) & 0xFF; // line_frequency_ref high - data[ 7] = (mcp_frequency_registers.line_frequency_ref >> 0) & 0xFF; // line_frequency_ref low + data[ 5] = MCP_WRITE_16; // Write register + data[ 6] = (line_frequency_ref >> 8) & 0xFF; // line_frequency_ref high + data[ 7] = (line_frequency_ref >> 0) & 0xFF; // line_frequency_ref low - data[ 8] = MCP_SET_ADDRESS; // Set address pointer - data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address - data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address + data[ 8] = MCP_SET_ADDRESS; // Set address pointer + data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address + data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address - data[11] = MCP_WRITE_16; // Write register - data[12] = (mcp_frequency_registers.gain_line_frequency >> 8) & 0xFF; // gain_line_frequency high - data[13] = (mcp_frequency_registers.gain_line_frequency >> 0) & 0xFF; // gain_line_frequency low + data[11] = MCP_WRITE_16; // Write register + data[12] = (gain_line_frequency >> 8) & 0xFF; // gain_line_frequency high + data[13] = (gain_line_frequency >> 0) & 0xFF; // gain_line_frequency low - data[14] = MCP_SAVE_REGISTERS; // Save registers to flash - data[15] = mcp_address; // Device address + data[14] = MCP_SAVE_REGISTERS; // Save registers to flash + data[15] = mcp_address; // Device address McpSend(data); } +/********************************************************************************************/ + void McpSetSystemConfiguration(uint16 interval) { // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A uint8_t data[17]; data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; // Set address pointer - data[ 3] = 0x00; // address - data[ 4] = 0x42; // address - data[ 5] = MCP_WRITE_32; // Write 4 bytes - data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration - data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration - data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration - data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration - data[10] = MCP_SET_ADDRESS; // Set address pointer - data[11] = 0x00; // address - data[12] = 0x5A; // address - data[13] = MCP_WRITE_16; // Write 2 bytes - data[14] = (interval >> 8) & 0xFF; // interval - data[15] = (interval >> 0) & 0xFF; // interval + data[ 2] = MCP_SET_ADDRESS; // Set address pointer + data[ 3] = 0x00; // address + data[ 4] = 0x42; // address + data[ 5] = MCP_WRITE_32; // Write 4 bytes + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration + data[10] = MCP_SET_ADDRESS; // Set address pointer + data[11] = 0x00; // address + data[12] = 0x5A; // address + data[13] = MCP_WRITE_16; // Write 2 bytes + data[14] = (interval >> 8) & 0xFF; // interval + data[15] = (interval >> 0) & 0xFF; // interval McpSend(data); } -void McpSingleWireStart() -{ - if ((mcp_system_configuration & (1 << 8)) != 0) { return; } - mcp_system_configuration = mcp_system_configuration | (1 << 8); - McpSetSystemConfiguration(6); // 64 - mcp_single_wire_active = 1; -} - void McpSingleWireStop(uint8_t force) { if (!force && ((mcp_system_configuration & (1 << 8)) == 0)) { return; } mcp_system_configuration = mcp_system_configuration & (~(1 << 8)); McpSetSystemConfiguration(2); // 4 - mcp_single_wire_active = 0; } /********************************************************************************************/ -void McpAddressReceive() -{ - // 06 05 004D 58 - mcp_address = serial_in_buffer[2] * 256 + serial_in_buffer[3]; -} - -void McpParseCalibration() -{ - bool action = false; - - // 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24 - mcp_calibration_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2); - mcp_calibration_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2); - mcp_calibration_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2); - mcp_calibration_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2); - mcp_calibration_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4); - mcp_calibration_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4); - mcp_calibration_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4); - mcp_calibration_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2); - mcp_calibration_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2); - mcp_calibration_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2); - - mcp_calibration_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4); - mcp_calibration_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2); - mcp_calibration_registers.range = McpExtractInt(serial_in_buffer, 34, 4); - - mcp_calibration_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4); - mcp_calibration_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2); - mcp_calibration_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4); - mcp_calibration_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4); - mcp_calibration_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2); - - if (mcp_calibration_setpoint.calibration_active_power) { - mcp_calibration_registers.calibration_active_power = mcp_calibration_setpoint.calibration_active_power; - if (McpCalibrationCalc(16)) { action = true; } - } - if (mcp_calibration_setpoint.calibration_voltage) { - mcp_calibration_registers.calibration_voltage = mcp_calibration_setpoint.calibration_voltage; - if (McpCalibrationCalc(0)) { action = true; } - } - if (mcp_calibration_setpoint.calibration_current) { - mcp_calibration_registers.calibration_current = mcp_calibration_setpoint.calibration_current; - if (McpCalibrationCalc(8)) { action = true; } - } - mcp_timeout = 0; - if (action) { McpSetCalibration(); } - McpResetSetpoints(); -} - -void McpParseFrequency() +void McpGetData(void) { - // 06 07 C350 8000 A0 - mcp_frequency_registers.line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3]; - mcp_frequency_registers.gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5]; + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; - if (mcp_calibration_setpoint.line_frequency_ref) { - mcp_frequency_registers.line_frequency_ref = mcp_calibration_setpoint.line_frequency_ref; - McpCalibrationLineFrequency(); - mcp_timeout = 0; - McpSetFrequency(); - } - McpResetSetpoints(); + McpSend(data); } -void McpParseData(uint8_t single_wire) +void McpParseData(void) { - if (single_wire) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // AB CD EF 51 06 00 00 B8 08 FC 0D 00 00 0A C4 11 - // Header-- Current---- Volt- Power------ Freq- Ck - - mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 3, 4); - mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 7, 2); - mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 9, 4); - mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 13, 2); - } else { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 - // 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35 - // 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7 - // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck - - mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 2, 4); - mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); - mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 8, 4); - mcp_output_registers.reactive_power = McpExtractInt(serial_in_buffer, 12, 4); - mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 22, 2); - } + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 + // 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35 + // 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7 + // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck + + mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 2, 4); + mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); + mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 8, 4); +// mcp_output_registers.reactive_power = McpExtractInt(serial_in_buffer, 12, 4); +// mcp_output_registers.power_factor = McpExtractInt(serial_in_buffer, 20, 2); + mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 22, 2); if (energy_power_on) { // Powered on energy_frequency = (float)mcp_output_registers.line_frequency / 1000; @@ -533,7 +464,9 @@ void McpParseData(uint8_t single_wire) } } -bool McpSerialInput() +/********************************************************************************************/ + +bool McpSerialInput(void) { serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; unsigned long start = millis(); @@ -564,7 +497,7 @@ bool McpSerialInput() AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); } else { if (5 == serial_in_buffer[1]) { McpAddressReceive(); } - if (25 == serial_in_buffer[1]) { McpParseData(0); } + if (25 == serial_in_buffer[1]) { McpParseData(); } if (MCP_CALIBRATION_LEN + 3 == serial_in_buffer[1]) { McpParseCalibration(); } if (MCP_FREQUENCY_LEN + 3 == serial_in_buffer[1]) { McpParseFrequency(); } } @@ -573,15 +506,6 @@ bool McpSerialInput() mcp_timeout = 0; } else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) { - if (serial_in_byte_counter == 16) { - - if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); - } else { - McpParseData(1); - } - - } mcp_timeout = 0; } return 1; @@ -589,10 +513,8 @@ bool McpSerialInput() /********************************************************************************************/ -void McpEverySecond() +void McpEverySecond(void) { - uint8_t get_state[] = { 0xA5, 0x08, 0x41, 0x00, 0x04, 0x4E, 0x16, 0x00 }; - if (mcp_output_registers.active_power) { energy_kWhtoday_delta += ((mcp_output_registers.active_power * 10) / 36); EnergyUpdateToday(); @@ -611,18 +533,18 @@ void McpEverySecond() else if (!mcp_address) { McpGetAddress(); } - else if (!mcp_single_wire_active) { - McpSend(get_state); + else { + McpGetData(); } } -void McpSnsInit() +void McpSnsInit(void) { SetSeriallog(LOG_LEVEL_NONE); // Free serial interface from logging interference digitalWrite(15, 1); // GPIO15 - MCP enable } -void McpDrvInit() +void McpDrvInit(void) { if (!energy_flg) { if (SHELLY2 == Settings.module) { @@ -638,7 +560,7 @@ void McpDrvInit() } } -boolean McpCommand() +boolean McpCommand(void) { boolean serviced = true; unsigned long value = 0; From ecd3175257360ac13c46042000c1297acd385242 Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Mon, 24 Sep 2018 16:16:14 -0300 Subject: [PATCH 32/34] Prevent Command NtpServer to restart Tasmota #3890 --- sonoff/sonoff.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 91c1fbb9b41e..2e64b904609b 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1043,7 +1043,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) { if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.'; } - restart_flag = 2; +// restart_flag = 2; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); } From 2029440fae58b204e1d23c50e6b74fbb1383388e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 25 Sep 2018 11:35:37 +0200 Subject: [PATCH 33/34] 6.2.1.7 Fix NTPserver change 6.2.1.7 20180925 * Remove restart after ntpserver change and force NTP re-sync (#3890) * Release full Shelly2 support --- sonoff/_changelog.ino | 6 +- sonoff/sonoff.ino | 3 +- sonoff/sonoff_version.h | 2 +- sonoff/xnrg_04_mcp39f501.ino | 213 ++++++++++++++++++----------------- 4 files changed, 116 insertions(+), 108 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index b7988c69c1e5..5aa6e9e13763 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,8 @@ -/* 6.2.1.6 20180922 +/* 6.2.1.7 20180925 + * Remove restart after ntpserver change and force NTP re-sync (#3890) + * Release full Shelly2 support + * + * 6.2.1.6 20180922 * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet * Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 2e64b904609b..f0619f36230f 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -1043,7 +1043,8 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len) for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) { if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.'; } -// restart_flag = 2; +// restart_flag = 2; // Issue #3890 + ntp_force_sync = 1; } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]); } diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 228a48d92459..16c5547834b8 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020106 +#define VERSION 0x06020107 #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index abf11b71faf0..f2ce321bc5d0 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -29,6 +29,13 @@ #define XNRG_04 4 #define MCP_TIMEOUT 4 +#define MCP_CALIBRATION_TIMEOUT 2 + +#define MCP_CALIBRATE_POWER 0x001 +#define MCP_CALIBRATE_VOLTAGE 0x002 +#define MCP_CALIBRATE_CURRENT 0x004 +#define MCP_CALIBRATE_FREQUENCY 0x008 +#define MCP_SINGLE_WIRE_FLAG 0x100 #define MCP_START_FRAME 0xA5 #define MCP_ACK_FRAME 0x06 @@ -79,32 +86,20 @@ typedef struct mcp_cal_registers_type { uint16_t accumulation_interval; } mcp_cal_registers_type; -typedef struct mcp_calibration_setpoint_type { - uint32_t calibration_current; - uint16_t calibration_voltage; - uint32_t calibration_active_power; -// uint32_t calibration_reactive_power; - uint16_t line_frequency_ref; -} mcp_calibration_setpoint_type; -mcp_calibration_setpoint_type mcp_calibration_setpoint; - -typedef struct mcp_output_registers_type { - uint32_t current_rms; - uint16_t voltage_rms; - uint32_t active_power; - uint32_t reactive_power; - uint32_t apparent_power; - sint16_t power_factor; - uint16_t line_frequency; -} mcp_output_registers_type; -mcp_output_registers_type mcp_output_registers; - +unsigned long mcp_kWhcounter = 0; uint32_t mcp_system_configuration = 0x03000000; +uint32_t mcp_active_power; +//uint32_t mcp_reactive_power; +//uint32_t mcp_apparent_power; +uint32_t mcp_current_rms; +uint16_t mcp_voltage_rms; +uint16_t mcp_line_frequency; +//sint16_t mcp_power_factor; uint8_t mcp_address = 0; uint8_t mcp_calibration_active = 0; uint8_t mcp_init = 0; uint8_t mcp_timeout = 0; -unsigned long mcp_kWhcounter = 0; +uint8_t mcp_calibrate = 0; /*********************************************************************************************\ * Olimex tools @@ -155,11 +150,6 @@ void McpSend(uint8_t *data) } } -void McpResetSetpoints(void) -{ - memset(&mcp_calibration_setpoint, 0, sizeof(mcp_calibration_setpoint)); -} - /********************************************************************************************/ void McpGetAddress(void) @@ -180,7 +170,7 @@ void McpAddressReceive(void) void McpGetCalibration(void) { if (mcp_calibration_active) { return; } - mcp_calibration_active = 4; + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; @@ -214,21 +204,33 @@ void McpParseCalibration(void) cal_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4); cal_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2); - if (mcp_calibration_setpoint.calibration_active_power) { - cal_registers.calibration_active_power = mcp_calibration_setpoint.calibration_active_power; + if (mcp_calibrate & MCP_CALIBRATE_POWER) { + cal_registers.calibration_active_power = Settings.energy_power_calibration; if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } } - if (mcp_calibration_setpoint.calibration_voltage) { - cal_registers.calibration_voltage = mcp_calibration_setpoint.calibration_voltage; + if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { + cal_registers.calibration_voltage = Settings.energy_voltage_calibration; if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } } - if (mcp_calibration_setpoint.calibration_current) { - cal_registers.calibration_current = mcp_calibration_setpoint.calibration_current; + if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { + cal_registers.calibration_current = Settings.energy_current_calibration; if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } } mcp_timeout = 0; if (action) { McpSetCalibration(&cal_registers); } - McpResetSetpoints(); + + mcp_calibrate = 0; + + Settings.energy_power_calibration = cal_registers.calibration_active_power; + Settings.energy_voltage_calibration = cal_registers.calibration_voltage; + Settings.energy_current_calibration = cal_registers.calibration_current; + + mcp_system_configuration = cal_registers.system_configuration; + + if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { + mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; // Reset SingleWire flag + McpSetSystemConfiguration(2); + } } bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) @@ -239,15 +241,15 @@ bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t ra uint32_t new_gain; if (range_shift == 0) { - measured = mcp_output_registers.voltage_rms; + measured = mcp_voltage_rms; expected = cal_registers->calibration_voltage; gain = &(cal_registers->gain_voltage_rms); } else if (range_shift == 8) { - measured = mcp_output_registers.current_rms; + measured = mcp_current_rms; expected = cal_registers->calibration_current; gain = &(cal_registers->gain_current_rms); } else if (range_shift == 16) { - measured = mcp_output_registers.active_power; + measured = mcp_active_power; expected = cal_registers->calibration_active_power; gain = &(cal_registers->gain_active_power); } else { @@ -287,7 +289,7 @@ calc: /* void McpCalibrationReactivePower(void) { - cal_registers.gain_reactive_power = cal_registers.gain_reactive_power * cal_registers.calibration_reactive_power / mcp_output_registers.reactive_power; + cal_registers.gain_reactive_power = cal_registers.gain_reactive_power * cal_registers.calibration_reactive_power / mcp_reactive_power; } */ void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) @@ -331,10 +333,36 @@ void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) /********************************************************************************************/ +void McpSetSystemConfiguration(uint16 interval) +{ + // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; // Set address pointer + data[ 3] = 0x00; // address + data[ 4] = 0x42; // address + data[ 5] = MCP_WRITE_32; // Write 4 bytes + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration + data[10] = MCP_SET_ADDRESS; // Set address pointer + data[11] = 0x00; // address + data[12] = 0x5A; // address + data[13] = MCP_WRITE_16; // Write 2 bytes + data[14] = (interval >> 8) & 0xFF; // interval + data[15] = (interval >> 0) & 0xFF; // interval + + McpSend(data); +} + +/********************************************************************************************/ + void McpGetFrequency(void) { if (mcp_calibration_active) { return; } - mcp_calibration_active = 4; + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; @@ -348,19 +376,22 @@ void McpParseFrequency(void) uint16_t line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3]; uint16_t gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5]; - if (mcp_calibration_setpoint.line_frequency_ref) { - line_frequency_ref = mcp_calibration_setpoint.line_frequency_ref; + if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { + line_frequency_ref = Settings.energy_frequency_calibration; - if ((0xFFFF == mcp_output_registers.line_frequency) || (0 == gain_line_frequency)) { // Reset values to 50Hz - mcp_output_registers.line_frequency = 50000; + if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { // Reset values to 50Hz + mcp_line_frequency = 50000; gain_line_frequency = 0x8000; } - gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_output_registers.line_frequency; + gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; mcp_timeout = 0; McpSetFrequency(line_frequency_ref, gain_line_frequency); } - McpResetSetpoints(); + + Settings.energy_frequency_calibration = line_frequency_ref; + + mcp_calibrate = 0; } void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) @@ -393,39 +424,6 @@ void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) /********************************************************************************************/ -void McpSetSystemConfiguration(uint16 interval) -{ - // A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A - uint8_t data[17]; - - data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; // Set address pointer - data[ 3] = 0x00; // address - data[ 4] = 0x42; // address - data[ 5] = MCP_WRITE_32; // Write 4 bytes - data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration - data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration - data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration - data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration - data[10] = MCP_SET_ADDRESS; // Set address pointer - data[11] = 0x00; // address - data[12] = 0x5A; // address - data[13] = MCP_WRITE_16; // Write 2 bytes - data[14] = (interval >> 8) & 0xFF; // interval - data[15] = (interval >> 0) & 0xFF; // interval - - McpSend(data); -} - -void McpSingleWireStop(uint8_t force) -{ - if (!force && ((mcp_system_configuration & (1 << 8)) == 0)) { return; } - mcp_system_configuration = mcp_system_configuration & (~(1 << 8)); - McpSetSystemConfiguration(2); // 4 -} - -/********************************************************************************************/ - void McpGetData(void) { uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; @@ -440,21 +438,21 @@ void McpParseData(void) // 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7 // Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck - mcp_output_registers.current_rms = McpExtractInt(serial_in_buffer, 2, 4); - mcp_output_registers.voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); - mcp_output_registers.active_power = McpExtractInt(serial_in_buffer, 8, 4); -// mcp_output_registers.reactive_power = McpExtractInt(serial_in_buffer, 12, 4); -// mcp_output_registers.power_factor = McpExtractInt(serial_in_buffer, 20, 2); - mcp_output_registers.line_frequency = McpExtractInt(serial_in_buffer, 22, 2); + mcp_current_rms = McpExtractInt(serial_in_buffer, 2, 4); + mcp_voltage_rms = McpExtractInt(serial_in_buffer, 6, 2); + mcp_active_power = McpExtractInt(serial_in_buffer, 8, 4); +// mcp_reactive_power = McpExtractInt(serial_in_buffer, 12, 4); +// mcp_power_factor = McpExtractInt(serial_in_buffer, 20, 2); + mcp_line_frequency = McpExtractInt(serial_in_buffer, 22, 2); if (energy_power_on) { // Powered on - energy_frequency = (float)mcp_output_registers.line_frequency / 1000; - energy_voltage = (float)mcp_output_registers.voltage_rms / 10; - energy_power = (float)mcp_output_registers.active_power / 100; + energy_frequency = (float)mcp_line_frequency / 1000; + energy_voltage = (float)mcp_voltage_rms / 10; + energy_power = (float)mcp_active_power / 100; if (0 == energy_power) { energy_current = 0; } else { - energy_current = (float)mcp_output_registers.current_rms / 10000; + energy_current = (float)mcp_current_rms / 10000; } } else { // Powered off energy_frequency = 0; @@ -515,8 +513,8 @@ bool McpSerialInput(void) void McpEverySecond(void) { - if (mcp_output_registers.active_power) { - energy_kWhtoday_delta += ((mcp_output_registers.active_power * 10) / 36); + if (mcp_active_power) { + energy_kWhtoday_delta += ((mcp_active_power * 10) / 36); EnergyUpdateToday(); } @@ -527,14 +525,19 @@ void McpEverySecond(void) mcp_calibration_active--; } else if (mcp_init) { - McpSingleWireStop(1); - mcp_init = 0; + if (2 == mcp_init) { + McpGetCalibration(); // Get calibration parameters and disable SingleWire mode if enabled + } + else if (1 == mcp_init) { + McpGetFrequency(); // Get calibration parameter + } + mcp_init--; } else if (!mcp_address) { - McpGetAddress(); + McpGetAddress(); // Get device address for future calibration changes } else { - McpGetData(); + McpGetData(); // Get energy data } } @@ -551,10 +554,10 @@ void McpDrvInit(void) pinMode(15, OUTPUT); digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's baudrate = 4800; + mcp_calibrate = 0; + mcp_timeout = 2; // Initial wait + mcp_init = 2; // Initial setup steps energy_calc_power_factor = 1; // Calculate power factor from data - mcp_timeout = 4; // Wait for initialization - mcp_init = 1; // Execute initial setup - McpResetSetpoints(); energy_flg = XNRG_04; } } @@ -566,41 +569,41 @@ boolean McpCommand(void) unsigned long value = 0; if (CMND_POWERSET == energy_command_code) { - if (XdrvMailbox.data_len && mcp_output_registers.active_power) { + if (XdrvMailbox.data_len && mcp_active_power) { value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100); if ((value > 100) && (value < 200000)) { // Between 1W and 2000W Settings.energy_power_calibration = value; - mcp_calibration_setpoint.calibration_active_power = value; + mcp_calibrate |= MCP_CALIBRATE_POWER; McpGetCalibration(); } } } else if (CMND_VOLTAGESET == energy_command_code) { - if (XdrvMailbox.data_len && mcp_output_registers.voltage_rms) { + if (XdrvMailbox.data_len && mcp_voltage_rms) { value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); if ((value > 1000) && (value < 2600)) { // Between 100V and 260V Settings.energy_voltage_calibration = value; - mcp_calibration_setpoint.calibration_voltage = value; + mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; McpGetCalibration(); } } } else if (CMND_CURRENTSET == energy_command_code) { - if (XdrvMailbox.data_len && mcp_output_registers.current_rms) { + if (XdrvMailbox.data_len && mcp_current_rms) { value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10); if ((value > 100) && (value < 80000)) { // Between 10mA and 8A Settings.energy_current_calibration = value; - mcp_calibration_setpoint.calibration_current = value; + mcp_calibrate |= MCP_CALIBRATE_CURRENT; McpGetCalibration(); } } } else if (CMND_FREQUENCYSET == energy_command_code) { - if (XdrvMailbox.data_len && mcp_output_registers.line_frequency) { + if (XdrvMailbox.data_len && mcp_line_frequency) { value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 1000); if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz Settings.energy_frequency_calibration = value; - mcp_calibration_setpoint.line_frequency_ref = value; + mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; McpGetFrequency(); } } From ec421e614477115c99c67275bfafa5ecf33a8eb4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 25 Sep 2018 14:08:36 +0200 Subject: [PATCH 34/34] Released decode-config.py Released tools/decode-config.py by Norbert Richter to decode configuration data. --- sonoff/_changelog.ino | 1 + tools/decode-config.py | 1924 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1925 insertions(+) create mode 100644 tools/decode-config.py diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 5aa6e9e13763..333a524a60a1 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /* 6.2.1.7 20180925 * Remove restart after ntpserver change and force NTP re-sync (#3890) * Release full Shelly2 support + * Released tools/decode-config.py by Norbert Richter to decode configuration data. See file for information * * 6.2.1.6 20180922 * Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet diff --git a/tools/decode-config.py b/tools/decode-config.py new file mode 100644 index 000000000000..299f152c46f7 --- /dev/null +++ b/tools/decode-config.py @@ -0,0 +1,1924 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" + decode-config.py - Decode configuration of Sonoff-Tasmota device + + Copyright (C) 2018 Norbert Richter + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Requirements: + - Python + - pip json pycurl urllib2 configargparse + +Instructions: + Execute command with option -d to retrieve config data from device or + use -f to read out a previously saved configuration file. + + For help execute command with argument -h + + +Usage: + decode-config.py [-h] [-f ] [-d ] + [-u ] [-p ] [--format ] + [--sort ] [--raw] [--unhide-pw] [-o ] + [-c ] [-V] + + Decode configuration of Sonoff-Tasmota device. Args that start with '--' (eg. + -f) can also be set in a config file (specified via -c). Config file syntax + allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at + https://goo.gl/R74nmi). If an arg is specified in more than one place, then + commandline values override config file values which override defaults. + + optional arguments: + -h, --help show this help message and exit + -c , --config + Config file, can be used instead of command parameter + (defaults to None) + + source: + -f , --file + file to retrieve Tasmota configuration from (default: + None) + -d , --device + device to retrieve configuration from (default: None) + -u , --username + for -d usage: http access username (default: admin) + -p , --password + for -d usage: http access password (default: None) + + output: + --format output format ("json" or "text", default: "json") + --sort sort result - can be "none" or "name" (default: + "name") + --raw output raw values (default: processed) + --unhide-pw unhide passwords (default: hide) + -o , --output-file + file to store decrypted raw binary configuration to + (default: None) + + info: + -V, --version show program's version number and exit + + Note: Either argument -d or -f must be given. + + +Examples: + Read configuration from hostname 'sonoff1' and output default json config + ./decode-config.py -d sonoff1 + + Read configuration from file 'Config__6.2.1.dmp' and output default json config + ./decode-config.py -f Config__6.2.1.dmp + + Read configuration from hostname 'sonoff1' using web login data + ./decode-config.py -d sonoff1 -u admin -p xxxx + + Read configuration from hostname 'sonoff1' using web login data and unhide passwords + ./decode-config.py -d sonoff1 -u admin -p xxxx --unhide-pw + + Read configuration from hostname 'sonoff1' using web login data, unhide passwords + and sort key names + ./decode-config.py -d sonoff1 -u admin -p xxxx --unhide-pw --sort name +""" + +import os.path +import io +import sys +import configargparse +import collections +import struct +import re +import json +try: + import pycurl +except ImportError: + print("module not found. Try 'pip pycurl' to install it") + sys.exit(9) +try: + import urllib2 +except ImportError: + print("module not found. Try 'pip urllib2' to install it") + sys.exit(9) + + +VER = '1.5.0008' +PROG='{} v{} by Norbert Richter'.format(os.path.basename(sys.argv[0]),VER) + +CONFIG_FILE_XOR = 0x5A + + +args = {} +DEFAULTS = { + 'DEFAULT': + { + 'configfile': None, + }, + 'source': + { + 'device': None, + 'username': 'admin', + 'password': None, + 'tasmotafile': None, + }, + 'output': + { + 'format': 'json', + 'sort': 'name', + 'raw': False, + 'unhide-pw': False, + 'outputfile': None, + }, +} + + +""" +Settings dictionary describes the config file fields definition: + + Each setting name has a tuple containing the following items: + + (format, baseaddr, datadef, ) + + where + + format + Define the data interpretation. + For details see struct module format string + https://docs.python.org/2.7/library/struct.html#format-strings + + baseaddr + The address (starting from 0) within config data + + datadef + Define the field interpretation different from simple + standard types (like char, byte, int) e. g. lists or bit fields + Can be None, a single integer, a list or a dictionary + None: + None must be given if the field contains a simple value + desrcibed by the prefix + n: + Same as [n] below + [n]: + Defines a one-dimensional array of size + [n, n <,n...>] + Defines a multi-dimensional array + [{} <,{}...] + Defines a bit struct. The items are simply dict + {'bitname', bitlen}, the dict order is important. + + convert (optional) + Define an output/conversion methode, can be a simple string + or a previously defined function name. + 'xxx': + a string defines a format specification of the string + formatter, see + https://docs.python.org/2.7/library/string.html#format-string-syntax + func: + a function defines the name of a formating function + +""" +# config data conversion function and helper +def baudrate(value): + return value * 1200 + +def int2ip(value): + return '{:d}.{:d}.{:d}.{:d}'.format(value & 0xff, value>>8 & 0xff, value>>16 & 0xff, value>>24 & 0xff) + +def int2geo(value): + return float(value) / 1000000 + +def password(value): + if args.unhidepw: + return value + return '********' + +def fingerprintstr(value): + s = list(value) + result = '' + for c in s: + if c in '0123456789abcdefABCDEF': + result += c + return result + + +Setting_6_2_1 = { + 'cfg_holder': ('0 and isinstance(fielddef[2][0], int)) or isinstance(fielddef[2], int): + for i in range(0, fielddef[2][0] if isinstance(fielddef[2], list) else fielddef[2] ): + # multidimensional array + if isinstance(fielddef[2], list) and len(fielddef[2])>1: + length += GetFieldLength( (fielddef[0], fielddef[1], fielddef[2][1:]) ) + else: + length += GetFieldLength( (fielddef[0], fielddef[1], None) ) + else: + if fielddef[0][-1:].lower() in ['b','c','?']: + length=1 + elif fielddef[0][-1:].lower() in ['h']: + length=2 + elif fielddef[0][-1:].lower() in ['i','l','f']: + length=4 + elif fielddef[0][-1:].lower() in ['q','d']: + length=8 + elif fielddef[0][-1:].lower() in ['s','p']: + # s and p needs prefix as length + match = re.search("\s*(\d+)", fielddef[0]) + if match: + length=int(match.group(0)) + + # it's a single value + return length + +def ConvertFieldValue(value, fielddef): + """ + Convert field value based on field desc + + @param value: + original value read from binary data + @param fielddef + field definition (contains possible conversion defiinition) + + @return: (und)converted value + """ + if not args.raw and len(fielddef)>3: + if isinstance(fielddef[3],str): # use a format string + return fielddef[3].format(value) + elif callable(fielddef[3]): # use a format function + return fielddef[3](value) + return value + + +def GetField(dobj, fieldname, fielddef): + """ + Get field value from definition + + @param dobj: + uncrypted binary config data + @param fieldname: + name of the field + @param fielddef: + see Settings desc above + + @return: read field value + """ + + result = None + + if fielddef[2] is not None: + result = [] + + # tuple 2 contains a list with integer or an integer value + if (isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], int)) or isinstance(fielddef[2], int): + addr = fielddef[1] + for i in range(0, fielddef[2][0] if isinstance(fielddef[2], list) else fielddef[2] ): + # multidimensional array + if isinstance(fielddef[2], list) and len(fielddef[2])>1: + subfielddef = (fielddef[0], addr, fielddef[2][1:], None if len(fielddef)<4 else fielddef[3]) + else: # single array + subfielddef = (fielddef[0], addr, None, None if len(fielddef)<4 else fielddef[3]) + length = GetFieldLength(subfielddef) + if length != 0: + result.append(GetField(dobj, fieldname, subfielddef)) + addr += length + # tuple 2 contains a list with dict + elif isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], dict): + d = {} + value = struct.unpack_from(fielddef[0], dobj, fielddef[1])[0] + d['base'] = ConvertFieldValue(value, fielddef); + union = fielddef[2] + i = 0 + for l in union: + for name,bits in l.items(): + bitval = (value & ( ((1<> i + d[name] = bitval + i += bits + result = d + else: + # it's a single value + if GetFieldLength(fielddef) != 0: + result = struct.unpack_from(fielddef[0], dobj, fielddef[1])[0] + if fielddef[0][-1:].lower() in ['s','p']: + if ord(result[:1])==0x00 or ord(result[:1])==0xff: + result = '' + s = str(result).split('\0')[0] + result = s #unicode(s, errors='replace') + result = ConvertFieldValue(result, fielddef) + + return result + + +def DeEncrypt(obj): + """ + Decrpt/Encrypt binary config data + + @param obj: + binary config data + + @return: decrypted configuration (if obj contains encrypted data) + encrypted configuration (if obj contains decrypted data) + """ + dobj = obj[0:2] + for i in range(2, len(obj)): + dobj += chr( (ord(obj[i]) ^ (CONFIG_FILE_XOR +i)) & 0xff ) + return dobj + + +def Decode(obj): + """ + Decodes (already decrypted) binary data stream + + @param obj: + binary config data + """ + # get header data + cfg_size = GetField(obj, 'cfg_size', Setting_6_2_1['cfg_size']) + version = GetField(obj, 'version', Setting_6_2_1['version']) + + # search setting definition + setting = None + for cfg in Settings: + if version >= cfg[0] and cfg_size == cfg[1]: + template = cfg + break + + setting = template[2] + # if we did not found a mathching setting + if setting is None: + exit(2, "Can't handle Tasmota configuration data for version 0x{:x} with {} bytes".format(version, cfg_size) ) + + if GetField(obj, 'cfg_crc', setting['cfg_crc']) != GetSettingsCrc(obj): + exit(3, 'Data crc error' ) + + config = {} + config['version_template'] = '0x{:x}'.format(template[0]) + for name in setting: + config[name] = GetField(obj, name, setting[name]) + + if args.sort == 'name': + config = collections.OrderedDict(sorted(config.items())) + + if args.format == 'json': + print json.dumps(config, sort_keys=args.sort=='name') + else: + for key,value in config.items(): + print '{} = {}'.format(key, repr(value)) + + + +if __name__ == "__main__": + parser = configargparse.ArgumentParser(description='Decode configuration of Sonoff-Tasmota device.', + epilog='Note: Either argument -d or -f must be given.') + + source = parser.add_argument_group('source') + source.add_argument('-f', '--file', + metavar='', + dest='tasmotafile', + default=DEFAULTS['source']['tasmotafile'], + help='file to retrieve Tasmota configuration from (default: {})'.format(DEFAULTS['source']['tasmotafile'])) + source.add_argument('-d', '--device', + metavar='', + dest='device', + default=DEFAULTS['source']['device'], + help='device to retrieve configuration from (default: {})'.format(DEFAULTS['source']['device']) ) + source.add_argument('-u', '--username', + metavar='', + dest='username', + default=DEFAULTS['source']['username'], + help='for -d usage: http access username (default: {})'.format(DEFAULTS['source']['username'])) + source.add_argument('-p', '--password', + metavar='', + dest='password', + default=DEFAULTS['source']['password'], + help='for -d usage: http access password (default: {})'.format(DEFAULTS['source']['password'])) + + output = parser.add_argument_group('output') + output.add_argument('--format', + metavar='', + dest='format', + choices=['json', 'text'], + default=DEFAULTS['output']['format'], + help='output format ("json" or "text", default: "{}")'.format(DEFAULTS['output']['format']) ) + output.add_argument('--sort', + metavar='', + dest='sort', + choices=['none', 'name'], + default=DEFAULTS['output']['sort'], + help='sort result - can be "none" or "name" (default: "{}")'.format(DEFAULTS['output']['sort']) ) + output.add_argument('--raw', + dest='raw', + action='store_true', + default=DEFAULTS['output']['raw'], + help='output raw values (default: {})'.format('raw' if DEFAULTS['output']['raw'] else 'processed') ) + output.add_argument('--unhide-pw', + dest='unhidepw', + action='store_true', + default=DEFAULTS['output']['unhide-pw'], + help='unhide passwords (default: {})'.format('unhide' if DEFAULTS['output']['unhide-pw'] else 'hide') ) + output.add_argument('-o', '--output-file', + metavar='', + dest='outputfile', + default=DEFAULTS['output']['outputfile'], + help='file to store decrypted raw binary configuration to (default: {})'.format(DEFAULTS['output']['outputfile'])) + + parser.add_argument('-c', '--config', + metavar='', + dest='configfile', + default=DEFAULTS['DEFAULT']['configfile'], + is_config_file=True, + help='Config file, can be used instead of command parameter (defaults to {})'.format(DEFAULTS['DEFAULT']['configfile']) ) + + info = parser.add_argument_group('info') + info.add_argument('-V', '--version', action='version', version=PROG) + + args = parser.parse_args() + + configobj = None + + if args.device is not None: + + # read config direct from device via http + buffer = io.BytesIO() + url = str("http://{}/dl".format(args.device)) + c = pycurl.Curl() + c.setopt(c.URL, url) + c.setopt(c.VERBOSE, 0) + if args.username is not None and args.password is not None: + c.setopt(c.HTTPAUTH, c.HTTPAUTH_BASIC) + c.setopt(c.USERPWD, args.username + ':' + args.password) + c.setopt(c.WRITEDATA, buffer) + try: + c.perform() + except Exception, e: + exit(e[0], e[1]) + response = c.getinfo(c.RESPONSE_CODE) + c.close() + if response>=400: + exit(response, 'HTTP returns {}'.format(response) ) + + configobj = buffer.getvalue() + + elif args.tasmotafile is not None: + # read config from a file + + if not os.path.isfile(args.tasmotafile): # check file exists + exit(1, "file '{}' not found".format(args.tasmotafile)) + try: + tasmotafile = open(args.tasmotafile, "rb") + configobj = tasmotafile.read() + tasmotafile.close() + except Exception, e: + exit(e[0], e[1]) + + else: + parser.print_help() + sys.exit(0) + + if configobj is not None and len(configobj)>0: + cfg = DeEncrypt(configobj) + + if args.outputfile is not None: + outputfile = open(args.outputfile, "wb") + outputfile.write(cfg) + outputfile.close() + + Decode(cfg) + + else: + exit(4, "Could not read configuration data from {} '{}'".format('device' if args.device is not None else 'file', args.device if args.device is not None else args.tasmotafile) ) \ No newline at end of file