diff --git a/CHANGELOG.md b/CHANGELOG.md index 01713c1f37a0..8537cae71f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,19 +3,27 @@ All notable changes to this project will be documented in this file. ## [Released] -## [12.4.0] 20230215 +## [12.4.0] 20230216 - Release Peter -## [12.3.1.6] 20230215 +## [12.3.1.6] 20230216 ### Added - ESP32 preliminary support for Matter protocol, milestone 1 (commissioning) by Stephan Hadinger - Basic support for Shelly Pro 4PM +- Command ``DhtDelay ,`` to allow user control over high and low delay in microseconds (#17944) +- Berry `int64.fromstring()` to convert a string to an int64 (#17953) ### Breaking Changed - TM1638 button and led support are handled as virtual switches and relays (#11031) +### Changed +- Dht driver from v6 to v7 +- LVGL allow access to `lv.LAYOUT_GRID` and `lv.LAYOUT_FLEX` (#17948) +- TuyaMcu support of virtual switches + ### Fixed - ESP8266 Fix TLS SNI which would prevent AWS IoT connection (#17936) +- TuyaMcu exception 3 regression from v12.3.1.4 ## [12.3.1.5] 20230208 ### Added diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0258cb061e02..7fa36024ee49 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -112,6 +112,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ## Changelog v12.4.0 Peter ### Added +- Command ``DhtDelay ,`` to allow user control over high and low delay in microseconds [#17944](https://github.com/arendst/Tasmota/issues/17944) - Support for up to 3 (ESP8266) or 8 (ESP32) phase modbus energy monitoring device using generic Energy Modbus driver - Support for RGB displays [#17414](https://github.com/arendst/Tasmota/issues/17414) - Support for IPv6 DNS records (AAAA) and IPv6 ``Ping`` for ESP32 and ESP8266 [#17417](https://github.com/arendst/Tasmota/issues/17417) @@ -126,6 +127,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry crypto add ``HKDF_HMAC_SHA256`` - Berry crypto add ``SPAKE2P_Matter`` for Matter support - Berry add ``mdns`` advanced features and query +- Berry `int64.fromstring()` to convert a string to an int64 [#17953](https://github.com/arendst/Tasmota/issues/17953) - ESP32 command ``EnergyCols 1..8`` to change number of GUI columns - ESP32 command ``EnergyDisplay 1..3`` to change GUI column presentation - ESP32 support for eigth energy phases/channels @@ -137,13 +139,16 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - TM1638 button and led support are handled as virtual switches and relays [#11031](https://github.com/arendst/Tasmota/issues/11031) ### Changed +- Dht driver from v6 to v7 - ESP32 Framework (Core) from v2.0.5.3 to v2.0.6 (IPv6 support) - Energy totals max supported value from +/-21474.83647 to +/-2147483.647 kWh - Removed delays in TasmotaSerial and TasmotaModbus Tx enable switching - Keep webserver enabled on command ``upload`` - Better support for virtual buttons and switches up to a total of 28 +- TuyaMcu support of virtual switches - Increase rule event buffer from 100 to 256 characters [#16943](https://github.com/arendst/Tasmota/issues/16943) - Tasmota OTA scripts now support both unzipped and gzipped file uploads [#17378](https://github.com/arendst/Tasmota/issues/17378) +- LVGL allow access to `lv.LAYOUT_GRID` and `lv.LAYOUT_FLEX` [#17948](https://github.com/arendst/Tasmota/issues/17948) ### Fixed - Modbus transmit enable GPIO enabled once during write buffer diff --git a/lib/libesp32/berry_int64/src/be_int64_class.c b/lib/libesp32/berry_int64/src/be_int64_class.c index 5cbfa54d21cd..5b3d3e3c27b2 100644 --- a/lib/libesp32/berry_int64/src/be_int64_class.c +++ b/lib/libesp32/berry_int64/src/be_int64_class.c @@ -53,6 +53,15 @@ char* int64_tostring(int64_t *i64) { } BE_FUNC_CTYPE_DECLARE(int64_tostring, "s", ".") +int64_t* int64_fromstring(bvm *vm, const char* s) { + int64_t *i64 = (int64_t*)be_malloc(vm, sizeof(int64_t)); + if (i64 == NULL) { be_raise(vm, "memory_error", "cannot allocate buffer"); } + if (s) { *i64 = atoll(s); } + else { *i64 = 0; } + return i64; +} +BE_FUNC_CTYPE_DECLARE(int64_fromstring, "int64", "@s") + int32_t int64_toint(int64_t *i64) { return (int32_t) *i64; } @@ -190,6 +199,7 @@ class be_class_int64 (scope: global, name: int64) { set, ctype_func(int64_set) tostring, ctype_func(int64_tostring) + fromstring, static_ctype_func(int64_fromstring) toint, ctype_func(int64_toint) +, ctype_func(int64_add) diff --git a/lib/libesp32/berry_matter/src/embedded/Matter_Device.be b/lib/libesp32/berry_matter/src/embedded/Matter_Device.be index 39ba9116023a..7765c16b838d 100644 --- a/lib/libesp32/berry_matter/src/embedded/Matter_Device.be +++ b/lib/libesp32/berry_matter/src/embedded/Matter_Device.be @@ -545,7 +545,7 @@ class Matter_Device var wifi = tasmota.wifi() self.hostname_wifi = string.replace(wifi.find("mac"), ':', '') mdns.add_hostname(self.hostname_wifi, wifi.find('ip6local',''), wifi.find('ip',''), wifi.find('ip6','')) - mdns.add_service("_matter", "_tcp", 5540, services, self.commissioning_instance_wifi, self.hostname_wifi) + mdns.add_service("_matterc", "_udp", 5540, services, self.commissioning_instance_wifi, self.hostname_wifi) tasmota.log(string.format("MTR: starting mDNS on %s '%s' ptr to `%s.local`", is_eth ? "eth" : "wifi", is_eth ? self.commissioning_instance_eth : self.commissioning_instance_wifi, diff --git a/lib/libesp32/berry_matter/src/solidify/solidified_Matter_Device.h b/lib/libesp32/berry_matter/src/solidify/solidified_Matter_Device.h index 982db1afd80e..b5267830251e 100644 --- a/lib/libesp32/berry_matter/src/solidify/solidified_Matter_Device.h +++ b/lib/libesp32/berry_matter/src/solidify/solidified_Matter_Device.h @@ -1940,7 +1940,7 @@ be_local_closure(Matter_Device__start_mdns_announce, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - ( &(const bvalue[50]) { /* constants */ + ( &(const bvalue[48]) { /* constants */ /* K0 */ be_nested_str_weak(mdns), /* K1 */ be_nested_str_weak(string), /* K2 */ be_nested_str_weak(start), @@ -1986,11 +1986,9 @@ be_local_closure(Matter_Device__start_mdns_announce, /* name */ /* K42 */ be_nested_str_weak(_S), /* K43 */ be_nested_str_weak(_V), /* K44 */ be_nested_str_weak(_CM1), - /* K45 */ be_nested_str_weak(_matter), - /* K46 */ be_nested_str_weak(_tcp), - /* K47 */ be_nested_str_weak(MTR_X3A_X20Exception), - /* K48 */ be_nested_str_weak(_X7C), - /* K49 */ be_nested_str_weak(mdns_announce_op_discovery_all_sessions), + /* K45 */ be_nested_str_weak(MTR_X3A_X20Exception), + /* K46 */ be_nested_str_weak(_X7C), + /* K47 */ be_nested_str_weak(mdns_announce_op_discovery_all_sessions), }), be_str_weak(_start_mdns_announce), &be_const_str_solidified, @@ -2170,8 +2168,8 @@ be_local_closure(Matter_Device__start_mdns_announce, /* name */ 0x7C2C0600, // 00AC CALL R11 3 0x7C180A00, // 00AD CALL R6 5 0x8C18051B, // 00AE GETMET R6 R2 K27 - 0x5820002D, // 00AF LDCONST R8 K45 - 0x5824002E, // 00B0 LDCONST R9 K46 + 0x5820001C, // 00AF LDCONST R8 K28 + 0x5824001D, // 00B0 LDCONST R9 K29 0x542A15A3, // 00B1 LDINT R10 5540 0x5C2C0800, // 00B2 MOVE R11 R4 0x88300123, // 00B3 GETMBR R12 R0 K35 @@ -2274,8 +2272,8 @@ be_local_closure(Matter_Device__start_mdns_announce, /* name */ 0x60240008, // 0114 GETGBL R9 G8 0x5C280A00, // 0115 MOVE R10 R5 0x7C240200, // 0116 CALL R9 1 - 0x00265E09, // 0117 ADD R9 K47 R9 - 0x00241330, // 0118 ADD R9 R9 K48 + 0x00265A09, // 0117 ADD R9 K45 R9 + 0x0024132E, // 0118 ADD R9 R9 K46 0x60280008, // 0119 GETGBL R10 G8 0x5C2C0C00, // 011A MOVE R11 R6 0x7C280200, // 011B CALL R10 1 @@ -2284,7 +2282,7 @@ be_local_closure(Matter_Device__start_mdns_announce, /* name */ 0x7C1C0600, // 011E CALL R7 3 0x70020000, // 011F JMP #0121 0xB0080000, // 0120 RAISE 2 R0 R0 - 0x8C140131, // 0121 GETMET R5 R0 K49 + 0x8C14012F, // 0121 GETMET R5 R0 K47 0x7C140200, // 0122 CALL R5 1 0x80000000, // 0123 RET 0 }) diff --git a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c index a0122baf3907..f628ac3a9abf 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c +++ b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c @@ -8,15 +8,19 @@ #include "lvgl.h" #include "be_mapping.h" +#include "be_ctypes.h" #include "lv_berry.h" #include "lv_theme_haspmota.h" +// declare accessors for non-const ints +int32_t be_LV_LAYOUT_GRID(void) { return LV_LAYOUT_GRID; }; BE_VAR_CTYPE_DECLARE(be_LV_LAYOUT_GRID, "i"); +int32_t be_LV_LAYOUT_FLEX(void) { return LV_LAYOUT_FLEX; }; BE_VAR_CTYPE_DECLARE(be_LV_LAYOUT_FLEX, "i"); + extern int lv0_member(bvm *vm); // resolve virtual members extern int lv0_load_font(bvm *vm); extern lv_ts_calibration_t * lv_get_ts_calibration(void); - static int lv_get_hor_res(void) { return lv_disp_get_hor_res(lv_disp_get_default()); } @@ -502,6 +506,8 @@ const be_const_member_t lv0_constants[] = { { "LAYER_TYPE_NONE", be_cconst_int(LV_LAYER_TYPE_NONE) }, { "LAYER_TYPE_SIMPLE", be_cconst_int(LV_LAYER_TYPE_SIMPLE) }, { "LAYER_TYPE_TRANSFORM", be_cconst_int(LV_LAYER_TYPE_TRANSFORM) }, + { ">LAYOUT_FLEX", be_ctype(be_LV_LAYOUT_FLEX) }, + { ">LAYOUT_GRID", be_ctype(be_LV_LAYOUT_GRID) }, { "LED_DRAW_PART_RECTANGLE", be_cconst_int(LV_LED_DRAW_PART_RECTANGLE) }, { "LOG_LEVEL_ERROR", be_cconst_int(LV_LOG_LEVEL_ERROR) }, { "LOG_LEVEL_INFO", be_cconst_int(LV_LOG_LEVEL_INFO) }, diff --git a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h index 71d8989ed090..dd05cec1bec4 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h +++ b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h @@ -12,6 +12,8 @@ anim_path_ease_out=&lv_anim_path_ease_out anim_path_linear=&lv_anim_path_linear anim_path_overshoot=&lv_anim_path_overshoot anim_path_step=&lv_anim_path_step +LV_LAYOUT_GRID=>be_LV_LAYOUT_GRID +LV_LAYOUT_FLEX=>be_LV_LAYOUT_FLEX // ====================================================================== // Colors diff --git a/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_extra.h b/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_extra.h index 12539d82d1f3..446b427ecbee 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_extra.h +++ b/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_extra.h @@ -49,6 +49,8 @@ be_local_closure(lv_extra__anonymous_, /* name */ /*******************************************************************/ +extern const bclass be_class_lv_coord_arr; + /******************************************************************** ** Solidified function: init ********************************************************************/ @@ -217,6 +219,8 @@ be_local_class(lv_coord_arr, be_str_weak(lv_coord_arr) ); +extern const bclass be_class_lv_point_arr; + /******************************************************************** ** Solidified function: init ********************************************************************/ @@ -319,6 +323,8 @@ be_local_class(lv_point_arr, be_str_weak(lv_point_arr) ); +extern const bclass be_class_lv_style_prop_arr; + /******************************************************************** ** Solidified function: init ********************************************************************/ diff --git a/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_glob.h b/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_glob.h index 4800b5d695f4..b4f4c5e9ec3b 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_glob.h +++ b/lib/libesp32_lvgl/lv_binding_berry/src/solidify/solidified_lvgl_glob.h @@ -4,6 +4,8 @@ \********************************************************************/ #include "be_constobj.h" +extern const bclass be_class_LVGL_glob; + /******************************************************************** ** Solidified function: get_object_from_ptr ********************************************************************/ diff --git a/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py b/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py index e3df18958a08..8089c817e58e 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py +++ b/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py @@ -718,15 +718,19 @@ class be_class_lv_{subtype} (scope: global, name: lv_{subtype}, super: be_class_ #include "lvgl.h" #include "be_mapping.h" +#include "be_ctypes.h" #include "lv_berry.h" #include "lv_theme_haspmota.h" +// declare accessors for non-const ints +int32_t be_LV_LAYOUT_GRID(void) { return LV_LAYOUT_GRID; }; BE_VAR_CTYPE_DECLARE(be_LV_LAYOUT_GRID, "i"); +int32_t be_LV_LAYOUT_FLEX(void) { return LV_LAYOUT_FLEX; }; BE_VAR_CTYPE_DECLARE(be_LV_LAYOUT_FLEX, "i"); + extern int lv0_member(bvm *vm); // resolve virtual members extern int lv0_load_font(bvm *vm); extern lv_ts_calibration_t * lv_get_ts_calibration(void); - static int lv_get_hor_res(void) { return lv_disp_get_hor_res(lv_disp_get_default()); } @@ -810,6 +814,7 @@ class be_class_lv_{subtype} (scope: global, name: lv_{subtype}, super: be_class_ if v[0] == '"': v_prefix = "$"; v_macro = "be_cconst_string" if v[0] == '&': v_prefix = "&"; v_macro = "be_cconst_ptr" if v[0] == '@': v_prefix = "@"; v_macro = "be_cconst_ptr"; v = "&" + v[1:] + if v[0] == '>': v_prefix = ">"; v_macro = "be_ctype"; v = v[1:] print(f" {{ \"{v_prefix}{k}\", {v_macro}({v}) }},") else: print(f" {{ \"{k}\", be_cconst_int(LV_{k}) }},") diff --git a/lib/libesp32_lvgl/lv_binding_berry/tools/preprocessor.py b/lib/libesp32_lvgl/lv_binding_berry/tools/preprocessor.py index cafb3d817f18..f71335eb9f23 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/tools/preprocessor.py +++ b/lib/libesp32_lvgl/lv_binding_berry/tools/preprocessor.py @@ -213,6 +213,8 @@ def clean_source(raw): anim_path_linear=&lv_anim_path_linear anim_path_overshoot=&lv_anim_path_overshoot anim_path_step=&lv_anim_path_step +LV_LAYOUT_GRID=>be_LV_LAYOUT_GRID +LV_LAYOUT_FLEX=>be_LV_LAYOUT_FLEX // ====================================================================== // Colors diff --git a/tasmota/tasmota_support/support_switch_v4.ino b/tasmota/tasmota_support/support_switch_v4.ino index 50bc7d4284e2..fdde1bb25d72 100644 --- a/tasmota/tasmota_support/support_switch_v4.ino +++ b/tasmota/tasmota_support/support_switch_v4.ino @@ -74,13 +74,9 @@ void SwitchSetVirtualPinState(uint32_t index, uint32_t state) { bitWrite(Switch.virtual_pin, index, state); } -void SwitchSetState(uint32_t index, uint32_t state) { - // Set debounced pin state to be used by late detected switches - if (!bitRead(Switch.used, index)) { - bitSet(Switch.used, index); - AddLog(LOG_LEVEL_DEBUG, PSTR("SWT: Add vSwitch%d, State %d"), index +1, state); - } - Switch.debounced_state[index] = state; +uint8_t SwitchLastState(uint32_t index) { + // Get last state + return Switch.last_state[index]; } uint8_t SwitchGetState(uint32_t index) { @@ -88,9 +84,17 @@ uint8_t SwitchGetState(uint32_t index) { return Switch.debounced_state[index]; } -uint8_t SwitchLastState(uint32_t index) { - // Get last state - return Switch.last_state[index]; +void SwitchSetState(uint32_t index, uint32_t state) { + // Set debounced pin state to be used by late detected switches + if (!bitRead(Switch.used, index)) { + for (uint32_t i = 0; i <= index; i++) { + if (!bitRead(Switch.used, i)) { + bitSet(Switch.used, i); + AddLog(LOG_LEVEL_DEBUG, PSTR("SWT: Add vSwitch%d, State %d"), i +1, Switch.debounced_state[i]); + } + } + } + Switch.debounced_state[index] = state; } /*------------------------------------------------------------------------------------------*/ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v1.ino b/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v1.ino index 82bac23961e1..bd316122ed20 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v1.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v1.ino @@ -848,10 +848,11 @@ void TuyaProcessStatePacket(void) { if (Tuya.buffer[dpidStart + 4]) { PowerOff = true; } } } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { - uint32_t switch_state = SwitchGetState(fnId - TUYA_MCU_FUNC_SWT1); - AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], switch_state); + uint32_t switch_index = fnId - TUYA_MCU_FUNC_SWT1; + uint32_t switch_state = SwitchGetState(switch_index); + AddLog(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"), switch_index +1, Tuya.buffer[dpidStart + 4], switch_state); if (switch_state != Tuya.buffer[dpidStart + 4]) { - SwitchSetState(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); + SwitchSetState(switch_index, Tuya.buffer[dpidStart + 4]); } } if (PowerOff) { Tuya.ignore_dimmer_cmd_timeout = millis() + 250; } @@ -1333,12 +1334,21 @@ void TuyaSerialInput(void) ResponseAppend_P(PSTR("}}")); if (Settings->flag3.tuya_serial_mqtt_publish) { // SetOption66 - Enable TuyaMcuReceived messages over Mqtt +/* for (uint8_t cmdsID = 0; sizeof(TuyaExcludeCMDsFromMQTT) > cmdsID; cmdsID++){ if (TuyaExcludeCMDsFromMQTT[cmdsID] == Tuya.buffer[3]) { isCmdToSuppress = true; break; } } +*/ + for (uint8_t cmdsID = 0; cmdsID < sizeof(TuyaExcludeCMDsFromMQTT); cmdsID++) { + if (pgm_read_byte(TuyaExcludeCMDsFromMQTT +cmdsID) == Tuya.buffer[3]) { + isCmdToSuppress = true; + break; + } + } + if (!(isCmdToSuppress && Settings->flag5.tuya_exclude_from_mqtt)) { // SetOption137 - (Tuya) When Set, avoid the (MQTT-) publish of defined Tuya CMDs (see TuyaExcludeCMDsFromMQTT) if SetOption66 is active MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); } else { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v2.ino b/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v2.ino index 640f5574e491..6c3eb15d5a7e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v2.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu_v2.ino @@ -1639,10 +1639,11 @@ void TuyaProcessRxedDP(uint8_t dpid, uint8_t type, uint8_t *data, int dpDataLen) if (value) { PowerOff = true; } } } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { - uint32_t switch_state = SwitchGetState(fnId - TUYA_MCU_FUNC_SWT1); - AddLog(LOG_LEVEL_DEBUG, PSTR("T:fn%d Switch%d --> M%d T%d"),fnId, fnId - TUYA_MCU_FUNC_SWT1 + 1, value, switch_state); + uint32_t switch_index = fnId - TUYA_MCU_FUNC_SWT1; + uint32_t switch_state = SwitchGetState(switch_index); + AddLog(LOG_LEVEL_DEBUG, PSTR("T:fn%d Switch%d --> M%d T%d"), fnId, switch_index +1, value, switch_state); if (switch_state != value) { - SwitchSetState(fnId - TUYA_MCU_FUNC_SWT1, value); + SwitchSetState(switch_index, value); } } //if (PowerOff) { pTuya->ignore_dimmer_cmd_timeout = millis() + 250; } @@ -2119,12 +2120,21 @@ void TuyaProcessCommand(unsigned char *buffer){ // SetOption66 - Enable TuyaMcuReceived messages over Mqtt if (Settings->flag3.tuya_serial_mqtt_publish) { +/* for (uint8_t cmdsID = 0; sizeof(TuyaExcludeCMDsFromMQTT) > cmdsID; cmdsID++){ if (TuyaExcludeCMDsFromMQTT[cmdsID] == cmd) { isCmdToSuppress = true; break; } } +*/ + for (uint8_t cmdsID = 0; cmdsID < sizeof(TuyaExcludeCMDsFromMQTT); cmdsID++) { + if (pgm_read_byte(TuyaExcludeCMDsFromMQTT +cmdsID) == Tuya.buffer[3]) { + isCmdToSuppress = true; + break; + } + } + // SetOption137 - (Tuya) When Set, avoid the (MQTT-) publish of defined Tuya CMDs // (see TuyaExcludeCMDsFromMQTT) if SetOption66 is active if (!(isCmdToSuppress && Settings->flag5.tuya_exclude_from_mqtt)) { diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino index c2160b512e7c..f3020e2996af 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino @@ -6,7 +6,7 @@ SPDX-License-Identifier: GPL-3.0-only */ -#ifdef USE_DHT +#ifdef USE_DHT_V6 /*********************************************************************************************\ * DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), SI7021, THS01, MS01 - Temperature and Humidity * diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v7.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v7.ino new file mode 100644 index 000000000000..5bd71ea123e0 --- /dev/null +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v7.ino @@ -0,0 +1,481 @@ +/* + xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity sensor support for Tasmota + + SPDX-FileCopyrightText: 2022 Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef USE_DHT +/*********************************************************************************************\ + * DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), SI7021, THS01, MS01 - Temperature and Humidity + * + * Reading temperature or humidity takes about 250 milliseconds! + * Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + * + * Changelog + * 20230215 - v7 + * - Add user high and low delay in microseconds + * DhtDelay1 - Show delays for first sensor + * DhtDelay1 1 - Reset to defaults + * DhtDelay1 500,40 - Set both delays for sensor 1 + * DhtDelay4 500,40 - Set both delays for sensor 4 + * 20220706 - v6 + * - Consolidate Adafruit DHT library + * - Fix ESP32 interrupt control to solve intermittent results + * 20211229 - Change poll time from to 2 to 4 seconds for better results + * 20211226 - https://github.com/arendst/Tasmota/pull/14173 + * 20210524 - https://github.com/arendst/Tasmota/issues/12180 + * 20200621 - https://github.com/arendst/Tasmota/pull/7468#issuecomment-647067015 + * 20200313 - https://github.com/arendst/Tasmota/issues/7717#issuecomment-585833243 +\*********************************************************************************************/ + +#define XSNS_06 6 + +#ifndef DHT_MAX_SENSORS +#define DHT_MAX_SENSORS 4 +#endif +#define DHT_MAX_RETRY 8 + +const uint16_t dht_delays_const[4][2] = { + { 19000, 50 }, // DHT11 + { 2000, 50 }, // DHT22 +#ifdef ESP8266 + { 500, 30 }, // SI7021 / THS-01 + { 450, 30 } // MS01 +#else + { 400, 30 }, // SI7021 / THS-01 + { 400, 30 } // MS01 +#endif +}; + +uint32_t dht_maxcycles; +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin; +uint8_t dht_pin_out = 0; // Shelly GPIO00 output only +bool dht_active = true; // DHT configured +bool dht_dual_mode = false; // Single pin mode + +struct DHTSTRUCT { + float t = NAN; + float h = NAN; + uint16_t delay_lo; + uint16_t delay_hi; + uint16_t type; + int16_t raw; + char stype[12]; + int8_t pin; + uint8_t lastresult; +} Dht[DHT_MAX_SENSORS]; + +/*********************************************************************************************/ + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function +uint32_t DhtExpectPulse(bool level) { + uint32_t count = 0; + while (digitalRead(dht_pin) == level) { + if (count++ >= dht_maxcycles) { +// AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d timeout waiting for %s pulse"), +// dht_pin, (level) ? "high" : "low"); + return UINT32_MAX; // Exceeded timeout, fail. + } + } + return count; +} + +bool DhtRead(uint32_t sensor) { + dht_pin = Dht[sensor].pin; + if (!dht_dual_mode) { + // Go into high impedence state to let pull-up raise data line level and + // start the reading process. + pinMode(dht_pin, INPUT_PULLUP); + delay(1); + + // First set data line low for a period according to sensor type + pinMode(dht_pin, OUTPUT); + digitalWrite(dht_pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } +/* + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + delay(19); // minimum 18ms + break; + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 +// delay(2); // minimum 1ms + delayMicroseconds(2000); // 20200621: See https://github.com/arendst/Tasmota/pull/7468#issuecomment-647067015 + break; + case GPIO_SI7021: // iTead SI7021 +#ifdef ESP8266 + delayMicroseconds(500); +#else + delayMicroseconds(400); // Higher (or lower) results in Timeout waiting for high pulse on ESP32 +#endif + break; + case GPIO_MS01: // Sonoff MS01 +#ifdef ESP8266 + delayMicroseconds(450); +#else + delayMicroseconds(400); // Higher (or lower) results in Timeout waiting for high pulse on ESP32 +#endif + break; + } +*/ + delayMicroseconds(Dht[sensor].delay_lo); + + uint32_t cycles[80] = { 0 }; + uint32_t i = 0; + + // End the start signal by setting data line high for 40 microseconds. + if (!dht_dual_mode) { + pinMode(dht_pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + // Delay a moment to let sensor pull data line low. +/* + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 + delayMicroseconds(50); + break; + case GPIO_SI7021: // iTead SI7021 + case GPIO_MS01: // Sonoff MS01 + delayMicroseconds(30); // See: https://github.com/letscontrolit/ESPEasy/issues/1798 and 20210524: https://github.com/arendst/Tasmota/issues/12180 + break; + } +*/ + delayMicroseconds(Dht[sensor].delay_hi); + + // Now start reading the data line to get the value from the DHT sensor. + + // Turn off interrupts temporarily because the next sections + // are timing critical and we don't want any interruptions. +#ifdef ESP32 + {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&mux); +#else + noInterrupts(); +#endif + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if ((DhtExpectPulse(LOW) != UINT32_MAX) && (DhtExpectPulse(HIGH) != UINT32_MAX)) { + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed + // all the pulses are read into a array and then examined in a later step. + for (i = 0; i < 80; i += 2) { + cycles[i] = DhtExpectPulse(LOW); + if (cycles[i] == UINT32_MAX) { break; } + cycles[i + 1] = DhtExpectPulse(HIGH); + if (cycles[1 + i] == UINT32_MAX) { break; } + } + } +#ifdef ESP32 + portEXIT_CRITICAL(&mux);} +#else + interrupts(); +#endif + + char cycle_dump[200] = { 0 }; + for (uint32_t i = 0; i < 20; i++) { + snprintf_P(cycle_dump, sizeof(cycle_dump), PSTR("%s %u"), cycle_dump, cycles[i]); + } + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d cycles (%d/80) %s .."), dht_pin, i, cycle_dump); + +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d cycles (%d/80) %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u .."), +// dht_pin, i, cycles[0], cycles[1], cycles[2], cycles[3], cycles[4], cycles[5], cycles[6], cycles[7], cycles[8], cycles[9], cycles[10], cycles[11], cycles[12], cycles[13], cycles[14], cycles[15]); + + // DHT11 on ESP8266 - 80MHz + // 10:49:06.532 DHT: Pin14 cycles 81 35 74 34 81 106 81 35 81 34 81 34 81 106 81 35 .. + // 10:49:06.533 DHT: Pin14 read 22001A003C + // DHT11 on ESP32 - 80MHz + // 10:55:51.868 DHT: Pin25 cycles 94 33 86 41 94 124 94 40 95 40 94 41 94 124 94 40 .. + // 10:55:51.872 DHT: Pin25 read 22001A003C + // DHT11 on ESP32-S3 - 240MHz + // 11:13:44.712 DHT: Pin21 cycles 264 116 264 117 267 350 258 117 267 117 267 117 267 349 268 116 .. + // 11:13:44.713 DHT: Pin21 read 22001A003C + // AM2301 on ESP8266 - 80MHz + // 11:00:06.423 DHT: Pin14 cycles 92 38 83 38 89 38 89 38 90 38 89 38 89 38 89 114 .. + // 11:00:06.425 DHT: Pin14 read 01F900FCF6 + // AM2301 on ESP32 - 80MHz + // 14:54:15.930 DHT: Pin25 cycles 99 45 96 45 104 45 103 45 104 45 103 46 103 132 104 45 .. + // 14:54:15.932 DHT: Pin25 read 020B010513 + // AM2301 on ESP32-S3 - 240MHz + // 11:07:29.700 DHT: Pin21 cycles 301 129 290 129 294 127 293 129 294 129 294 129 293 129 294 374 .. + // 11:07:29.701 DHT: Pin21 read 01E300FFE3 + // Sonoff MS01 on ESP8266 - 80MHz + // 10:54:38.409 DHT: Pin14 cycles 80 39 72 105 79 105 79 39 78 106 78 106 79 105 79 39 .. + // 10:54:38.412 DHT: Pin14 read 6E620FA07F + // Sonoff MS01 on ESP32 - 80MHz + // 14:34:34.811 DHT: Pin25 cycles 84 47 83 123 91 123 91 46 91 123 91 123 91 123 91 47 .. + // 14:34:34.816 DHT: Pin25 read 6EE30FA000 + // Sonoff THS01 on ESP32 - 80MHz + // 14:36:43.787 DHT: Pin25 cycles 67 42 66 41 75 42 74 42 75 42 75 41 75 131 74 52 .. + // 14:36:43.789 DHT: Pin25 read 020B00FC09 + + if (i < 80) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d timeout waiting for pulse %d"), dht_pin, i); + return false; + } + + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i = 0; i < 40; ++i) { + uint32_t lowCycles = cycles[2 * i]; + uint32_t highCycles = cycles[2 * i + 1]; + dht_data[i / 8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + dht_data[i / 8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d checksum failure %5_H =? %02X"), + dht_pin, dht_data, checksum); + return false; + } + + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d read %5_H"), dht_pin, dht_data); + + float temperature = NAN; + float humidity = NAN; + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + humidity = dht_data[0]; + // 20200313: DHT11 (Adafruit): + temperature = dht_data[2]; + if (dht_data[3] & 0x80) { + temperature = -1 - temperature; + } + temperature += (dht_data[3] & 0x0f) * 0.1f; +/* + // DHT12 (Adafruit): + temperature = dht_data[2]; + temperature += (dht_data[3] & 0x0f) * 0.1f; + if (dht_data[2] & 0x80) { + temperature *= -1; + } +*/ + break; + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 + case GPIO_SI7021: { // iTead SI7021 + humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1f; + // DHT21/22 (Adafruit): + int16_t temp16 = dht_data[2] << 8 | dht_data[3]; // case 1 : signed 16 bits + if ((dht_data[2] & 0xF0) == 0x80) { // case 2 : negative when high nibble = 0x80 + temp16 = -(0xFFF & temp16); + } + temperature = 0.1f * temp16; + break; + } + case GPIO_MS01: { // Sonoff MS01 + int16_t voltage = ((dht_data[0] << 8) | dht_data[1]); + +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d MS01 %d"), dht_pin, voltage); + + // Rough approximate of soil moisture % (based on values observed in the eWeLink app) + // Observed values are available here: https://gist.github.com/minovap/654cdcd8bc37bb0d2ff338f8d144a509 + + float x; + if (voltage < 15037) { + x = voltage - 15200; + humidity = - FastPrecisePowf(0.0024f * x, 3) - 0.0004f * x + 20.1f; + } + else if (voltage < 22300) { + humidity = - 0.00069f * voltage + 30.6f; + } + else { + x = voltage - 22800; + humidity = - FastPrecisePowf(0.00046f * x, 3) - 0.0004f * x + 15; + } + temperature = 0; + Dht[sensor].raw = voltage; + break; + } + } + if (isnan(temperature) || isnan(humidity)) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d invalid reading"), dht_pin); + return false; + } + + if (humidity > 100) { humidity = 100.0f; } + if (humidity < 0) { humidity = 0.1f; } + Dht[sensor].h = ConvertHumidity(humidity); + Dht[sensor].t = ConvertTemp(temperature); + Dht[sensor].lastresult = 0; + + return true; +} + +/********************************************************************************************/ + +void DhtDelayDefault(uint32_t sensor) { + uint32_t index = Dht[sensor].type - GPIO_DHT11; // GPIO_DHT11, GPIO_DHT22, GPIO_SI7021 + if (index > 2) { index = 3; } // GPIO_MS01 + Dht[sensor].delay_lo = dht_delays_const[index][0]; + Dht[sensor].delay_hi = dht_delays_const[index][1]; +} + +bool DhtPinState() { + if (((XdrvMailbox.index >= AGPIO(GPIO_DHT11)) && (XdrvMailbox.index <= AGPIO(GPIO_SI7021))) || + (XdrvMailbox.index == AGPIO(GPIO_MS01))) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = BGPIO(XdrvMailbox.index); + DhtDelayDefault(dht_sensors); + dht_sensors++; + XdrvMailbox.index = AGPIO(GPIO_DHT11); + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) { + if (dht_sensors) { + if (PinUsed(GPIO_DHT11_OUT)) { + dht_pin_out = Pin(GPIO_DHT11_OUT); + dht_dual_mode = true; // Dual pins mode as used by Shelly + dht_sensors = 1; // We only support one sensor in pseudo mode + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastresult = DHT_MAX_RETRY; // Start with NAN + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + + dht_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. + + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: (v7) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) { + if (!(TasmotaGlobal.uptime %4)) { // Every 4 seconds + for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { + // DHT11 and AM2301 25mS per sensor, SI7021 5mS per sensor + if (!DhtRead(sensor)) { + Dht[sensor].lastresult++; + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { // Reset after 8 misses + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + } + } + } +} + +void DhtShow(bool json) { + for (uint32_t i = 0; i < dht_sensors; i++) { + if (GPIO_MS01 == Dht[i].type) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_HUMIDITY "\":%*_f,\"Raw\":%d}"), + Dht[i].stype, Settings->flag2.humidity_resolution, &Dht[i].h, Dht[i].raw); +#ifdef USE_WEBSERVER + } else { + char parameter[FLOATSZ]; + dtostrfd(Dht[i].h, Settings->flag2.humidity_resolution, parameter); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, parameter); +#endif // USE_WEBSERVER + } + } else { + TempHumDewShow(json, ((0 == TasmotaGlobal.tele_period) && (0 == i)), Dht[i].stype, Dht[i].t, Dht[i].h); + } + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +const char kDhtCommands[] PROGMEM = "Dht|" // Prefix + "Delay"; + +void (* const DhtCommand[])(void) PROGMEM = { + &CmndDhtDelay }; + +void CmndDhtDelay(void) { + // DhtDelay1 - Show delays for first sensor + // DhtDelay1 1 - Reset to defaults + // DhtDelay1 500,40 - Set both delays for sensor 1 + // DhtDelay4 500,40 - Set both delays for sensor 4 + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= dht_sensors)) { + uint32_t sensor = XdrvMailbox.index -1; + if (XdrvMailbox.data_len > 0) { + uint32_t parm[2] = { Dht[sensor].delay_lo, Dht[sensor].delay_hi }; + ParseParameters(2, parm); + if (1 == parm[0]) { + DhtDelayDefault(sensor); + } else { + Dht[sensor].delay_lo = parm[0]; + Dht[sensor].delay_hi = parm[1]; + } + } + Response_P(PSTR("{\"%s%d\":[%d,%d]}"), XdrvMailbox.command, XdrvMailbox.index, Dht[sensor].delay_lo, Dht[sensor].delay_hi); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns06(uint32_t function) { + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif // USE_WEBSERVER + case FUNC_COMMAND: + result = DecodeCommand(kDhtCommands, DhtCommand); + break; + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif // USE_DHT