From cee2248129ad679005600503ed4961716ca762e6 Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Mon, 9 Sep 2019 15:29:05 -0300 Subject: [PATCH 01/44] Update xdrv_12_home_assistant.ino --- sonoff/xdrv_12_home_assistant.ino | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index b2fe72d20db4..538c25e06124 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -88,7 +88,8 @@ const char HASS_DISCOVER_SENSOR[] PROGMEM = const char HASS_DISCOVER_SENSOR_TEMP[] PROGMEM = ",\"unit_of_meas\":\"°%c\"," // °C / °F - "\"val_tpl\":\"{{value_json['%s'].Temperature}}\""; // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} + "\"val_tpl\":\"{{value_json['%s'].Temperature}}\"," // "SI7021-14":{"Temperature":null,"Humidity":null} -> {{ value_json['SI7021-14'].Temperature }} + "\"dev_cla\":\"temperature\""; // temperature const char HASS_DISCOVER_SENSOR_HUM[] PROGMEM = ",\"unit_of_meas\":\"%%\"," // % @@ -103,19 +104,27 @@ const char HASS_DISCOVER_SENSOR_PRESS[] PROGMEM = //ENERGY const char HASS_DISCOVER_SENSOR_KWH[] PROGMEM = ",\"unit_of_meas\":\"kWh\"," // kWh - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }} + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Total/Yesterday/Today }} + "\"dev_cla\":\"power\""; // power const char HASS_DISCOVER_SENSOR_WATT[] PROGMEM = ",\"unit_of_meas\":\"W\"," // W - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} - + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].POWER }} + "\"dev_cla\":\"power\""; const char HASS_DISCOVER_SENSOR_VOLTAGE[] PROGMEM = ",\"unit_of_meas\":\"V\"," // V - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} - + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Voltage }} + "\"dev_cla\":\"power\""; const char HASS_DISCOVER_SENSOR_AMPERE[] PROGMEM = ",\"unit_of_meas\":\"A\"," // A - "\"val_tpl\":\"{{value_json['%s'].%s}}\""; // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} + "\"val_tpl\":\"{{value_json['%s'].%s}}\"," // "ENERGY":{"TotalStartTime":null,"Total":null,"Yesterday":null,"Today":null,"Power":null,"ApparentPower":null,"ReactivePower":null,"Factor":null,"Voltage":null,"Current":null} -> {{ value_json['ENERGY'].Current }} + "\"dev_cla\":\"power\""; + +//ILLUMINANCE +const char HASS_DISCOVER_SENSOR_ILLUMINANCE[] PROGMEM = + ",\"unit_of_meas\":\"LX\"," // LX by default + "\"val_tpl\":\"{{value_json['%s'].Illuminance}}\"," // "ANALOG":{"Illuminance":34}} + "\"dev_cla\":\"illuminance\""; // illuminance const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = ",\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass @@ -124,7 +133,7 @@ const char HASS_DISCOVER_SENSOR_ANY[] PROGMEM = const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = ",\"json_attributes_topic\":\"%s\"," "\"unit_of_meas\":\" \"," // " " As unit of measurement to get a value graph in Hass - "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"";// "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\""; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER'].C1 }} const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = ",\"uniq_id\":\"%s\"," @@ -438,6 +447,8 @@ void HAssAnnounceSensor(const char* sensorname, const char* subsensortype) TryResponseAppend_P(HASS_DISCOVER_SENSOR_VOLTAGE, sensorname, subsensortype); } else if (!strcmp_P(subsensortype, PSTR(D_JSON_CURRENT))){ TryResponseAppend_P(HASS_DISCOVER_SENSOR_AMPERE, sensorname, subsensortype); + } else if (!strcmp_P(subsensortype, PSTR(D_JSON_ILLUMINANCE))){ + TryResponseAppend_P(HASS_DISCOVER_SENSOR_ILLUMINANCE, sensorname, subsensortype); } else { TryResponseAppend_P(HASS_DISCOVER_SENSOR_ANY, sensorname, subsensortype); From cf47866c1808b31688d59d75465e278c4a621e03 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Tue, 10 Sep 2019 15:04:37 +0200 Subject: [PATCH 02/44] Use Python 3.7. for Travis... since 2.7. goes EoL soon --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4fb1c153838b..945730e42fed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: python python: - - '2.7' + - '3.7' sudo: false install: - pip install -U platformio From 657dbff570a3d4914203c8e1e7fc9db1024adef0 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Sep 2019 15:14:51 +0200 Subject: [PATCH 03/44] Add support for Hiking DDS238-2 Modbus energy meter Add support for Hiking DDS238-2 Modbus energy meter by Matteo Campanella (#6384) --- sonoff/_changelog.ino | 1 + sonoff/language/bg-BG.h | 15 +++-- sonoff/language/cs-CZ.h | 14 ++-- sonoff/language/de-DE.h | 14 ++-- sonoff/language/el-GR.h | 14 ++-- sonoff/language/en-GB.h | 14 ++-- sonoff/language/es-ES.h | 14 ++-- sonoff/language/fr-FR.h | 14 ++-- sonoff/language/he-HE.h | 14 ++-- sonoff/language/hu-HU.h | 14 ++-- sonoff/language/it-IT.h | 14 ++-- sonoff/language/ko-KO.h | 14 ++-- sonoff/language/nl-NL.h | 14 ++-- sonoff/language/pl-PL.h | 14 ++-- sonoff/language/pt-BR.h | 14 ++-- sonoff/language/pt-PT.h | 14 ++-- sonoff/language/ru-RU.h | 14 ++-- sonoff/language/sk-SK.h | 14 ++-- sonoff/language/sv-SE.h | 14 ++-- sonoff/language/tr-TR.h | 16 +++-- sonoff/language/uk-UK.h | 14 ++-- sonoff/language/zh-CN.h | 16 +++-- sonoff/language/zh-TW.h | 14 ++-- sonoff/my_user_config.h | 7 +- sonoff/sonoff_post.h | 16 ++++- sonoff/sonoff_template.h | 7 ++ sonoff/support_features.ino | 4 +- sonoff/xnrg_05_pzem_ac.ino | 60 ++++++------------ sonoff/xnrg_06_pzem_dc.ino | 2 +- sonoff/xnrg_08_sdm120.ino | 3 +- sonoff/xnrg_09_dds2382.ino | 123 ++++++++++++++++++++++++++++++++++++ tools/decode-status.py | 2 +- 32 files changed, 355 insertions(+), 183 deletions(-) create mode 100644 sonoff/xnrg_09_dds2382.ino diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 40c3dd7727ed..ae249c6104bb 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -4,6 +4,7 @@ * Add support for up to 4 INA226 Voltage and Current sensors by Steve Rogers (#6342) * Change Improve reliability of TasmotaSerial at 115200 bauds and reduce IRAM usage for Stage/pre-2.6 * Add support for A4988 stepper-motor-driver-circuit by Tim Leuschner (#6370) + * Add support for Hiking DDS238-2 Modbus energy meter by Matteo Campanella (#6384) * * 6.6.0.10 20190905 * Redesign Tuya support by Shantur Rathore (#6353) diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index a6d05efc53e8..69e35925da09 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -596,13 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" - -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index 264faf797977..3cc0d31978d3 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index d3f280deddc6..a516eeb42fb7 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 8f056dcdf4f5..100791bb1fcf 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 4cd665634b69..3e415c6e2de4 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-ES.h b/sonoff/language/es-ES.h index 0300d6f4dd4d..bbd1d776a83d 100644 --- a/sonoff/language/es-ES.h +++ b/sonoff/language/es-ES.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 28eb6a8a60a9..d08f654ff69d 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 504614de48c0..e2a54fe573d2 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 529bb8604881..564d385c1fad 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index dc3d6c0ddbfa..257dd3d55212 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index 98cae588b6b7..d713fa79d45e 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index b0009f3e0018..03f77b4eb6b2 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index b2d465288d0e..6b4d356787b7 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 2025a665b45b..388db5620ea9 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 9313cf287226..44e5b34692a8 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 743e2bf9ebe5..614fea8feac9 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index 9d99c63ee965..18e3fcb412ed 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 33ebf9f3ae27..477e17e67b4f 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index ab9c9e11e6f4..6826c9e92451 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -596,16 +596,19 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CENTIMETER "cm" +#define D_UNIT_HERTZ "Hz" #define D_UNIT_HOUR "Hr" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" @@ -635,7 +638,6 @@ #define D_UNIT_WATT "W" #define D_UNIT_WATTHOUR "Wh" #define D_UNIT_WATT_METER_QUADRAT "W/m²" -#define D_UNIT_HERTZ "Hz" // Log message prefix #define D_LOG_APPLICATION "APP: " // Application diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 72c1c6144b1c..eb9db991381a 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index d0b6b7a5ebab..0f16bf057061 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -596,16 +596,19 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "安" #define D_UNIT_CENTIMETER "厘米" +#define D_UNIT_HERTZ "赫兹" #define D_UNIT_HOUR "时" #define D_UNIT_GALLONS "gal" #define D_UNIT_GALLONS_PER_MIN "g/m" @@ -634,7 +637,6 @@ #define D_UNIT_VOLT "伏" #define D_UNIT_WATT "瓦" #define D_UNIT_WATTHOUR "瓦时" -#define D_UNIT_HERTZ "赫兹" #define D_UNIT_WATT_METER_QUADRAT "瓦/平米" // Log message prefix diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 6febe1a6b11f..8d65c1c67a87 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -596,12 +596,14 @@ #define D_SENSOR_IBEACON_RX "iBeacon RX" #define D_SENSOR_RDM6300_RX "RDM6300 RX" #define D_SENSOR_CC1101_CS "CC1101 CS" -#define D_SENSOR_A4988_DIR "A4988 DIR" -#define D_SENSOR_A4988_STP "A4988 STP" -#define D_SENSOR_A4988_ENA "A4988 ENA" -#define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_A4988_MS2 "A4988 MS2" -#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_A4988_MS2 "A4988 MS2" +#define D_SENSOR_A4988_MS3 "A4988 MS3" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index e289d8f932c6..8507cd662fa6 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -431,10 +431,13 @@ #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) -//#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy meter (+1k4 code) +//#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) + #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) +//#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #define DDS2382_SPEED 9600 // Hiking DDS2382 Modbus RS485 serial speed (default: 9600 baud) //#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+2k4 code) - #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) +// #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) #define USE_SDM220 // Add extra parameters for SDM220 (+0k1 code) //#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) diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index bf4f57c3e203..802d8ad25c05 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -148,7 +148,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) #define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) -#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) +//#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) #define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) #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) @@ -161,6 +161,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) +#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) +#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) @@ -244,6 +247,8 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson @@ -294,6 +299,8 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 + #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter + #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #undef USE_DOMOTICZ // Disable Domoticz #undef USE_HOME_ASSISTANT // Disable Home Assistant @@ -375,6 +382,8 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + //#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_WS2812 // Disable WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // @@ -468,6 +477,9 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor //#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter @@ -551,6 +563,8 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) + #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI #undef USE_IR_REMOTE // Disable IR driver diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index aa0dfbe9a97f..64e62db7175e 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -200,6 +200,8 @@ enum UserSelectablePins { GPIO_A4988_MS1, // A4988 microstep pin1 GPIO_A4988_MS2, // A4988 microstep pin2 GPIO_A4988_MS3, // A4988 microstep pin3 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -274,6 +276,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_RDM6300_RX "|" D_SENSOR_IBEACON_TX "|" D_SENSOR_IBEACON_RX "|" D_SENSOR_A4988_DIR "|" D_SENSOR_A4988_STP "|" D_SENSOR_A4988_ENA "|" D_SENSOR_A4988_MS1 "|" D_SENSOR_A4988_MS2 "|" D_SENSOR_A4988_MS3 "|" + D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|" ; // User selectable ADC0 functionality @@ -617,6 +620,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDM120_TX, // SDM120 Serial interface GPIO_SDM120_RX, // SDM120 Serial interface #endif +#ifdef USE_DDS2382 + GPIO_DDS2382_TX, // DDS2382 Serial interface + GPIO_DDS2382_RX, // DDS2382 Serial interface +#endif #endif // USE_ENERGY_SENSOR #ifndef USE_SDM120_2 #ifdef USE_SDM120 diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino index 9f36d3349c5f..35d900041933 100644 --- a/sonoff/support_features.ino +++ b/sonoff/support_features.ino @@ -447,7 +447,9 @@ void GetFeatures(void) #ifdef USE_A4988_Stepper feature5 |= 0x00000020; // xdrv_25_A4988.ino #endif -// feature5 |= 0x00000040; +#ifdef USE_DDS2382 + feature5 |= 0x00000040; // Xnrg_09_dds2382.ino +#endif // feature5 |= 0x00000080; // feature5 |= 0x00000100; diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 0a02587873e9..12b08eb8d98f 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -36,63 +36,43 @@ #include TasmotaModbus *PzemAcModbus; -/* -uint16_t PzemCalculateCRC(uint8_t *buffer, uint8_t num) -{ - uint16_t crc = 0xFFFF; - for (uint32_t i = 0; i < num; i++) { - crc ^= buffer[i]; - for (uint32_t j = 8; j; j--) { - if ((crc & 0x0001) != 0) { // If the LSB is set - crc >>= 1; // Shift right and XOR 0xA001 - crc ^= 0xA001; - } else { // Else LSB is not set - crc >>= 1; // Just shift right - } - } - } - return crc; -} -*/ +uint8_t PzemAc_send_retry = 0; + void PzemAcEverySecond(void) { - static uint8_t send_retry = 0; - bool data_ready = PzemAcModbus->ReceiveReady(); if (data_ready) { - uint8_t buffer[26]; + uint8_t buffer[30]; // At least 5 + (2 * 10) = 25 uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); } else { -// if ((PzemCalculateCRC(buffer, 23)) == ((buffer[24] << 8) | buffer[23])) { - Energy.data_valid = 0; - - // 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 - // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 - // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - Energy.current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - Energy.active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - Energy.frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - Energy.power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh - - EnergyUpdateTotal(energy, false); -// } + Energy.data_valid = 0; + + // 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 + // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 + // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- + Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 + float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + + EnergyUpdateTotal(energy, false); } } - if (0 == send_retry || data_ready) { - send_retry = 5; + if (0 == PzemAc_send_retry || data_ready) { + PzemAc_send_retry = 5; PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS, 0x04, 0, 10); } else { - send_retry--; + PzemAc_send_retry--; } } diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 21c256e7506b..855d3645780a 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -43,7 +43,7 @@ void PzemDcEverySecond(void) bool data_ready = PzemDcModbus->ReceiveReady(); if (data_ready) { - uint8_t buffer[22]; + uint8_t buffer[26]; // At least 5 + (2 * 8) = 21 uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); diff --git a/sonoff/xnrg_08_sdm120.ino b/sonoff/xnrg_08_sdm120.ino index 472886442f4f..c7644b740d16 100644 --- a/sonoff/xnrg_08_sdm120.ino +++ b/sonoff/xnrg_08_sdm120.ino @@ -77,7 +77,7 @@ void SDM120Every250ms(void) bool data_ready = Sdm120Modbus->ReceiveReady(); if (data_ready) { - uint8_t buffer[9]; + uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); @@ -85,7 +85,6 @@ void SDM120Every250ms(void) if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); } else { - uint32_t max_table = Energy.data_valid = 0; // 0 1 2 3 4 5 6 7 8 diff --git a/sonoff/xnrg_09_dds2382.ino b/sonoff/xnrg_09_dds2382.ino new file mode 100644 index 000000000000..668f21358eaf --- /dev/null +++ b/sonoff/xnrg_09_dds2382.ino @@ -0,0 +1,123 @@ +/* + xnrg_09_dds2382.ino - Hiking DDS238-2 Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Matteo Campanella - based on the work of Gennaro Tortone + + 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_DDS2382 +/*********************************************************************************************\ + * Hiking DDS238-2 Modbus energy meter + * + * Based on: /~https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#define XNRG_09 9 + +#ifndef DDS2382_SPEED +#define DDS2382_SPEED 9600 // default dds2382 Modbus address +#endif +#ifndef DDS2382_ADDR +#define DDS2382_ADDR 1 // default dds2382 Modbus address +#endif + +#include +TasmotaModbus *Dds2382Modbus; + +uint8_t Dds2382_send_retry = 0; + +void Dds2382EverySecond(void) +{ + bool data_ready = Dds2382Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[46]; // At least 5 + (2 * 18) = 41 + + uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + } else { + Energy.data_valid = 0; + + // 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 + // SA FC BC EnergyTotal ExportActiv ImportActiv Volta Curre APowe RPowe PFact Frequ Crc-- + + Energy.voltage = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; // 1.00 + Energy.frequency = (float)((buffer[37] << 8) + buffer[38]) / 100.0; // 50.0 Hz + Energy.export_active = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[13] << 8) + buffer[14]) / 100.0; // 429496729.0 W +// float energy_total = (float)((buffer[3] << 24) + (buffer[4] << 16) + (buffer[5] << 8) + buffer[6]) / 100.0; // 429496729.0 W + float import_active = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[17] << 8) + buffer[18]) / 100.0; // 429496729.0 W + + EnergyUpdateTotal(import_active, false); // 484.708 kWh + } + } // end data ready + + if (0 == Dds2382_send_retry || data_ready) { + Dds2382_send_retry = 5; + Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); + } else { + Dds2382_send_retry--; + } +} + +void Dds2382SnsInit(void) +{ + Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Dds2382DrvInit(void) +{ + if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + energy_flg = XNRG_09; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { Dds2382EverySecond(); } + break; + case FUNC_INIT: + Dds2382SnsInit(); + break; + case FUNC_PRE_INIT: + Dds2382DrvInit(); + break; + } + return result; +} + +#endif // USE_DDS2382 +#endif // USE_ENERGY_SENSOR diff --git a/tools/decode-status.py b/tools/decode-status.py index c14628bf33ba..f1c3bacf2835 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -167,7 +167,7 @@ "USE_MAX31865","USE_CHIRP","USE_SOLAX_X1","USE_PAJ7620" ],[ "USE_BUZZER","USE_RDM6300","USE_IBEACON","USE_SML_M", - "USE_INA226","USE_A4988_Stepper","","", + "USE_INA226","USE_A4988_Stepper","USE_DDS2382","", "","","","", "","","","", "","","","", From 4b647928aaa019bc1214fa7cb385684d3e245a5b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Sep 2019 16:18:23 +0200 Subject: [PATCH 04/44] Bump version to 6.6.0.12 * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour (#6282) * Commands Tariff1 23 = Tariff1 ST, Tariff2 7 = Tariff2 ST, Tarriff3 22 = Tarrif1 DST, Tariff4 6 = Tariff2 DST, Tariff9 0/1 = Weekend toggle --- sonoff/_changelog.ino | 4 ++++ sonoff/settings.h | 4 +--- sonoff/settings.ino | 17 ++++---------- sonoff/sonoff.h | 17 ++++++++++---- sonoff/sonoff_version.h | 2 +- sonoff/support_rtc.ino | 12 +++++----- sonoff/xdrv_03_energy.ino | 48 +++++++++++++++++++++++++++------------ sonoff/xsns_33_ds3231.ino | 8 +++---- 8 files changed, 66 insertions(+), 46 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index ae249c6104bb..b4bff23ed01f 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,4 +1,8 @@ /*********************************************************************************************\ + * 6.6.0.12 20190910 + * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour + * Commands Tariff1 23 = Tariff1 ST, Tariff2 7 = Tariff2 ST, Tarriff3 22 = Tarrif1 DST, Tariff4 6 = Tariff2 DST, Tariff9 0/1 = Weekend toggle + * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility * Add support for up to 4 INA226 Voltage and Current sensors by Steve Rogers (#6342) diff --git a/sonoff/settings.h b/sonoff/settings.h index eb58877cbe88..9fdfb1e34d81 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -227,9 +227,7 @@ struct SYSCFG { uint8_t weblog_level; // 1AC uint8_t mqtt_fingerprint[2][20]; // 1AD uint8_t adc_param_type; // 1D5 - - uint8_t free_1D6[18]; // 1D6 Free since 5.12.0e - + uint8_t register8[18]; // 1D6 - 18 x 8-bit registers indexed by enum SettingsRegister8 uint8_t sps30_inuse_hours; // 1E8 char mqtt_host[33]; // 1E9 - Keep together with below as being copied as one chunck with reset 6 uint16_t mqtt_port; // 20A - Keep together diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 9534c0ab679b..289c8392b55e 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -128,15 +128,6 @@ #ifndef TUYA_DIMMER_MAX #define TUYA_DIMMER_MAX 100 #endif -#ifndef ENERGY_TARIFF1_HOUR -#define ENERGY_TARIFF1_HOUR 23 // Start hour "nighttime" or "off-peak" tariff -#endif -#ifndef ENERGY_TARIFF2_HOUR -#define ENERGY_TARIFF2_HOUR 7 // Start hour "daytime" or "standard" tariff -#endif -#ifndef ENERGY_TARIFF_WEEKEND -#define ENERGY_TARIFF_WEEKEND 1 // 0 = No difference in weekend, 1 = off-peak during weekend -#endif enum WebColors { COL_TEXT, COL_BACKGROUND, COL_FORM, @@ -1097,9 +1088,6 @@ void SettingsDelta(void) } else { Settings.param[P_TUYA_DIMMER_MAX] = 255; } - Settings.param[P_ENERGY_TARIFF1] = ENERGY_TARIFF1_HOUR; - Settings.param[P_ENERGY_TARIFF2] = ENERGY_TARIFF2_HOUR; - Settings.flag3.energy_weekend = ENERGY_TARIFF_WEEKEND; } if (Settings.version < 0x06060009) { Settings.baudrate = Settings.ex_baudrate * 4; @@ -1139,8 +1127,11 @@ void SettingsDelta(void) Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; tuyaindex++; } - } + if (Settings.version < 0x0606000C) { + memset(&Settings.register8, 0x00, sizeof(Settings.register8)); + } + Settings.version = VERSION; SettingsSave(1); } diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index e773bf7a7e74..89e3c53f3793 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -245,11 +245,18 @@ enum ButtonStates { PRESSED, NOT_PRESSED }; enum Shortcuts { SC_CLEAR, SC_DEFAULT, SC_USER }; -enum SettingsParamIndex {P_HOLD_TIME, P_MAX_POWER_RETRY, P_ex_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 - P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 - P_TUYA_DIMMER_MAX, P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46 - P_ENERGY_TARIFF1, P_ENERGY_TARIFF2, // SetOption47 .. SetOption48 - P_MAX_PARAM8}; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 +enum SettingsParamIndex { P_HOLD_TIME, P_MAX_POWER_RETRY, P_ex_TUYA_DIMMER_ID, P_MDNS_DELAYED_START, P_BOOT_LOOP_OFFSET, P_RGB_REMAP, P_IR_UNKNOW_THRESHOLD, // SetOption32 .. SetOption38 + P_CSE7766_INVALID_POWER, P_HOLD_IGNORE, P_ex_TUYA_RELAYS, P_OVER_TEMP, // SetOption39 .. SetOption42 + P_TUYA_DIMMER_MAX, + P_ex_TUYA_VOLTAGE_ID, P_ex_TUYA_CURRENT_ID, P_ex_TUYA_POWER_ID, // SetOption43 .. SetOption46 + P_ex_ENERGY_TARIFF1, P_ex_ENERGY_TARIFF2, // SetOption47 .. SetOption48 + P_MAX_PARAM8 }; // Max is PARAM8_SIZE (18) - SetOption32 until SetOption49 + +enum SettingsRegister8 { R8_ENERGY_TARIFF1_ST, R8_ENERGY_TARIFF2_ST, R8_ENERGY_TARIFF1_DS, R8_ENERGY_TARIFF2_DS, + R8_SPARE04, R8_SPARE05, R8_SPARE06, R8_SPARE07, + R8_SPARE08, R8_SPARE09, R8_SPARE10, R8_SPARE11, + R8_SPARE12, R8_SPARE13, R8_SPARE14, R8_SPARE15, + R8_SPARE16, R8_SPARE17 }; // Max size is 18 (Settings.register8[]) enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, DZ_ILLUMINANCE, DZ_COUNT, DZ_VOLTAGE, DZ_CURRENT, DZ_AIRQUALITY, DZ_P1_SMART_METER, DZ_MAX_SENSORS}; diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index b7eae8a8f928..dc8f2f2e9d0f 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,6 +20,6 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -const uint32_t VERSION = 0x0606000B; +const uint32_t VERSION = 0x0606000C; #endif // _SONOFF_VERSION_H_ diff --git a/sonoff/support_rtc.ino b/sonoff/support_rtc.ino index e75046598910..961869687f41 100644 --- a/sonoff/support_rtc.ino +++ b/sonoff/support_rtc.ino @@ -299,7 +299,7 @@ void BreakTime(uint32_t time_input, TIME_T &tm) strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); tm.month = month + 1; // jan is month 1 tm.day_of_month = time + 1; // day of month - tm.valid = (time_input > 1451602800); // 2016-01-01 + tm.valid = (time_input > START_VALID_TIME); // 2016-01-01 } uint32_t MakeTime(TIME_T &tm) @@ -374,9 +374,9 @@ void RtcSecond(void) uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; // First try ASAP to sync. If fails try once every 60 seconds based on chip id if (!global_state.wifi_down && (((offset == RtcTime.second) && ((RtcTime.year < 2016) || (Rtc.ntp_sync_minute == RtcTime.minute))) || ntp_force_sync)) { Rtc.ntp_time = sntp_get_current_timestamp(); - if (Rtc.ntp_time > 1451602800) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) + if (Rtc.ntp_time > START_VALID_TIME) { // Fix NTP bug in core 2.4.1/SDK 2.2.1 (returns Thu Jan 01 08:00:10 1970 after power on) ntp_force_sync = false; - if (Rtc.utc_time > 1451602800) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; } + if (Rtc.utc_time > START_VALID_TIME) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; } Rtc.utc_time = Rtc.ntp_time; Rtc.ntp_sync_minute = 60; // Sync so block further requests if (Rtc.restart_time == 0) { @@ -391,7 +391,7 @@ void RtcSecond(void) // AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "(" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); ntp_synced_message = true; - if (Rtc.local_time < 1451602800) { // 2016-01-01 + if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01 rules_flag.time_init = 1; } else { rules_flag.time_set = 1; @@ -403,7 +403,7 @@ void RtcSecond(void) } Rtc.utc_time++; Rtc.local_time = Rtc.utc_time; - if (Rtc.local_time > 1451602800) { // 2016-01-01 + if (Rtc.local_time > START_VALID_TIME) { // 2016-01-01 int16_t timezone_minutes = Settings.timezone_minutes; if (Settings.timezone < 0) { timezone_minutes *= -1; } Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); @@ -447,7 +447,7 @@ void RtcSecond(void) void RtcSetTime(uint32_t epoch) { - if (epoch < 1451602800) { // 2016-01-01 + if (epoch < START_VALID_TIME) { // 2016-01-01 Rtc.user_time_entry = false; ntp_force_sync = true; } else { diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 007aa871faad..b9d8d92b4e8c 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -123,6 +123,25 @@ Ticker ticker_energy; /********************************************************************************************/ +bool EnergyTariff1Active() // Off-Peak hours +{ + uint8_t tariff1 = Settings.register8[R8_ENERGY_TARIFF1_ST]; + uint8_t tariff2 = Settings.register8[R8_ENERGY_TARIFF2_ST]; + if (IsDst() && (Settings.register8[R8_ENERGY_TARIFF1_DS] != Settings.register8[R8_ENERGY_TARIFF2_DS])) { + tariff1 = Settings.register8[R8_ENERGY_TARIFF1_DS]; + tariff2 = Settings.register8[R8_ENERGY_TARIFF2_DS]; + } + if (tariff1 != tariff2) { + return ((RtcTime.hour < tariff2) || // Tarrif1 = Off-Peak + (RtcTime.hour >= tariff1) || + (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || + (RtcTime.day_of_week == 7))) + ); + } else { + return false; + } +} + void EnergyUpdateToday(void) { if (Energy.kWhtoday_delta > 1000) { @@ -143,11 +162,7 @@ void EnergyUpdateToday(void) Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; - if ((RtcTime.hour < Settings.param[P_ENERGY_TARIFF2]) || // Tarrif1 = Off-Peak - (RtcTime.hour >= Settings.param[P_ENERGY_TARIFF1]) || - (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || - (RtcTime.day_of_week == 7))) - ) { + if (EnergyTariff1Active()) { // Tarrif1 = Off-Peak RtcSettings.energy_usage.usage1_kWhtoday += energy_diff; RtcSettings.energy_usage.return1_kWhtotal += return_diff; Energy.total1 = (float)(RtcSettings.energy_usage.usage1_kWhtotal + RtcSettings.energy_usage.usage1_kWhtoday) / 100000; @@ -414,8 +429,8 @@ void EnergyOverTempCheck() if (!isnan(Energy.reactive_power)) { Energy.reactive_power = 0; } if (!isnan(Energy.frequency)) { Energy.frequency = 0; } if (!isnan(Energy.power_factor)) { Energy.power_factor = 0; } - Energy.start_energy = 0; if (!isnan(Energy.export_active)) { Energy.export_active = 0; } + Energy.start_energy = 0; XnrgCall(FUNC_ENERGY_RESET); } @@ -501,19 +516,24 @@ void CmndEnergyReset(void) void CmndTariff(void) { - // Tariff1 23 - // Tariff2 7 - // Tariff3 0/1 - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + // Tariff1 23 - Standard Time Tariff1 start hour + // Tariff2 7 - Standard Time Tariff2 start hour + // Tariff3 22 - Daylight Savings Time Tariff1 start hour + // Tariff4 6 - Daylight Savings Time Tariff2 start hour + // Tariff9 0/1 + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { - Settings.param[P_ENERGY_TARIFF1 + XdrvMailbox.index -1] = XdrvMailbox.payload; + Settings.register8[R8_ENERGY_TARIFF1_ST + XdrvMailbox.index -1] = XdrvMailbox.payload; } } - else if (XdrvMailbox.index == 3) { + else if (XdrvMailbox.index == 9) { Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; } - Response_P(PSTR("{\"%s\":{\"Off-Peak\":%d,\"Standard\":%d,\"Weekend\":\"%s\"}}"), - XdrvMailbox.command, Settings.param[P_ENERGY_TARIFF1], Settings.param[P_ENERGY_TARIFF2], GetStateText(Settings.flag3.energy_weekend)); + Response_P(PSTR("{\"%s\":{\"Off-Peak\":[%d,%d],\"Standard\":[%d,%d],\"Weekend\":\"%s\"}}"), + XdrvMailbox.command, + Settings.register8[R8_ENERGY_TARIFF1_ST], Settings.register8[R8_ENERGY_TARIFF1_DS], + Settings.register8[R8_ENERGY_TARIFF2_ST], Settings.register8[R8_ENERGY_TARIFF2_DS], + GetStateText(Settings.flag3.energy_weekend)); } void CmndPowerCal(void) diff --git a/sonoff/xsns_33_ds3231.ino b/sonoff/xsns_33_ds3231.ino index f1243a3a463c..ef2acfbd4aad 100644 --- a/sonoff/xsns_33_ds3231.ino +++ b/sonoff/xsns_33_ds3231.ino @@ -142,13 +142,13 @@ bool Xsns33(uint8_t function) break; case FUNC_EVERY_SECOND: TIME_T tmpTime; - if (!ds3231ReadStatus && DS3231chipDetected && Rtc.utc_time < 1451602800 ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231 + if (!ds3231ReadStatus && DS3231chipDetected && Rtc.utc_time < START_VALID_TIME ) { // We still did not sync with NTP (time not valid) , so, read time from DS3231 ntp_force_sync = true; //force to sync with ntp Rtc.utc_time = ReadFromDS3231(); //we read UTC TIME from DS3231 // from this line, we just copy the function from "void RtcSecond()" at the support.ino ,line 2143 and above // We need it to set rules etc. BreakTime(Rtc.utc_time, tmpTime); - if (Rtc.utc_time < 1451602800 ) { + if (Rtc.utc_time < START_VALID_TIME ) { ds3231ReadStatus = true; //if time in DS3231 is valid, do not update again } RtcTime.year = tmpTime.year + 1970; @@ -156,13 +156,13 @@ bool Xsns33(uint8_t function) Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - if (Rtc.local_time < 1451602800) { // 2016-01-01 + if (Rtc.local_time < START_VALID_TIME) { // 2016-01-01 rules_flag.time_init = 1; } else { rules_flag.time_set = 1; } } - else if (!ds3231WriteStatus && DS3231chipDetected && Rtc.utc_time > 1451602800 && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second + else if (!ds3231WriteStatus && DS3231chipDetected && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) {//if time is valid and is drift from RTC in more that 60 second AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); SetDS3231Time (Rtc.utc_time); //update the DS3231 time From 72990ad9aeeaf2ae9bb10cc5cef3d444df902f83 Mon Sep 17 00:00:00 2001 From: Tim Leuschner Date: Tue, 10 Sep 2019 16:19:23 +0200 Subject: [PATCH 05/44] Add files via upload Hi all, now that it's in the world I found the implementation of my A4988 - module not good concerning usability. I use ioBroker as Backend and the device reports there as expected with a Property named "MOTOR" but no reasonable way to pass the commands to it. Iv'e forked Tasmota again and changed the xdrv_25_A4988_Stepper.ino. I expose the commands directly and changed the names so they can be found close together in the propertylist. New CommandNames: motorMove motorRotate motorTurn motorMIS motorSPR motorRPM if you pull it, I instantly would change the wiki. --- sonoff/xdrv_25_A4988_Stepper.ino | 126 +++++++++++-------------------- 1 file changed, 42 insertions(+), 84 deletions(-) diff --git a/sonoff/xdrv_25_A4988_Stepper.ino b/sonoff/xdrv_25_A4988_Stepper.ino index 89b0a09c1ab9..a1fa4037da82 100644 --- a/sonoff/xdrv_25_A4988_Stepper.ino +++ b/sonoff/xdrv_25_A4988_Stepper.ino @@ -22,8 +22,6 @@ #include #define XDRV_25 25 -enum A4988Errors { A4988_NO_ERROR, A4988_NO_JSON_COMMAND, A4988_INVALID_JSON, A4988_MOVE, A4988_ROTATE, A4988_TURN}; - short A4988_dir_pin = pin[GPIO_MAX]; short A4988_stp_pin = pin[GPIO_MAX]; short A4988_ms1_pin = pin[GPIO_MAX]; @@ -59,97 +57,57 @@ void A4988Init(void) , A4988_ms3_pin ); } -const char kA4988Commands[] PROGMEM = "|" - "MOTOR"; +const char kA4988Commands[] PROGMEM = "|" //no prefix + "motorMove|motorRotate|motorTurn|motorMIS|motorSPR|motorRPM"; -void (* const A4988Command[])(void) PROGMEM = { &CmndMOTOR}; +void (* const A4988Command[])(void) PROGMEM = { &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; -uint32_t MOTORCmndJson(void) -{ - // MOTOR {"doMove":200} - // MOTOR {"doRotate":360} - // MOTOR {"doTurn":1.0} - uint32_t returnValue =A4988_NO_JSON_COMMAND; - - char parm_uc[12]; - char dataBufUc[XdrvMailbox.data_len]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { returnValue =A4988_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (json.success()) { - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_SPR)); - if (json.containsKey(parm_uc)){ - int howManySteps =strtoul(json[parm_uc],nullptr,10); - myA4988->setSPR(howManySteps); - returnValue = A4988_NO_ERROR; - } - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_RPM)); - if (json.containsKey(parm_uc)){ - int howManyRounds =strtoul(json[parm_uc],nullptr,10); - myA4988->setRPM(howManyRounds); - returnValue = A4988_NO_ERROR; - } - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_MIS)); - if (json.containsKey(parm_uc)){ - short oneToSixteen =strtoul(json[parm_uc],nullptr,10); - myA4988->setMIS(oneToSixteen); - returnValue = A4988_NO_ERROR; - } - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_MOVE)); - if (json.containsKey(parm_uc)){ - long stepsPlease = strtoul(json[parm_uc],nullptr,10); - myA4988->doMove(stepsPlease); - returnValue = A4988_MOVE; - } - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_ROTATE)); - if (json.containsKey(parm_uc)){ - long degrsPlease = strtoul(json[parm_uc],nullptr,10); - myA4988->doRotate(degrsPlease); - returnValue = A4988_ROTATE; - } - UpperCase_P(parm_uc, PSTR(D_JSON_MOTOR_TURN)); - if (json.containsKey(parm_uc)){ - float turnsPlease = strtod(json[parm_uc],nullptr); - myA4988->doTurn(turnsPlease); - returnValue = A4988_TURN; - } - } else returnValue =A4988_INVALID_JSON; - return returnValue; +void CmndDoMove(void) { + if (XdrvMailbox.data_len > 0) { + long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doMove(stepsPlease); + ResponseCmndDone(); + } } -void CmndMOTOR(void){ - uint32_t error; - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, "}") == nullptr) { - error = A4988_NO_JSON_COMMAND; - } else { - error = MOTORCmndJson(); - } +void CmndDoRotate(void) { + if (XdrvMailbox.data_len > 0) { + long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doRotate(degrsPlease); + ResponseCmndDone(); } - A4988CmndResponse(error); } -void A4988CmndResponse(uint32_t error){ - switch (error) { - case A4988_NO_JSON_COMMAND: - ResponseCmndChar(PSTR("No command!")); - break; - case A4988_MOVE: - ResponseCmndChar(PSTR("Stepping!")); - break; - case A4988_ROTATE: - ResponseCmndChar(PSTR("Rotating!")); - break; - case A4988_TURN: - ResponseCmndChar(PSTR("Turning!")); - break; - default: // A4988_NO_ERROR - ResponseCmndDone(); +void CmndDoTurn(void) { + if (XdrvMailbox.data_len > 0) { + float turnsPlease = strtod(XdrvMailbox.data,nullptr); + myA4988->doTurn(turnsPlease); + ResponseCmndDone(); } +} + +void CmndSetMIS(void) { + if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { + short newMIS = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setMIS(newMIS); + ResponseCmndDone(); + } +} +void CmndSetSPR(void) { + if (XdrvMailbox.data_len > 0) { + int newSPR = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setSPR(newSPR); + ResponseCmndDone(); + } +} + +void CmndSetRPM(void) { + if (XdrvMailbox.data_len > 0) { + short newRPM = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setRPM(newRPM); + ResponseCmndDone(); + } } /*********************************************************************************************\ From 19c42f3893d51fd094d2bfeef130f1aa43937601 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Sep 2019 16:29:19 +0200 Subject: [PATCH 06/44] Update xdrv_25_A4988_Stepper.ino --- sonoff/xdrv_25_A4988_Stepper.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sonoff/xdrv_25_A4988_Stepper.ino b/sonoff/xdrv_25_A4988_Stepper.ino index a1fa4037da82..f80cc1a818fc 100644 --- a/sonoff/xdrv_25_A4988_Stepper.ino +++ b/sonoff/xdrv_25_A4988_Stepper.ino @@ -57,10 +57,11 @@ void A4988Init(void) , A4988_ms3_pin ); } -const char kA4988Commands[] PROGMEM = "|" //no prefix - "motorMove|motorRotate|motorTurn|motorMIS|motorSPR|motorRPM"; +const char kA4988Commands[] PROGMEM = "Motor|" // prefix + "Move|Rotate|Turn|MIS|SPR|RPM"; -void (* const A4988Command[])(void) PROGMEM = { &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; +void (* const A4988Command[])(void) PROGMEM = { + &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; void CmndDoMove(void) { if (XdrvMailbox.data_len > 0) { From a4e1c10953ad0f4a2ec869711e2c15b6e40e4d62 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 10 Sep 2019 18:04:56 +0200 Subject: [PATCH 07/44] Change commands Tariff Change commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) --- sonoff/_changelog.ino | 2 +- sonoff/xdrv_03_energy.ino | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index b4bff23ed01f..8a84f0bd910d 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,7 +1,7 @@ /*********************************************************************************************\ * 6.6.0.12 20190910 * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour - * Commands Tariff1 23 = Tariff1 ST, Tariff2 7 = Tariff2 ST, Tarriff3 22 = Tarrif1 DST, Tariff4 6 = Tariff2 DST, Tariff9 0/1 = Weekend toggle + * Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index b9d8d92b4e8c..35f65e5757aa 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -516,14 +516,21 @@ void CmndEnergyReset(void) void CmndTariff(void) { - // Tariff1 23 - Standard Time Tariff1 start hour - // Tariff2 7 - Standard Time Tariff2 start hour - // Tariff3 22 - Daylight Savings Time Tariff1 start hour - // Tariff4 6 - Daylight Savings Time Tariff2 start hour + // Tariff1 22,23 - Tariff1 start hour for Standard Time and Daylight Savings Time + // Tariff2 6,7 - Tariff2 start hour for Standard Time and Daylight Savings Time // Tariff9 0/1 - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { - Settings.register8[R8_ENERGY_TARIFF1_ST + XdrvMailbox.index -1] = XdrvMailbox.payload; + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + uint32_t time_type = 0; + while ((str != nullptr) && (time_type <= 2)) { + uint8_t value = strtol(str, nullptr, 10); + if ((value >= 0) && (value < 24)) { + Settings.register8[R8_ENERGY_TARIFF1_ST + (XdrvMailbox.index -1) + time_type] = value; + } + str = strtok_r(nullptr, ", ", &p); + time_type += 2; } } else if (XdrvMailbox.index == 9) { From 05e96041167a8b87a4cb6b1b5d489f3abdaf128e Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 10 Sep 2019 20:45:27 +0200 Subject: [PATCH 08/44] Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) --- sonoff/_changelog.ino | 1 + sonoff/i18n.h | 1 + sonoff/xdrv_05_irremote.ino | 31 +++++++++++++++++++++---------- sonoff/xdrv_05_irremote_full.ino | 27 +++++++++++++++++++-------- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 8a84f0bd910d..ed88da8c4eb1 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -2,6 +2,7 @@ * 6.6.0.12 20190910 * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour * Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) + * Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 0aba9f63db14..e44b4a92800f 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -385,6 +385,7 @@ #define D_JSON_IR_BITS "Bits" #define D_JSON_IR_DATA "Data" #define D_JSON_IR_DATALSB "DataLSB" + #define D_JSON_IR_HASH "Hash" #define D_JSON_IR_RAWDATA "RawData" #define D_JSON_IR_REPEAT "Repeat" #define D_CMND_IRHVAC "IRHVAC" diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index cc0614264149..db5332081e05 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -129,13 +129,21 @@ void IrReceiveCheck(void) if (irrecv->decode(&results)) { char hvalue[65]; // Max 256 bits - if (results.bits > 64) { - // This emulates IRutils resultToHexidecimal and may needs a larger IR_RCV_BUFFER_SIZE - uint32_t digits2 = results.bits / 8; - if (results.bits % 8) { digits2++; } - ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200 + + iridx = results.decode_type; + if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN + + if (iridx) { + if (results.bits > 64) { + // This emulates IRutils resultToHexidecimal and may needs a larger IR_RCV_BUFFER_SIZE + uint32_t digits2 = results.bits / 8; + if (results.bits % 8) { digits2++; } + ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200 + } else { + IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456 + } } else { - IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456 + IrUint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash } AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), @@ -146,16 +154,19 @@ void IrReceiveCheck(void) if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { ir_lasttime = now; - iridx = results.decode_type; - if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN char svalue[64]; if (Settings.flag.ir_receive_decimal) { ulltoa(results.value, svalue, 10); } else { snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); } - ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), - GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, svalue); + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), + GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); + if (iridx) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); + } else { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); + } if (Settings.flag3.receive_raw) { ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); diff --git a/sonoff/xdrv_05_irremote_full.ino b/sonoff/xdrv_05_irremote_full.ino index 3c82bee21872..d35c5f4bafa4 100644 --- a/sonoff/xdrv_05_irremote_full.ino +++ b/sonoff/xdrv_05_irremote_full.ino @@ -177,20 +177,31 @@ String sendIRJsonState(const struct decode_results &results) { json += resultToHexidecimal(&results); json += "\""; } else { - json += ",\"" D_JSON_IR_DATA "\":"; + if (UNKNOWN != results.decode_type) { + json += ",\"" D_JSON_IR_DATA "\":"; + } else { + json += ",\"" D_JSON_IR_HASH "\":"; + } if (Settings.flag.ir_receive_decimal) { char svalue[32]; ulltoa(results.value, svalue, 10); json += svalue; } else { char hvalue[64]; - IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 - json += "\""; - json += hvalue; - json += "\",\"" D_JSON_IR_DATALSB "\":\""; - IrUint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB - json += hvalue; - json += "\""; + if (UNKNOWN != results.decode_type) { + IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 + json += "\""; + json += hvalue; + json += "\",\"" D_JSON_IR_DATALSB "\":\""; + IrUint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB + json += hvalue; + json += "\""; + } else { // UNKNOWN + IrUint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits + json += "\""; + json += hvalue; + json += "\""; + } } } json += ",\"" D_JSON_IR_REPEAT "\":"; From 56b0f6efdc92390cc4f2c04e39e9236e1709615f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Sep 2019 12:14:37 +0200 Subject: [PATCH 09/44] Fix interlocked relays turning off Fix interlocked relays turning off (#6385) --- sonoff/sonoff.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 6a60ff49eecc..ff771c95ef76 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -581,8 +581,11 @@ void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) MqttPublishPowerBlinkState(device); } - if (Settings.flag.interlock && !interlock_mutex) { // Clear all but masked relay in interlock group - interlock_mutex = true; + if (Settings.flag.interlock && + !interlock_mutex && + ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) + ) { + interlock_mutex = true; // Clear all but masked relay in interlock group if new set requested for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { if (Settings.interlock[i] & mask) { // Find interlock group for (uint32_t j = 0; j < devices_present; j++) { From 05b9317b7573ef82b050b43cf65d488b1023ad1d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 11 Sep 2019 17:04:39 +0200 Subject: [PATCH 10/44] Allow user control over clock text size Allow user control over clock text size (#6395) --- sonoff/xdsp_02_ssd1306.ino | 3 ++- sonoff/xdsp_07_sh1106.ino | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sonoff/xdsp_02_ssd1306.ino b/sonoff/xdsp_02_ssd1306.ino index 704226a25474..078ef1f095c9 100755 --- a/sonoff/xdsp_02_ssd1306.ino +++ b/sonoff/xdsp_02_ssd1306.ino @@ -137,7 +137,8 @@ void Ssd1306Time(void) char line[12]; renderer->clearDisplay(); - renderer->setTextSize(2); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); renderer->setCursor(0, 0); snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] renderer->println(line); diff --git a/sonoff/xdsp_07_sh1106.ino b/sonoff/xdsp_07_sh1106.ino index d694129ec200..6c535b02a1a3 100644 --- a/sonoff/xdsp_07_sh1106.ino +++ b/sonoff/xdsp_07_sh1106.ino @@ -131,7 +131,8 @@ void SH1106Time(void) char line[12]; renderer->clearDisplay(); - renderer->setTextSize(2); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); renderer->setCursor(0, 0); snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); // [ 12:34:56 ] renderer->println(line); From ea3fcaf22cea8eeeb9eec79a7753bf56b9bf78ce Mon Sep 17 00:00:00 2001 From: Laurent <44267323+laurentdong@users.noreply.github.com> Date: Wed, 11 Sep 2019 21:46:01 -0400 Subject: [PATCH 11/44] Rule did not been triggered if there's no comparation operator Rule did not been triggered if there's no comparation operator provided as trigger. For example: Rule1 ON EVENT#POWERON DO ... ENDON The rule_name should be assigned as "POWERON" by default. --- sonoff/xdrv_10_rules.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index 26bdbcb860de..d59011e18f9c 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -339,6 +339,7 @@ int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) { char compare_operator[3]; int8_t compare = COMPARE_OPERATOR_NONE; + leftExpr = expr; int position; for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); From 65532af236e9965bedb2d10d4d45ca356a684ddd Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 12 Sep 2019 10:58:47 +0200 Subject: [PATCH 12/44] Update FT6236.cpp --- lib/FT6236-gemu-1.0/FT6236.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FT6236-gemu-1.0/FT6236.cpp b/lib/FT6236-gemu-1.0/FT6236.cpp index 28aeff09180e..6397190fb85c 100644 --- a/lib/FT6236-gemu-1.0/FT6236.cpp +++ b/lib/FT6236-gemu-1.0/FT6236.cpp @@ -69,7 +69,7 @@ uint16_t FT6236GetButtonMask(void) { void FT6236begin(uint8_t i2c_addr) { FT6236_i2c_addr=i2c_addr; - Wire.begin(FT6236_i2c_addr); + Wire.begin(); FT6236writeTouchRegister(0,FT6236_MODE_NORMAL); lenLibVersion = FT6236readTouchAddr(0x0a1, FT6236buf, 2 ); firmwareId = FT6236readTouchRegister( 0xa6 ); From 3a0fce25035793833eb5f05b6b20c71e3731e661 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 12 Sep 2019 12:32:33 +0200 Subject: [PATCH 13/44] Add command Gpios 255/All to show all available GPIO components Add command Gpios 255/All to show all available GPIO components (#6407) --- sonoff/_changelog.ino | 1 + sonoff/i18n.h | 6 ------ sonoff/support.ino | 33 +++++++++++++++++---------------- sonoff/support_command.ino | 27 +++++++++++++-------------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index ed88da8c4eb1..e9050aafbfed 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -3,6 +3,7 @@ * Redesign command Tariff to now default to 0 (=disabled) and allowing to set both Standard Time (ST) and Daylight Savings Time (DST) start hour * Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) * Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) + * Add command Gpios 255/All to show all available GPIO components (#6407) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/i18n.h b/sonoff/i18n.h index e44b4a92800f..a6695fb73130 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -604,12 +604,6 @@ const char kCodeImage[] PROGMEM = "sonoff|minimal|classic|sensors|knx|basic|disp // support.ino static const char kMonthNames[] = D_MONTH3LIST; -const char kOptionOff[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS ; -const char kOptionOn[] PROGMEM = "ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER ; -const char kOptionToggle[] PROGMEM = "TOGGLE|" D_TOGGLE "|" D_ADMIN ; -const char kOptionBlink[] PROGMEM = "BLINK|" D_BLINK ; -const char kOptionBlinkOff[] PROGMEM = "BLINKOFF|" D_BLINKOFF ; - // xdrv_02_webserver.ino #ifdef USE_WEBSERVER const char HTTP_SNS_TEMP[] PROGMEM = "{s}%s " D_TEMPERATURE "{m}%s°%c{e}"; // {s} = , {m} = , {e} = diff --git a/sonoff/support.ino b/sonoff/support.ino index b9fec8651396..cca75b377da6 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -761,25 +761,26 @@ bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) return false; } +const char kOptions[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" D_CELSIUS "|" // 0 + "ON|" D_ON "|" D_TRUE "|" D_START "|" D_FAHRENHEIT "|" D_USER "|" // 1 + "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" // 2 + "BLINK|" D_BLINK "|" // 3 + "BLINKOFF|" D_BLINKOFF "|" // 4 + "ALL" ; // 255 + +const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0, + 1,1,1,1,1,1, + 2,2,2, + 3,3, + 4,4, + 255 }; + int GetStateNumber(char *state_text) { char command[CMDSZ]; - int state_number = -1; - - if (GetCommandCode(command, sizeof(command), state_text, kOptionOff) >= 0) { - state_number = 0; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionOn) >= 0) { - state_number = 1; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionToggle) >= 0) { - state_number = 2; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlink) >= 0) { - state_number = 3; - } - else if (GetCommandCode(command, sizeof(command), state_text, kOptionBlinkOff) >= 0) { - state_number = 4; + int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); + if (state_number >= 0) { + state_number = pgm_read_byte(sNumbers + state_number); } return state_number; } diff --git a/sonoff/support_command.ino b/sonoff/support_command.ino index c181a70bf1f4..bd7107648f12 100644 --- a/sonoff/support_command.ino +++ b/sonoff/support_command.ino @@ -859,20 +859,19 @@ void CmndGpios(void) bool jsflg = false; for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { midx = pgm_read_byte(kGpioNiceList + i); - if (!GetUsedInModule(midx, cmodule.io)) { - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - ResponseAppend_P(PSTR("]}")); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); - jsflg = false; - lines++; - } + if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + char stemp1[TOPSZ]; + if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseAppend_P(PSTR("]}")); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); + jsflg = false; + lines++; } } mqtt_data[0] = '\0'; From 1341db1392debc43d7b936e352bdec935ebb9985 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 12 Sep 2019 14:19:44 +0200 Subject: [PATCH 14/44] Change JSON output format for some commands Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) --- sonoff/_changelog.ino | 1 + sonoff/i18n.h | 6 +++--- sonoff/support.ino | 5 +++++ sonoff/support_command.ino | 16 ++++++++-------- sonoff/xdrv_05_irremote.ino | 2 +- sonoff/xdrv_05_irremote_full.ino | 2 +- sonoff/xdrv_09_timers.ino | 2 +- sonoff/xsns_02_analog.ino | 8 ++++---- sonoff/xsns_21_sgp30.ino | 2 +- sonoff/xsns_53_sml.ino | 4 ++-- 10 files changed, 27 insertions(+), 21 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index e9050aafbfed..a02cc1636d55 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -4,6 +4,7 @@ * Commands Tariff1 22,23 = Tariff1 (Off-Peak) ST,DST Tariff2 (Standard) 6,7 = Tariff2 ST,DST Tariff9 0/1 = Weekend toggle (1 = Off-Peak during weekend) * Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) * Add command Gpios 255/All to show all available GPIO components (#6407) + * Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/i18n.h b/sonoff/i18n.h index a6695fb73130..7659b9335216 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -537,8 +537,8 @@ const char S_JSON_COMMAND_SVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%s %s\" const char S_JSON_COMMAND_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s\"}"; const char S_JSON_COMMAND_NVALUE_UNIT_NVALUE_UNIT[] PROGMEM = "{\"%s\":\"%d%s (%d%s)\"}"; -const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":\"%d (%s)\"}"; -const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":\"%d (" D_JSON_ACTIVE " %d)\"}"; +const char S_JSON_COMMAND_NVALUE_SVALUE[] PROGMEM = "{\"%s\":{\"%d\":\"%s\"}}"; +const char S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s\":{\"%d\":{\"" D_JSON_ACTIVE "\":\"%d\"}}}"; const char S_JSON_COMMAND_NVALUE[] PROGMEM = "{\"%s\":%d}"; const char S_JSON_COMMAND_LVALUE[] PROGMEM = "{\"%s\":%lu}"; @@ -551,7 +551,7 @@ const char S_JSON_COMMAND_INDEX_LVALUE[] PROGMEM = "{\"%s%d\":%lu}"; const char S_JSON_COMMAND_INDEX_SVALUE[] PROGMEM = "{\"%s%d\":\"%s\"}"; const char S_JSON_COMMAND_INDEX_ASTERISK[] PROGMEM = "{\"%s%d\":\"" D_ASTERISK_PWD "\"}"; const char S_JSON_COMMAND_INDEX_SVALUE_SVALUE[] PROGMEM = "{\"%s%d\":\"%s%s\"}"; -const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d (" D_JSON_ACTIVE " %d)\"}"; +const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":{\"%d\":{\"" D_JSON_ACTIVE "\":\"%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\"}"; diff --git a/sonoff/support.ino b/sonoff/support.ino index cca75b377da6..1f9911e09516 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -951,6 +951,11 @@ int ResponseJsonEnd(void) return ResponseAppend_P(PSTR("}")); } +int ResponseJsonEndEnd(void) +{ + return ResponseAppend_P(PSTR("}}")); +} + /*********************************************************************************************\ * GPIO Module and Template management \*********************************************************************************************/ diff --git a/sonoff/support_command.ino b/sonoff/support_command.ino index bd7107648f12..fff2831cb178 100644 --- a/sonoff/support_command.ino +++ b/sonoff/support_command.ino @@ -366,7 +366,7 @@ void CmndStatus(void) XsnsDriverState(); ResponseAppend_P(PSTR(",\"Sensors\":")); XsnsSensorState(); - ResponseAppend_P(PSTR("}}")); + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); } @@ -795,14 +795,14 @@ void CmndModules(void) for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_MODULES "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); } else { ResponseAppend_P(PSTR(",")); } jsflg = true; uint32_t j = i ? midx +1 : 0; - if ((ResponseAppend_P(PSTR("\"%d (%s)\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { - ResponseAppend_P(PSTR("]}")); + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); jsflg = false; lines++; @@ -839,7 +839,7 @@ void CmndGpio(void) if (jsflg) { ResponseAppend_P(PSTR(",")); } jsflg = true; char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":\"%d (%s)\""), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), i, Settings.my_gp.io[i], GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_gp.io[i], kSensorNames)); } } if (jsflg) { @@ -861,14 +861,14 @@ void CmndGpios(void) midx = pgm_read_byte(kGpioNiceList + i); if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":["), lines); + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); } else { ResponseAppend_P(PSTR(",")); } jsflg = true; char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d (%s)\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - ResponseAppend_P(PSTR("]}")); + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); jsflg = false; lines++; diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index db5332081e05..0e8a009823c4 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -189,7 +189,7 @@ void IrReceiveCheck(void) ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); } - ResponseAppend_P(PSTR("}}")); + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); if (iridx) { diff --git a/sonoff/xdrv_05_irremote_full.ino b/sonoff/xdrv_05_irremote_full.ino index d35c5f4bafa4..85c81684bcc6 100644 --- a/sonoff/xdrv_05_irremote_full.ino +++ b/sonoff/xdrv_05_irremote_full.ino @@ -254,7 +254,7 @@ void IrReceiveCheck(void) ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); } - ResponseAppend_P(PSTR("}}")); + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); if (iridx) { diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 4b767e77c504..130ef04c41e3 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -482,7 +482,7 @@ void CmndTimers(void) jsflg++; PrepShowTimer(i +1); if (jsflg > 3) { - ResponseAppend_P(PSTR("}}")); + ResponseJsonEndEnd(); MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); jsflg = 0; } diff --git a/sonoff/xsns_02_analog.ino b/sonoff/xsns_02_analog.ino index b3c29e9d893c..aebb12e4fee4 100644 --- a/sonoff/xsns_02_analog.ino +++ b/sonoff/xsns_02_analog.ino @@ -196,12 +196,12 @@ void CmndAdc(void) restart_flag = 2; } char stemp1[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_ADC "0\":\"%d (%s)\"}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); + Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); } void CmndAdcs(void) { - Response_P(PSTR("{\"" D_CMND_ADCS "\":[")); + Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); bool jsflg = false; char stemp1[TOPSZ]; for (uint32_t i = 0; i < ADC0_END; i++) { @@ -209,9 +209,9 @@ void CmndAdcs(void) ResponseAppend_P(PSTR(",")); } jsflg = true; - ResponseAppend_P(PSTR("\"%d (%s)\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); + ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); } - ResponseAppend_P(PSTR("]}")); + ResponseJsonEndEnd(); } void CmndAdcParam(void) diff --git a/sonoff/xsns_21_sgp30.ino b/sonoff/xsns_21_sgp30.ino index e9c097dc4845..356c690b8bd7 100644 --- a/sonoff/xsns_21_sgp30.ino +++ b/sonoff/xsns_21_sgp30.ino @@ -134,7 +134,7 @@ void Sgp30Show(bool json) if (global_update) { ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); } - ResponseAppend_P(PSTR("}")); + ResponseJsonEnd(); #ifdef USE_DOMOTICZ if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); #endif // USE_DOMOTICZ diff --git a/sonoff/xsns_53_sml.ino b/sonoff/xsns_53_sml.ino index 9dabb29cda28..bb8ba1e4c4ca 100644 --- a/sonoff/xsns_53_sml.ino +++ b/sonoff/xsns_53_sml.ino @@ -1616,7 +1616,7 @@ void SML_Show(boolean json) { if (lastmind!=mindex) { // meter changed, close mqtt //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data); - ResponseAppend_P(PSTR("}")); + ResponseJsonEnd(); // and open new //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr); ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"),meter_desc_p[mindex].prefix,jname,tpowstr); @@ -1643,7 +1643,7 @@ void SML_Show(boolean json) { if (json) { //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data); //ResponseAppend_P(PSTR("%s"),b_mqtt_data); - ResponseAppend_P(PSTR("}")); + ResponseJsonEnd(); } else { //WSContentSend_PD(PSTR("%s"),b_mqtt_data); } From 37c5bad95d86fb80fde0b8a8928517913ecd07b3 Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Thu, 12 Sep 2019 12:45:47 -0300 Subject: [PATCH 15/44] Update it-IT.h --- sonoff/language/it-IT.h | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 257dd3d55212..cfb564eca86e 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -65,7 +65,7 @@ #define D_BY "da" // Written by me #define D_BYTES "Bytes" #define D_CELSIUS "Celsius" -#define D_CHANNEL "Channel" +#define D_CHANNEL "Canale" #define D_CO2 "CO2" #define D_CODE "codice" // Button code #define D_COLDLIGHT "Fredda" @@ -106,7 +106,7 @@ #define D_IMMEDIATE "immediato" // Button immediate #define D_INDEX "Indice" #define D_INFO "Info" -#define D_INFRARED "Infrared" +#define D_INFRARED "Infrarosso" #define D_INITIALIZED "Inizializzato" #define D_IP_ADDRESS "Indirizzo IP" #define D_LIGHT "Luce" @@ -199,7 +199,7 @@ #define D_BLOCKED_LOOP "Ciclo Bloccato" #define D_WPS_FAILED_WITH_STATUS "WPSconfig Fallito con stato" #define D_ACTIVE_FOR_3_MINUTES "Attivo per 3 minuti" -#define D_FAILED_TO_START "partenza fallita" +#define D_FAILED_TO_START "Partenza fallita" #define D_PATCH_ISSUE_2186 "Patch issue 2186" #define D_CONNECTING_TO_AP "Connessione ad AP" #define D_IN_MODE "In modalità" @@ -305,7 +305,7 @@ #define D_CONFIGURE_TEMPLATE "Configurare Modello" #define D_TEMPLATE_PARAMETERS "Parametri Modello" #define D_TEMPLATE_NAME "Nome" -#define D_BASE_TYPE "Baseto nel" +#define D_BASE_TYPE "Basato nel" #define D_TEMPLATE_FLAGS "Opzioni" #define D_SAVE_CONFIGURATION "Salva configurazione" @@ -492,7 +492,7 @@ #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" -#define D_TX20_WEST "W" +#define D_TX20_WEST "O" //xsns_43_hre.ino #define D_LOG_HRE "HRE: " @@ -675,27 +675,27 @@ #define D_UNIT_ANGLE "°" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" -#define D_STATUS "Status" -#define D_WAITING "Waiting" -#define D_CHECKING "Checking" -#define D_WORKING "Working" -#define D_FAILURE "Failure" -#define D_SOLAX_ERROR_0 "No Error Code" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "Pv Voltage Fault" -#define D_SOLAX_ERROR_5 "Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_PV1_VOLTAGE "PV1 Voltaggio" +#define D_PV1_CURRENT "PV1 Corrente" +#define D_PV1_POWER "PV1 Eergia" +#define D_PV2_VOLTAGE "PV2 Voltaggio" +#define D_PV2_CURRENT "PV2 Corrente" +#define D_PV2_POWER "PV2 Energia" +#define D_SOLAR_POWER "Energia Solar" +#define D_INVERTER_POWER "Energia Inverter" +#define D_STATUS "Stato" +#define D_WAITING "In attesa" +#define D_CHECKING "Controllando" +#define D_WORKING "Lavorando" +#define D_FAILURE "Errore" +#define D_SOLAX_ERROR_0 "No Codice Errore" +#define D_SOLAX_ERROR_1 "Errore Grid Persa" +#define D_SOLAX_ERROR_2 "Errore Voltaggio Grid" +#define D_SOLAX_ERROR_3 "Errore Frequenza Grid" +#define D_SOLAX_ERROR_4 "Errore Voltaggio Pv" +#define D_SOLAX_ERROR_5 "Errore Isolamento" +#define D_SOLAX_ERROR_6 "Errore Temperatura Eccessiva" +#define D_SOLAX_ERROR_7 "Errore Ventilatore" +#define D_SOLAX_ERROR_8 "Altro Errore del Dispositivo" #endif // _LANGUAGE_IT_IT_H_ From 188e3bb65a9cd8d34adfb01dd244f42a71e6ece2 Mon Sep 17 00:00:00 2001 From: Staars Date: Fri, 13 Sep 2019 08:57:09 +0200 Subject: [PATCH 16/44] obj_dump after .elf creation --- pio/obj-dump.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 pio/obj-dump.py diff --git a/pio/obj-dump.py b/pio/obj-dump.py new file mode 100644 index 000000000000..91bc3de58141 --- /dev/null +++ b/pio/obj-dump.py @@ -0,0 +1,9 @@ +# Little convenience script to get an object dump + +Import('env') + +def obj_dump_after_elf(source, target, env): + print("Create firmware.asm") + env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm") + +env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf]) From 83b2c858153b2b92788c40d35d3e3a6faccb7b0d Mon Sep 17 00:00:00 2001 From: Staars Date: Fri, 13 Sep 2019 09:08:15 +0200 Subject: [PATCH 17/44] call obj_dump.py as extra_script --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index d19018416164..17d32aadf423 100755 --- a/platformio.ini +++ b/platformio.ini @@ -220,6 +220,7 @@ upload_resetmethod = nodemcu ; *** Upload Serial reset method for Wemos and NodeMCU upload_port = COM5 extra_scripts = pio/strip-floats.py + pio/obj_dump.py ; *** Upload file to OTA server using SCP ;upload_port = user@host:/path From 28bd4113111567c2bb09c825c3074e671530bbb6 Mon Sep 17 00:00:00 2001 From: Staars Date: Fri, 13 Sep 2019 09:10:36 +0200 Subject: [PATCH 18/44] call obj-dump.py as extra_script --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 17d32aadf423..99ff2aee8adb 100755 --- a/platformio.ini +++ b/platformio.ini @@ -220,7 +220,7 @@ upload_resetmethod = nodemcu ; *** Upload Serial reset method for Wemos and NodeMCU upload_port = COM5 extra_scripts = pio/strip-floats.py - pio/obj_dump.py + pio/obj-dump.py ; *** Upload file to OTA server using SCP ;upload_port = user@host:/path From 622fc70566fa42c73c30672718957ce324929d1f Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Fri, 13 Sep 2019 15:39:57 +0200 Subject: [PATCH 19/44] decode-config.py: adapt settings - add Sensor54 (ina226_r_shunt, ina226_i_fs) - add/change Tariffx (register8) - add crc32 calc --- tools/decode-config.py | 66 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/tools/decode-config.py b/tools/decode-config.py index f04854acd499..7582a932be78 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.3.0033' +VER = '2.3.0034' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -998,7 +998,26 @@ def WebSensor(value, idx): }) Setting_6_6_0_10['flag3'][0].pop('tuya_show_dimmer',None) # ====================================================================== +Setting_6_6_0_11 = copy.deepcopy(Setting_6_6_0_10) +Setting_6_6_0_11.update ({ + 'ina226_r_shunt': (' 0x0606000A: + size = 3584 crc = 0 - for i in range(0, len(dobj)): + for i in range(0, size): if not i in [14,15]: # Skip crc byte = ord(dobj[i]) crc += byte * (i+1) @@ -1705,6 +1727,28 @@ def GetSettingsCrc(dobj): return crc & 0xffff +def GetSettingsCrc32(dobj): + """ + Return binary config data calclulated crc32 + + @param dobj: + decrypted binary config data + + @return: + 4 byte unsigned integer crc value + + """ + if isinstance(dobj, bytearray): + dobj = str(dobj) + crc = 0 + for i in range(0, len(dobj)-4): + crc ^= ord(dobj[i]) + for j in range(0, 8): + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + + return ~crc & 0xffffffff + + def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, datadef, arraydef, validate, cmd, group, tasmotacmnd, converter, readconverter, writeconverter"): """ @@ -2583,8 +2627,16 @@ def Bin2Mapping(decode_cfg): cfg_crc = GetField(decode_cfg, 'cfg_crc', setting['cfg_crc'], raw=True) else: cfg_crc = GetSettingsCrc(decode_cfg) - if cfg_crc != GetSettingsCrc(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + if 'cfg_crc32' in setting: + cfg_crc32 = GetField(decode_cfg, 'cfg_crc32', setting['cfg_crc32'], raw=True) + else: + cfg_crc32 = GetSettingsCrc32(decode_cfg) + if version < 0x0606000B: + if cfg_crc != GetSettingsCrc(decode_cfg): + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:4x} should be 0x{:4x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + else: + if cfg_crc32 != GetSettingsCrc32(decode_cfg): + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC32 error, read 0x{:8x} should be 0x{:8x}'.format(cfg_crc32, GetSettingsCrc32(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) # get valuemapping valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None)))) @@ -2615,6 +2667,9 @@ def Bin2Mapping(decode_cfg): } if 'cfg_crc' in setting: valuemapping['header']['template'].update({'size': cfg_size}) + if 'cfg_crc32' in setting: + valuemapping['header']['template'].update({'crc32': cfg_crc32}) + valuemapping['header']['data'].update({'crc32': hex(GetSettingsCrc32(decode_cfg))}) if 'version' in setting: valuemapping['header']['data'].update({'version': hex(cfg_version)}) @@ -2660,6 +2715,9 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): if 'cfg_crc' in setting: crc = GetSettingsCrc(_buffer) struct.pack_into(setting['cfg_crc'][0], _buffer, setting['cfg_crc'][1], crc) + if 'cfg_crc32' in setting: + crc32 = GetSettingsCrc32(_buffer) + struct.pack_into(setting['cfg_crc32'][0], _buffer, setting['cfg_crc32'][1], crc32) return _buffer else: From 777956b634101fe64fed74bfff97ccd9107edb58 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sat, 14 Sep 2019 07:48:55 +0200 Subject: [PATCH 20/44] scripter bug fix fix bug that webfrefresh was affected by permanent variables p:var --- sonoff/xdrv_10_scripter.ino | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index 3a87aec4444a..a8dc6adf434d 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -2045,6 +2045,8 @@ void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { if (isdigit(*cp)) { dprec=*cp&0xf; cp++; + } else { + dprec=glob_script_mem.script_dprec; } cp=isvar(cp,&vtype,&ind,&fvar,string,0); if (vtype!=VAR_NV) { @@ -3343,7 +3345,6 @@ bool ScriptCommand(void) { if (XdrvMailbox.data[count]==';') XdrvMailbox.data[count]='\n'; } execute_script(XdrvMailbox.data); - Scripter_save_pvars(); } } return serviced; @@ -3735,6 +3736,7 @@ bool Xdrv10(uint8_t function) // assure permanent memory is 4 byte aligned { uint32_t ptr=(uint32_t)glob_script_mem.script_pram; ptr&=0xfffffffc; + ptr+=4; glob_script_mem.script_pram=(uint8_t*)ptr; glob_script_mem.script_pram_size-=4; } From fa7796a2b2474f5791b78a2be93db5993d90be60 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 14 Sep 2019 10:55:23 +0200 Subject: [PATCH 21/44] Adding firmware.asm to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2c1ceae12d16..bd39647e3885 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ sonoff/user_config_override.h build firmware.map +firmware.asm ## Visual Studio Code specific ###### .vscode From 1e07b4752943c10fa81543d327f8872cc9ad1437 Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Sat, 14 Sep 2019 21:50:45 -0300 Subject: [PATCH 22/44] Need feedback: Home Assistant discovery --- sonoff/xdrv_12_home_assistant.ino | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index 538c25e06124..d794d9543983 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -152,6 +152,11 @@ const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = const char HASS_DISCOVER_TOPIC_PREFIX[] PROGMEM = ",\"~\":\"%s\""; +const char HASS_DISCOVER_INFO_WIFI[] PROGMEM = + // Combined WiFi sensor, not usable for graph in Hass // + ",\"val_tpl\":\"{{[value_json.Wifi." D_JSON_SSID ", value_json.Wifi." D_JSON_RSSI "] | join (' (') + '%%)' }}\"," + "\"dev_cla\":\"signal_strength\""; // device_class: signal_strength + uint8_t hass_init_step = 0; uint8_t hass_mode = 0; int hass_tele_period = 0; @@ -554,6 +559,41 @@ void HAssPublishStatus(void) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); } +void HAssAnnounceWifiSensor(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + // Announce sensor + mqtt_data[0] = '\0'; // Clear retained message + + // Clear or Set topic + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_conn"), ESP.getChipId()); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) { + char name[33+11]; // friendlyname(33) + + " " + "connection" + char *state_topic = stemp1; + char *availability_topic = stemp2; + char prefix[TOPSZ]; + + snprintf_P(name, sizeof(name), PSTR("%s Connection"), Settings.friendlyname[0]); // "Connection" could be translated, should be added to language files + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_STATE)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + FindPrefix(state_topic, availability_topic, prefix); + Shorten(&state_topic, prefix); + Shorten(&availability_topic, prefix); + + Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_INFO_WIFI, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); + TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); +} + void HAssDiscovery(void) { // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible @@ -584,6 +624,9 @@ void HAssDiscovery(void) // Send info about status sensor HAssAnnounceStatusSensor(); + + // Send info about Wifi + HAssAnnounceWifiSensor(); } } From c3638289e2887f121416bb232a8ddbb1e3b1bbff Mon Sep 17 00:00:00 2001 From: Laurent <44267323+laurentdong@users.noreply.github.com> Date: Sat, 14 Sep 2019 21:35:55 -0400 Subject: [PATCH 23/44] Increase MQTT subscribe JSON data size. --- sonoff/xdrv_10_rules.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index d59011e18f9c..287f4b231eb7 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -685,7 +685,7 @@ void RulesTeleperiod(void) bool RulesMqttData(void) { bool serviced = false; - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 128) { + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { return false; } String sTopic = XdrvMailbox.topic; @@ -704,7 +704,7 @@ bool RulesMqttData(void) if (event_item.Key.length() == 0) { //If did not specify Key value = sData; } else { //If specified Key, need to parse Key/Value from JSON data - StaticJsonBuffer<400> jsonBuf; + StaticJsonBuffer<500> jsonBuf; JsonObject& jsonData = jsonBuf.parseObject(sData); String key1 = event_item.Key; String key2; From 8f1a3ebdd05ddfee7e3f33fab3eb1a19d01a3ca6 Mon Sep 17 00:00:00 2001 From: Laurent <44267323+laurentdong@users.noreply.github.com> Date: Sat, 14 Sep 2019 21:39:21 -0400 Subject: [PATCH 24/44] IF command alwasy report a command error IF command did not send command finish signal. --- sonoff/xdrv_10_rules.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/sonoff/xdrv_10_rules.ino b/sonoff/xdrv_10_rules.ino index d59011e18f9c..8364c7864070 100644 --- a/sonoff/xdrv_10_rules.ino +++ b/sonoff/xdrv_10_rules.ino @@ -1186,6 +1186,7 @@ void CmndIf() parameters[XdrvMailbox.data_len] = '\0'; ProcessIfStatement(parameters); } + ResponseCmndDone(); } /********************************************************************************************/ From f91f99a2e74406fa0d9bbf86f635e897e7273bb1 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 15 Sep 2019 11:10:59 +0200 Subject: [PATCH 25/44] Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch --- sonoff/_changelog.ino | 1 + sonoff/i18n.h | 2 + sonoff/support.ino | 18 + sonoff/support_static_buffer.ino | 46 +- sonoff/xdrv_05_irremote.ino | 24 +- sonoff/xdrv_05_irremote_full.ino | 29 +- ...nts.ino => xdrv_23_zigbee_0_constants.ino} | 21 + sonoff/xdrv_23_zigbee_4_converters.ino | 619 ++++++++++++++++++ ...bee_impl.ino => xdrv_23_zigbee_9_impl.ino} | 466 ++++++++----- 9 files changed, 1006 insertions(+), 220 deletions(-) rename sonoff/{xdrv_23_zigbee_constants.ino => xdrv_23_zigbee_0_constants.ino} (95%) create mode 100644 sonoff/xdrv_23_zigbee_4_converters.ino rename sonoff/{xdrv_23_zigbee_impl.ino => xdrv_23_zigbee_9_impl.ino} (71%) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index a02cc1636d55..fd47efc7e113 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -5,6 +5,7 @@ * Change rename "Data" to "Hash" and limit to 32 bits when receiving UNKNOWN IR protocol (see DECODE_HASH from IRremoteESP8266) * Add command Gpios 255/All to show all available GPIO components (#6407) * Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) + * Add Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 7659b9335216..c3f4333bc8f2 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -452,7 +452,9 @@ #define D_CMND_TUYA_MCU "TuyaMCU" // Commands xdrv_23_zigbee.ino +#define D_CMND_ZIGBEE_PERMITJOIN "ZigbeePermitJoin" #define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend" + #define D_JSON_ZIGBEE_STATUS "ZigbeeStatus" #define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived" #define D_JSON_ZIGBEEZNPSENT "ZigbeeZNPSent" #define D_JSON_ZIGBEEZCLRECEIVED "ZigbeeZCLReceived" diff --git a/sonoff/support.ino b/sonoff/support.ino index 1f9911e09516..6fd41b8f0bd5 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -325,6 +325,24 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c return out; } +char* Uint64toHex(uint64_t value, char *str, uint16_t bits) +{ + ulltoa(value, str, 16); // Get 64bit value + + int fill = 8; + if ((bits > 3) && (bits < 65)) { + fill = bits / 4; // Max 16 + if (bits % 4) { fill++; } + } + int len = strlen(str); + fill -= len; + if (fill > 0) { + memmove(str + fill, str, len +1); + memset(str, '0', fill); + } + return str; +} + char* dtostrfd(double number, unsigned char prec, char *s) { if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript) diff --git a/sonoff/support_static_buffer.ino b/sonoff/support_static_buffer.ino index d2817753b54e..38838c084d74 100644 --- a/sonoff/support_static_buffer.ino +++ b/sonoff/support_static_buffer.ino @@ -23,6 +23,8 @@ typedef struct SBuffer_impl { uint8_t buf[]; // the actual data } SBuffer_impl; + + typedef class SBuffer { protected: @@ -43,7 +45,8 @@ public: inline size_t getLen(void) const { return _buf->len; } inline size_t len(void) const { return _buf->len; } inline uint8_t *getBuffer(void) const { return _buf->buf; } - inline uint8_t *buf(void) const { return _buf->buf; } + inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } + inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } virtual ~SBuffer(void) { delete[] _buf; @@ -57,6 +60,12 @@ public: } } + void set8(const size_t offset, const uint8_t data) { + if (offset < _buf->len) { + _buf->buf[offset] = data; + } + } + size_t add8(const uint8_t data) { // append 8 bits value if (_buf->len < _buf->size) { // do we have room for 1 byte _buf->buf[_buf->len++] = data; @@ -124,6 +133,15 @@ public: } return 0; } + uint64_t get64(const size_t offset) const { + if (offset < len() - 7) { + return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | + ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | + ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | + ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); + } + return 0; + } SBuffer subBuffer(const size_t start, size_t len) const { if (start >= _buf->len) { @@ -138,6 +156,32 @@ public: return buf2; } + static SBuffer SBufferFromHex(const char *hex, size_t len) { + size_t buf_len = (len + 3) / 2; + SBuffer buf2(buf_len); + uint8_t val; + + for (; len > 1; len -= 2) { + val = asc2byte(*hex++) << 4; + val |= asc2byte(*hex++); + buf2.add8(val); + } + return buf2; + } + +protected: + + static uint8_t asc2byte(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { rVal = chr - '0'; } + else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; + } + + static void unHex(const char* in, uint8_t *out, size_t len) { + } + protected: SBuffer_impl * _buf; diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 0e8a009823c4..30b2f532fa8f 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -70,24 +70,6 @@ void IrSendInit(void) irsend->begin(); } -char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) -{ - ulltoa(value, str, 16); // Get 64bit value - - int fill = 8; - if ((bits > 3) && (bits < 65)) { - fill = bits / 4; // Max 16 - if (bits % 4) { fill++; } - } - int len = strlen(str); - fill -= len; - if (fill > 0) { - memmove(str + fill, str, len +1); - memset(str, '0', fill); - } - return str; -} - #ifdef USE_IR_RECEIVE /*********************************************************************************************\ * IR Receive @@ -140,10 +122,10 @@ void IrReceiveCheck(void) if (results.bits % 8) { digits2++; } ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); // Get n-bit value as hex 56341200 } else { - IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456 + Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 00123456 } } else { - IrUint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash + Uint64toHex(results.value, hvalue, 32); // UNKNOWN is always 32 bits hash } AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), @@ -938,7 +920,7 @@ uint32_t IrRemoteCmndIrSendJson(void) char dvalue[64]; char hvalue[20]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), - protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat, protocol_code); + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); irsend_active = true; switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t diff --git a/sonoff/xdrv_05_irremote_full.ino b/sonoff/xdrv_05_irremote_full.ino index 85c81684bcc6..6c2fe945261d 100644 --- a/sonoff/xdrv_05_irremote_full.ino +++ b/sonoff/xdrv_05_irremote_full.ino @@ -72,27 +72,6 @@ uint64_t reverseBitsInBytes64(uint64_t b) { return a.i; } -char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) -{ - ulltoa(value, str, 16); // Get 64bit value - - int fill = 8; - if ((bits > 3) && (bits < 65)) { - fill = bits / 4; // Max 16 - if (bits % 4) { fill++; } - } - int len = strlen(str); - fill -= len; - if (fill > 0) { - memmove(str + fill, str, len +1); - memset(str, '0', fill); - } - memmove(str + 2, str, strlen(str) +1); - str[0] = '0'; - str[1] = 'x'; - return str; -} - /*********************************************************************************************\ * IR Receive \*********************************************************************************************/ @@ -189,15 +168,15 @@ String sendIRJsonState(const struct decode_results &results) { } else { char hvalue[64]; if (UNKNOWN != results.decode_type) { - IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 + Uint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456 json += "\""; json += hvalue; json += "\",\"" D_JSON_IR_DATALSB "\":\""; - IrUint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB + Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); // Get 64bit value as hex 0x00123456, LSB json += hvalue; json += "\""; } else { // UNKNOWN - IrUint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits + Uint64toHex(results.value, hvalue, 32); // Unknown is always 32 bits json += "\""; json += hvalue; json += "\""; @@ -460,7 +439,7 @@ uint32_t IrRemoteCmndIrSendJson(void) char dvalue[32]; char hvalue[32]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data %s (%s), repeat %d"), - protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat); + protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); irsend_active = true; // deactivate receive bool success = irsend->send(protocol, data, bits, repeat); diff --git a/sonoff/xdrv_23_zigbee_constants.ino b/sonoff/xdrv_23_zigbee_0_constants.ino similarity index 95% rename from sonoff/xdrv_23_zigbee_constants.ino rename to sonoff/xdrv_23_zigbee_0_constants.ino index d87cc4af5a9b..651596878697 100644 --- a/sonoff/xdrv_23_zigbee_constants.ino +++ b/sonoff/xdrv_23_zigbee_0_constants.ino @@ -404,4 +404,25 @@ enum Z_Util { Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 }; +enum ZCL_Global_Commands { + ZCL_READ_ATTRIBUTES = 0x00, + ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, + ZCL_WRITE_ATTRIBUTES = 0x02, + ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, + ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, + ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, + ZCL_CONFIGURE_REPORTING = 0x06, + ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, + ZCL_READ_REPORTING_CONFIGURATION = 0x08, + ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, + ZCL_REPORT_ATTRIBUTES = 0x0a, + ZCL_DEFAULT_RESPONSE = 0x0b, + ZCL_DISCOVER_ATTRIBUTES = 0x0c, + ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d + +}; + +enum class ZclGlobalCommandId : uint8_t { +}; + #endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_4_converters.ino b/sonoff/xdrv_23_zigbee_4_converters.ino new file mode 100644 index 000000000000..3f13d08f6d40 --- /dev/null +++ b/sonoff/xdrv_23_zigbee_4_converters.ino @@ -0,0 +1,619 @@ +/* + xdrv_23_zigbee_converters.ino - zigbee support for Sonoff-Tasmota + + Copyright (C) 2019 Theo Arends and Stephan Hadinger + + 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_ZIGBEE + +/*********************************************************************************************\ + * ZCL +\*********************************************************************************************/ + +typedef union ZCLHeaderFrameControl_t { + struct { + uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific + uint8_t manuf_specific : 1; // Manufacturer Specific Sub-field + uint8_t direction : 1; // 0 = tasmota to zigbee, 1 = zigbee to tasmota + uint8_t disable_def_resp : 1; // don't send back default response + uint8_t reserved : 3; + } b; + uint32_t d8; // raw 8 bits field +} ZCLHeaderFrameControl_t; + + +class ZCLFrame { +public: + + ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, + const char *buf, size_t buf_len, uint16_t clusterid = 0, uint16_t groupid = 0): + _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), + _payload(buf_len ? buf_len : 250), // allocate the data frame from source or preallocate big enough + _cluster_id(clusterid), _group_id(groupid) + { + _frame_control.d8 = frame_control; + _payload.addBuffer(buf, buf_len); + }; + + + void publishMQTTReceived(uint16_t groupid, uint16_t clusterid, Z_ShortAddress srcaddr, + uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp) { + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZCLRECEIVED "\":{" + "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," + "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," + "\"linkquality\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," + "\"timestamp\":%d," + "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\""), + groupid, clusterid, srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp, + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); + + ResponseJsonEnd(); // append '}' + ResponseJsonEnd(); // append '}' + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLSENT)); + XdrvRulesProcess(); + } + + static ZCLFrame parseRawFrame(SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid) { // parse a raw frame and build the ZCL frame object + uint32_t i = offset; + ZCLHeaderFrameControl_t frame_control; + uint16_t manuf_code = 0; + uint8_t transact_seq; + uint8_t cmd_id; + + frame_control.d8 = buf.get8(i++); + if (frame_control.b.manuf_specific) { + manuf_code = buf.get16(i); + i += 2; + } + transact_seq = buf.get8(i++); + cmd_id = buf.get8(i++); + ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, + (const char *)(buf.buf() + i), len + offset - i, + clusterid, groupid); + return zcl_frame; + } + + bool isClusterSpecificCommand(void) { + return _frame_control.b.frame_type & 1; + } + + void parseRawAttributes(JsonObject& json, uint8_t offset = 0); + void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); + void postProcessAttributes(JsonObject& json); + + inline void setGroupId(uint16_t groupid) { + _group_id = groupid; + } + + inline void setClusterId(uint16_t clusterid) { + _cluster_id = clusterid; + } + + inline uint8_t getCmdId(void) const { + return _cmd_id; + } + + inline uint16_t getClusterId(void) const { + return _cluster_id; + } + + const SBuffer &getPayload(void) const { + return _payload; + } + +private: + ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; + uint16_t _manuf_code = 0; // optional + uint8_t _transact_seq = 0; // transaction sequence number + uint8_t _cmd_id = 0; + uint16_t _cluster_id = 0; + uint16_t _group_id = 0; + SBuffer _payload; +}; + + +// Zigbee ZCL converters + +// from /~https://github.com/Koenkk/zigbee-shepherd-converters/blob/638d29f0cace6343052b9a4e7fd60980fa785479/converters/fromZigbee.js#L55 +// Input voltage in mV, i.e. 3000 = 3.000V +// Output percentage from 0 to 100 as int +uint8_t toPercentageCR2032(uint32_t voltage) { + uint32_t percentage; + if (voltage < 2100) { + percentage = 0; + } else if (voltage < 2440) { + percentage = 6 - ((2440 - voltage) * 6) / 340; + } else if (voltage < 2740) { + percentage = 18 - ((2740 - voltage) * 12) / 300; + } else if (voltage < 2900) { + percentage = 42 - ((2900 - voltage) * 24) / 160; + } else if (voltage < 3000) { + percentage = 100 - ((3000 - voltage) * 58) / 100; + } else if (voltage >= 3000) { + percentage = 100; + } + return percentage; +} + + +uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, + uint32_t offset, uint32_t len) { + + uint32_t i = offset; + uint32_t attrtype = buf.get8(i++); + + // fallback - enter a null value + json[attrid_str] = (char*) nullptr; + + // now parse accordingly to attr type + switch (attrtype) { + case 0x00: // nodata + case 0xFF: // unk + break; + case 0x10: // bool + { + uint8_t val_bool = buf.get8(i++); + if (0xFF != val_bool) { + json[attrid_str] = (bool) (val_bool ? true : false); + } + } + break; + case 0x20: // uint8 + { + uint8_t uint8_val = buf.get8(i); + i += 1; + if (0xFF != uint8_val) { + json[attrid_str] = uint8_val; + } + } + break; + case 0x21: // uint16 + { + uint16_t uint16_val = buf.get16(i); + i += 2; + if (0xFFFF != uint16_val) { + json[attrid_str] = uint16_val; + } + } + break; + case 0x23: // uint16 + { + uint32_t uint32_val = buf.get32(i); + i += 4; + if (0xFFFFFFFF != uint32_val) { + json[attrid_str] = uint32_val; + } + } + break; + // Note: uint40, uint48, uint56, uint64 are not used in ZCL, so they are not implemented (yet) + case 0x24: // int40 + case 0x25: // int48 + case 0x26: // int56 + case 0x27: // int64 + i += attrtype - 0x1F; // 5 - 8; + break; + case 0x28: // uint8 + { + int8_t int8_val = buf.get8(i); + i += 1; + if (0x80 != int8_val) { + json[attrid_str] = int8_val; + } + } + break; + case 0x29: // uint16 + { + int16_t int16_val = buf.get16(i); + i += 2; + if (0x8000 != int16_val) { + json[attrid_str] = int16_val; + } + } + break; + case 0x2B: // uint16 + { + int32_t int32_val = buf.get32(i); + i += 4; + if (0x80000000 != int32_val) { + json[attrid_str] = int32_val; + } + } + break; + // Note: int40, int48, int56, int64 are not used in ZCL, so they are not implemented (yet) + case 0x2C: // int40 + case 0x2D: // int48 + case 0x2E: // int56 + case 0x2F: // int64 + i += attrtype - 0x27; // 5 - 8; + break; + + case 0x41: // octet string, 1 byte len + case 0x42: // char string, 1 byte len + case 0x43: // octet string, 2 bytes len + case 0x44: // char string, 2 bytes len + // For strings, default is to try to do a real string, but reverts to octet stream if null char is present or on some exceptions + { + bool parse_as_string = true; + uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); // len is 8 or 16 bits + i += (attrtype <= 0x42) ? 1 : 2; // increment pointer + + // check if we can safely use a string + if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } + else { + for (uint32_t j = 0; j < len; j++) { + if (0x00 == buf.get8(i+j)) { + parse_as_string = false; + break; + } + } + } + + if (parse_as_string) { + char str[len+1]; + strncpy(str, buf.charptr(i), len); + str[len] = 0x00; + json[attrid_str] = str; + } else { + // print as HEX + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + } + + i += len; + break; + } + i += buf.get8(i) + 1; + break; + + + // TODO + case 0x08: // data8 + i++; + break; + case 0x18: // map8 + i++; + break; + case 0x19: // map16 + i += 2; + break; + case 0x1B: // map32 + i += 4; + break; + // enum + case 0x30: // enum8 + case 0x31: // enum16 + i += attrtype - 0x2F; + break; + + case 0x39: // float + i += 4; + break; + + case 0xE0: // ToD + case 0xE1: // date + case 0xE2: // UTC + i += 4; + break; + + case 0xE8: // clusterId + case 0xE9: // attribId + i += 2; + break; + case 0xEA: // bacOID + i += 4; + break; + + case 0xF0: // EUI64 + i += 8; + break; + case 0xF1: // key128 + i += 16; + break; + + // Other un-implemented data types + case 0x09: // data16 + case 0x0A: // data24 + case 0x0B: // data32 + case 0x0C: // data40 + case 0x0D: // data48 + case 0x0E: // data56 + case 0x0F: // data64 + i += attrtype - 0x07; // 2-8 + break; + // map + case 0x1A: // map24 + case 0x1C: // map40 + case 0x1D: // map48 + case 0x1E: // map56 + case 0x1F: // map64 + i += attrtype - 0x17; + break; + // semi + case 0x38: // semi (float on 2 bytes) + i += 2; + break; + case 0x3A: // double precision + i += 8; + break; + } + + // String pp; // pretty print + // json[attrid_str].prettyPrintTo(pp); + // // now store the attribute + // AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: ZCL attribute decoded, id %s, type 0x%02X, val=%s"), + // attrid_str, attrtype, pp.c_str()); + return i - offset; // how much have we increased the index +} + + +// First pass, parse all attributes in their native format +// The key is 32 bits, high 16 bits is cluserid, low 16 bits is attribute id +void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + uint32_t attrid = _cluster_id << 16; // set high 16 bits with cluster id + + while (len + offset - i >= 3) { + attrid = (attrid & 0xFFFF0000) | _payload.get16(i); // get lower 16 bits + i += 2; + + char shortaddr[12]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%08X"), attrid); + + // exception for Xiaomi lumi.weather - specific field to be treated as octet and not char + if (0x0000FF01 == attrid) { + if (0x42 == _payload.get8(i)) { + _payload.set8(i, 0x41); // change type from 0x42 to 0x41 + } + } + i += parseSingleAttribute(json, shortaddr, _payload, i, len); + } +} + +// Parse non-normalized attributes +// The key is 24 bits, high 16 bits is cluserid, low 8 bits is command id +void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + uint32_t attrid = _cluster_id << 8 | _cmd_id; + + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("0x%06X"), attrid); // 24 bits + + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + + json[attrid_str] = hex_char; +} + +#define ZCL_MODELID "0x00000005" // Cluster 0x0000, attribute 0x05 +#define ZCL_TEMPERATURE "0x04020000" // Cluster 0x0402, attribute 0x00 +#define ZCL_PRESSURE "0x04030000" // Cluster 0x0403, attribute 0x00 +#define ZCL_PRESSURE_SCALED "0x04030010" // Cluster 0x0403, attribute 0x10 +#define ZCL_PRESSURE_SCALE "0x04030014" // Cluster 0x0403, attribute 0x14 +#define ZCL_HUMIDITY "0x04050000" // Cluster 0x0403, attribute 0x00 +#define ZCL_LUMI_WEATHER "0x0000FF01" // Cluster 0x0000, attribute 0xFF01 - proprietary + +#define ZCL_OO_OFF "0x000600" // Cluster 0x0006, cmd 0x00 - On/Off - Off +#define ZCL_OO_ON "0x000601" // Cluster 0x0006, cmd 0x01 - On/Off - On +#define ZCL_COLORTEMP_MOVE "0x03000A" // Cluster 0x0300, cmd 0x0A, Move to Color Temp +#define ZCL_LC_MOVE "0x000800" // Cluster 0x0008, cmd 0x00, Level Control Move to Level +#define ZCL_LC_MOVE_1 "0x000801" // Cluster 0x0008, cmd 0x01, Level Control Move +#define ZCL_LC_STEP "0x000802" // Cluster 0x0008, cmd 0x02, Level Control Step +#define ZCL_LC_STOP "0x000803" // Cluster 0x0008, cmd 0x03, Level Control Stop +#define ZCL_LC_MOVE_WOO "0x000804" // Cluster 0x0008, cmd 0x04, Level Control Move to Level, with On/Off +#define ZCL_LC_MOVE_1_WOO "0x000805" // Cluster 0x0008, cmd 0x05, Level Control Move, with On/Off +#define ZCL_LC_STEP_WOO "0x000806" // Cluster 0x0008, cmd 0x05, Level Control Step, with On/Off +#define ZCL_LC_STOP_WOO "0x000807" // Cluster 0x0008, cmd 0x07, Level Control Stop + +void ZCLFrame::postProcessAttributes(JsonObject& json) { + const __FlashStringHelper *key; + + // ModelID ZCL 3.2 + key = F(ZCL_MODELID); + if (json.containsKey(key)) { + json[F(D_JSON_MODEL D_JSON_ID)] = json[key]; + json.remove(key); + } + + // Temperature ZCL 4.4 + key = F(ZCL_TEMPERATURE); + if (json.containsKey(key)) { + // parse temperature + int32_t temperature = json[key]; + json.remove(key); + json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f; + } + + // Pressure ZCL 4.5 + key = F(ZCL_PRESSURE); + if (json.containsKey(key)) { + json[F(D_JSON_PRESSURE)] = json[key]; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa + json.remove(key); + } + json.remove(F(ZCL_PRESSURE_SCALE)); + json.remove(F(ZCL_PRESSURE_SCALED)); + + // Humidity ZCL 4.7 + key = F(ZCL_HUMIDITY); + if (json.containsKey(key)) { + // parse temperature + uint32_t humidity = json[key]; + json.remove(key); + json[F(D_JSON_HUMIDITY)] = humidity / 100.0f; + } + + // Osram Mini Switch + key = F(ZCL_OO_OFF); + if (json.containsKey(key)) { + json.remove(key); + json[F(D_CMND_POWER)] = F("Off"); + } + key = F(ZCL_OO_ON); + if (json.containsKey(key)) { + json.remove(key); + json[F(D_CMND_POWER)] = F("On"); + } + key = F(ZCL_COLORTEMP_MOVE); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint16_t color_temp = buf2.get16(0); + uint16_t transition_time = buf2.get16(2); + json.remove(key); + json[F("ColorTemp")] = color_temp; + json[F("TransitionTime")] = transition_time / 10.0f; + } + key = F(ZCL_LC_MOVE_WOO); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t level = buf2.get8(0); + uint16_t transition_time = buf2.get16(1); + json.remove(key); + json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage + json[F("TransitionTime")] = transition_time / 10.0f; + if (0 == level) { + json[F(D_CMND_POWER)] = F("Off"); + } else { + json[F(D_CMND_POWER)] = F("On"); + } + } + key = F(ZCL_LC_MOVE); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t level = buf2.get8(0); + uint16_t transition_time = buf2.get16(1); + json.remove(key); + json[F("Dimmer")] = changeUIntScale(level, 0, 255, 0, 100); // change to percentage + json[F("TransitionTime")] = transition_time / 10.0f; + } + key = F(ZCL_LC_MOVE_1); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t move_mode = buf2.get8(0); + uint8_t move_rate = buf2.get8(1); + json.remove(key); + json[F("Move")] = move_mode ? F("Down") : F("Up"); + json[F("Rate")] = move_rate; + } + key = F(ZCL_LC_MOVE_1_WOO); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t move_mode = buf2.get8(0); + uint8_t move_rate = buf2.get8(1); + json.remove(key); + json[F("Move")] = move_mode ? F("Down") : F("Up"); + json[F("Rate")] = move_rate; + if (0 == move_mode) { + json[F(D_CMND_POWER)] = F("On"); + } + } + key = F(ZCL_LC_STEP); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t step_mode = buf2.get8(0); + uint8_t step_size = buf2.get8(1); + uint16_t transition_time = buf2.get16(2); + json.remove(key); + json[F("Step")] = step_mode ? F("Down") : F("Up"); + json[F("StepSize")] = step_size; + json[F("TransitionTime")] = transition_time / 10.0f; + } + key = F(ZCL_LC_STEP_WOO); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint8_t step_mode = buf2.get8(0); + uint8_t step_size = buf2.get8(1); + uint16_t transition_time = buf2.get16(2); + json.remove(key); + json[F("Step")] = step_mode ? F("Down") : F("Up"); + json[F("StepSize")] = step_size; + json[F("TransitionTime")] = transition_time / 10.0f; + if (0 == step_mode) { + json[F(D_CMND_POWER)] = F("On"); + } + } + key = F(ZCL_LC_STOP); + if (json.containsKey(key)) { + json.remove(key); + json[F("Stop")] = 1; + } + key = F(ZCL_LC_STOP_WOO); + if (json.containsKey(key)) { + json.remove(key); + json[F("Stop")] = 1; + } + + // Lumi.weather proprietary field + key = F(ZCL_LUMI_WEATHER); + if (json.containsKey(key)) { + String hex = json[key]; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + DynamicJsonBuffer jsonBuffer; + JsonObject& json_lumi = jsonBuffer.createObject(); + uint32_t i = 0; + uint32_t len = buf2.len(); + char shortaddr[8]; + + while (len - i >= 2) { + uint8_t attrid = buf2.get8(i++); + + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%02X"), attrid); + + i += parseSingleAttribute(json_lumi, shortaddr, buf2, i, len); + } + // parse output + if (json_lumi.containsKey("0x64")) { // Temperature + int32_t temperature = json_lumi["0x64"]; + json[F(D_JSON_TEMPERATURE)] = temperature / 100.0f; + } + if (json_lumi.containsKey("0x65")) { // Humidity + uint32_t humidity = json_lumi["0x65"]; + json[F(D_JSON_HUMIDITY)] = humidity / 100.0f; + } + if (json_lumi.containsKey("0x66")) { // Pressure + int32_t pressure = json_lumi["0x66"]; + json[F(D_JSON_PRESSURE)] = pressure / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); // hPa + } + if (json_lumi.containsKey("0x01")) { // Battery Voltage + uint32_t voltage = json_lumi["0x01"]; + json[F(D_JSON_VOLTAGE)] = voltage / 1000.0f; + json[F("Battery")] = toPercentageCR2032(voltage); + } + json.remove(key); + } + +} + +#endif // USE_ZIGBEE diff --git a/sonoff/xdrv_23_zigbee_impl.ino b/sonoff/xdrv_23_zigbee_9_impl.ino similarity index 71% rename from sonoff/xdrv_23_zigbee_impl.ino rename to sonoff/xdrv_23_zigbee_9_impl.ino index 1653ed78caa1..6f23cbc5aff4 100644 --- a/sonoff/xdrv_23_zigbee_impl.ino +++ b/sonoff/xdrv_23_zigbee_9_impl.ino @@ -23,17 +23,35 @@ const uint32_t ZIGBEE_BUFFER_SIZE = 256; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255 const uint8_t ZIGBEE_SOF = 0xFE; -const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error -const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 99 in case of fatal error - +// Status code used for ZigbeeStatus MQTT message +// Ex: {"ZigbeeStatus":{"code": 3,"message":"Configured, starting coordinator"}} +const uint8_t ZIGBEE_STATUS_OK = 0; // Zigbee started and working +const uint8_t ZIGBEE_STATUS_BOOT = 1; // CC2530 booting +const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; // Resetting CC2530 configuration +const uint8_t ZIGBEE_STATUS_STARTING = 3; // Starting CC2530 as coordinator +const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; // Disable PermitJoin +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; // Enable PermitJoin for 60 seconds +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; // Enable PermitJoin until next boot +const uint8_t ZIGBEE_STATUS_DEVICE_VERSION = 50; // Status: CC2530 ZNP Version +const uint8_t ZIGBEE_STATUS_DEVICE_INFO = 51; // Status: CC2530 Device Configuration +const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version +const uint8_t ZIGBEE_STATUS_ABORT = 99; // Fatal error, Zigbee not working + +//#define Z_USE_SOFTWARE_SERIAL + +#ifdef Z_USE_SOFTWARE_SERIAL +#include +SoftwareSerial *ZigbeeSerial = nullptr; +#else #include - TasmotaSerial *ZigbeeSerial = nullptr; +#endif -const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND; -void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend }; +const char kZigbeeCommands[] PROGMEM = "|" D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN; + +void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend, &CmndZigbeePermitJoin }; typedef int32_t (*ZB_Func)(uint8_t value); typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, class SBuffer &buf); @@ -74,6 +92,7 @@ enum Zigbee_StateMachine_Instruction_Set { ZGB_INSTR_8_BYTES = 0x80, ZGB_INSTR_CALL = 0x80, // call a function ZGB_INSTR_LOG, // log a message, if more detailed logging required, call a function + ZGB_INSTR_MQTT_STATUS, // send MQTT status string with code ZGB_INSTR_SEND, // send a ZNP message ZGB_INSTR_WAIT_UNTIL, // wait until the specified message is received, ignore all others ZGB_INSTR_WAIT_RECV, // wait for a message according to the filter @@ -95,12 +114,24 @@ enum Zigbee_StateMachine_Instruction_Set { #define ZI_CALL(f, x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, #define ZI_LOG(x, m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_MQTT_STATUS(x, m) { .i = { ZGB_INSTR_MQTT_STATUS, (x), 0x0000 } }, { .p = ((const void*)(m)) }, #define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, #define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, #define ZI_WAIT_RECV(x, m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, #define ZI_WAIT_UNTIL(x, m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, #define ZI_WAIT_RECV_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, +// Labels used in the State Machine -- internal only +const uint8_t ZIGBEE_LABEL_START = 10; // Start ZNP +const uint8_t ZIGBEE_LABEL_READY = 20; // goto label 20 for main loop +const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; // main loop +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; // disable permit join +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; // enable permit join for 60 seconds +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; // enable permit join for 60 seconds +// errors +const uint8_t ZIGBEE_LABEL_ABORT = 99; // goto label 99 in case of fatal error +const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version + struct ZigbeeStatus { bool active = true; // is Zigbee active for this device, i.e. GPIOs configured bool state_machine = false; // the state machine is running @@ -124,74 +155,6 @@ struct ZigbeeStatus zigbee; SBuffer *zigbee_buffer = nullptr; - - -/*********************************************************************************************\ - * ZCL -\*********************************************************************************************/ - -typedef union ZCLHeaderFrameControl_t { - struct { - uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific - uint8_t manuf_specific : 1; // Manufacturer Specific Sub-field - uint8_t direction : 1; // 0 = tasmota to zigbee, 1 = zigbee to tasmota - uint8_t disable_def_resp : 1; // don't send back default response - uint8_t reserved : 3; - } b; - uint8_t d8; // raw 8 bits field -} ZCLHeaderFrameControl_t; - -class ZCLFrame { -public: - - ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, - const char *buf, size_t buf_len ): - _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), - _payload(buf_len ? buf_len : 250) // allocate the data frame from source or preallocate big enough - { - _frame_control.d8 = frame_control; - _payload.addBuffer(buf, buf_len); - }; - - void publishMQTTReceived(void) { - char hex_char[_payload.len()*2+2]; - ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); - ResponseTime_P(PSTR(",\"" D_JSON_ZIGBEEZCLRECEIVED "\":{\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," - "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), - _frame_control, _manuf_code, _transact_seq, _cmd_id, - hex_char); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLSENT)); - XdrvRulesProcess(); - } - - static ZCLFrame parseRawFrame(SBuffer &buf, uint8_t offset, uint8_t len) { // parse a raw frame and build the ZCL frame object - uint32_t i = offset; - ZCLHeaderFrameControl_t frame_control; - uint16_t manuf_code = 0; - uint8_t transact_seq; - uint8_t cmd_id; - - frame_control.d8 = buf.get8(i++); - if (frame_control.b.manuf_specific) { - manuf_code = buf.get16(i); - i += 2; - } - transact_seq = buf.get8(i++); - cmd_id = buf.get8(i++); - ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, - (const char *)(buf.buf() + i), len + offset - i); - return zcl_frame; - } - -private: - ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; - uint16_t _manuf_code = 0; // optional - uint8_t _transact_seq = 0; // transaction sequence number - uint8_t _cmd_id = 0; - SBuffer _payload; -}; - - /*********************************************************************************************\ * State Machine \*********************************************************************************************/ @@ -209,8 +172,8 @@ private: // ZBS_* Zigbee Send // ZBR_* Zigbee Recv -ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x01 ) // 410001 SYS_RESET_REQ Software reset -ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) // 4180 SYS_RESET_REQ Software reset response +ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) // 410001 SYS_RESET_REQ Hardware reset +ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) // 4180 SYS_RESET_REQ Hardware reset response ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) // 2102 Z_SYS:version ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) // 6102 Z_SYS:version @@ -295,7 +258,7 @@ ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x01, 0x00 /* InitLen 16 bits */, 0x01 /* len */, 0x00 ) // 2107000F01000100 - 610709 // Init succeeded -ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_Created ) // 610709 - NV Write +ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT, Z_Created ) // 610709 - NV Write // Write ZNP Has Configured ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), 0x00 /* offset */, 0x01 /* len */, 0x55 ) // 2109000F000155 - 610900 @@ -352,11 +315,14 @@ ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B /* endpoint */, Z_B0(Z_P // Z_ZDO:mgmtPermitJoinReq ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 /* AddrMode */, // 25360200000000 0x00, 0x00 /* DstAddr */, 0x00 /* Duration */, 0x00 /* TCSignificance */) -ZBM(ZBS_PERMITJOINREQ_OPEN, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFCFF00 +ZBM(ZBS_PERMITJOINREQ_OPEN_60, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFC3C00 + 0xFC, 0xFF /* DstAddr */, 60 /* Duration */, 0x00 /* TCSignificance */) +ZBM(ZBS_PERMITJOINREQ_OPEN_XX, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F /* AddrMode */, // 25360FFFFCFF00 0xFC, 0xFF /* DstAddr */, 0xFF /* Duration */, 0x00 /* TCSignificance */) ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_Success) // 653600 -ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 /* Duration */) // 45CB00 -ZBM(ZBR_PERMITJOIN_AREQ_OPEN, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF /* Duration */) // 45CBFF +ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 /* Duration */) // 45CB00 +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_60, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 60 /* Duration */) // 45CB3C +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF /* Duration */) // 45CBFF ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 /* srcAddr*/, Z_Success ) // 45B6000000 // Filters for ZCL frames @@ -371,119 +337,209 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = { ZI_WAIT(15000) // wait for 15 seconds for Tasmota to stabilize ZI_ON_ERROR_GOTO(50) - ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device") + ZI_MQTT_STATUS(ZIGBEE_STATUS_BOOT, "Booting") + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device") ZI_SEND(ZBS_RESET) // reboot cc2530 just in case we rebooted ESP8266 but not cc2530 ZI_WAIT_RECV(5000, ZBR_RESET) // timeout 5s ZI_LOG(LOG_LEVEL_INFO, "ZIG: checking device configuration") ZI_SEND(ZBS_ZNPHC) // check value of ZNP Has Configured ZI_WAIT_RECV(2000, ZBR_ZNPHC) ZI_SEND(ZBS_VERSION) // check ZNP software version - ZI_WAIT_RECV(500, ZBR_VERSION) + ZI_WAIT_RECV_FUNC(1000, ZBR_VERSION, &Z_ReceiveCheckVersion) // Check version ZI_SEND(ZBS_PAN) // check PAN ID - ZI_WAIT_RECV(500, ZBR_PAN) + ZI_WAIT_RECV(1000, ZBR_PAN) ZI_SEND(ZBS_EXTPAN) // check EXT PAN ID - ZI_WAIT_RECV(500, ZBR_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_EXTPAN) ZI_SEND(ZBS_CHANN) // check CHANNEL - ZI_WAIT_RECV(500, ZBR_CHANN) + ZI_WAIT_RECV(1000, ZBR_CHANN) ZI_SEND(ZBS_PFGK) // check PFGK - ZI_WAIT_RECV(500, ZBR_PFGK) + ZI_WAIT_RECV(1000, ZBR_PFGK) ZI_SEND(ZBS_PFGKEN) // check PFGKEN - ZI_WAIT_RECV(500, ZBR_PFGKEN) - ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok") + ZI_WAIT_RECV(1000, ZBR_PFGKEN) + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok") // all is good, we can start - ZI_LABEL(10) // START ZNP App - ZI_CALL(&Z_State_Ready, 1) + ZI_LABEL(ZIGBEE_LABEL_START) // START ZNP App + ZI_MQTT_STATUS(ZIGBEE_STATUS_STARTING, "Configured, starting coordinator") + //ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) // Z_ZDO:startupFromApp - ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator") - ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator - ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command - ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started - ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo - ZI_WAIT_RECV(500, ZBR_GETDEVICEINFO) // TODO memorize info - ZI_SEND(ZBS_ZDO_NODEDESCREQ) // Z_ZDO:nodeDescReq - ZI_WAIT_RECV(500, ZBR_ZDO_NODEDESCREQ) + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator") +ZI_SEND(ZBS_STARTUPFROMAPP) // start coordinator + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command + ZI_WAIT_UNTIL(5000, AREQ_STARTUPFROMAPP) // wait for async message that coordinator started + ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) + //ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // TODO memorize info + ZI_SEND(ZBS_ZDO_NODEDESCREQ) // Z_ZDO:nodeDescReq + ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCREQ) - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq - ZI_WAIT_RECV(500, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(500, ZBR_ZDO_ACTIVEEPRSP_NONE) - ZI_SEND(ZBS_AF_REGISTER01) // Z_AF register for endpoint 01, profile 0x0104 Home Automation - ZI_WAIT_RECV(500, ZBR_AF_REGISTER) - ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation - ZI_WAIT_RECV(500, ZBR_AF_REGISTER) + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) + ZI_SEND(ZBS_AF_REGISTER01) // Z_AF register for endpoint 01, profile 0x0104 Home Automation + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_AF_REGISTER0B) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) // Z_ZDO:nodeDescReq ?? Is is useful to redo it? TODO // redo Z_ZDO:activeEpReq to check that Ep are available - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq - ZI_WAIT_RECV(500, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(500, ZBR_ZDO_ACTIVEEPRSP_OK) - ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join - ZI_WAIT_RECV(500, ZBR_PERMITJOINREQ) - ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) // Z_ZDO:activeEpReq + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE) - ZI_SEND(ZBS_PERMITJOINREQ_OPEN) // Opening Permit Join, normally through command TODO - ZI_WAIT_RECV(500, ZBR_PERMITJOINREQ) - ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful - //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN) + //ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) // Opening Permit Join, normally through command + //ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX) ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATUS(ZIGBEE_STATUS_OK, "Started") ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device ready, listening...") - ZI_CALL(&Z_State_Ready, 1) + ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) ZI_WAIT_FOREVER() ZI_GOTO(ZIGBEE_LABEL_READY) - ZI_LABEL(50) // reformat device - ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset") + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE) + ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_CLOSE, "Disable Pairing mode") + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) // Closing the Permit Join + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60) + ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_60, "Enable Pairing mode for 60 seconds") + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_60) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_60) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX) + ZI_MQTT_STATUS(ZIGBEE_STATUS_PERMITJOIN_OPEN_XX, "Enable Pairing mode until next boot") + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + //ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful + //ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX) + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(50) // reformat device + ZI_MQTT_STATUS(ZIGBEE_STATUS_RESET_CONF, "Reseting configuration") + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset") ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - ZI_SEND(ZBS_FACTRES) // factory reset - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_RESET) // reset device + ZI_SEND(ZBS_FACTRES) // factory reset + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) // reset device ZI_WAIT_RECV(5000, ZBR_RESET) - ZI_SEND(ZBS_W_PAN) // write PAN ID - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_W_CHANN) // write CHANNEL - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable - ZI_WAIT_RECV(500, ZBR_W_OK) - ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode - ZI_WAIT_RECV(500, ZBR_WNV_OK) - ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB - ZI_WAIT_RECV(500, ZBR_W_OK) + ZI_SEND(ZBS_W_PAN) // write PAN ID + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_EXTPAN) // write EXT PAN ID + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_CHANN) // write CHANNEL + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_LOGTYP) // write Logical Type = coordinator + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGK) // write PRECFGKEY + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGKEN) // write PRECFGKEY Enable + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_WNV_SECMODE) // write Security Mode + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + ZI_SEND(ZBS_W_ZDODCB) // write Z_ZDO Direct CB + ZI_WAIT_RECV(1000, ZBR_W_OK) // Now mark the device as ready, writing 0x55 in memory slot 0x0F00 - ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured - ZI_WAIT_RECV(500, ZBR_WNV_INIT_OK) - ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured - ZI_WAIT_RECV(500, ZBR_WNV_OK) + ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured + ZI_WAIT_RECV(1000, ZBR_WNV_INIT_OK) + ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured + ZI_WAIT_RECV(1000, ZBR_WNV_OK) - ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured") - ZI_GOTO(10) + //ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured") + ZI_GOTO(ZIGBEE_LABEL_START) - ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATUS(ZIGBEE_STATUS_UNSUPPORTED_VERSION, "Only ZNP 1.2 is currently supported") + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort + ZI_MQTT_STATUS(ZIGBEE_STATUS_ABORT, "Abort") ZI_LOG(LOG_LEVEL_ERROR, "ZIG: Abort") ZI_STOP(ZIGBEE_LABEL_ABORT) }; +int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { + // Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991 + // IEEE Adr (8 bytes) = 0x00124B001D156362 + // Short Addr (2 bytes) = 0x0000 + // Device Type (1 byte) = 0x07 (coord?) + // Device State (1 byte) = 0x09 (coordinator started) + // NumAssocDevices (1 byte) = 0x02 + // List of devices: 0x8683, 0x9199 + Z_IEEEAddress long_adr = buf.get64(3); + Z_ShortAddress short_adr = buf.get16(11); + uint8_t device_type = buf.get8(13); + uint8_t device_state = buf.get8(14); + uint8_t device_associated = buf.get8(15); + + char hex[20]; + Uint64toHex(long_adr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"code\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d,\"DeviceState\":%d" + ",\"NumAssocDevices\":%d"), + ZIGBEE_STATUS_DEVICE_INFO, hex, short_adr, device_type, device_state, + device_associated); + + if (device_associated > 0) { + uint idx = 16; + ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); + for (uint32_t i = 0; i < device_associated; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); + idx += 2; + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); // append '}' + ResponseJsonEnd(); // append '}' + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + + return res; +} -int32_t Z_Recv_Vers(int32_t res, class SBuffer &buf) { +int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { // check that the version is supported // typical version for ZNP 1.2 - // 61020200-020603D91434010200000000 - // TranportRev = 02 - // Product = 00 - // MajorRel = 2 - // MinorRel = 6 - // MaintRel = 3 - // Revision = 20190425 d (0x013414D9) - if ((0x02 == buf.get8(4)) && (0x06 == buf.get8(5))) { + // 61020200-02.06.03.D9143401.0200000000 + // TranportRev = 02 + // Product = 00 + // MajorRel = 2 + // MinorRel = 6 + // MaintRel = 3 + // Revision = 20190425 d (0x013414D9) + uint8_t major_rel = buf.get8(4); + uint8_t minor_rel = buf.get8(5); + uint8_t maint_rel = buf.get8(6); + uint32_t revision = buf.get32(7); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{" + "\"code\":%d,\"MajorRel\":%d,\"MinorRel\":%d" + ",\"MaintRel\":%d,\"Revision\":%d}}"), + ZIGBEE_STATUS_DEVICE_VERSION, major_rel, minor_rel, + maint_rel, revision); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { return 0; // version 2.6.x is ok } else { - return -2; // abort + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; // abort } } @@ -496,10 +552,44 @@ int32_t Z_Recv_Default(int32_t res, class SBuffer &buf) { } else { if ( (pgm_read_byte(&ZBR_AF_INCOMING_MESSAGE[0]) == buf.get8(0)) && (pgm_read_byte(&ZBR_AF_INCOMING_MESSAGE[1]) == buf.get8(1)) ) { - // AF_INCOMING_MSG, extract ZCL part TODO - // skip first 19 bytes - ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18)); - zcl_received.publishMQTTReceived(); + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + Z_ShortAddress srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + uint32_t timestamp = buf.get32(13); + uint8_t seqnumber = buf.get8(17); + + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid); + + zcl_received.publishMQTTReceived(groupid, clusterid, srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + + char shortaddr[8]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json_root = jsonBuffer.createObject(); + JsonObject& json = json_root.createNestedObject(shortaddr); + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseRawAttributes(json); + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(json); + } + zcl_received.postProcessAttributes(json); + + String msg(""); + msg.reserve(100); + json_root.printTo(msg); + + Response_P(PSTR("%s"), msg.c_str()); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCLRECEIVED)); + XdrvRulesProcess(); } return -1; } @@ -658,11 +748,16 @@ void ZigbeeStateMachine_Run(void) { continue; } } - // TODO break; case ZGB_INSTR_LOG: AddLog_P(cur_d8, (char*) cur_ptr1); break; + case ZGB_INSTR_MQTT_STATUS: + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATUS "\":{\"code\":%d,\"message\":\"%s\"}}"), + cur_d8, (char*) cur_ptr1); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATUS)); + XdrvRulesProcess(); + break; case ZGB_INSTR_SEND: ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 /* len */); break; @@ -791,7 +886,7 @@ void ZigbeeInput(void) if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { // waiting for SOF (Start Of Frame) byte, discard anything else - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte); + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZigbeeInput discarding byte %02X"), zigbee_in_byte); continue; // discard } @@ -820,6 +915,9 @@ void ZigbeeInput(void) char hex_char[(zigbee_buffer->len() * 2) + 2]; ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); +#ifndef Z_USE_SOFTWARE_SERIAL + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZIG: Bytes follor_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); +#endif // buffer received, now check integrity if (zigbee_buffer->len() != zigbee_frame_len) { // Len is not correct, log and reject frame @@ -852,19 +950,25 @@ void ZigbeeInit(void) zigbee.active = false; if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("Zigbee: GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); +#ifdef Z_USE_SOFTWARE_SERIAL + ZigbeeSerial = new SoftwareSerial(); + ZigbeeSerial->begin(115200, pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], SWSERIAL_8N1, false, 256); // ZNP is 115200, RTS/CTS (ignored), 8N1 + ZigbeeSerial->enableIntTx(false); + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); +#else ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], 0, 0, 256); // set a receive buffer of 256 bytes - if (ZigbeeSerial->begin(115200)) { // ZNP is 115200, RTS/CTS (ignored), 8N1 - if (ZigbeeSerial->hardwareSerial()) { - ClaimSerial(); - zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer); - } else { - zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); - } - zigbee.active = true; - zigbee.init_phase = true; // start the state machine - zigbee.state_machine = true; // start the state machine - ZigbeeSerial->flush(); - } + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer), serial_in_buffer); + } else { + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); + } +#endif + zigbee.active = true; + zigbee.init_phase = true; // start the state machine + zigbee.state_machine = true; // start the state machine + ZigbeeSerial->flush(); } } @@ -874,7 +978,6 @@ void ZigbeeInit(void) void CmndZigbeeZNPSend(void) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("CmndZigbeeZNPSend: entering, data_len = %d"), XdrvMailbox.data_len); // TODO if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { uint8_t code; @@ -928,6 +1031,23 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { XdrvRulesProcess(); } + +void CmndZigbeePermitJoin(void) +{ + uint32_t payload = XdrvMailbox.payload; + if (payload < 0) { payload = 0; } + if ((99 != payload) && (payload > 1)) { payload = 1; } + + if (1 == payload) { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60); + } else if (99 == payload){ + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX); + } else { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE); + } + ResponseCmndDone(); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ From 5eb85075e5bd372328e3b675de48be7eeae80fab Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 13:10:32 +0200 Subject: [PATCH 26/44] Add Energy 3 phase/channel support * Change energy sensors for three phase/channel support * Add Shelly 2.5 energy dual channel support (#6160) --- sonoff/_changelog.ino | 2 + sonoff/xdrv_03_energy.ino | 250 ++++++++++++++++++++++------------- sonoff/xdrv_16_tuyamcu.ino | 10 +- sonoff/xnrg_01_hlw8012.ino | 14 +- sonoff/xnrg_02_cse7766.ino | 29 ++-- sonoff/xnrg_03_pzem004t.ino | 6 +- sonoff/xnrg_04_mcp39f501.ino | 20 +-- sonoff/xnrg_05_pzem_ac.ino | 10 +- sonoff/xnrg_06_pzem_dc.ino | 6 +- sonoff/xnrg_07_ade7953.ino | 116 ++++++++++------ sonoff/xnrg_08_sdm120.ino | 14 +- sonoff/xnrg_09_dds2382.ino | 12 +- 12 files changed, 292 insertions(+), 197 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index fd47efc7e113..176c5c08addd 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -6,6 +6,8 @@ * Add command Gpios 255/All to show all available GPIO components (#6407) * Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) * Add Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch + * Change energy sensors for three phase/channel support + * Add Shelly 2.5 energy dual channel support (#6160) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 35f65e5757aa..1ad177b1f507 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -68,33 +68,37 @@ void (* const EnergyCommand[])(void) PROGMEM = { #endif // USE_ENERGY_MARGIN_DETECTION &CmndEnergyReset, &CmndTariff }; +const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; + struct ENERGY { - float voltage = 0; // 123.1 V - float current = 0; // 123.123 A - float active_power = 0; // 123.1 W - float apparent_power = NAN; // 123.1 VA - float reactive_power = NAN; // 123.1 VAr - float power_factor = NAN; // 0.12 - float frequency = NAN; // 123.1 Hz - - float start_energy = 0; // 12345.12345 kWh total previous - - float daily = 0; // 123.123 kWh - float total = 0; // 12345.12345 kWh tariff 1 + 2 - float total1 = 0; // 12345.12345 kWh tariff 1 - off-peak - float export_active = NAN; // 123.123 KWh - - unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) - unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily - unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + float voltage[3] = { 0, 0, 0 }; // 123.1 V + float current[3] = { 0, 0, 0 }; // 123.123 A + float active_power[3] = { 0, 0, 0 }; // 123.1 W + float apparent_power[3] = { NAN, NAN, NAN }; // 123.1 VA + float reactive_power[3] = { NAN, NAN, NAN }; // 123.1 VAr + float power_factor[3] = { NAN, NAN, NAN }; // 0.12 + float frequency[3] = { NAN, NAN, NAN }; // 123.1 Hz + + float start_energy = 0; // 12345.12345 kWh total previous + float daily = 0; // 123.123 kWh + float total = 0; // 12345.12345 kWh tariff 1 + 2 + float total1 = 0; // 12345.12345 kWh tariff 1 - off-peak + float export_active = NAN; // 123.123 KWh + + unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only) + unsigned long kWhtoday_offset = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily + unsigned long period = 0; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily uint8_t fifth_second = 0; uint8_t command_code = 0; uint8_t data_valid = 0; - bool voltage_available = true; // Enable if voltage is measured - bool current_available = true; // Enable if current is measured + uint8_t phase_count = 1; // Number of phases active + bool voltage_common = false; // Use single voltage + + bool voltage_available = true; // Enable if voltage is measured + bool current_available = true; // Enable if current is measured bool type_dc = false; bool power_on = true; @@ -275,23 +279,23 @@ void EnergyMarginCheck(void) } if (Settings.energy_power_delta) { - float delta = abs(Energy.power_history[0] - Energy.active_power); + float delta = abs(Energy.power_history[0] - Energy.active_power[0]); // Any delta compared to minimal delta - float min_power = (Energy.power_history[0] > Energy.active_power) ? Energy.active_power : Energy.power_history[0]; + float min_power = (Energy.power_history[0] > Energy.active_power[0]) ? Energy.active_power[0] : Energy.power_history[0]; if (((delta / min_power) * 100) > Settings.energy_power_delta) { Energy.power_delta = 1; - Energy.power_history[1] = Energy.active_power; // We only want one report so reset history - Energy.power_history[2] = Energy.active_power; + Energy.power_history[1] = Energy.active_power[0]; // We only want one report so reset history + Energy.power_history[2] = Energy.active_power[0]; } } Energy.power_history[0] = Energy.power_history[1]; // Shift in history every second allowing power changes to settle for up to three seconds Energy.power_history[1] = Energy.power_history[2]; - Energy.power_history[2] = Energy.active_power; + Energy.power_history[2] = Energy.active_power[0]; if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - energy_power_u = (uint16_t)(Energy.active_power); - energy_voltage_u = (uint16_t)(Energy.voltage); - energy_current_u = (uint16_t)(Energy.current * 1000); + energy_power_u = (uint16_t)(Energy.active_power[0]); + energy_voltage_u = (uint16_t)(Energy.voltage[0]); + energy_current_u = (uint16_t)(Energy.current[0] * 1000); DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); @@ -331,7 +335,7 @@ void EnergyMarginCheck(void) #ifdef USE_ENERGY_POWER_LIMIT // Max Power if (Settings.energy_max_power_limit) { - if (Energy.active_power > Settings.energy_max_power_limit) { + if (Energy.active_power[0] > Settings.energy_max_power_limit) { if (!Energy.mplh_counter) { Energy.mplh_counter = Settings.energy_max_power_limit_hold; } else { @@ -422,13 +426,15 @@ void EnergyOverTempCheck() Energy.data_valid++; if (Energy.data_valid > ENERGY_WATCHDOG) { // Reset energy registers - Energy.voltage = 0; - Energy.current = 0; - Energy.active_power = 0; - if (!isnan(Energy.apparent_power)) { Energy.apparent_power = 0; } - if (!isnan(Energy.reactive_power)) { Energy.reactive_power = 0; } - if (!isnan(Energy.frequency)) { Energy.frequency = 0; } - if (!isnan(Energy.power_factor)) { Energy.power_factor = 0; } + for (uint32_t i = 0; i < Energy.phase_count; i++) { + Energy.voltage[i] = 0; + Energy.current[i] = 0; + Energy.active_power[i] = 0; + if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } + if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } + if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } + if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } + } if (!isnan(Energy.export_active)) { Energy.export_active = 0; } Energy.start_energy = 0; @@ -778,55 +784,93 @@ const char HTTP_ENERGY_SNS3[] PROGMEM = "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; #endif // USE_WEBSERVER +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 + + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); // Dirty + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); // Even dirtier + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + void EnergyShow(bool json) { - float power_factor = Energy.power_factor; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.voltage_common) { + Energy.voltage[i] = Energy.voltage[0]; + } + } + + float power_factor_knx = Energy.power_factor[0]; - char apparent_power_chr[FLOATSZ]; - char reactive_power_chr[FLOATSZ]; - char power_factor_chr[FLOATSZ]; - char frequency_chr[FLOATSZ]; + char apparent_power_chr[Energy.phase_count][FLOATSZ]; + char reactive_power_chr[Energy.phase_count][FLOATSZ]; + char power_factor_chr[Energy.phase_count][FLOATSZ]; + char frequency_chr[Energy.phase_count][FLOATSZ]; if (!Energy.type_dc) { if (Energy.current_available && Energy.voltage_available) { - float apparent_power = Energy.apparent_power; - if (isnan(apparent_power)) { - apparent_power = Energy.voltage * Energy.current; - } - if (apparent_power < Energy.active_power) { // Should be impossible - Energy.active_power = apparent_power; - } - - if (isnan(power_factor)) { - power_factor = (Energy.active_power && apparent_power) ? Energy.active_power / apparent_power : 0; - if (power_factor > 1) power_factor = 1; - } + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float apparent_power = Energy.apparent_power[i]; + if (isnan(apparent_power)) { + apparent_power = Energy.voltage[i] * Energy.current[i]; + } + if (apparent_power < Energy.active_power[i]) { // Should be impossible + Energy.active_power[i] = apparent_power; + } - float reactive_power = Energy.reactive_power; - if (isnan(reactive_power)) { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power * 100)) / 10; - if ((Energy.current > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { - // calculating reactive power only if current is greater than 0.005A and - // difference between active and apparent power is greater than 1.5W or 1% - reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power * Energy.active_power * 100))) / 10; + float power_factor = Energy.power_factor[i]; + if (isnan(power_factor)) { + power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; + if (power_factor > 1) { + power_factor = 1; + } + } + if (0 == i) { power_factor_knx = power_factor; } + + float reactive_power = Energy.reactive_power[i]; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + // calculating reactive power only if current is greater than 0.005A and + // difference between active and apparent power is greater than 1.5W or 1% + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + } } - } - dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr); - dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr); - dtostrfd(power_factor, 2, power_factor_chr); + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); + dtostrfd(power_factor, 2, power_factor_chr[i]); + } } - if (!isnan(Energy.frequency)) { - dtostrfd(Energy.frequency, Settings.flag2.frequency_resolution, frequency_chr); + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float frequency = Energy.frequency[i]; + if (isnan(Energy.frequency[i])) { + frequency = 0; + } + dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); } } - char voltage_chr[FLOATSZ]; - dtostrfd(Energy.voltage, Settings.flag2.voltage_resolution, voltage_chr); - char current_chr[FLOATSZ]; - dtostrfd(Energy.current, Settings.flag2.current_resolution, current_chr); - char active_power_chr[FLOATSZ]; - dtostrfd(Energy.active_power, Settings.flag2.wattage_resolution, active_power_chr); + char voltage_chr[Energy.phase_count][FLOATSZ]; + char current_chr[Energy.phase_count][FLOATSZ]; + char active_power_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); + dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); + dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); + } + char energy_daily_chr[FLOATSZ]; dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); char energy_yesterday_chr[FLOATSZ]; @@ -836,6 +880,10 @@ void EnergyShow(bool json) char export_active_chr[FLOATSZ]; dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr); + char value_chr[FLOATSZ *3]; + char value2_chr[FLOATSZ *3]; + char value3_chr[FLOATSZ *3]; + if (json) { bool show_energy_period = (0 == tele_period); @@ -854,21 +902,27 @@ void EnergyShow(bool json) dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); } - ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), active_power_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), + EnergyFormat(value_chr, active_power_chr[0], json)); if (!Energy.type_dc) { if (Energy.current_available && Energy.voltage_available) { ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), - apparent_power_chr, reactive_power_chr, power_factor_chr); + EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); } - if (!isnan(Energy.frequency)) { - ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), frequency_chr); + if (!isnan(Energy.frequency[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), + EnergyFormat(value_chr, frequency_chr[0], json)); } } if (Energy.voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), voltage_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); } if (Energy.current_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), current_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), + EnergyFormat(value_chr, current_chr[0], json)); } XnrgCall(FUNC_JSON_APPEND); ResponseJsonEnd(); @@ -876,7 +930,7 @@ void EnergyShow(bool json) #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry dtostrfd(Energy.total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)Energy.active_power, energy_total_chr); // PowerUsage, EnergyToday + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr); // PowerUsage, EnergyToday dtostrfd((Energy.total - Energy.total1) * 1000, 1, energy_total_chr); // Tariff2 char energy_total1_chr[FLOATSZ]; @@ -885,26 +939,28 @@ void EnergyShow(bool json) dtostrfd(RtcSettings.energy_usage.return1_kWhtotal, 1, return1_total_chr); char return2_total_chr[FLOATSZ]; dtostrfd(RtcSettings.energy_usage.return2_kWhtotal, 1, return2_total_chr); - DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, return1_total_chr, return2_total_chr, (int)Energy.active_power); + DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, return1_total_chr, return2_total_chr, (int)Energy.active_power[0]); if (Energy.voltage_available) { - DomoticzSensor(DZ_VOLTAGE, voltage_chr); // Voltage + DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage } if (Energy.current_available) { - DomoticzSensor(DZ_CURRENT, current_chr); // Current + DomoticzSensor(DZ_CURRENT, current_chr[0]); // Current } } #endif // USE_DOMOTICZ #ifdef USE_KNX if (show_energy_period) { if (Energy.voltage_available) { - KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage); + KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); } if (Energy.current_available) { - KnxSensor(KNX_ENERGY_CURRENT, Energy.current); + KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); + } + KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + if (!Energy.type_dc) { + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); } - KnxSensor(KNX_ENERGY_POWER, Energy.active_power); - if (!Energy.type_dc) { KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor); } KnxSensor(KNX_ENERGY_DAILY, Energy.daily); KnxSensor(KNX_ENERGY_TOTAL, Energy.total); KnxSensor(KNX_ENERGY_START, Energy.start_energy); @@ -913,18 +969,24 @@ void EnergyShow(bool json) #ifdef USE_WEBSERVER } else { if (Energy.voltage_available) { - WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"), voltage_chr); + WSContentSend_PD(PSTR("{s}" D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); } if (Energy.current_available) { - WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"), current_chr); + WSContentSend_PD(PSTR("{s}" D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}"), + EnergyFormat(value_chr, current_chr[0], json)); } - WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"), active_power_chr); + WSContentSend_PD(PSTR("{s}" D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"), + EnergyFormat(value_chr, active_power_chr[0], json)); if (!Energy.type_dc) { if (Energy.current_available && Energy.voltage_available) { - WSContentSend_PD(HTTP_ENERGY_SNS1, apparent_power_chr, reactive_power_chr, power_factor_chr); + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); } - if (!isnan(Energy.frequency)) { - WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), frequency_chr); + if (!isnan(Energy.frequency[0])) { + WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), + EnergyFormat(value_chr, frequency_chr[0], json)); } } WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr); diff --git a/sonoff/xdrv_16_tuyamcu.ino b/sonoff/xdrv_16_tuyamcu.ino index 6d9b26367a65..d849d47123a8 100644 --- a/sonoff/xdrv_16_tuyamcu.ino +++ b/sonoff/xdrv_16_tuyamcu.ino @@ -386,17 +386,17 @@ void TuyaPacketProcess(void) #ifdef USE_ENERGY_SENSOR else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { - Energy.voltage = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; + Energy.voltage[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { - Energy.current = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000; + Energy.current[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 1000; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { - Energy.active_power = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; + Energy.active_power[0] = (float)(Tuya.buffer[12] << 8 | Tuya.buffer[13]) / 10; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[6], (Tuya.buffer[12] << 8 | Tuya.buffer[13])); - if (Tuya.lastPowerCheckTime != 0 && Energy.active_power > 0) { - Energy.kWhtoday += (float)Energy.active_power * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; EnergyUpdateToday(); } Tuya.lastPowerCheckTime = Rtc.utc_time; diff --git a/sonoff/xnrg_01_hlw8012.ino b/sonoff/xnrg_01_hlw8012.ino index 7bf039999915..f2bd96734f18 100644 --- a/sonoff/xnrg_01_hlw8012.ino +++ b/sonoff/xnrg_01_hlw8012.ino @@ -128,13 +128,13 @@ void HlwEvery200ms(void) if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; // W *10 - Energy.active_power = (float)hlw_w / 10; + Energy.active_power[0] = (float)hlw_w / 10; Hlw.power_retry = 1; // Workaround issue #5161 } else { if (Hlw.power_retry) { Hlw.power_retry--; } else { - Energy.active_power = 0; + Energy.active_power[0] = 0; } } @@ -175,19 +175,19 @@ void HlwEvery200ms(void) if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { // If powered on always provide voltage hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; // V *10 - Energy.voltage = (float)hlw_u / 10; + Energy.voltage[0] = (float)hlw_u / 10; } else { - Energy.voltage = 0; + Energy.voltage[0] = 0; } } else { Hlw.cf1_current_pulse_length = cf1_pulse_length; - if (Hlw.cf1_current_pulse_length && Energy.active_power) { // No current if no power being consumed + if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { // No current if no power being consumed hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; // mA - Energy.current = (float)hlw_i / 1000; + Energy.current[0] = (float)hlw_i / 1000; } else { - Energy.current = 0; + Energy.current[0] = 0; } } diff --git a/sonoff/xnrg_02_cse7766.ino b/sonoff/xnrg_02_cse7766.ino index a8ab38916047..5fd927c28f25 100644 --- a/sonoff/xnrg_02_cse7766.ino +++ b/sonoff/xnrg_02_cse7766.ino @@ -52,8 +52,9 @@ struct CSE { void CseReceived(void) { // 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 + // F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load + // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = 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 - 55 = Ok, 71 = Ok // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck uint8_t header = serial_in_buffer[0]; @@ -93,19 +94,19 @@ void CseReceived(void) if (Energy.power_on) { // Powered on if (adjustement & 0x40) { // Voltage valid - Energy.voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; + Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; } if (adjustement & 0x10) { // Power valid Cse.power_invalid = 0; if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range - Energy.active_power = 0; + Energy.active_power[0] = 0; } else { if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } // Skip first incomplete Cse.power_cycle if (Cse.power_cycle_first != Cse.power_cycle) { Cse.power_cycle_first = -1; - Energy.active_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; + Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; } else { - Energy.active_power = 0; + Energy.active_power[0] = 0; } } } else { @@ -113,21 +114,21 @@ void CseReceived(void) Cse.power_invalid++; } else { Cse.power_cycle_first = 0; - Energy.active_power = 0; // Powered on but no load + Energy.active_power[0] = 0; // Powered on but no load } } if (adjustement & 0x20) { // Current valid - if (0 == Energy.active_power) { - Energy.current = 0; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; } else { - Energy.current = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; + Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; } } } else { // Powered off Cse.power_cycle_first = 0; - Energy.voltage = 0; - Energy.active_power = 0; - Energy.current = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; } } @@ -189,7 +190,7 @@ void CseEverySecond(void) } else { cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; } - if (cf_frequency && Energy.active_power) { + if (cf_frequency && Energy.active_power[0]) { unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; // prevent invalid load delta steps even checksum is valid (issue #5789): if (delta <= (3680*100/36) * 10 ) { // max load for S31/Pow R2: 3.68kW diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 1fc6e5e5efde..c0dff42dfc83 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -172,13 +172,13 @@ void PzemEvery200ms(void) Energy.data_valid = 0; switch (pzem_read_state) { case 1: // Voltage as 230.2V - Energy.voltage = value; + Energy.voltage[0] = value; break; case 2: // Current as 17.32A - Energy.current = value; + Energy.current[0] = value; break; case 3: // Power as 20W - Energy.active_power = value; + Energy.active_power[0] = value; break; case 4: // Total energy as 99999Wh EnergyUpdateTotal(value, false); diff --git a/sonoff/xnrg_04_mcp39f501.ino b/sonoff/xnrg_04_mcp39f501.ino index 01b9159541e1..462049b0dfd2 100644 --- a/sonoff/xnrg_04_mcp39f501.ino +++ b/sonoff/xnrg_04_mcp39f501.ino @@ -455,19 +455,19 @@ void McpParseData(void) mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); if (Energy.power_on) { // Powered on - Energy.frequency = (float)mcp_line_frequency / 1000; - Energy.voltage = (float)mcp_voltage_rms / 10; - Energy.active_power = (float)mcp_active_power / 100; - if (0 == Energy.active_power) { - Energy.current = 0; + Energy.frequency[0] = (float)mcp_line_frequency / 1000; + Energy.voltage[0] = (float)mcp_voltage_rms / 10; + Energy.active_power[0] = (float)mcp_active_power / 100; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; } else { - Energy.current = (float)mcp_current_rms / 10000; + Energy.current[0] = (float)mcp_current_rms / 10000; } } else { // Powered off - Energy.frequency = 0; - Energy.voltage = 0; - Energy.active_power = 0; - Energy.current = 0; + Energy.frequency[0] = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; } Energy.data_valid = 0; } diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 12b08eb8d98f..788838c5e5c1 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -56,11 +56,11 @@ void PzemAcEverySecond(void) // 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 // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - Energy.current = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - Energy.active_power = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - Energy.frequency = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - Energy.power_factor = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 + Energy.voltage[0] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current[0] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power[0] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency[0] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor[0] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh EnergyUpdateTotal(energy, false); diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 855d3645780a..9b9dd06a1122 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -56,9 +56,9 @@ void PzemDcEverySecond(void) // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- - Energy.voltage = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V - Energy.current = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - Energy.active_power = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W + Energy.voltage[0] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V + Energy.current[0] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A + Energy.active_power[0] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh EnergyUpdateTotal(energy, false); diff --git a/sonoff/xnrg_07_ade7953.ino b/sonoff/xnrg_07_ade7953.ino index dcb958f63846..4d2b3f3e428d 100644 --- a/sonoff/xnrg_07_ade7953.ino +++ b/sonoff/xnrg_07_ade7953.ino @@ -36,14 +36,22 @@ #define ADE7953_ADDR 0x38 +const uint8_t Ade7953Registers[] { + 0x1B, // RMS current channel B (Relay 1) + 0x13, // Active power channel B + 0x11, // Apparent power channel B + 0x15, // Reactive power channel B + 0x1A, // RMS current channel A (Relay 2) + 0x12, // Active power channel A + 0x10, // Apparent power channel A + 0x14, // Reactive power channel A + 0x1C // RMS voltage (Both relays) +}; + struct Ade7953 { - uint32_t active_power = 0; - uint32_t active_power1 = 0; - uint32_t active_power2 = 0; - uint32_t current_rms = 0; - uint32_t current_rms1 = 0; - uint32_t current_rms2 = 0; uint32_t voltage_rms = 0; + uint32_t current_rms[2] = { 0, 0 }; + uint32_t active_power[2] = { 0, 0 }; uint8_t init_step = 0; } Ade7953; @@ -80,7 +88,7 @@ void Ade7953Write(uint16_t reg, uint32_t val) } } -uint32_t Ade7953Read(uint16_t reg) +int32_t Ade7953Read(uint16_t reg) { uint32_t response = 0; @@ -109,48 +117,65 @@ void Ade7953Init(void) void Ade7953GetData(void) { - int32_t active_power; - - Ade7953.voltage_rms = Ade7953Read(0x31C); // Both relays - Ade7953.current_rms1 = Ade7953Read(0x31B); // Relay 1 - if (Ade7953.current_rms1 < 2000) { // No load threshold (20mA) - Ade7953.current_rms1 = 0; - Ade7953.active_power1 = 0; - } else { - active_power = (int32_t)Ade7953Read(0x313) * -1; // Relay 1 - Ade7953.active_power1 = (active_power > 0) ? active_power : 0; + int32_t reg[2][4]; + for (uint32_t i = 0; i < sizeof(Ade7953Registers); i++) { + int32_t value = Ade7953Read(0x300 + Ade7953Registers[i]); + if (8 == i) { + Ade7953.voltage_rms = value; // RMS voltage (Both relays) + } else { + reg[i >> 2][i &3] = value; + } } - Ade7953.current_rms2 = Ade7953Read(0x31A); // Relay 2 - if (Ade7953.current_rms2 < 2000) { // No load threshold (20mA) - Ade7953.current_rms2 = 0; - Ade7953.active_power2 = 0; - } else { - active_power = (int32_t)Ade7953Read(0x312); // Relay 2 - Ade7953.active_power2 = (active_power > 0) ? active_power : 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + Ade7953.voltage_rms, reg[0][0], reg[0][1], reg[0][2], reg[0][3], + reg[1][0], reg[1][1], reg[1][2], reg[1][3]); + + uint32_t apparent_power[2] = { 0, 0 }; + uint32_t reactive_power[2] = { 0, 0 }; + + for (uint32_t channel = 0; channel < 2; channel++) { + Ade7953.current_rms[channel] = reg[channel][0]; + if (Ade7953.current_rms[channel] < 2000) { // No load threshold (20mA) + Ade7953.current_rms[channel] = 0; + Ade7953.active_power[channel] = 0; + } else { + Ade7953.active_power[channel] = abs(reg[channel][1]); + apparent_power[channel] = abs(reg[channel][2]); + reactive_power[channel] = abs(reg[channel][3]); + } } - // First phase only supports accumulated Current and Power - Ade7953.current_rms = Ade7953.current_rms1 + Ade7953.current_rms2; - Ade7953.active_power = Ade7953.active_power1 + Ade7953.active_power2; + + uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; + uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, I %d + %d = %d, P %d + %d = %d"), - Ade7953.voltage_rms, Ade7953.current_rms1, Ade7953.current_rms2, Ade7953.current_rms, Ade7953.active_power1, Ade7953.active_power2, Ade7953.active_power); + Ade7953.voltage_rms, Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); if (Energy.power_on) { // Powered on - Energy.voltage = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; - Energy.active_power = (float)Ade7953.active_power / (Settings.energy_power_calibration / 10); - if (0 == Energy.active_power) { - Energy.current = 0; - } else { - Energy.current = (float)Ade7953.current_rms / (Settings.energy_current_calibration * 10); + Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; + + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); + Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); + Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); + if (0 == Energy.active_power[channel]) { + Energy.current[channel] = 0; + } else { + Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); + } } } else { // Powered off - Energy.voltage = 0; - Energy.active_power = 0; - Energy.current = 0; + Energy.voltage[0] = 0; + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.current[channel] = 0; + Energy.active_power[channel] = 0; + Energy.reactive_power[channel] = 0; + Energy.apparent_power[channel] = 0; + } } - if (Ade7953.active_power) { - Energy.kWhtoday_delta += ((Ade7953.active_power * (100000 / (Settings.energy_power_calibration / 10))) / 3600); + if (active_power_sum) { + Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); EnergyUpdateToday(); } } @@ -179,6 +204,10 @@ void Ade7953DrvInit(void) } AddLog_P2(LOG_LEVEL_DEBUG, S_LOG_I2C_FOUND_AT, "ADE7953", ADE7953_ADDR); Ade7953.init_step = 2; + + Energy.phase_count = 2; // Handle two channels as two phases + Energy.voltage_common = true; // Use common voltage + energy_flg = XNRG_07; } } @@ -188,6 +217,7 @@ bool Ade7953Command(void) { bool serviced = true; + uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); // 1.23 = 123 if (CMND_POWERCAL == Energy.command_code) { @@ -203,9 +233,9 @@ bool Ade7953Command(void) // Service in xdrv_03_energy.ino } else if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.active_power) { + if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { if ((value > 100) && (value < 200000)) { // Between 1W and 2000W - Settings.energy_power_calibration = (Ade7953.active_power * 1000) / value; // 0.00 W + Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; // 0.00 W } } } @@ -217,9 +247,9 @@ bool Ade7953Command(void) } } else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.current_rms) { + if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { if ((value > 2000) && (value < 1000000)) { // Between 20mA and 10A - Settings.energy_current_calibration = ((Ade7953.current_rms * 100) / value) * 100; // 0.00 mA + Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; // 0.00 mA } } } diff --git a/sonoff/xnrg_08_sdm120.ino b/sonoff/xnrg_08_sdm120.ino index c7644b740d16..592edc2f81bd 100644 --- a/sonoff/xnrg_08_sdm120.ino +++ b/sonoff/xnrg_08_sdm120.ino @@ -98,31 +98,31 @@ void SDM120Every250ms(void) switch(Sdm120.read_state) { case 0: - Energy.voltage = value; // 230.2 V + Energy.voltage[0] = value; // 230.2 V break; case 1: - Energy.current = value; // 1.260 A + Energy.current[0] = value; // 1.260 A break; case 2: - Energy.active_power = value; // -196.3 W + Energy.active_power[0] = value; // -196.3 W break; case 3: - Energy.apparent_power = value; // 223.4 VA + Energy.apparent_power[0] = value; // 223.4 VA break; case 4: - Energy.reactive_power = value; // 92.2 + Energy.reactive_power[0] = value; // 92.2 break; case 5: - Energy.power_factor = value; // -0.91 + Energy.power_factor[0] = value; // -0.91 break; case 6: - Energy.frequency = value; // 50.0 Hz + Energy.frequency[0] = value; // 50.0 Hz break; case 7: diff --git a/sonoff/xnrg_09_dds2382.ino b/sonoff/xnrg_09_dds2382.ino index 668f21358eaf..c2ae027463b1 100644 --- a/sonoff/xnrg_09_dds2382.ino +++ b/sonoff/xnrg_09_dds2382.ino @@ -57,12 +57,12 @@ void Dds2382EverySecond(void) // 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 // SA FC BC EnergyTotal ExportActiv ImportActiv Volta Curre APowe RPowe PFact Frequ Crc-- - Energy.voltage = (float)((buffer[27] << 8) + buffer[28]) / 10.0; - Energy.current = (float)((buffer[29] << 8) + buffer[30]) / 100.0; - Energy.active_power = (float)((buffer[31] << 8) + buffer[32]); - Energy.reactive_power = (float)((buffer[33] << 8) + buffer[34]); - Energy.power_factor = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; // 1.00 - Energy.frequency = (float)((buffer[37] << 8) + buffer[38]) / 100.0; // 50.0 Hz + Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; // 1.00 + Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; // 50.0 Hz Energy.export_active = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[13] << 8) + buffer[14]) / 100.0; // 429496729.0 W // float energy_total = (float)((buffer[3] << 24) + (buffer[4] << 16) + (buffer[5] << 8) + buffer[6]) / 100.0; // 429496729.0 W float import_active = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[17] << 8) + buffer[18]) / 100.0; // 429496729.0 W From 098a2b27c3ec034bf30d4407e87db134dd311189 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 15:19:19 +0200 Subject: [PATCH 27/44] Add initial support for up to three PZEM-014/-016 Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) --- sonoff/_changelog.ino | 3 ++- sonoff/xnrg_05_pzem_ac.ino | 46 ++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 176c5c08addd..f976b5471bfa 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -7,7 +7,8 @@ * Change JSON output format for commands Adc, Adcs, Modules, Gpio and Gpios from list to dictionary (#6407) * Add Zigbee support phase 3 - support for Xiaomi lumi.weather air quality sensor, Osram mini-switch * Change energy sensors for three phase/channel support - * Add Shelly 2.5 energy dual channel support (#6160) + * Add support for Shelly 2.5 dual energy (#6160) + * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 788838c5e5c1..897ef7e36ede 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -36,7 +36,11 @@ #include TasmotaModbus *PzemAcModbus; -uint8_t PzemAc_send_retry = 0; +struct PZEMAC { + float energy = 0; + uint8_t send_retry = 0; + uint8_t phase = 0; +} PzemAc; void PzemAcEverySecond(void) { @@ -49,30 +53,40 @@ void PzemAcEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemAc response error %d"), error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d response error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { Energy.data_valid = 0; // 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 // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - Energy.voltage[0] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - Energy.current[0] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - Energy.active_power[0] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - Energy.frequency[0] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - Energy.power_factor[0] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - float energy = (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh - - EnergyUpdateTotal(energy, false); + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 + + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + if (PzemAc.phase == Energy.phase_count -1) { + EnergyUpdateTotal(PzemAc.energy, false); + PzemAc.energy = 0; + } } } - if (0 == PzemAc_send_retry || data_ready) { - PzemAc_send_retry = 5; - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS, 0x04, 0, 10); + if (0 == PzemAc.send_retry || data_ready) { + PzemAc.phase++; + if (PzemAc.phase >= Energy.phase_count) { + PzemAc.phase = 0; + } + PzemAc.send_retry = ENERGY_WATCHDOG; + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); } else { - PzemAc_send_retry--; + PzemAc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry)) { + Energy.phase_count--; // Decrement phases if no response after retry + } } } @@ -82,6 +96,10 @@ void PzemAcSnsInit(void) uint8_t result = PzemAcModbus->Begin(9600); if (result) { if (2 == result) { ClaimSerial(); } + + Energy.phase_count = 3; // Start off with three phases + PzemAc.phase = 2; + } else { energy_flg = ENERGY_NONE; } From 7e192cf624f4b220898c292fb0f2320629a183c3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 15:40:18 +0200 Subject: [PATCH 28/44] Add initial support for up to three PZEM-004T Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) --- sonoff/_changelog.ino | 1 + sonoff/xnrg_03_pzem004t.ino | 62 ++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index f976b5471bfa..43b0a4bfad1b 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -9,6 +9,7 @@ * Change energy sensors for three phase/channel support * Add support for Shelly 2.5 dual energy (#6160) * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) + * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index c0dff42dfc83..31d34ffb421d 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -56,6 +56,13 @@ TasmotaSerial *PzemSerial = nullptr; /*********************************************************************************************/ +struct PZEM { + float energy = 0; + uint8_t send_retry = 0; + uint8_t read_state = 0; + uint8_t phase = 0; +} Pzem; + struct PZEMCommand { uint8_t command; uint8_t addr[4]; @@ -68,7 +75,9 @@ IPAddress pzem_ip(192, 168, 1, 1); uint8_t PzemCrc(uint8_t *data) { uint16_t crc = 0; - for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) crc += *data++; + for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; + } return (uint8_t)(crc & 0xFF); } @@ -77,7 +86,10 @@ void PzemSend(uint8_t cmd) PZEMCommand pzem; pzem.command = cmd; - for (uint32_t i = 0; i < sizeof(pzem.addr); i++) pzem.addr[i] = pzem_ip[i]; + for (uint32_t i = 0; i < sizeof(pzem.addr) -1; i++) { + pzem.addr[i] = pzem_ip[i]; + } + pzem.addr[3] = pzem_ip[3] + Pzem.phase; pzem.data = 0; uint8_t *bytes = (uint8_t*)&pzem; @@ -159,42 +171,52 @@ bool PzemRecieve(uint8_t resp, float *data) const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; -uint8_t pzem_read_state = 0; -uint8_t pzem_sendRetry = 0; - void PzemEvery200ms(void) { bool data_ready = PzemReceiveReady(); if (data_ready) { float value = 0; - if (PzemRecieve(pzem_responses[pzem_read_state], &value)) { + if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { Energy.data_valid = 0; - switch (pzem_read_state) { + switch (Pzem.read_state) { case 1: // Voltage as 230.2V - Energy.voltage[0] = value; + Energy.voltage[Pzem.phase] = value; break; case 2: // Current as 17.32A - Energy.current[0] = value; + Energy.current[Pzem.phase] = value; break; case 3: // Power as 20W - Energy.active_power[0] = value; + Energy.active_power[Pzem.phase] = value; break; case 4: // Total energy as 99999Wh - EnergyUpdateTotal(value, false); + Pzem.energy += value; + if (Pzem.phase == Energy.phase_count -1) { + EnergyUpdateTotal(Pzem.energy, false); + Pzem.energy = 0; + } break; } - pzem_read_state++; - if (5 == pzem_read_state) pzem_read_state = 1; + Pzem.read_state++; + if (5 == Pzem.read_state) { + Pzem.read_state = 1; + } } } - if (0 == pzem_sendRetry || data_ready) { - pzem_sendRetry = 5; - PzemSend(pzem_commands[pzem_read_state]); + if (0 == Pzem.send_retry || data_ready) { + Pzem.phase++; + if (Pzem.phase >= Energy.phase_count) { + Pzem.phase = 0; + } + Pzem.send_retry = 5; + PzemSend(pzem_commands[Pzem.read_state]); } else { - pzem_sendRetry--; + Pzem.send_retry--; + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry)) { + Energy.phase_count--; // Decrement phases if no response after retry + } } } @@ -203,7 +225,11 @@ void PzemSnsInit(void) // Software serial init needs to be done here as earlier (serial) interrupts may lead to Exceptions PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); if (PzemSerial->begin(9600)) { - if (PzemSerial->hardwareSerial()) { ClaimSerial(); } + if (PzemSerial->hardwareSerial()) { + ClaimSerial(); + } + Energy.phase_count = 3; // Start off with three phases + Pzem.phase = 2; } else { energy_flg = ENERGY_NONE; } From 36d1f51a8211e80829631e5b43ae63b29ffd0f4c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 16:02:00 +0200 Subject: [PATCH 29/44] Add initial support for up to three PZEM-003/-017 Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) --- sonoff/_changelog.ino | 1 + sonoff/xnrg_03_pzem004t.ino | 4 ++-- sonoff/xnrg_05_pzem_ac.ino | 8 +++---- sonoff/xnrg_06_pzem_dc.ino | 42 +++++++++++++++++++++++++------------ 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 43b0a4bfad1b..ce32138d4460 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -10,6 +10,7 @@ * Add support for Shelly 2.5 dual energy (#6160) * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) + * Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 31d34ffb421d..5e1d3ceb686c 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -214,8 +214,8 @@ void PzemEvery200ms(void) } else { Pzem.send_retry--; - if ((Energy.phase_count > 1) && (0 == Pzem.send_retry)) { - Energy.phase_count--; // Decrement phases if no response after retry + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < 30)) { + Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart } } } diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 897ef7e36ede..54fe8a0791c7 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -53,7 +53,7 @@ void PzemAcEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d response error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { Energy.data_valid = 0; @@ -84,8 +84,8 @@ void PzemAcEverySecond(void) } else { PzemAc.send_retry--; - if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry)) { - Energy.phase_count--; // Decrement phases if no response after retry + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < 30)) { + Energy.phase_count--; // Decrement phases if no response after retry within 30 seconds after restart } } } @@ -96,10 +96,8 @@ void PzemAcSnsInit(void) uint8_t result = PzemAcModbus->Begin(9600); if (result) { if (2 == result) { ClaimSerial(); } - Energy.phase_count = 3; // Start off with three phases PzemAc.phase = 2; - } else { energy_flg = ENERGY_NONE; } diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 9b9dd06a1122..5188c051eb64 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -36,10 +36,14 @@ #include TasmotaModbus *PzemDcModbus; +struct PZEMDC { + float energy = 0; + uint8_t send_retry = 0; + uint8_t channel = 0; +} PzemDc; + void PzemDcEverySecond(void) { - static uint8_t send_retry = 0; - bool data_ready = PzemDcModbus->ReceiveReady(); if (data_ready) { @@ -49,28 +53,38 @@ void PzemDcEverySecond(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "PzemDc response error %d"), error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); } else { Energy.data_valid = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- - Energy.voltage[0] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V - Energy.current[0] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - Energy.active_power[0] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - float energy = (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh - - EnergyUpdateTotal(energy, false); + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W + + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + if (PzemDc.channel == Energy.phase_count -1) { + EnergyUpdateTotal(PzemDc.energy, false); + PzemDc.energy = 0; + } } } - if (0 == send_retry || data_ready) { - send_retry = 5; - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS, 0x04, 0, 8); + if (0 == PzemDc.send_retry || data_ready) { + PzemDc.channel++; + if (PzemDc.channel >= Energy.phase_count) { + PzemDc.channel = 0; + } + PzemDc.send_retry = ENERGY_WATCHDOG; + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); } else { - send_retry--; + PzemDc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < 30)) { + Energy.phase_count--; // Decrement channels if no response after retry within 30 seconds after restart + } } } @@ -81,6 +95,8 @@ void PzemDcSnsInit(void) if (result) { if (2 == result) { ClaimSerial(); } Energy.type_dc = true; + Energy.phase_count = 3; // Start off with three channels + PzemDc.channel = 2; } else { energy_flg = ENERGY_NONE; } From 26361bd8c3af61748156c788a8c8923cc496d2cc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 17:06:23 +0200 Subject: [PATCH 30/44] Add driver USE_SDM630_2 Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report --- sonoff/_changelog.ino | 1 + sonoff/my_user_config.h | 4 +- sonoff/sonoff_post.h | 9 +- sonoff/xnrg_08_sdm120.ino | 2 +- sonoff/xnrg_10_sdm630.ino | 214 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 sonoff/xnrg_10_sdm630.ino diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index ce32138d4460..34b6681a2107 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -11,6 +11,7 @@ * Add initial support for up to three PZEM-014/-016 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) + * Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 8507cd662fa6..17954ac652c3 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -433,6 +433,8 @@ #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) //#define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) +//#define USE_SDM630_2 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code) + #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) //#define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #define DDS2382_SPEED 9600 // Hiking DDS2382 Modbus RS485 serial speed (default: 9600 baud) @@ -440,7 +442,7 @@ // #define SDM120_SPEED 2400 // SDM120-Modbus RS485 serial speed (default: 2400 baud) #define USE_SDM220 // Add extra parameters for SDM220 (+0k1 code) //#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 SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) //#define USE_SOLAX_X1 // Add support for Solax X1 series Modbus log info (+4k1 code) #define SOLAXX1_SPEED 9600 // Solax X1 Modbus RS485 serial speed (default: 9600 baud) #define SOLAXX1_PV2 // Solax X1 using second PV diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 802d8ad25c05..dacdf3b4b48e 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -149,7 +149,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) //#define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) -#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) +//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) #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 USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger @@ -162,6 +162,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) #define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #define USE_SDM120_2 // Add support for Eastron SDM120-Modbus energy monitor (+1k1 code) +#define USE_SDM630_2 // Add support for Eastron SDM630-Modbus energy monitor (+0k6 code) #define USE_DDS2382 // Add support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor @@ -247,6 +248,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor @@ -300,6 +302,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter + #undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #undef USE_EMULATION // Disable Belkin WeMo and Hue Bridge emulation for Alexa (-16k code, -2k mem) #undef USE_DOMOTICZ // Disable Domoticz @@ -382,6 +385,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) //#define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor @@ -478,11 +482,11 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor //#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor #undef USE_MAX31855 // Disable MAX31855 K-Type thermocouple sensor using softSPI -#undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -563,6 +567,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c #undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor #undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_SDM120_2 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630_2 // Disable support for Eastron SDM630-Modbus energy monitor (+0k6 code) #undef USE_DDS2382 // Disable support for Hiking DDS2382 Modbus energy monitor (+0k6 code) #undef USE_DHT // Disable support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor diff --git a/sonoff/xnrg_08_sdm120.ino b/sonoff/xnrg_08_sdm120.ino index 592edc2f81bd..9b1cf6cbc669 100644 --- a/sonoff/xnrg_08_sdm120.ino +++ b/sonoff/xnrg_08_sdm120.ino @@ -83,7 +83,7 @@ void SDM120Every250ms(void) AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SDM120 response error %d"), error); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); } else { Energy.data_valid = 0; diff --git a/sonoff/xnrg_10_sdm630.ino b/sonoff/xnrg_10_sdm630.ino new file mode 100644 index 000000000000..5bdef0c9efa5 --- /dev/null +++ b/sonoff/xnrg_10_sdm630.ino @@ -0,0 +1,214 @@ +/* + xnrg_10_sdm630.ino - Eastron SDM630-Modbus energy meter support for Sonoff-Tasmota + + Copyright (C) 2019 Gennaro Tortone 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_ENERGY_SENSOR +#ifdef USE_SDM630_2 +/*********************************************************************************************\ + * Eastron SDM630-Modbus energy meter + * + * Based on: /~https://github.com/reaper7/SDM_Energy_Meter +\*********************************************************************************************/ + +#define XNRG_10 10 + +// can be user defined in my_user_config.h +#ifndef SDM630_SPEED + #define SDM630_SPEED 9600 // default SDM630 Modbus address +#endif +// can be user defined in my_user_config.h +#ifndef SDM630_ADDR + #define SDM630_ADDR 1 // default SDM630 Modbus address +#endif + +#include +TasmotaModbus *Sdm630Modbus; + +const uint16_t sdm630_start_addresses[] { + 0x0000, // L1 - SDM630_VOLTAGE [V] + 0x0002, // L2 - SDM630_VOLTAGE [V] + 0x0004, // L3 - SDM630_VOLTAGE [V] + 0x0006, // L1 - SDM630_CURRENT [A] + 0x0008, // L2 - SDM630_CURRENT [A] + 0x000A, // L3 - SDM630_CURRENT [A] + 0x000C, // L1 - SDM630_POWER [W] + 0x000E, // L2 - SDM630_POWER [W] + 0x0010, // L3 - SDM630_POWER [W] + 0x0018, // L1 - SDM630_REACTIVE_POWER [VAR] + 0x001A, // L2 - SDM630_REACTIVE_POWER [VAR] + 0x001C, // L3 - SDM630_REACTIVE_POWER [VAR] + 0x001E, // L1 - SDM630_POWER_FACTOR + 0x0020, // L2 - SDM630_POWER_FACTOR + 0x0022, // L3 - SDM630_POWER_FACTOR + 0x0156 // Total - SDM630_TOTAL_ACTIVE_ENERGY [Wh] +}; + +struct SDM630 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Sdm630; + +/*********************************************************************************************/ + +void SDM630Every250ms(void) +{ + bool data_ready = Sdm630Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 + + uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + } else { + Energy.data_valid = 0; + + // 0 1 2 3 4 5 6 7 8 + // SA FC BC Fh Fl Sh Sl Cl Ch + // 01 04 04 43 66 33 34 1B 38 = 230.2 Volt + float value; + ((uint8_t*)&value)[3] = buffer[3]; // Get float values + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm630.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + EnergyUpdateTotal(value, true); + break; + } + + Sdm630.read_state++; + if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { + Sdm630.read_state = 0; + } + } + } // end data ready + + if (0 == Sdm630.send_retry || data_ready) { + Sdm630.send_retry = 5; + Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); + } else { + Sdm630.send_retry--; + } +} + +void Sdm630SnsInit(void) +{ + Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm630DrvInit(void) +{ + if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + energy_flg = XNRG_10; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM630Every250ms(); } + break; + case FUNC_INIT: + Sdm630SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm630DrvInit(); + break; + } + return result; +} + +#endif // USE_SDM630_2 +#endif // USE_ENERGY_SENSOR From a964bd0a9c4d6e09790d8c719d7a18c3503d2c79 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 15 Sep 2019 18:05:23 +0200 Subject: [PATCH 31/44] Add Energy Tariff info to JSON message Add Energy Tariff info to JSON TOTAL energy message (#6429) --- sonoff/xdrv_03_energy.ino | 40 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 1ad177b1f507..60f312917c8f 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -784,10 +784,8 @@ const char HTTP_ENERGY_SNS3[] PROGMEM = "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; #endif // USE_WEBSERVER -char* EnergyFormat(char* result, char* input, bool json, bool single = false) +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) { - uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 - char layout[16]; GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); switch (index) { @@ -803,6 +801,12 @@ char* EnergyFormat(char* result, char* input, bool json, bool single = false) return result; } +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; // 1,2,3 + return EnergyFormatIndex(result, input, json, index, single); +} + void EnergyShow(bool json) { for (uint32_t i = 0; i < Energy.phase_count; i++) { @@ -875,8 +879,14 @@ void EnergyShow(bool json) dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); char energy_yesterday_chr[FLOATSZ]; dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - char energy_total_chr[FLOATSZ]; - dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_total_chr[3][FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); + uint8_t energy_total_fields = 1; + if (Settings.register8[R8_ENERGY_TARIFF1_ST] != Settings.register8[R8_ENERGY_TARIFF2_ST]) { + dtostrfd(Energy.total1, Settings.flag2.energy_resolution, energy_total_chr[1]); // Tariff1 + dtostrfd(Energy.total - Energy.total1, Settings.flag2.energy_resolution, energy_total_chr[2]); // Tariff2 + energy_total_fields = 3; + } char export_active_chr[FLOATSZ]; dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr); @@ -888,10 +898,15 @@ void EnergyShow(bool json) bool show_energy_period = (0 == tele_period); ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), - GetDateAndTime(DT_ENERGY).c_str(), energy_total_chr, energy_yesterday_chr, energy_daily_chr); + GetDateAndTime(DT_ENERGY).c_str(), + EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), + energy_yesterday_chr, + energy_daily_chr); + if (!isnan(Energy.export_active)) { ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), export_active_chr); } + if (show_energy_period) { float energy = 0; if (Energy.period) { @@ -929,17 +944,16 @@ void EnergyShow(bool json) #ifdef USE_DOMOTICZ if (show_energy_period) { // Only send if telemetry - dtostrfd(Energy.total * 1000, 1, energy_total_chr); - DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr); // PowerUsage, EnergyToday + dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); // PowerUsage, EnergyToday - dtostrfd((Energy.total - Energy.total1) * 1000, 1, energy_total_chr); // Tariff2 - char energy_total1_chr[FLOATSZ]; - dtostrfd(Energy.total1 * 1000, 1, energy_total1_chr); // Tariff1 + dtostrfd(Energy.total1 * 1000, 1, energy_total_chr[1]); // Tariff1 + dtostrfd((Energy.total - Energy.total1) * 1000, 1, energy_total_chr[2]); // Tariff2 char return1_total_chr[FLOATSZ]; dtostrfd(RtcSettings.energy_usage.return1_kWhtotal, 1, return1_total_chr); char return2_total_chr[FLOATSZ]; dtostrfd(RtcSettings.energy_usage.return2_kWhtotal, 1, return2_total_chr); - DomoticzSensorP1SmartMeter(energy_total1_chr, energy_total_chr, return1_total_chr, return2_total_chr, (int)Energy.active_power[0]); + DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], return1_total_chr, return2_total_chr, (int)Energy.active_power[0]); if (Energy.voltage_available) { DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); // Voltage @@ -989,7 +1003,7 @@ void EnergyShow(bool json) EnergyFormat(value_chr, frequency_chr[0], json)); } } - WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr); + WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); if (!isnan(Energy.export_active)) { WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr); } From 7e5b9eb810a57acb1b9b3d14759cd46b2cdfcd56 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 10:14:09 +0200 Subject: [PATCH 32/44] Fix SDM630 template Fix SDM630 template --- sonoff/sonoff_template.h | 379 ++++++++------------------------------- 1 file changed, 77 insertions(+), 302 deletions(-) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 64e62db7175e..0ab4d0752297 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -620,6 +620,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDM120_TX, // SDM120 Serial interface GPIO_SDM120_RX, // SDM120 Serial interface #endif +#ifdef USE_SDM630_2 + GPIO_SDM630_TX, // SDM630 Serial interface + GPIO_SDM630_RX, // SDM630 Serial interface +#endif #ifdef USE_DDS2382 GPIO_DDS2382_TX, // DDS2382 Serial interface GPIO_DDS2382_RX, // DDS2382 Serial interface @@ -631,10 +635,12 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SDM120_RX, // SDM120 Serial interface #endif #endif // USE_SDM120_2 +#ifndef USE_SDM630_2 #ifdef USE_SDM630 GPIO_SDM630_TX, // SDM630 Serial interface GPIO_SDM630_RX, // SDM630 Serial interface #endif +#endif // USE_SDM630_2 #ifdef USE_SOLAX_X1 GPIO_SOLAXX1_TX, // Solax Inverter tx pin GPIO_SOLAXX1_RX, // Solax Inverter rx pin @@ -802,7 +808,7 @@ const uint8_t kModuleNiceList[] PROGMEM = { // Default module settings const mytmplt kModules[MAXMODULE] PROGMEM = { - { "Sonoff Basic", // Sonoff Basic (ESP8266) + { "Sonoff Basic", // SONOFF_BASIC - Sonoff Basic (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Only available on newer Sonoff Basic R2 V1 @@ -822,7 +828,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO16 0 // ADC0 Analog input }, - { "Sonoff RF", // Sonoff RF (ESP8266) + { "Sonoff RF", // SONOFF_RF - Sonoff RF (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -840,7 +846,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff SV", // Sonoff SV (ESP8266) + { "Sonoff SV", // SONOFF_SV - Sonoff SV (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -859,7 +865,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, ADC0_USER // ADC0 Analog input }, - { "Sonoff TH", // Sonoff TH10/16 (ESP8266) + { "Sonoff TH", // SONOFF_TH - Sonoff TH10/16 (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -877,7 +883,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff Dual", // Sonoff Dual (ESP8266) + { "Sonoff Dual", // SONOFF_DUAL - Sonoff Dual (ESP8266) 0, GPIO_TXD, // GPIO01 Relay control 0, @@ -895,7 +901,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff Pow", // Sonoff Pow (ESP8266 - HLW8012) + { "Sonoff Pow", // SONOFF_POW - Sonoff Pow (ESP8266 - HLW8012) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, GPIO_NRG_SEL, // GPIO05 HLW8012 Sel output (1 = Voltage) @@ -911,7 +917,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO15 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0 }, - { "Sonoff 4CH", // Sonoff 4CH (ESP8285) + { "Sonoff 4CH", // SONOFF_4CH - Sonoff 4CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -930,7 +936,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 0, 0 }, - { "Sonoff S2X", // Sonoff S20, S22 and S26 Smart Socket (ESP8266) + { "Sonoff S2X", // SONOFF_S2X - Sonoff S20, S22 and S26 Smart Socket (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -946,7 +952,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green/Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, - { "Slampher", // Slampher (ESP8266) + { "Slampher", // SLAMPHER - Slampher (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -962,7 +968,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff Touch", // Sonoff Touch (ESP8285) + { "Sonoff Touch", // SONOFF_TOUCH - Sonoff Touch (ESP8285) GPIO_KEY1, // GPIO00 Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -978,7 +984,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) 0, 0, 0, 0 }, - { "Sonoff LED", // Sonoff LED (ESP8266) + { "Sonoff LED", // SONOFF_LED - Sonoff LED (ESP8266) GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_USER, // GPIO04 Optional sensor (PWM3 Green) @@ -995,7 +1001,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO15 Optional sensor (PWM4 Blue) 0, 0 }, - { "1 Channel", // 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) + { "1 Channel", // CH1 - 1 Channel Inching/Latching Relay using (PSA-B01 - ESP8266 and PSF-B01 - ESP8285) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) @@ -1008,7 +1014,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "4 Channel", // 4 Channel Inching/Latching Relays (ESP8266) + { "4 Channel", // CH4 - 4 Channel Inching/Latching Relays (ESP8266) 0, GPIO_TXD, // GPIO01 Relay control 0, @@ -1024,7 +1030,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Motor C/AC", // Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) + { "Motor C/AC", // MOTOR - Motor Clockwise / Anti clockwise (PSA-B01 - ESP8266) GPIO_KEY1, // GPIO00 Button 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) @@ -1037,7 +1043,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "ElectroDragon", // ElectroDragon IoT Relay Board (ESP8266) + { "ElectroDragon", // ELECTRODRAGON - ElectroDragon IoT Relay Board (ESP8266) GPIO_KEY2, // GPIO00 Button 2 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_KEY1, // GPIO02 Button 1 @@ -1057,7 +1063,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) - Link and Power status ADC0_USER // ADC0 A0 Analog input }, - { "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266) + { "EXS Relay(s)", // EXS_RELAY - ES-Store Latching relay(s) (ESP8266) // https://ex-store.de/ESP8266-WiFi-Relay-V31 // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage @@ -1080,7 +1086,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) 0 }, - { "WiOn", // Indoor Tap (ESP8266) + { "WiOn", // WION - Indoor Tap (ESP8266) // https://www.amazon.com/gp/product/B00ZYLUBJU/ref=s9_acsd_al_bw_c_x_3_w GPIO_USER, // GPIO00 Optional sensor (pm clock) 0, @@ -1098,7 +1104,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "Generic", // Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) + { "Generic", // WEMOS - Any ESP8266/ESP8285 device like WeMos and NodeMCU hardware (ESP8266) GPIO_USER, // GPIO00 D3 Wemos Button Shield GPIO_USER, // GPIO01 TX Serial RXD GPIO_USER, // GPIO02 D4 Wemos DHT Shield @@ -1118,7 +1124,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 D0 Wemos Wake ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff Dev", // Sonoff Dev (ESP8266) + { "Sonoff Dev", // SONOFF_DEV - Sonoff Dev (ESP8266) GPIO_KEY1, // GPIO00 E-FW Button GPIO_USER, // GPIO01 TX Serial RXD and Optional sensor 0, // GPIO02 @@ -1138,7 +1144,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, // GPIO16 ADC0_USER // ADC0 A0 Analog input }, - { "H801", // Lixada H801 Wifi (ESP8266) + { "H801", // H801 - Lixada H801 Wifi (ESP8266) GPIO_USER, // GPIO00 E-FW Button GPIO_LED1, // GPIO01 Green LED - Link and Power status GPIO_USER, // GPIO02 TX and Optional sensor - Pin next to TX on the PCB @@ -1157,7 +1163,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO15 Red 0, 0 }, - { "Sonoff SC", // Sonoff SC (ESP8266) + { "Sonoff SC", // SONOFF_SC - onoff SC (ESP8266) GPIO_KEY1, // GPIO00 Button GPIO_TXD, // GPIO01 RXD to ATMEGA328P GPIO_USER, // GPIO02 Optional sensor @@ -1173,7 +1179,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff BN-SZ", // Sonoff BN-SZ01 Ceiling led (ESP8285) + { "Sonoff BN-SZ", // SONOFF_BN - Sonoff BN-SZ01 Ceiling led (ESP8285) 0, 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) @@ -1185,7 +1191,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff 4CH Pro", // Sonoff 4CH Pro (ESP8285) + { "Sonoff 4CH Pro", // SONOFF_4CHPRO - Sonoff 4CH Pro (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional sensor @@ -1204,7 +1210,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 Red Led and Relay 4 (0 = Off, 1 = On) 0, 0 }, - { "Huafan SS", // Hua Fan Smart Socket (ESP8266) - like Sonoff Pow + { "Huafan SS", // HUAFAN_SS - Hua Fan Smart Socket (ESP8266) - like Sonoff Pow GPIO_LEDLNK_INV, // GPIO00 Blue Led (0 = On, 1 = Off) - Link status 0, 0, GPIO_LED1_INV, // GPIO03 Red Led (0 = On, 1 = Off) - Power status @@ -1221,7 +1227,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_HLW_CF, // GPIO14 HLW8012 CF power 0, 0, 0 }, - { "Sonoff Bridge", // Sonoff RF Bridge 433 (ESP8285) + { "Sonoff Bridge", // SONOFF_BRIDGE - Sonoff RF Bridge 433 (ESP8285) GPIO_KEY1, // GPIO00 Button GPIO_TXD, // GPIO01 RF bridge control GPIO_USER, // GPIO02 Optional sensor @@ -1239,7 +1245,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 Optional sensor 0, 0, 0 }, - { "Sonoff B1", // Sonoff B1 (ESP8285 - my9231) + { "Sonoff B1", // SONOFF_B1 - Sonoff B1 (ESP8285 - my9231) GPIO_KEY1, // GPIO00 Pad GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad GPIO_USER, // GPIO02 Optional sensor SDA pad @@ -1256,7 +1262,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_DCKI, // GPIO14 my9231 DCKI 0, 0, 0 }, - { "AiLight", // Ai-Thinker RGBW led (ESP8266 - my9291) + { "AiLight", // AILIGHT - Ai-Thinker RGBW led (ESP8266 - my9291) GPIO_KEY1, // GPIO00 Pad GPIO_USER, // GPIO01 Serial RXD and Optional sensor pad GPIO_USER, // GPIO02 Optional sensor SDA pad @@ -1274,7 +1280,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_DCKI, // GPIO15 my9291 DCKI 0, 0 }, - { "Sonoff T1 1CH", // Sonoff T1 1CH (ESP8285) + { "Sonoff T1 1CH", // SONOFF_T11 - Sonoff T1 1CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1290,7 +1296,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff T1 2CH", // Sonoff T1 2CH (ESP8285) + { "Sonoff T1 2CH", // SONOFF_T12 - Sonoff T1 2CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1307,7 +1313,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff T1 3CH", // Sonoff T1 3CH (ESP8285) + { "Sonoff T1 3CH", // SONOFF_T13 - Sonoff T1 3CH (ESP8285) GPIO_KEY1, // GPIO00 Button 1 GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_USER, // GPIO02 Optional Sensor (J3 Pin 5) @@ -1324,7 +1330,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Supla Espablo", // Supla Espablo (ESP8266) + { "Supla Espablo", // SUPLA1 - Supla Espablo (ESP8266) // http://www.wykop.pl/ramka/3325399/diy-supla-do-puszki-instalacyjnej-podtynkowej-supla-org/ 0, // GPIO00 Flash jumper GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1345,7 +1351,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO16 Led (1 = On, 0 = Off) - Link and Power status ADC0_USER // ADC0 A0 Analog input }, - { "Witty Cloud", // Witty Cloud Dev Board (ESP8266) + { "Witty Cloud", // WITTY - Witty Cloud Dev Board (ESP8266) // https://www.aliexpress.com/item/ESP8266-serial-WIFI-Witty-cloud-Development-Board-ESP-12F-module-MINI-nodemcu/32643464555.html GPIO_USER, // GPIO00 D3 flash push button on interface board GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1366,7 +1372,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 D0 optional sensor ADC0_USER // ADC0 A0 Light sensor / Requires USE_ADC_VCC in user_config.h to be disabled }, - { "Yunshan Relay", // Yunshan Wifi Relay (ESP8266) + { "Yunshan Relay", // YUNSHAN - Yunshan Wifi Relay (ESP8266) // https://www.ebay.com/p/Esp8266-220v-10a-Network-Relay-WiFi-Module/1369583381 // Schematics and Info https://ucexperiment.wordpress.com/2016/12/18/yunshan-esp8266-250v-15a-acdc-network-wifi-relay-module/ 0, // GPIO00 Flash jumper - Module Pin 8 @@ -1383,7 +1389,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // GPIO11 (SD_CMD Flash) 0, 0, 0, 0, 0, 0 }, - { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) + { "MagicHome", // MAGICHOME - Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1403,7 +1409,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ARIRFSEL, // GPIO15 RF receiver control (Arilux LC10) 0, 0 }, - { "Luani HVIO", // ESP8266_HVIO + { "Luani HVIO", // LUANIHVIO - ESP8266_HVIO // https://luani.de/projekte/esp8266-hvio/ 0, // GPIO00 Flash jumper GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1424,7 +1430,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, ADC0_USER // ADC0 A0 Analog input }, - { "KMC 70011", // KMC 70011 + { "KMC 70011", // KMC_70011 - KMC 70011 // https://www.amazon.com/KMC-Timing-Monitoring-Network-125V-240V/dp/B06XRX2GTQ GPIO_KEY1, // GPIO00 Button 0, 0, 0, @@ -1441,7 +1447,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, - { "Arilux LC01", // Arilux AL-LC01 (ESP8285) + { "Arilux LC01", // ARILUX_LC01 - Arilux AL-LC01 (ESP8285) // https://www.banggood.com/nl/ARILUX-AL-LC01-Super-Mini-LED-WIFI-Smart-RGB-Controller-For-RGB-LED-Strip-Light-DC-9-12V-p-1058603.html // (PwmFrequency 1111Hz) GPIO_KEY1, // GPIO00 Optional Button @@ -1461,7 +1467,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO14 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) 0, 0, 0 }, - { "Arilux LC11", // Arilux AL-LC11 (ESP8266) + { "Arilux LC11", // ARILUX_LC11 - Arilux AL-LC11 (ESP8266) // https://www.banggood.com/nl/ARILUX-AL-LC11-Super-Mini-LED-WIFI-APP-Controller-RF-Remote-Control-For-RGBWW-LED-Strip-DC9-28V-p-1085112.html // (PwmFrequency 540Hz) GPIO_KEY1, // GPIO00 Optional Button @@ -1482,7 +1488,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ARIRFRCV, // GPIO15 RF receiver input 0, 0 }, - { "Sonoff Dual R2", // Sonoff Dual R2 (ESP8285) + { "Sonoff Dual R2", // SONOFF_DUAL_R2 - Sonoff Dual R2 (ESP8285) GPIO_USER, // GPIO00 Button 0 on header (0 = On, 1 = Off) GPIO_USER, // GPIO01 Serial RXD and Optional sensor 0, @@ -1499,7 +1505,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Arilux LC06", // Arilux AL-LC06 (ESP8285) + { "Arilux LC06", // ARILUX_LC06 - Arilux AL-LC06 (ESP8285) // https://www.banggood.com/ARILUX-AL-LC06-LED-WIFI-Smartphone-Controller-Romote-5-Channels-DC12-24V-For-RGBWW-Strip-light-p-1061476.html GPIO_KEY1, // GPIO00 Optional Button GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1519,7 +1525,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO15 RGBW LED White 0, 0 }, - { "Sonoff S31", // Sonoff S31 (ESP8266 - CSE7766) + { "Sonoff S31", // SONOFF_S31 - Sonoff S31 (ESP8266 - CSE7766) GPIO_KEY1, // GPIO00 Button GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, @@ -1535,7 +1541,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Zengge WF017", // Zenggee ZJ-WF017-A (ESP12S)) + { "Zengge WF017", // ZENGGE_ZF_WF017 - Zenggee ZJ-WF017-A (ESP12S)) // https://www.ebay.com/p/Smartphone-Android-IOS-WiFi-Music-Controller-for-RGB-5050-3528-LED-Strip-Light/534446632?_trksid=p2047675.l2644 GPIO_KEY1, // GPIO00 Optional Button 0, @@ -1554,7 +1560,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM3, // GPIO14 RGB LED Blue 0, 0, 0 }, - { "Sonoff Pow R2", // Sonoff Pow R2 (ESP8285 - CSE7766) + { "Sonoff Pow R2", // SONOFF_POW_R2 - Sonoff Pow R2 (ESP8285 - CSE7766) GPIO_KEY1, // GPIO00 Button GPIO_CSE7766_TX, // GPIO01 Serial RXD 4800 baud 8E1 CSE7766 energy sensor 0, @@ -1570,7 +1576,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) - Link and Power status 0, 0, 0, 0 }, - { "Sonoff iFan02", // Sonoff iFan02 (ESP8285) + { "Sonoff iFan02", // SONOFF_IFAN02 - Sonoff iFan02 (ESP8285) GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC GPIO_USER, // GPIO01 ESP_TXD Serial RXD and Optional sensor 0, // GPIO02 ESP_LOG @@ -1589,7 +1595,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan 0, 0 }, - { "BlitzWolf SHP", // BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) + { "BlitzWolf SHP", // BLITZWOLF_BWSHP - BlitzWolf BW-SHP2 and BW-SHP6 (ESP8285 - BL0937 or HJL-01 Energy Monitoring) // https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html // https://www.amazon.de/Steckdose-Homecube-intelligente-Verbrauchsanzeige-funktioniert/dp/B076Q2LKHG/ref=sr_1_fkmr0_1 // https://www.amazon.de/Intelligente-Stromverbrauch-Fernsteurung-Schaltbare-Energieklasse/dp/B076WZQS4S/ref=sr_1_1 @@ -1612,7 +1618,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "Shelly 1", // Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ + { "Shelly 1", // SHELLY1 - Shelly1 Open Source (ESP8266 - 2MB) - https://shelly.cloud/shelly1-open-source/ 0, // GPIO00 - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC 0, // GPIO01 Serial RXD - Can be changed to GPIO_USER, only if Shelly is powered with 12V DC 0, @@ -1627,7 +1633,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { // GPIO11 (SD_CMD Flash) 0, 0, 0, 0, 0, 0 }, - { "Shelly 2", // Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ + { "Shelly 2", // SHELLY2 - Shelly2 (ESP8266 - 2MB) - https://shelly.cloud/shelly2/ 0, GPIO_MCP39F5_TX, // GPIO01 MCP39F501 Serial input 0, @@ -1647,7 +1653,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0 }, - { "Xiaomi Philips", // Xiaomi Philips bulb (ESP8266) + { "Xiaomi Philips", // PHILIPS - Xiaomi Philips bulb (ESP8266) 0, 0, 0, 0, 0, 0, // GPIO06 (SD_CLK Flash) // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) @@ -1660,7 +1666,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO15 light intensity 0, 0 }, - { "Neo Coolcam", // Neo Coolcam (ESP8266) + { "Neo Coolcam", // NEO_COOLCAM - Neo Coolcam (ESP8266) // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN 0, 0, 0, 0, GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) - Link and Power status @@ -1675,7 +1681,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY1, // GPIO13 Button 0, 0, 0, 0 }, - { "ESP Switch", // Michael Haustein 4 channel wall switch (ESP07 = ESP8266) + { "ESP Switch", // ESP_SWITCH - Michael Haustein 4 channel wall switch (ESP07 = ESP8266) // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon GPIO_KEY2, // GPIO00 Button 2 GPIO_USER, // GPIO01 Serial RXD and Optional sensor @@ -1696,7 +1702,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) 0 }, - { "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 + { "OBI Socket", // OBI - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 GPIO_USER, // GPIO00 GPIO_USER, // GPIO01 Serial RXD 0, @@ -1716,7 +1722,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 ADC0_USER // ADC0 A0 Analog input }, - { "Teckin", // https://www.amazon.de/gp/product/B07D5V139R + { "Teckin", // TECKIN - https://www.amazon.de/gp/product/B07D5V139R 0, GPIO_KEY1, // GPIO01 Serial TXD and Button 0, @@ -1734,7 +1740,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, - { "AplicWDP303075", // Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) + { "AplicWDP303075", // APLIC_WDP303075 - Aplic WDP 303075 (ESP8285 - HLW8012 Energy Monitoring) // https://www.amazon.de/dp/B07CNWVNJ2 0, 0, 0, GPIO_KEY1, // GPIO03 Button @@ -1751,7 +1757,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) 0, 0, 0 }, - { "Tuya MCU", // Tuya MCU device (ESP8266 w/ separate MCU) + { "Tuya MCU", // TUYA_DIMMER - Tuya MCU device (ESP8266 w/ separate MCU) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_USER, // Virtual Button (controlled by MCU) GPIO_USER, // GPIO01 MCU serial control @@ -1772,7 +1778,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "Gosund SP1 v23", // https://www.amazon.de/gp/product/B0777BWS1P + { "Gosund SP1 v23", // GOSUND - https://www.amazon.de/gp/product/B0777BWS1P 0, GPIO_LEDLNK_INV, // GPIO01 Serial RXD and LED1 (blue) inv - Link status 0, @@ -1790,7 +1796,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) 0, 0, 0 }, - { "ARMTR Dimmer", // ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) + { "ARMTR Dimmer", // ARMTRONIX_DIMMERS - ARMTRONIX Dimmer, one or two channel (ESP8266 w/ separate MCU dimmer) // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-two-triac-board/ // https://www.tindie.com/products/Armtronix/wifi-ac-dimmer-esp8266-one-triac-board-alexaecho/ GPIO_USER, @@ -1812,7 +1818,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "SK03 Outdoor", // Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV + { "SK03 Outdoor", // SK03_TUYA - Outdoor smart plug with power monitoring HLW8012 chip - https://www.amazon.com/gp/product/B07CG7MBPV GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power @@ -1829,7 +1835,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 }, - { "PS-16-DZ", // PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) + { "PS-16-DZ", // PS_16_DZ - PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html GPIO_USER, GPIO_TXD, // GPIO01 MCU serial control @@ -1850,7 +1856,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, 0 }, - { "Teckin US", // Teckin SP20 US with Energy Monitoring + { "Teckin US", // TECKIN_US - Teckin SP20 US with Energy Monitoring // https://www.amazon.com/Outlet-Compatible-Monitoring-Function-Required/dp/B079Q5W22B // https://www.amazon.com/Outlet-ZOOZEE-Monitoring-Function-Compatible/dp/B07J2LR5KN GPIO_LED1_INV, // GPIO00 Red Led (1 = On, 0 = Off) - Power status @@ -1870,7 +1876,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage 0, 0, 0 }, - { "Manzoku strip", // "MANZOKU" labeled power strip, EU version + { "Manzoku strip", // MANZOKU_EU_4 - "MANZOKU" labeled power strip, EU version // https://www.amazon.de/Steckdosenleiste-AOFO-Mehrfachsteckdose-Überspannungsschutz-Sprachsteuerung/dp/B07GBSD11P/ // https://www.amazon.de/Steckdosenleiste-Geekbes-USB-Anschluss-Kompatibel-gesteuert/dp/B078W23BW9/ 0, // GPIO00 @@ -1892,7 +1898,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 0 }, - { "OBI Socket 2", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 + { "OBI Socket 2", // OBI2 - OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko-2-stueck-weiss/p/4077673 0, // GPIO00 0, // GPIO01 Serial RXD 0, @@ -1909,7 +1915,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO13 Red LED - Power status 0, 0, 0, 0 }, - { "YTF IR Bridge", // https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html + { "YTF IR Bridge", // YTF_IR_BRIDGE - https://www.aliexpress.com/item/Tuya-universal-Smart-IR-Hub-remote-control-Voice-Control-AC-TV-Work-With-Alexa-Google-Home/32951202513.html GPIO_USER, // GPIO00 GPIO_USER, // GPIO01 Serial RXD GPIO_USER, // GPIO02 @@ -1927,7 +1933,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_IRSEND, // GPIO14 IR Transmitter 0, 0, 0 }, - { "Digoo DG-SP202", // Digoo DG-SP202 + { "Digoo DG-SP202", // DIGOO - Digoo DG-SP202 // https://www.banggood.com/DIGOO-DG-SP202-Dual-EU-Plug-Smart-WIFI-Socket-Individual-Controllable-Energy-Monitor-Remote-Control-Timing-Smart-Home-Outlet-let-p-1375323.html GPIO_KEY1, // GPIO00 Button1 0, // GPIO01 Serial RXD @@ -1948,7 +1954,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY2_NP, // GPIO16 Button2, externally pulled up 0 }, - { "KA10", // SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y + { "KA10", // KA10 - SMANERGY KA10 (ESP8285 - BL0937 Energy Monitoring) - https://www.amazon.es/dp/B07MBTCH2Y 0, // GPIO00 GPIO_LEDLNK_INV, // GPIO01 Blue LED - Link status 0, // GPIO02 @@ -1966,7 +1972,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 1 0, 0, 0 }, - { "Luminea ZX2820", + { "Luminea ZX2820", // ZX2820 GPIO_KEY1, // GPIO00 Button 0, 0, 0, GPIO_HLW_CF, // GPIO04 HLW8012 CF power @@ -1982,7 +1988,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay 0, 0, 0 }, - { "Mi Desk Lamp", // Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ + { "Mi Desk Lamp", // MI_DESK_LAMP - Mi LED Desk Lamp - https://www.mi.com/global/smartlamp/ 0, 0, GPIO_KEY1, // GPIO02 Button 0, @@ -1998,7 +2004,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_ROT1B, // GPIO13 Rotary switch B pin 0, 0, 0, 0 }, - { "SP10", // Tuya SP10 (BL0937 Energy Monitoring) + { "SP10", // SP10 - Tuya SP10 (BL0937 Energy Monitoring) // https://www.aliexpress.com/item/Smart-Mini-WiFi-Plug-Outlet-Switch-Work-With-ForEcho-Alexa-Google-Home-Remote-EU-Smart-Socket/32963670423.html 0, // GPIO00 GPIO_PWM1, // GPIO01 Nightlight @@ -2017,7 +2023,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1, // GPIO14 Relay and red LED 0, 0, 0 }, - { "WAGA CHCZ02MB", // WAGA life CHCZ02MB (HJL-01 Energy Monitoring) + { "WAGA CHCZ02MB", // WAGA - WAGA life CHCZ02MB (HJL-01 Energy Monitoring) // https://www.ebay.com/itm/332595697006 GPIO_LED1_INV, // GPIO00 Red LED 0, // GPIO01 Serial RXD @@ -2037,7 +2043,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LEDLNK_INV, // GPIO15 Blue LED - Link status 0, 0 }, - { "SYF05", // Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 + { "SYF05", // SYF05 - Sunyesmart SYF05 (a.k.a. Fcmila) = TYWE3S + SM16726 // Also works with Merkury 904 RGBW Bulbs with 13 set to GPIO_SM16716_SEL // https://www.flipkart.com/fc-mila-bxav-xs-ad-smart-bulb/p/itmf85zgs45fzr7n // https://docs.tuya.com/en/hardware/WiFi-module/wifi-e3s-module.html @@ -2061,7 +2067,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_USER, // GPIO16 N.C. ADC0_USER // ADC0 A0 Analog input }, - { "Sonoff L1", // Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) + { "Sonoff L1", // SONOFF_L1 - Sonoff L1 RGB LED controller (ESP8266 w/ separate Nuvoton MCU) 0, GPIO_TXD, // GPIO01 MCU serial control 0, @@ -2077,7 +2083,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 WiFi Blue Led - Link and Power status 0, 0, 0, 0 }, - { "Sonoff iFan03", // Sonoff iFan03 (ESP8285) + { "Sonoff iFan03", // SONOFF_IFAN03 - Sonoff iFan03 (ESP8285) GPIO_KEY1, // GPIO00 WIFI_KEY0 Button 1 GPIO_TXD, // GPIO01 ESP_TXD Serial RXD connection to P0.5 of RF microcontroller 0, // GPIO02 ESP_LOG @@ -2098,235 +2104,4 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { } }; -/* - Optionals - - { "RGB Smart Plug", // Tuya based smart plug with power monitoring and RGB light - // https://www.aliexpress.com/item/ET-Smart-Plug-Wifi-Socket-With-Switch-Phone-APP-Voice-Remote-Control-Monitor-Smart-Timing-Switch/32964036349.html?spm=a2g0s.9042311.0.0.439c4c4d4N8N2Q - // https://xiangshangcn.en.alibaba.com/product/60844251239-807590977/RGB_wifi_smart_plug_smart_socket_smart_outlet_EU_works_with_Amazon_alexa_google_home_mobile_app_tuya_solution_smart_life.html?spm=a2700.icbuShop.41413.24.4e996767oFAAmO - GPIO_PWM1, // GPIO00 Red - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_PWM3, // GPIO02 Blue - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM2, // GPIO04 Green - GPIO_HJL_CF, // GPIO05 BL0937 or HJL-01 CF power - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_NRG_SEL_INV, // GPIO12 BL0937 or HJL-01 Sel output (0 = Voltage) - GPIO_KEY1, // GPIO13 Button - GPIO_NRG_CF1, // GPIO14 BL0937 or HJL-01 CF1 current / voltage - GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) - 0, 0 - } - - { "ESP RGBWWC", // esp rgbww controller /~https://github.com/pljakobs/esp_rgbww_controller/tree/v2.3 - GPIO_KEY1, // GPIO00 Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_PWM5, // GPIO04 LED Warm White - GPIO_PWM4, // GPIO05 LED Cold White - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM2, // GPIO12 LED Green - GPIO_PWM1, // GPIO13 LED Red - GPIO_PWM3, // GPIO14 LED Blue - 0, // GPIO15 - GPIO_KEY2, // GPIO16 Button - 0 - } - - { "N0DY Relay", // N0DY Wifi Dual Relay (ESP-07) - // https://www.n0dy.com/product/web-controlled-dual-relay/ - // https://www.amazon.com/dp/B072MKV8ZM - GPIO_KEY1, // GPIO00 PROG Button - GPIO_USER, // GPIO01 Serial RXD or Optional sensor on J2 RXD (if not using serial io) - 0, // GPIO02 - GPIO_USER, // GPIO03 Serial TXD or Optional sensor on J2 TXD (if not using serial io) - GPIO_REL2_INV, // GPIO04 Relay 2 (active low) - GPIO_REL1_INV, // GPIO05 Relay 1 (active low) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, 0, 0, 0, 0, 0 - } - - { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) - // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html - 0, - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 IR receiver (optional) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) - GPIO_PWM1, // GPIO14 RGB LED Red - 0, 0, 0 - } - - { "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF - // /~https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285 - // https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html - // https://www.aliexpress.com/item/Wifi-LED-RGB-Controler-DC12V-MIni-Wifi-RGB-RGBW-LED-Controller-for-RGB-RGBW-LED-Strip/32673444047.html - GPIO_USER, // GPIO00 Optional Button - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - 0, - GPIO_USER, // GPIO03 Serial TXD and Optional sensor0 - GPIO_ARIRFRCV, // GPIO04 RF receiver input - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_PWM4, // GPIO13 RGBW LED White - GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_ARIRFSEL, // GPIO15 RF receiver control - 0, 0 - } - - { "Xenon 3CH", // Xenon 3CH (ESP8266) - (#1128) - 0, 0, 0, - GPIO_KEY2, // GPIO03 Serial TXD and Optional sensor - GPIO_REL2, // GPIO04 Relay 2 - GPIO_KEY3, // GPIO05 Input 2 - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_KEY1, // GPIO12 Key input - GPIO_REL1, // GPIO13 Relay 1 - 0, - GPIO_REL3, // GPIO15 Relay 3 - 0, 0 - } - - { "PowStro Basic", // PowStro (ESP8266) - (#1419) - 0, 0, 0, 0, - GPIO_REL1, // GPIO04 Relay (0 = Off, 1 = On) - 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_KEY1, // GPIO12 Button - 0, 0, - GPIO_LED1, // GPIO15 Led (1 = On, 0 = Off) - Link and Power status - 0, 0 - } - - { "SMPW701E", // SM-PW701E WLAN Socket (#1190) - 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO04 Blue Led (0 = On, 1 = Off) - Link and Power status - 0, // GPIO05 IR or RF receiver (optional) - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Relay and Red Led (0 = Off, 1 = On) - GPIO_KEY1, // GPIO13 Button - 0, 0, 0, 0 - } - - { "SWA1", // Smart Plugs (ESP8266) - 0, - GPIO_USER, // GPIO01 - 0, - GPIO_USER, // GPIO03 - GPIO_LED1_INV, // GPIO04 Blue LED - Link and Power status - GPIO_REL1, // GPIO05 Red LED and relay - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - 0, - GPIO_KEY1, // GPIO13 Button (normally GPIO00) - GPIO_USER, // GPIO14 - 0, 0, 0 - } - - { "MagicHome v2.3", // Magic Home (aka Flux-light) (ESP8266) (#1353) - 0, 0, - GPIO_LED1_INV, // GPIO02 Blue onboard LED - Link and Power status - 0, - GPIO_USER, // GPIO04 IR receiver (optional) - GPIO_PWM2, // GPIO05 RGB LED Green - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_PWM1, // GPIO12 RGB LED Red - GPIO_PWM3, // GPIO13 RGB LED Blue - 0, - GPIO_PWM4, // GPIO15 RGBW LED White - 0, 0 - } - - { "Ledunia", // Ledunia (ESP8266 - 32MB) - http://ledunia.de/ - GPIO_USER, // GPIO00 (D0) - GPIO_USER, // GPIO01 (D7) Serial RXD - GPIO_USER, // GPIO02 (D2) - GPIO_USER, // GPIO03 (D8) Serial TXD - GPIO_USER, // GPIO04 (D4) 4 x WS2812 Leds, (DOUT) Extents WS2812 string - GPIO_USER, // GPIO05 (D5) Blue Led - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_USER, // GPIO12 (D12) - GPIO_USER, // GPIO13 (D13) - GPIO_USER, // GPIO14 (D14) - GPIO_USER, // GPIO15 (D15) - GPIO_USER, // GPIO16 (D16) - 0 // ADC0 Analog input (A0) - } - - { "Delock 11826", // Delock 11826 (ESP8285) = Sonoff Basic - GPIO_KEY1, // GPIO00 Button - 0, 0, 0, 0, 0, - // GPIO06 (SD_CLK Flash) - // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT) - // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT) - 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285) - 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285) - // GPIO11 (SD_CMD Flash) - GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) - GPIO_LED1_INV, // GPIO13 Green Led (0 = On, 1 = Off) - Link and Power status - 0, 0, 0, 0 - } - -*/ - #endif // _SONOFF_TEMPLATE_H_ From 9c6b4259a428c5e09f9934c35541c9f213a08436 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 15:55:59 +0200 Subject: [PATCH 33/44] Fix SDM630_2 phase display Fix SDM630_2 phase display (#6431) --- sonoff/xnrg_10_sdm630.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/sonoff/xnrg_10_sdm630.ino b/sonoff/xnrg_10_sdm630.ino index 5bdef0c9efa5..0478e8037a29 100644 --- a/sonoff/xnrg_10_sdm630.ino +++ b/sonoff/xnrg_10_sdm630.ino @@ -176,6 +176,7 @@ void Sdm630SnsInit(void) uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); if (result) { if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; } else { energy_flg = ENERGY_NONE; } From d193b8cb1a2196acd6781dd0077b9a3f6d50d718 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 16:27:35 +0200 Subject: [PATCH 34/44] Update TasmotaModbus to 1.2.0 Update TasmotaModbus to 1.2.0 --- .../README.md | 0 .../examples/modbustest/modbustest.ino | 0 .../keywords.txt | 0 .../library.json | 2 +- .../library.properties | 2 +- .../src/TasmotaModbus.cpp | 49 ++++++++++++------- .../src/TasmotaModbus.h | 23 ++++++--- 7 files changed, 48 insertions(+), 28 deletions(-) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/README.md (100%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/examples/modbustest/modbustest.ino (100%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/keywords.txt (100%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/library.json (93%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/library.properties (93%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/src/TasmotaModbus.cpp (67%) rename lib/{TasmotaModbus-1.1.1 => TasmotaModbus-1.2.0}/src/TasmotaModbus.h (76%) diff --git a/lib/TasmotaModbus-1.1.1/README.md b/lib/TasmotaModbus-1.2.0/README.md similarity index 100% rename from lib/TasmotaModbus-1.1.1/README.md rename to lib/TasmotaModbus-1.2.0/README.md diff --git a/lib/TasmotaModbus-1.1.1/examples/modbustest/modbustest.ino b/lib/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino similarity index 100% rename from lib/TasmotaModbus-1.1.1/examples/modbustest/modbustest.ino rename to lib/TasmotaModbus-1.2.0/examples/modbustest/modbustest.ino diff --git a/lib/TasmotaModbus-1.1.1/keywords.txt b/lib/TasmotaModbus-1.2.0/keywords.txt similarity index 100% rename from lib/TasmotaModbus-1.1.1/keywords.txt rename to lib/TasmotaModbus-1.2.0/keywords.txt diff --git a/lib/TasmotaModbus-1.1.1/library.json b/lib/TasmotaModbus-1.2.0/library.json similarity index 93% rename from lib/TasmotaModbus-1.1.1/library.json rename to lib/TasmotaModbus-1.2.0/library.json index 75066a512e39..c9639e1648c3 100644 --- a/lib/TasmotaModbus-1.1.1/library.json +++ b/lib/TasmotaModbus-1.2.0/library.json @@ -1,6 +1,6 @@ { "name": "TasmotaModbus", - "version": "1.1.1", + "version": "1.2.0", "keywords": [ "serial", "io", "TasmotaModbus" ], diff --git a/lib/TasmotaModbus-1.1.1/library.properties b/lib/TasmotaModbus-1.2.0/library.properties similarity index 93% rename from lib/TasmotaModbus-1.1.1/library.properties rename to lib/TasmotaModbus-1.2.0/library.properties index 709a71106e52..9197e7cfec84 100644 --- a/lib/TasmotaModbus-1.1.1/library.properties +++ b/lib/TasmotaModbus-1.2.0/library.properties @@ -1,5 +1,5 @@ name=TasmotaModbus -version=1.1.1 +version=1.2.0 author=Theo Arends maintainer=Theo Arends sentence=Basic modbus wrapper for TasmotaSerial for ESP8266. diff --git a/lib/TasmotaModbus-1.1.1/src/TasmotaModbus.cpp b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp similarity index 67% rename from lib/TasmotaModbus-1.1.1/src/TasmotaModbus.cpp rename to lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp index 842d242b2530..dde74fd7cae2 100644 --- a/lib/TasmotaModbus-1.1.1/src/TasmotaModbus.cpp +++ b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.cpp @@ -61,10 +61,10 @@ void TasmotaModbus::Send(uint8_t device_address, uint8_t function_code, uint16_t frame[0] = mb_address; // 0xFE default device address or dedicated like 0x01 frame[1] = function_code; - frame[2] = (uint8_t)(start_address >> 8); - frame[3] = (uint8_t)(start_address); - frame[4] = (uint8_t)(register_count >> 8); - frame[5] = (uint8_t)(register_count); + frame[2] = (uint8_t)(start_address >> 8); // MSB + frame[3] = (uint8_t)(start_address); // LSB + frame[4] = (uint8_t)(register_count >> 8); // MSB + frame[5] = (uint8_t)(register_count); // LSB uint16_t crc = CalculateCRC(frame, 6); frame[6] = (uint8_t)(crc); frame[7] = (uint8_t)(crc >> 8); @@ -80,35 +80,48 @@ bool TasmotaModbus::ReceiveReady() uint8_t TasmotaModbus::ReceiveBuffer(uint8_t *buffer, uint8_t register_count) { - uint8_t len = 0; + mb_len = 0; uint32_t last = millis(); - while ((available() > 0) && (len < (register_count *2) + 5) && (millis() - last < 10)) { + while ((available() > 0) && (mb_len < (register_count *2) + 5) && (millis() - last < 10)) { uint8_t data = (uint8_t)read(); - if (!len) { // Skip leading data as provided by hardware serial + if (!mb_len) { // Skip leading data as provided by hardware serial if (mb_address == data) { - buffer[len++] = data; + buffer[mb_len++] = data; } } else { - buffer[len++] = data; - if (3 == len) { + buffer[mb_len++] = data; + if (3 == mb_len) { if (buffer[1] & 0x80) { // 01 84 02 f2 f1 - return buffer[2]; // 1 = Illegal Function, 2 = Illegal Address, 3 = Illegal Data, 4 = Slave Error + return buffer[2]; // 1 = Illegal Function, + // 2 = Illegal Data Address, + // 3 = Illegal Data Value, + // 4 = Slave Error + // 5 = Acknowledge but not finished (no error) + // 6 = Slave Busy + // 8 = Memory Parity error + // 10 = Gateway Path Unavailable + // 11 = Gateway Target device failed to respond } } } last = millis(); } - if (len < 7) { return 7; } // 7 = Not enough data - if (len != buffer[2] + 5) { - buffer[2] = len - 5; // As it's wrong anyway let's store actual number received in here (5 will be added by client) - return 8; // 8 = Unexpected result + if (mb_len < 7) { return 7; } // 7 = Not enough data + +/* + if (mb_len != buffer[2] + 5) { + buffer[2] = mb_len - 5; // As it's wrong anyway let's store actual number received in here (5 will be added by client) + return 3; // 3 = Unexpected result } +*/ - uint16_t crc = (buffer[len -1] << 8) | buffer[len -2]; - if (CalculateCRC(buffer, len -2) != crc) { return 9; } // 9 = crc error + uint16_t crc = (buffer[mb_len -1] << 8) | buffer[mb_len -2]; + if (CalculateCRC(buffer, mb_len -2) != crc) { + return 9; // 9 = crc error + } - return 0; // 0 = No error + return 0; // 0 = No error } uint8_t TasmotaModbus::Receive16BitRegister(uint16_t *value) diff --git a/lib/TasmotaModbus-1.1.1/src/TasmotaModbus.h b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h similarity index 76% rename from lib/TasmotaModbus-1.1.1/src/TasmotaModbus.h rename to lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h index 5176aa89e06e..2138e0c9dd36 100644 --- a/lib/TasmotaModbus-1.1.1/src/TasmotaModbus.h +++ b/lib/TasmotaModbus-1.2.0/src/TasmotaModbus.h @@ -37,21 +37,28 @@ class TasmotaModbus : public TasmotaSerial { bool ReceiveReady(); /* Return codes: - * 0 - No error - * 1 - Illegal function - * 2 - Illegal address - * 3 - Illegal data - * 4 - Slave error - * 7 - Not enough minimal data received - * 8 - Not enough data receieved - * 9 - Crc error + * 0 = No error + * 1 = Illegal Function, + * 2 = Illegal Data Address, + * 3 = Illegal Data Value, + * 4 = Slave Error + * 5 = Acknowledge but not finished (no error) + * 6 = Slave Busy + * 7 = Not enough minimal data received + * 8 = Memory Parity error + * 9 = Crc error + * 10 = Gateway Path Unavailable + * 11 = Gateway Target device failed to respond */ uint8_t ReceiveBuffer(uint8_t *buffer, uint8_t register_count); uint8_t Receive16BitRegister(uint16_t *value); uint8_t Receive32BitRegister(float *value); + uint8_t ReceiveCount(void) { return mb_len; } + private: uint8_t mb_address; + uint8_t mb_len; }; #endif // TasmotaModbus_h From 7b70c042bdd6167ef67dabf041590120d9d20d9f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 16:56:16 +0200 Subject: [PATCH 35/44] Add command ModuleAddress 1/2/3 Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315) --- sonoff/_changelog.ino | 1 + sonoff/sonoff.h | 2 ++ sonoff/xdrv_03_energy.ino | 17 ++++++++-- sonoff/xnrg_03_pzem004t.ino | 34 ++++++++++++++++---- sonoff/xnrg_05_pzem_ac.ino | 64 +++++++++++++++++++++++++++---------- sonoff/xnrg_06_pzem_dc.ino | 60 +++++++++++++++++++++++++--------- 6 files changed, 136 insertions(+), 42 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 34b6681a2107..d00dc075dfb2 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -12,6 +12,7 @@ * Add initial support for up to three PZEM-004T on serial connection with addresses x.x.x.1 (default), 2 and 3 (#2315) * Add initial support for up to three PZEM-003/-017 on serial modbus connection with addresses 1 (default), 2 and 3 (#2315) * Add driver USE_SDM630_2 as future replacement for USE_SDM630 - Pls test and report + * Add command ModuleAddress 1/2/3 to set Pzem module address when a single module is connected (#2315) * * 6.6.0.11 20190907 * Change Settings crc calculation allowing short term backward compatibility diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index 89e3c53f3793..9cefa9ca12db 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -279,6 +279,8 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; +enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND }; + enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER, SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_MAX }; const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote"; diff --git a/sonoff/xdrv_03_energy.ino b/sonoff/xdrv_03_energy.ino index 60f312917c8f..5a9356985e1c 100644 --- a/sonoff/xdrv_03_energy.ino +++ b/sonoff/xdrv_03_energy.ino @@ -37,14 +37,15 @@ #define D_CMND_VOLTAGECAL "VoltageCal" #define D_CMND_CURRENTCAL "CurrentCal" #define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" enum EnergyCommands { CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, - CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET }; + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; const char kEnergyCommands[] PROGMEM = "|" // No prefix D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" - D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" #ifdef USE_ENERGY_MARGIN_DETECTION D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" #ifdef USE_ENERGY_POWER_LIMIT @@ -57,7 +58,7 @@ const char kEnergyCommands[] PROGMEM = "|" // No prefix void (* const EnergyCommand[])(void) PROGMEM = { &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, - &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, #ifdef USE_ENERGY_MARGIN_DETECTION &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, #ifdef USE_ENERGY_POWER_LIMIT @@ -614,6 +615,16 @@ void CmndFrequencySet(void) } } +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { // Module address + ResponseCmndDone(); + } + } +} + #ifdef USE_ENERGY_MARGIN_DETECTION void CmndPowerDelta(void) { diff --git a/sonoff/xnrg_03_pzem004t.ino b/sonoff/xnrg_03_pzem004t.ino index 5e1d3ceb686c..46a39f44c35d 100644 --- a/sonoff/xnrg_03_pzem004t.ino +++ b/sonoff/xnrg_03_pzem004t.ino @@ -59,8 +59,9 @@ TasmotaSerial *PzemSerial = nullptr; struct PZEM { float energy = 0; uint8_t send_retry = 0; - uint8_t read_state = 0; + uint8_t read_state = 0; // Set address uint8_t phase = 0; + uint8_t address = 0; } Pzem; struct PZEMCommand { @@ -70,8 +71,6 @@ struct PZEMCommand { uint8_t crc; }; -IPAddress pzem_ip(192, 168, 1, 1); - uint8_t PzemCrc(uint8_t *data) { uint16_t crc = 0; @@ -86,10 +85,10 @@ void PzemSend(uint8_t cmd) PZEMCommand pzem; pzem.command = cmd; - for (uint32_t i = 0; i < sizeof(pzem.addr) -1; i++) { - pzem.addr[i] = pzem_ip[i]; - } - pzem.addr[3] = pzem_ip[3] + Pzem.phase; + pzem.addr[0] = 0; // Address 0.0.0.1 + pzem.addr[1] = 0; + pzem.addr[2] = 0; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; pzem.data = 0; uint8_t *bytes = (uint8_t*)&pzem; @@ -97,6 +96,8 @@ void PzemSend(uint8_t cmd) PzemSerial->flush(); PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; } bool PzemReceiveReady(void) @@ -209,6 +210,9 @@ void PzemEvery200ms(void) if (Pzem.phase >= Energy.phase_count) { Pzem.phase = 0; } + if (Pzem.address) { + Pzem.read_state = 0; // Set address + } Pzem.send_retry = 5; PzemSend(pzem_commands[Pzem.read_state]); } @@ -230,6 +234,7 @@ void PzemSnsInit(void) } Energy.phase_count = 3; // Start off with three phases Pzem.phase = 2; + Pzem.read_state = 1; } else { energy_flg = ENERGY_NONE; } @@ -242,6 +247,18 @@ void PzemDrvInit(void) } } +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + Pzem.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -254,6 +271,9 @@ bool Xnrg03(uint8_t function) case FUNC_EVERY_200_MSECOND: if (PzemSerial) { PzemEvery200ms(); } break; + case FUNC_COMMAND: + result = PzemCommand(); + break; case FUNC_INIT: PzemSnsInit(); break; diff --git a/sonoff/xnrg_05_pzem_ac.ino b/sonoff/xnrg_05_pzem_ac.ino index 54fe8a0791c7..60362adbc87a 100644 --- a/sonoff/xnrg_05_pzem_ac.ino +++ b/sonoff/xnrg_05_pzem_ac.ino @@ -40,6 +40,8 @@ struct PZEMAC { float energy = 0; uint8_t send_retry = 0; uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; } PzemAc; void PzemAcEverySecond(void) @@ -49,27 +51,34 @@ void PzemAcEverySecond(void) if (data_ready) { uint8_t buffer[30]; // At least 5 + (2 * 10) = 25 - uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, 10); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); } else { Energy.data_valid = 0; - - // 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 - // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 - // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- - Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V - Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A - Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W - Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz - Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 - - PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh - if (PzemAc.phase == Energy.phase_count -1) { - EnergyUpdateTotal(PzemAc.energy, false); - PzemAc.energy = 0; + if (10 == registers) { + + // 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 + // 01 04 14 08 D1 00 6C 00 00 00 F4 00 00 00 26 00 00 01 F4 00 64 00 00 51 34 + // Id Cc Sz Volt- Current---- Power------ Energy----- Frequ PFact Alarm Crc-- + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; // 6553.0 V + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; // 4294967.000 A + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; // 429496729.0 W + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; // 50.0 Hz + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; // 1.00 + + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); // 4294967295 Wh + if (PzemAc.phase == Energy.phase_count -1) { + EnergyUpdateTotal(PzemAc.energy, false); + PzemAc.energy = 0; + } } } } @@ -80,7 +89,12 @@ void PzemAcEverySecond(void) PzemAc.phase = 0; } PzemAc.send_retry = ENERGY_WATCHDOG; - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } } else { PzemAc.send_retry--; @@ -110,6 +124,19 @@ void PzemAcDrvInit(void) } } +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -122,6 +149,9 @@ bool Xnrg05(uint8_t function) case FUNC_ENERGY_EVERY_SECOND: if (uptime > 4) { PzemAcEverySecond(); } // Fix start up issue #5875 break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; case FUNC_INIT: PzemAcSnsInit(); break; diff --git a/sonoff/xnrg_06_pzem_dc.ino b/sonoff/xnrg_06_pzem_dc.ino index 5188c051eb64..983c2db6a4d0 100644 --- a/sonoff/xnrg_06_pzem_dc.ino +++ b/sonoff/xnrg_06_pzem_dc.ino @@ -40,6 +40,8 @@ struct PZEMDC { float energy = 0; uint8_t send_retry = 0; uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; } PzemDc; void PzemDcEverySecond(void) @@ -49,25 +51,32 @@ void PzemDcEverySecond(void) if (data_ready) { uint8_t buffer[26]; // At least 5 + (2 * 8) = 21 - uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, 8); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; // Need 1 byte extra as response is F8 06 00 02 00 01 FD A3 + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); } else { Energy.data_valid = 0; - - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 - // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- - Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V - Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A - Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W - - PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh - if (PzemDc.channel == Energy.phase_count -1) { - EnergyUpdateTotal(PzemDc.energy, false); - PzemDc.energy = 0; + if (8 == registers) { + + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + // 01 04 10 05 40 00 0A 00 0D 00 00 00 02 00 00 00 00 00 00 D6 29 + // Id Cc Sz Volt- Curre Power------ Energy----- HiAlm LoAlm Crc-- + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; // 655.00 V + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; // 655.00 A + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; // 429496729.0 W + + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); // 4294967295 Wh + if (PzemDc.channel == Energy.phase_count -1) { + EnergyUpdateTotal(PzemDc.energy, false); + PzemDc.energy = 0; + } } } } @@ -78,7 +87,12 @@ void PzemDcEverySecond(void) PzemDc.channel = 0; } PzemDc.send_retry = ENERGY_WATCHDOG; - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } } else { PzemDc.send_retry--; @@ -109,6 +123,19 @@ void PzemDcDrvInit(void) } } +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; // Valid addresses are 1, 2 and 3 + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; // Unknown command + + return serviced; +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -121,6 +148,9 @@ bool Xnrg06(uint8_t function) case FUNC_ENERGY_EVERY_SECOND: if (uptime > 4) { PzemDcEverySecond(); } // Fix start up issue #5875 break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; case FUNC_INIT: PzemDcSnsInit(); break; From d107933e80f82dbe3fc38ba5c60d9ed1f5147d9d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 16:59:05 +0200 Subject: [PATCH 36/44] Revert "Merge branch 'development' of /~https://github.com/arendst/Sonoff-Tasmota into development" This reverts commit d82afa1190c1d35a23c981e3439f2b83d044cf37, reversing changes made to 7b70c042bdd6167ef67dabf041590120d9d20d9f. --- sonoff/xdrv_12_home_assistant.ino | 43 ------------------------------- 1 file changed, 43 deletions(-) diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index d794d9543983..538c25e06124 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -152,11 +152,6 @@ const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = const char HASS_DISCOVER_TOPIC_PREFIX[] PROGMEM = ",\"~\":\"%s\""; -const char HASS_DISCOVER_INFO_WIFI[] PROGMEM = - // Combined WiFi sensor, not usable for graph in Hass // - ",\"val_tpl\":\"{{[value_json.Wifi." D_JSON_SSID ", value_json.Wifi." D_JSON_RSSI "] | join (' (') + '%%)' }}\"," - "\"dev_cla\":\"signal_strength\""; // device_class: signal_strength - uint8_t hass_init_step = 0; uint8_t hass_mode = 0; int hass_tele_period = 0; @@ -559,41 +554,6 @@ void HAssPublishStatus(void) MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); } -void HAssAnnounceWifiSensor(void) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - // Announce sensor - mqtt_data[0] = '\0'; // Clear retained message - - // Clear or Set topic - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_conn"), ESP.getChipId()); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - - if (Settings.flag.hass_discovery) { - char name[33+11]; // friendlyname(33) + + " " + "connection" - char *state_topic = stemp1; - char *availability_topic = stemp2; - char prefix[TOPSZ]; - - snprintf_P(name, sizeof(name), PSTR("%s Connection"), Settings.friendlyname[0]); // "Connection" could be translated, should be added to language files - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_STATE)); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - FindPrefix(state_topic, availability_topic, prefix); - Shorten(&state_topic, prefix); - Shorten(&availability_topic, prefix); - - Response_P(HASS_DISCOVER_SENSOR, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_INFO_WIFI, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); - TryResponseAppend_P(HASS_DISCOVER_TOPIC_PREFIX, prefix); - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); -} - void HAssDiscovery(void) { // Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible @@ -624,9 +584,6 @@ void HAssDiscovery(void) // Send info about status sensor HAssAnnounceStatusSensor(); - - // Send info about Wifi - HAssAnnounceWifiSensor(); } } From 7d768905cdef570cc71eabc0db67db0b8dd4f68c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 16 Sep 2019 17:11:38 +0200 Subject: [PATCH 37/44] Change modbus log buffer size Change modbus log buffer size --- sonoff/xnrg_08_sdm120.ino | 2 +- sonoff/xnrg_09_dds2382.ino | 2 +- sonoff/xnrg_10_sdm630.ino | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sonoff/xnrg_08_sdm120.ino b/sonoff/xnrg_08_sdm120.ino index 9b1cf6cbc669..0cfe9a4b227c 100644 --- a/sonoff/xnrg_08_sdm120.ino +++ b/sonoff/xnrg_08_sdm120.ino @@ -80,7 +80,7 @@ void SDM120Every250ms(void) uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); diff --git a/sonoff/xnrg_09_dds2382.ino b/sonoff/xnrg_09_dds2382.ino index c2ae027463b1..b6e5a7e5c68e 100644 --- a/sonoff/xnrg_09_dds2382.ino +++ b/sonoff/xnrg_09_dds2382.ino @@ -47,7 +47,7 @@ void Dds2382EverySecond(void) uint8_t buffer[46]; // At least 5 + (2 * 18) = 41 uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, (buffer[2]) ? buffer[2] +5 : sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); diff --git a/sonoff/xnrg_10_sdm630.ino b/sonoff/xnrg_10_sdm630.ino index 0478e8037a29..140226589734 100644 --- a/sonoff/xnrg_10_sdm630.ino +++ b/sonoff/xnrg_10_sdm630.ino @@ -73,7 +73,7 @@ void SDM630Every250ms(void) uint8_t buffer[14]; // At least 5 + (2 * 2) = 9 uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, sizeof(buffer)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); if (error) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); From 7fa31850243ed92182d23735904ede5a59bb1081 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 16 Sep 2019 19:52:53 +0200 Subject: [PATCH 38/44] scripter web user io --- sonoff/xdrv_01_webserver.ino | 11 +++ sonoff/xdrv_10_scripter.ino | 174 ++++++++++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 2 deletions(-) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 606e406169df..ec1303c33887 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -92,6 +92,7 @@ const char HTTP_SCRIPT_COUNTER[] PROGMEM = "wl(u);"; const char HTTP_SCRIPT_ROOT[] PROGMEM = + "function la(p){" "var a='';" "if(la.arguments.length==1){" @@ -110,6 +111,12 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = "x.send();" "lt=setTimeout(la,%d);" // Settings.web_refresh "}" +#ifdef USE_SCRIPT_WEB_DISPLAY + "function seva(par,ivar){" + "la('&sv='+ivar+'_'+par);" + "}" +#endif + #ifdef USE_JAVASCRIPT_ES6 "lb=p=>la('&d='+p);" // Dark - Bright &d related to lb(value) and WebGetArg("d", tmp, sizeof(tmp)); @@ -1009,6 +1016,10 @@ bool HandleRootStatusRefresh(void) return false; } + #ifdef USE_SCRIPT_WEB_DISPLAY + Script_Check_HTML_Setvars(); + #endif + char tmp[8]; // WebGetArg numbers only char svalue[32]; // Command and number parameter diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index a8dc6adf434d..fc9995aa9331 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -3206,6 +3206,7 @@ void HandleScriptTextareaConfiguration(void) { } void HandleScriptConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); @@ -3579,6 +3580,70 @@ String ScriptUnsubscribe(const char * data, int data_len) #endif // SUPPORT_MQTT_EVENT #ifdef USE_SCRIPT_WEB_DISPLAY + + +void Script_Check_HTML_Setvars(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("sv")) { + String stmp = WebServer->arg("sv"); + char cmdbuf[64]; + memset(cmdbuf,0,sizeof(cmdbuf)); + char *cp=cmdbuf; + *cp++='>'; + strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); + char *cp1=strchr(cp,'_'); + if (!cp1) return; + *cp1=0; + char vname[32]; + strncpy(vname,cp,sizeof(vname)); + *cp1='='; + cp1++; + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname,&vtype,&ind,0,0,0); + if (vtype!=NUM_RES && vtype&STYPE) { + // string type must insert quotes + uint8_t tlen=strlen(cp1); + memmove(cp1+1,cp1,tlen); + *cp1='\"'; + *(cp1+tlen+1)='\"'; + } + + //toLog(cmdbuf); + execute_script(cmdbuf); + } +} + +const char SCRIPT_MSG_SLIDER[] PROGMEM = + "
%s
%s%s
" + "
"; + +const char SCRIPT_MSG_BUTTON[] PROGMEM = + "
"; + +const char SCRIPT_MSG_CHKBOX[] PROGMEM = + "
"; + +const char SCRIPT_MSG_TEXTINP[] PROGMEM = + "
"; + +// +// + +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { +uint32_t cnt; + for (cnt=0;cntW",-2,0); if (web_script==99) { @@ -3604,8 +3669,112 @@ void ScriptWebShow(void) { } cp++; } - Replace_Cmd_Vars(line,tmp,sizeof(tmp)); - WSContentSend_PD(PSTR("{s}%s{e}"),tmp); + // check for input elements + if (!strncmp(line,"sl(",3)) { + // insert slider sl(min max var left mid right) + char *lp=line; + float min; + lp=GetNumericResult(lp+3,OPER_EQU,&min,0); + SCRIPT_SKIP_SPACES + // arg2 + float max; + lp=GetNumericResult(lp,OPER_EQU,&max,0); + SCRIPT_SKIP_SPACES + float val; + char *slp=lp; + lp=GetNumericResult(lp,OPER_EQU,&val,0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname,slp,sizeof(vname)); + + char left[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,left,0); + SCRIPT_SKIP_SPACES + char mid[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,mid,0); + SCRIPT_SKIP_SPACES + char right[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,right,0); + SCRIPT_SKIP_SPACES + + WSContentSend_PD(SCRIPT_MSG_SLIDER,left,mid,right,(uint32_t)min,(uint32_t)max,(uint32_t)val,vname); + + + } else if (!strncmp(line,"ck(",3)) { + char *lp=line+3; + char *slp=lp; + float val; + lp=GetNumericResult(lp,OPER_EQU,&val,0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname,slp,sizeof(vname)); + + char label[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,label,0); + char *cp; + uint8_t uval; + if (val>0) { + cp="checked='checked'"; + uval=0; + } else { + cp=""; + uval=1; + } + WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,cp,uval,vname); + + } else if (!strncmp(line,"bu(",3)) { + char *lp=line+3; + char *slp=lp; + float val; + lp=GetNumericResult(lp,OPER_EQU,&val,0); + SCRIPT_SKIP_SPACES + + char vname[16]; + ScriptGetVarname(vname,slp,sizeof(vname)); + + SCRIPT_SKIP_SPACES + char ontxt[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,ontxt,0); + SCRIPT_SKIP_SPACES + char offtxt[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,offtxt,0); + + char *cp; + uint8_t uval; + if (val>0) { + cp=ontxt; + uval=0; + } else { + cp=offtxt; + uval=1; + } + WSContentSend_PD(SCRIPT_MSG_BUTTON,uval,vname,cp); + + } else if (!strncmp(line,"tx(",3)) { + char *lp=line+3; + char *slp=lp; + char str[SCRIPT_MAXSSIZE]; + lp=ForceStringVar(lp,str); + SCRIPT_SKIP_SPACES + char label[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,label,0); + + char vname[16]; + ScriptGetVarname(vname,slp,sizeof(vname)); + + WSContentSend_PD(SCRIPT_MSG_TEXTINP,label,str,vname); + + } + else { + Replace_Cmd_Vars(line,tmp,sizeof(tmp)); + if (tmp[0]=='@') { + WSContentSend_PD(PSTR("
%s
"),&tmp[1]); + } else { + WSContentSend_PD(PSTR("{s}%s{e}"),tmp); + } + } } if (*lp==SCRIPT_EOL) { lp++; @@ -3769,6 +3938,7 @@ bool Xdrv10(uint8_t function) case FUNC_WEB_ADD_HANDLER: WebServer->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); WebServer->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); + #ifdef USE_SCRIPT_FATFS WebServer->on("/u3", HTTP_POST,[]() { WebServer->sendHeader("Location","/u3");WebServer->send(303);},script_upload); WebServer->on("/u3", HTTP_GET,ScriptFileUploadSuccess); From 37f6fc6bac9e974260b442e4e71d4b57b73c674c Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 16 Sep 2019 19:58:22 +0200 Subject: [PATCH 39/44] sml, median filter, mutiple modbus, pzem --- sonoff/xsns_53_sml.ino | 291 +++++++++++++++++++++++++++++++---------- 1 file changed, 219 insertions(+), 72 deletions(-) diff --git a/sonoff/xsns_53_sml.ino b/sonoff/xsns_53_sml.ino index bb8ba1e4c4ca..77c88bd36537 100644 --- a/sonoff/xsns_53_sml.ino +++ b/sonoff/xsns_53_sml.ino @@ -22,7 +22,6 @@ */ #ifdef USE_SML_M -// #define XSNS_53 53 @@ -30,13 +29,14 @@ #define SML_BAUDRATE 9600 // send this every N seconds (for meters that only send data on demand) +// not longer supported, use scripting instead //#define SML_SEND_SEQ // debug counter input to led for counter1 and 2 //#define DEBUG_CNT_LED1 2 //#define DEBUG_CNT_LED1 2 -// use analog optical counter sensor with AD Converter ADS1115 (not yet) +// use analog optical counter sensor with AD Converter ADS1115 (not yet functional) //#define ANALOG_OPTO_SENSOR // fototransistor with pullup at A0, A1 of ADS1115 A3 and +3.3V // level and amplification are automatically set @@ -44,7 +44,7 @@ #include -// use special no wait serial driver +// use special no wait serial driver, should be always on #define SPECIAL_SS // addresses a bug in meter DWS74 @@ -147,6 +147,9 @@ struct METER_DESC { uint8_t max_index; }; +// this descriptor method is no longer supported +// but still functional for simple meters +// use scripting method instead // meter list , enter new meters here //===================================================== #define EHZ161_0 1 @@ -391,7 +394,7 @@ const uint8_t meter[]= #define METERS_USED 1 struct METER_DESC const meter_desc[METERS_USED]={ [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; -// 2 Richtungszähler EHZ SML 8 bit 9600 baud, binär +// 2 direction meter EHZ SML 8 bit 9600 baud, binary const uint8_t meter[]= //0x77,0x07,0x01,0x00,0x01,0x08,0x00,0xff "1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" @@ -407,7 +410,7 @@ const uint8_t meter[]= "1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; #endif -// Beispiel für einen OBIS Stromzähler und einen Gaszähler + Wasserzähler +// example OBIS power meter + gas and water counter #if METER==COMBO3b #undef METERS_USED #define METERS_USED 3 @@ -416,14 +419,14 @@ struct METER_DESC const meter_desc[METERS_USED]={ [1]={14,'c',0,50,"Gas"}, // GPIO14 gas counter [2]={1,'c',0,10,"Wasser"}}; // water counter -// 3 Zähler definiert +// 3 meters defined const uint8_t meter[]= "1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" "1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" "1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" "1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -// bei gaszählern (countern) muss der Vergleichsstring so aussehen wie hier +// with counters the comparison string must be exactly this string "2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" "3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; @@ -435,8 +438,8 @@ const uint8_t meter[]= #define METERS_USED 3 struct METER_DESC const meter_desc[METERS_USED]={ - [0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 Wasser Zähler - [1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas Zähler + [0]={1,'c',0,10,"H20",-1,1,0}, // GPIO1 water counter + [1]={4,'c',0,50,"GAS",-1,1,0}, // GPIO4 gas counter [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; // SML harware serial RX pin const uint8_t meter[]= @@ -491,10 +494,12 @@ const uint8_t meter[]= // median filter eliminates outliers, but uses much RAM and CPU cycles // 672 bytes extra RAM with MAX_VARS = 16 -//#define USE_MEDIAN_FILTER +// default compile on, but must be enabled by descriptor flag 16 +// may be undefined if RAM must be saved +#define USE_SML_MEDIAN_FILTER // max number of vars , may be adjusted -#define MAX_VARS 16 +#define MAX_VARS 20 // max number of meters , may be adjusted #define MAX_METERS 5 double meter_vars[MAX_VARS]; @@ -506,6 +511,7 @@ uint8_t meters_used; struct METER_DESC const *meter_desc_p; const uint8_t *meter_p; +uint8_t meter_spos[MAX_METERS]; // software serial pointers TasmotaSerial *meter_ss[MAX_METERS]; @@ -520,24 +526,58 @@ char meter_id[MAX_METERS][METER_ID_SIZE]; #define EBUS_SYNC 0xaa #define EBUS_ESC 0xa9 -uint8_t ebus_pos; -uint8_t mbus_pos; -#ifdef USE_MEDIAN_FILTER +uint8_t sml_send_blocks; +uint8_t sml_100ms_cnt; +uint8_t sml_desc_cnt; + +#ifdef USE_SML_MEDIAN_FILTER // median filter, should be odd size #define MEDIAN_SIZE 5 -struct MEDIAN_FILTER { +struct SML_MEDIAN_FILTER { double buffer[MEDIAN_SIZE]; int8_t index; } sml_mf[MAX_VARS]; + +double sml_median_array(double *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + double min=FLT_MAX; + + for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; mf->index++; if (mf->index>=MEDIAN_SIZE) mf->index=0; + + return sml_median_array(mf->buffer,MEDIAN_SIZE); +/* // sort list and take median memmove(tbuff,mf->buffer,sizeof(tbuff)); for (byte ocnt=0; ocntavailable()) { + meter_ss[meters]->read(); + } +} + + void sml_shift_in(uint32_t meters,uint32_t shard) { uint32_t count; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m') { + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') { // shift in for (count=0; count=9) { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=9) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else if (meter_desc_p[meters].type=='p') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=7) { SML_Decode(meters); - mbus_pos=0; + sml_empty_receiver(meters); + meter_spos[meters]=0; } } else { if (iob==EBUS_SYNC) { // should be end of telegramm // QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC - if (ebus_pos>4+5) { + if (meter_spos[meters]>4+5) { // get telegramm lenght uint8_t tlen=smltbuf[meters][4]+5; // test crc if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { ebus_esc(smltbuf[meters],tlen); - //eBus_analyze(); - // XX0204UUSS@ SML_Decode(meters); - //AddLog_P(LOG_LEVEL_INFO, PSTR("ebus block found")); - //ebus_set_timeout(); } else { // crc error //AddLog_P(LOG_LEVEL_INFO, PSTR("ebus crc error")); } } - ebus_pos=0; + meter_spos[meters]=0; return; } - smltbuf[meters][ebus_pos] = iob; - ebus_pos++; - if (ebus_pos>=SML_BSIZ) { - ebus_pos=0; + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; } } sb_counter++; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m') SML_Decode(meters); + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') SML_Decode(meters); } @@ -1356,14 +1407,22 @@ void SML_Decode(uint8_t index) { found=0; } } else { - // ebus mbus or raw + // ebus mbus pzem or raw // XXHHHHSSUU if (*mp=='x' && *(mp+1)=='x') { //ignore mp+=2; cp++; - } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='u' && *(mp+3)=='u'){ - uint16_t val = *cp|(*(cp+1)<<8); + } else if (!strncmp(mp,"uuuuuuuu",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } + else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[0]|(cp[1]<<8); + mbus_dval=val; ebus_dval=val; mp+=4; cp+=2; @@ -1383,13 +1442,44 @@ void SML_Decode(uint8_t index) { ebus_dval=val; mp+=2; } - else if (*mp=='f' && *(mp+1)=='f' && *(mp+2)=='f' && *(mp+3)=='f' && *(mp+4)=='f' && *(mp+5)=='f' && *(mp+6)=='f' && *(mp+7)=='f') { - uint32_t val= (*(cp+0)<<24)|(*(cp+1)<<16)|(*(cp+2)<<8)|(*(cp+3)<<0); + else if (!strncmp(mp,"ffffffff",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); float *fp=(float*)&val; + ebus_dval=*fp; mbus_dval=*fp; mp+=8; cp+=4; } + else if (!strncmp(mp,"FFffFFff",8)) { + // reverse word float + uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"eeeeee",6)) { + uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); + mbus_dval=val; + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"vvvvvv",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"cccccc",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"pppp",4)) { + mbus_dval=(float)((cp[0]<<8)|cp[1]); + mp+=4; + cp+=2; + } else { uint8_t val = hexnibble(*mp++) << 4; val |= hexnibble(*mp++); @@ -1419,15 +1509,15 @@ void SML_Decode(uint8_t index) { } } else { double dval; - if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m') { + if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='p') { // get numeric values if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { - dval=xCharToDouble((char*)cp); + dval=CharToDouble((char*)cp); } else { dval=sml_getvalue(cp,mindex); } } else { - // ebus or mbus + // ebus pzem or mbus or raw if (*mp=='b') { mp++; uint8_t shift=*mp&7; @@ -1449,17 +1539,28 @@ void SML_Decode(uint8_t index) { //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp); mp++; } else { - dval=ebus_dval; + if (meter_desc_p[mindex].type=='p') { + uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); + if (crc!=smltbuf[mindex][6]) goto nextsect; + dval=mbus_dval; + } else { + dval=ebus_dval; + } } } -#ifdef USE_MEDIAN_FILTER - meter_vars[vindex]=median(&sml_mf[vindex],dval); +#ifdef USE_SML_MEDIAN_FILTER + if (meter_desc_p[mindex].flag&16) { + meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); + } else { + meter_vars[vindex]=dval; + } #else meter_vars[vindex]=dval; #endif +//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp); // get scaling factor - double fac=xCharToDouble((char*)mp); + double fac=CharToDouble((char*)mp); meter_vars[vindex]/=fac; SML_Immediate_MQTT((const char*)mp,vindex,mindex); } @@ -1616,7 +1717,7 @@ void SML_Show(boolean json) { if (lastmind!=mindex) { // meter changed, close mqtt //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data); - ResponseJsonEnd(); + ResponseAppend_P(PSTR("}")); // and open new //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr); ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"),meter_desc_p[mindex].prefix,jname,tpowstr); @@ -1643,7 +1744,7 @@ void SML_Show(boolean json) { if (json) { //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data); //ResponseAppend_P(PSTR("%s"),b_mqtt_data); - ResponseJsonEnd(); + ResponseAppend_P(PSTR("}")); } else { //WSContentSend_PD(PSTR("%s"),b_mqtt_data); } @@ -1733,10 +1834,15 @@ void SML_Init(void) { meter_desc_p=meter_desc; meter_p=meter; + sml_desc_cnt=0; + for (uint32_t cnt=0;cntM",-2,0); if (meter_script==99) { // use script definition if (script_meter) free(script_meter); + script_meter=0; uint8_t *tp=0; uint16_t index=0; uint8_t section=0; + uint8_t srcpin=0; char *lp=glob_script_mem.scriptptr; + sml_send_blocks=0; while (lp) { if (!section) { if (*lp=='>' && *(lp+1)=='M') { @@ -1770,7 +1879,9 @@ void SML_Init(void) { } if (mlen==0) return; // missing end # script_meter=(uint8_t*)calloc(mlen,1); - if (!script_meter) return; + if (!script_meter) { + goto dddef_exit; + } tp=script_meter; goto next_line; } @@ -1784,14 +1895,18 @@ void SML_Init(void) { // add descriptor +1,1,c,0,10,H20 //toLogEOL(">>",lp); lp++; - uint8_t index=*lp&7; + index=*lp&7; lp+=2; if (index<1 || index>meters_used) goto next_line; index--; - uint8_t srcpin=strtol(lp,&lp,10); + srcpin=strtol(lp,&lp,10); if (Gpio_used(srcpin)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("gpio double define!")); - return; + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); +dddef_exit: + if (script_meter) free(script_meter); + script_meter=0; + meters_used=METERS_USED; + goto init10; } script_meter_desc[index].srcpin=srcpin; if (*lp!=',') goto next_line; @@ -1815,6 +1930,10 @@ void SML_Init(void) { if (*lp==',') { lp++; script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (Gpio_used(script_meter_desc[index].trxpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); + goto dddef_exit; + } if (*lp!=',') goto next_line; lp++; script_meter_desc[index].tsecs=strtol(lp,&lp,10); @@ -1838,6 +1957,7 @@ void SML_Init(void) { } script_meter_desc[index].index=0; script_meter_desc[index].max_index=tx_entries; + sml_send_blocks++; } } } @@ -1879,6 +1999,7 @@ next_line: } #endif +init10: typedef void (*function)(); function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; uint8_t cindex=0; @@ -1916,7 +2037,7 @@ next_line: } else { // serial input, init #ifdef SPECIAL_SS - if (meter_desc_p[meters].type=='m') { + if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='p') { meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1); } else { meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1); @@ -2028,26 +2149,38 @@ char *SML_Get_Sequence(char *cp,uint32_t index) { } } -uint8_t sml_250ms_cnt; - - void SML_Check_Send(void) { - sml_250ms_cnt++; - for (uint32_t cnt=0; cnt=0 && script_meter_desc[cnt].txmem) { - if ((sml_250ms_cnt%script_meter_desc[cnt].tsecs)==0) { + if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { if (script_meter_desc[cnt].max_index>1) { script_meter_desc[cnt].index++; if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { script_meter_desc[cnt].index=0; + sml_desc_cnt++; } - char *cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); - SML_Send_Seq(cnt,cp); - //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp); + cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + //SML_Send_Seq(cnt,cp); } else { - SML_Send_Seq(cnt,script_meter_desc[cnt].txmem); + cp=script_meter_desc[cnt].txmem; + //SML_Send_Seq(cnt,cp); + sml_desc_cnt++; + } + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),cp); + SML_Send_Seq(cnt,cp); + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; } + break; } + } else { + sml_desc_cnt++; + } + + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; } } } @@ -2066,7 +2199,7 @@ uint8_t sml_hexnibble(char chr) { // send sequence every N Seconds void SML_Send_Seq(uint32_t meter,char *seq) { uint8_t sbuff[32]; - uint8_t *ucp=sbuff,slen; + uint8_t *ucp=sbuff,slen=0; char *cp=seq; while (*cp) { if (!*cp || !*(cp+1)) break; @@ -2086,6 +2219,15 @@ void SML_Send_Seq(uint32_t meter,char *seq) { *ucp++=highByte(crc); slen+=4; } + if (script_meter_desc[meter].type=='p') { + *ucp++=0xc0; + *ucp++=0xa8; + *ucp++=1; + *ucp++=1; + *ucp++=0; + *ucp++=SML_PzemCrc(sbuff,6); + slen+=6; + } meter_ss[meter]->write(sbuff,slen); } #endif // USE_SCRIPT @@ -2107,6 +2249,11 @@ uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { return crc; } +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { + uint16_t crc = 0; + for (uint32_t i = 0; i < len; i++) crc += *data++; + return (uint8_t)(crc & 0xFF); +} /* // for odd parity init with 1 uint8_t CalcEvenParity(uint8_t data) { @@ -2200,7 +2347,7 @@ bool Xsns53(byte function) { else SML_Poll(); break; #ifdef USE_SCRIPT - case FUNC_EVERY_250_MSECOND: + case FUNC_EVERY_100_MSECOND: SML_Check_Send(); break; #endif // USE_SCRIPT From 90c7ad41ddfaa5d859a5b1a09951b787dfc5e855 Mon Sep 17 00:00:00 2001 From: Michael Ingraham <34340210+meingraham@users.noreply.github.com> Date: Tue, 17 Sep 2019 21:47:26 -0400 Subject: [PATCH 40/44] 2.6 Core --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a5e66c306986..fb31333b554f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,6 @@ - [ ] The pull request is done against the latest dev branch - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR. - - [ ] The code change is tested and works on core 2.3.0, 2.4.2 and 2.5.2 + - [ ] The code change is tested and works on core 2.3.0, 2.4.2, 2.5.2, and pre-2.6 - [ ] The code change pass travis tests. **Your PR cannot be merged unless tests pass** - [ ] I accept the [CLA](/~https://github.com/arendst/Sonoff-Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). From fd859919e3ab81c65ecd1e45a3e218955605c178 Mon Sep 17 00:00:00 2001 From: Michael Ingraham <34340210+meingraham@users.noreply.github.com> Date: Tue, 17 Sep 2019 22:29:35 -0400 Subject: [PATCH 41/44] Additional information --- .github/ISSUE_TEMPLATE/Bug_report.md | 72 ++++++++++++++++------------ 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index eeb30307dd36..8dfbe91687d3 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,28 +1,27 @@ --- name: Bug report about: Create a report to help us improve - --- - - - - - - - - - - - -Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. +> **GUIDE** +> +> This BUG issue template is meant to REPORT Tasmota software BUGS ONLY> +> +> Please DO NOT OPEN AN ISSUE: +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/> +> - If your issue is a flashing issue, please address it to the Tasmota Support Chat> +> - If your issue is compilation problem, please address it to the Tasmota Support Chat> +> - If your issue has been addresed before (duplicated issue), please ask in the original issue> +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the FAQ and troubleshooting wiki articles> +> +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. ### BUG DESCRIPTION _A clear and concise description of what the bug is._ ### REQUESTED INFORMATION -_Make sure these boxes are checked before submitting your issue. Thank you_ +_Make sure your have performed every step and checked the applicable boxes before submitting your issue. Thank you!_ **FAILURE TO COMPLETE THE REQUESTED INFORMATION WILL RESULT IN YOUR ISSUE BEING CLOSED** @@ -31,22 +30,35 @@ _Make sure these boxes are checked before submitting your issue. Thank you_ - [ ] 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) -- [ ] Device used (i.e. Sonoff Basic) : _____ -- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) -- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ -- [ ] Provide the output of command ``status 0`` : -``` -STATUS 0 OUTPUT HERE: - - -``` -- [ ] Provide the output of console when you experience your issue if apply : -_(Please use_ ``weblog 4`` _for more debug information)_ -``` -CONSOLE OUTPUT HERE: - - -``` +- [ ] Device used (i.e. Sonoff Basic): _____ +- [ ] Tasmota binary firmware version number used: _____ + - [ ] Pre-compiled + - [ ] Self-compiled + - [ ] IDE / Compiler +- [ ] Flashing tools used: _____ +- [ ] Provide the output of command ``Backlog Template; Module; GPIO``: + ``` + Configuration output here: + + ``` +- [ ] If using rules, provide the output of command ``Backlog Rule1; Rule2; Rule3``: + ``` + Rules output here: + + ``` +- [ ] Provide the output of command ``Status 0``: + ``` + STATUS 0 output here: + + + ``` +- [ ] Provide the output of console when you experience your issue if applicable: + _(Please use_ ``weblog 4`` _for more debug information)_ + ``` + Console output here: + + + ``` ### TO REPRODUCE _Steps to reproduce the behavior:_ From 93ff15c556a526525686b42bc45ed525cb0d65ba Mon Sep 17 00:00:00 2001 From: Michael Ingraham <34340210+meingraham@users.noreply.github.com> Date: Tue, 17 Sep 2019 22:31:38 -0400 Subject: [PATCH 42/44] Formatting --- .github/ISSUE_TEMPLATE/Feature_request.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index edc2ea93e52b..5aa100e01075 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -1,22 +1,22 @@ --- name: Feature request about: Suggest an idea for this project - --- -Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. -**Have you looked for this feature in other issues and in the wiki?** +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. + +**Have you looked for this feature in other issues and in the wiki?** -**Is your feature request related to a problem? Please describe.** +**Is your feature request related to a problem? Please describe.** _A clear and concise description of what the problem is._ -**Describe the solution you'd like** +**Describe the solution you'd like** _A clear and concise description of what you want to happen._ -**Describe alternatives you've considered** +**Describe alternatives you've considered** _A clear and concise description of any alternative solutions or features you've considered._ -**Additional context** +**Additional context** _Add any other context or screenshots about the feature request here._ -**(Please, remember to close the issue when the problem has been addressed)** +**(Please, remember to close the issue when the problem has been addressed)** From 04f59d3ece57a94f0871562f6a79d8ec84e3d982 Mon Sep 17 00:00:00 2001 From: Michael Ingraham <34340210+meingraham@users.noreply.github.com> Date: Tue, 17 Sep 2019 22:40:35 -0400 Subject: [PATCH 43/44] Additional information --- .github/ISSUE_TEMPLATE/Custom.md | 73 +++++++++++++++++++------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index b0662d317519..d5c6bd89ccf4 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -1,23 +1,22 @@ --- name: Troubleshooting about: Users Troubleshooting Help - --- - - - - - - - - - - - - - -Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. +> **GUIDE** +> +> This troubleshooting issue template is meant to help Tasmota users with difficult problems. It is aimed to be opened if using the wiki and the support chat could not solve the issue. The Github Issue tracker is NOT a general discussion forum! +> +> Please DO NOT OPEN AN ISSUE: +> - If you have general questions or you need help on Tasmota usage, go to the Tasmota support chat +> - If your Tasmota version is not the latest from the development branch, please update your device before submitting your issue. Your problem might already be solved. The latest precompiled binaries of Tasmota can be downloaded from http://thehackbox.org/tasmota/ +> - If your issue is about a new device, please use the Tasmota [Template](../wiki/Templates) feature. +> - If your issue is a flashing issue, please address it to the Tasmota Support Chat +> - If your issue is compilation problem, please address it to the Tasmota Support Chat +> - If your issue has been addresed before (duplicated issue), please ask in the original issue +> - If your issue is a Wi-Fi problem or MQTT problem, please try the steps provided in the FAQ and troubleshooting wiki articles +> +> Please take a few minutes to complete the requested information below. Our ability to provide assistance is greatly hampered without it. The details requested potentially affect which options to pursue. The small amount of time you spend completing the template will also help the volunteers providing the assistance to you to reduce the time required to help you. ### ISSUE DESCRIPTION - TROUBLESHOOTING _A clear description of what the issue is and be as extensive as possible_ @@ -33,20 +32,34 @@ _Make sure these boxes are checked before submitting your issue. Thank you_ - [ ] 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) -- [ ] Device used (i.e. Sonoff Basic) : _____ -- [ ] Tasmota binary firmware version number used : ____ / (pre-compiled or self-compiled ?) -- [ ] Development IDE - Compiler / Upload tools used : ____ / ____ -- [ ] Provide the output of command ``status 0`` : -``` -STATUS 0 OUTPUT HERE: - - -``` -- [ ] Provide the output of console when you experience your issue if apply : -_(Please use_ ``weblog 4`` _for more debug information)_ -``` -CONSOLE OUTPUT HERE: - +- [ ] Device used (e.g., Sonoff Basic): _____ +- [ ] Tasmota binary firmware version number used: _____ + - [ ] Pre-compiled + - [ ] Self-compiled + - [ ] IDE / Compiler +- [ ] Flashing tools used: _____ +- [ ] Provide the output of command ``Backlog Template; Module; GPIO``: + ``` + Configuration output here: + + ``` +- [ ] If using rules, provide the output of command ``Backlog Rule1; Rule2; Rule3``: + ``` + Rules output here: + + ``` +- [ ] Provide the output of command ``Status 0``: + ``` + STATUS 0 output here: + + + ``` +- [ ] Provide the output of console when you experience your issue if applicable: + _(Please use_ ``weblog 4`` _for more debug information)_ + ``` + Console output here: + + + ``` -``` **(Please, remember to close the issue when the problem has been addressed)** From 0fbd5afd945dfb0592b96eec8e96527be02bf78c Mon Sep 17 00:00:00 2001 From: Michael Ingraham <34340210+meingraham@users.noreply.github.com> Date: Tue, 17 Sep 2019 22:41:46 -0400 Subject: [PATCH 44/44] 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 8dfbe91687d3..2a962ecb73f4 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -30,7 +30,7 @@ _Make sure your have performed every step and checked the applicable boxes befor - [ ] 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) -- [ ] Device used (i.e. Sonoff Basic): _____ +- [ ] Device used (e.g., Sonoff Basic): _____ - [ ] Tasmota binary firmware version number used: _____ - [ ] Pre-compiled - [ ] Self-compiled