From 056493740bfd131c3ee687222c5866279f12db2c Mon Sep 17 00:00:00 2001 From: Alexander Kabakaev Date: Sat, 12 Dec 2020 10:35:57 +0100 Subject: [PATCH 01/66] show relay state in prometheus metrics --- tasmota/xsns_75_prometheus.ino | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/xsns_75_prometheus.ino index cea6b64ee..503291cd2 100644 --- a/tasmota/xsns_75_prometheus.ino +++ b/tasmota/xsns_75_prometheus.ino @@ -75,6 +75,11 @@ void HandleMetrics(void) WSContentSend_P(PSTR("# TYPE energy_power_kilowatts_total counter\nenergy_power_kilowatts_total %s\n"), parameter); #endif + for (uint32_t device = 0; device < TasmotaGlobal.devices_present; device++) { + power_t mask = 1 << device; + WSContentSend_P(PSTR("# TYPE relay%d_state gauge\nrelay%d_state %d\n"), device+1, device+1, (TasmotaGlobal.power & mask)); + } + /* // Alternative method using the complete sensor JSON data // For prometheus it may need to be decoded to # TYPE messages From 1558f0c7315ffc69bc251d44207db86a42d0c24e Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 12 Dec 2020 19:05:47 +0100 Subject: [PATCH 02/66] Zigbee add visual map of network --- CHANGELOG.md | 1 + tasmota/xdrv_01_webserver.ino | 12 ++ tasmota/xdrv_23_zigbee_1_headers.ino | 4 + tasmota/xdrv_23_zigbee_2_devices.ino | 6 + ...no => xdrv_23_zigbee_7_0_statemachine.ino} | 0 tasmota/xdrv_23_zigbee_7_5_map.ino | 180 ++++++++++++++++++ tasmota/xdrv_23_zigbee_8_parsers.ino | 25 ++- tasmota/xdrv_23_zigbee_A_impl.ino | 109 +++++++++-- 8 files changed, 323 insertions(+), 14 deletions(-) rename tasmota/{xdrv_23_zigbee_7_statemachine.ino => xdrv_23_zigbee_7_0_statemachine.ino} (100%) create mode 100644 tasmota/xdrv_23_zigbee_7_5_map.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a64d5f62..9a9ce16b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file. - Zigbee better support for Tuya Protocol (#10074) - Support for SPI connected MFRC522 13.56MHz rfid card reader (#9916) - Letsencrypt R3 in addition to X3 CA (#10086) +- Zigbee add visual map of network ### Breaking Changed - KNX DPT9 (16-bit float) to DPT14 (32-bit float) by Adrian Scillato (#9811, #9888) diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index e9ae48e78..7e74f2e89 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -1661,6 +1661,18 @@ bool HandleRootStatusRefresh(void) ExecuteWebCommand(svalue, SRC_WEBGUI); } #endif // USE_SONOFF_RF +#ifdef USE_ZIGBEE + WebGetArg("zbj", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ZbPermitJoin")); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("zbr", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ZbMap")); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif // USE_ZIGBEE WSContentBegin(200, CT_HTML); WSContentSend_P(PSTR("{t}")); XsnsCall(FUNC_WEB_SENSOR); diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 04448711c..33ca5381e 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -90,6 +90,10 @@ public: bool recv_until = false; // ignore all messages until the received frame fully matches bool eeprom_present = false; // is the ZBBridge EEPROM present? bool eeprom_ready = false; // is the ZBBridge EEPROM formatted and ready? + // Zigbee mapping + bool mapping_in_progress = false; // is there a mapping in progress + bool mapping_ready = false; // do we have mapping information ready + uint32_t mapping_end_time = 0; uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; // on error goto label, 99 default to abort uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; // on timeout goto label, 99 default to abort diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index 3c6137bc5..8532e5da8 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -717,6 +717,7 @@ public: // sequence number for Zigbee frames uint16_t shortaddr; // unique key if not null, or unspecified if null uint8_t seqNumber; + bool is_router; // flag used by ZbMap to distibguish routers from end-devices bool hidden; bool reachable; // Light information for Hue integration integration, last known values @@ -742,6 +743,7 @@ public: attr_list(), shortaddr(_shortaddr), seqNumber(0), + is_router(false), hidden(false), reachable(false), data(), @@ -768,6 +770,10 @@ public: inline bool getReachable(void) const { return reachable; } inline bool getPower(uint8_t ep =0) const; + inline bool isRouter(void) const { return is_router; } + inline bool isCoordinator(void) const { return 0x0000 == shortaddr; } + inline void setRouter(bool router) { is_router = router; } + inline void setLQI(uint8_t _lqi) { lqi = _lqi; } inline void setBatteryPercent(uint8_t bp) { batterypercent = bp; } diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_0_statemachine.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_7_statemachine.ino rename to tasmota/xdrv_23_zigbee_7_0_statemachine.ino diff --git a/tasmota/xdrv_23_zigbee_7_5_map.ino b/tasmota/xdrv_23_zigbee_7_5_map.ino new file mode 100644 index 000000000..17c79d5e5 --- /dev/null +++ b/tasmota/xdrv_23_zigbee_7_5_map.ino @@ -0,0 +1,180 @@ +/* + xdrv_23_zigbee_7_5_map.ino - zigbee support for Tasmota + + Copyright (C) 2020 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 + +class Z_Mapper_Edge { +public: + + // enum Edge_Type : uint8_t { + // Unknown = 0x00, + // Parent = 0x01, // node_1 is parent of node_2 + // Child = 0x02, // node_1 is child of node_2 + // Sibling = 0x03, // both nodes are siblings + // }; + + Z_Mapper_Edge(void) : + node_1(BAD_SHORTADDR), + node_2(BAD_SHORTADDR), + lqi(0x00) + // edge_type(Unknown) + {} + + // Z_Mapper_Edge(uint16_t node_a, uint16_t node_b, uint8_t _lqi, Edge_Type _type) : + Z_Mapper_Edge(uint16_t node_a, uint16_t node_b, uint8_t _lqi) : + node_1(BAD_SHORTADDR), + node_2(BAD_SHORTADDR), + lqi(_lqi) + // edge_type(_type) + { + setEdges(node_a, node_b); + } + + void setEdges(uint16_t node_a, uint16_t node_b); + + bool sameEdge(const Z_Mapper_Edge & edge2) const; + + // Edge_Type Z_Mapper_Edge_Type_Reverse(Edge_Type _type) { + // switch (_type) { + // case Parent: return Child; + // case Child: return Parent; + // default: return _type; + // } + // } + + // we always orientate the edge from with shortaddresses in ascending order + // invariant: node_1 < node_2 + uint16_t node_1; + uint16_t node_2; + + uint8_t lqi; + // Edge_Type edge_type; +}; + +// +// Handles the mapping of Zigbee devices +// +class Z_Mapper { +public: + Z_Mapper(void) : + edges() + {} + + void reset(void) { edges.reset(); } + + Z_Mapper_Edge & findEdge(const Z_Mapper_Edge & edge2); + bool addEdge(const Z_Mapper_Edge & edge2); + + void dumpInternals(void) const; + + LList edges; + +}; + +// global +Z_Mapper zigbee_mapper; + +/*********************************************************************************************\ + * Implementation for Z_Mapper_Edge +\*********************************************************************************************/ +void Z_Mapper_Edge::setEdges(uint16_t node_a, uint16_t node_b) { + if (node_a < node_b) { + node_1 = node_a; + node_2 = node_b; + } else if (node_a > node_b) { + node_1 = node_b; + node_2 = node_a; + } else { + // do nothing + } +} + +bool Z_Mapper_Edge::sameEdge(const Z_Mapper_Edge & edge2) const { + return (node_1 == edge2.node_1) && (node_2 == edge2.node_2); +} + +/*********************************************************************************************\ + * Implementation for Z_Mapper +\*********************************************************************************************/ +Z_Mapper_Edge & Z_Mapper::findEdge(const Z_Mapper_Edge & edge2) { + if ((edge2.node_1 == BAD_SHORTADDR) || (edge2.node_2 == BAD_SHORTADDR)) { return *(Z_Mapper_Edge*)nullptr; } + for (auto & edge : edges) { + if (edge2.sameEdge(edge)) { + return edge; + } + } + return *(Z_Mapper_Edge*)nullptr; +} + +bool Z_Mapper::addEdge(const Z_Mapper_Edge & edge2) { + if ((edge2.node_1 == BAD_SHORTADDR) || (edge2.node_2 == BAD_SHORTADDR)) { return false; } + Z_Mapper_Edge & cur_edge = findEdge(edge2); + if (&cur_edge == nullptr) { + edges.addHead(edge2); + } else { + //upgrade fields + if (edge2.lqi > cur_edge.lqi) { + cur_edge.lqi = edge2.lqi; + } + // if (cur_edge.edge_type == Z_Mapper_Edge::Unknown) { + // cur_edge.edge_type = edge2.edge_type; + // } else if ((edge2.edge_type == Z_Mapper_Edge::Parent) || (edge2.edge_type == Z_Mapper_Edge::Child)) { + // // Parent or Child always has priority over Sibling + // cur_edge.edge_type = edge2.edge_type; + // } + } + return true; +} + +void Z_Mapper::dumpInternals(void) const { + WSContentSend_P(PSTR("nodes:[" "{id:\"0x0000\",label:\"Coordinator\",group:\"o\",title:\"0x0000\"}")); + for (const auto & device : zigbee_devices.getDevices()) { + WSContentSend_P(PSTR(",{id:\"0x%04X\",group:\"%c\",title:\"0x%04X\",label:\""), + device.shortaddr, device.isRouter() ? 'r' : 'e', device.shortaddr); + + const char *fname = device.friendlyName; + if (fname != nullptr) { + WSContentSend_P(PSTR("%s"), fname); + } else { + WSContentSend_P(PSTR("0x%04X"), device.shortaddr); + } + WSContentSend_P("\"}"); + } + WSContentSend_P(PSTR("],")); + + WSContentSend_P(PSTR("edges:[")); + for (auto & edge : edges) { + uint32_t lqi_color = 0x000; + // if (edge.lqi >= 192) { + // lqi_color = 0x364; + // } else if (edge.lqi >= 128) { + // lqi_color = 0x346; + // } else if (edge.lqi > 0) { + // lqi_color = 0xd56; + // } + char hex[8]; + snprintf(hex, sizeof(hex), PSTR("%d"), edge.lqi); + + WSContentSend_P("{from:\"0x%04X\",to:\"0x%04X\",label:\"%s\",color:\"#%03X\"},", + edge.node_1, edge.node_2, (edge.lqi > 0) ? hex : "", lqi_color); + } + WSContentSend_P(PSTR("],")); +} + +#endif // USE_ZIGBEE diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index f9c8019b9..d97aa8335 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -229,7 +229,7 @@ void Z_Send_State_or_Map(uint16_t shortaddr, uint8_t index, uint16_t zdo_cmd) { // This callback is registered to send ZbMap(s) to each device one at a time void Z_Map(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { if (BAD_SHORTADDR != shortaddr) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "sending `ZnMap 0x%04X`"), shortaddr); + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "sending `ZbMap 0x%04X`"), shortaddr); #ifdef USE_ZIGBEE_ZNP Z_Send_State_or_Map(shortaddr, value, ZDO_MGMT_LQI_REQ); #endif // USE_ZIGBEE_ZNP @@ -238,6 +238,8 @@ void Z_Map(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t end #endif // USE_ZIGBEE_EZSP } else { AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "ZbMap done")); + zigbee.mapping_in_progress = false; + zigbee.mapping_ready = true; } } /*********************************************************************************************\ @@ -1110,6 +1112,27 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) TrueFalseNull(m_permitjoin & 0x02), m_depth, m_lqi); + + // detect any router + Z_Device & device = zigbee_devices.findShortAddr(m_shortaddr); + if (device.valid()) { + if ((m_dev_type & 0x03) == 1) { // it is a router + device.setRouter(true); + } + } + + // Add information to zigbee mapper + // Z_Mapper_Edge::Edge_Type edge_type; + // switch ((m_dev_type & 0x70) >> 4) { + // case 0: edge_type = Z_Mapper_Edge::Parent; break; + // case 1: edge_type = Z_Mapper_Edge::Child; break; + // case 2: edge_type = Z_Mapper_Edge::Sibling; break; + // default: edge_type = Z_Mapper_Edge::Unknown; break; + + // } + // Z_Mapper_Edge edge(m_shortaddr, shortaddr, m_lqi, edge_type); + Z_Mapper_Edge edge(m_shortaddr, shortaddr, m_lqi); + zigbee_mapper.addEdge(edge); } ResponseAppend_P(PSTR("]}}")); diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index 1b3aad1c7..f7d6fbdc7 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -1052,6 +1052,26 @@ void CmndZbBindState(void) { CmndZbBindState_or_Map(false); } +void ZigbeeMapAllDevices(void) { + // we can't abort a mapping in progress + if (zigbee.mapping_in_progress) { return; } + // defer sending ZbMap to each device + zigbee_mapper.reset(); // clear all data in Zigbee mapper + + const static uint32_t DELAY_ZBMAP = 2000; // wait for 1s between commands + uint32_t wait_ms = DELAY_ZBMAP; + zigbee.mapping_in_progress = true; // mark mapping in progress + + zigbee_devices.setTimer(0x0000, 0, 0 /*wait_ms*/, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); + for (const auto & device : zigbee_devices.getDevices()) { + zigbee_devices.setTimer(device.shortaddr, 0, wait_ms, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); + wait_ms += DELAY_ZBMAP; + } + wait_ms += DELAY_ZBMAP*2; + zigbee_devices.setTimer(BAD_SHORTADDR, 0, wait_ms, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); + zigbee.mapping_end_time = wait_ms + millis(); +} + // // Command `ZbMap` // `ZbMap` as index if it does not fit. If default, `1` starts at the beginning @@ -1061,15 +1081,7 @@ void CmndZbMap(void) { RemoveSpace(XdrvMailbox.data); if (strlen(XdrvMailbox.data) == 0) { - // defer sending ZbMap to each device - const static uint32_t DELAY_ZBMAP = 2000; // wait for 1s between commands - uint32_t wait_ms = DELAY_ZBMAP; - zigbee_devices.setTimer(0x0000, 0, 0 /*wait_ms*/, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); - for (const auto & device : zigbee_devices.getDevices()) { - zigbee_devices.setTimer(device.shortaddr, 0, wait_ms, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); - wait_ms += DELAY_ZBMAP; - } - zigbee_devices.setTimer(BAD_SHORTADDR, 0, wait_ms, 0, 0, Z_CAT_ALWAYS, 0 /* value = index */, &Z_Map); + ZigbeeMapAllDevices(); ResponseCmndDone(); } else { CmndZbBindState_or_Map(true); @@ -1317,8 +1329,8 @@ void CmndZbSave(void) { case -10: { // reinit EEPROM ZFS::erase(); - break; } + break; #endif default: saveZigbeeDevices(); @@ -1698,6 +1710,16 @@ extern "C" { } } // extern "C" +#define WEB_HANDLE_ZB_MAP "Zigbee Map" +#define WEB_HANDLE_ZB_PERMIT_JOIN "Zigbee Permit Join" +#define WEB_HANDLE_ZB_MAP_REFRESH "Zigbee Map Refresh" +const char HTTP_BTN_ZB_BUTTONS[] PROGMEM = + "" + "

" + "
"; +const char HTTP_AUTO_REFRESH_PAGE[] PROGMEM = ""; +const char HTTP_BTN_ZB_MAP_REFRESH[] PROGMEM = "

"; + void ZigbeeShow(bool json) { if (json) { @@ -1879,11 +1901,67 @@ void ZigbeeShow(bool json) } } - WSContentSend_P(PSTR("{t}")); // Terminate current multi column table and open new table + WSContentSend_P(PSTR("{t}

")); // Terminate current multi column table and open new table + if (zigbee.permit_end_time) { + // PermitJoin in progress + WSContentSend_P(PSTR("

[ Devices allowed to join ]

")); // Terminate current multi column table and open new table + } #endif } } +// Web handler to refresh the map, the redirect to show map +void ZigbeeMapRefresh(void) { + if ((!zigbee.init_phase) && (!zigbee.mapping_in_progress)) { + ZigbeeMapAllDevices(); + } + Webserver->sendHeader("Location","/zbm"); // Add a header to respond with a new location for the browser to go to the home page again + Webserver->send(302); +} + +// Display a graphical representation of the Zigbee map using vis.js network +void ZigbeeShowMap(void) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Zigbee Mapper")); + + // if no map, then launch a new mapping + if ((!zigbee.init_phase) && (!zigbee.mapping_ready) && (!zigbee.mapping_in_progress)) { + ZigbeeMapAllDevices(); + } + + WSContentStart_P(PSTR("Tasmota Zigbee Mapping")); + WSContentSendStyle(); + + if (zigbee.init_phase) { + WSContentSend_P(PSTR("Zigbee not started")); + } else if (zigbee.mapping_in_progress) { + int32_t mapping_remaining = 1 + (zigbee.mapping_end_time - millis()) / 1000; + if (mapping_remaining < 0) { mapping_remaining = 0; } + WSContentSend_P(PSTR("Mapping in progress (%d s. remaining)"), mapping_remaining); + WSContentSend_P(HTTP_AUTO_REFRESH_PAGE); + } else if (!zigbee.mapping_ready) { + WSContentSend_P(PSTR("No mapping")); + } else { + WSContentSend_P(PSTR( + "" + "
" + "" + )); + WSContentSend_P(HTTP_BTN_ZB_MAP_REFRESH); + } + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -1920,12 +1998,17 @@ bool Xdrv23(uint8_t function) case FUNC_WEB_SENSOR: ZigbeeShow(false); break; -#ifdef USE_ZIGBEE_EZSP // GUI xmodem case FUNC_WEB_ADD_HANDLER: +#ifdef USE_ZIGBEE_EZSP WebServer_on(PSTR("/" WEB_HANDLE_ZIGBEE_XFER), HandleZigbeeXfer); - break; #endif // USE_ZIGBEE_EZSP + WebServer_on(PSTR("/zbm"), ZigbeeShowMap, HTTP_GET); // add web handler for Zigbee map + WebServer_on(PSTR("/zbr"), ZigbeeMapRefresh, HTTP_GET); // add web handler for Zigbee map refresh + break; + case FUNC_WEB_ADD_MAIN_BUTTON: + WSContentSend_P(HTTP_BTN_ZB_BUTTONS); + break; #endif // USE_WEBSERVER case FUNC_PRE_INIT: ZigbeeInit(); From 5f1a24370fd8ac24cff01975a9fa50e701acef6e Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 12 Dec 2020 13:32:14 -0600 Subject: [PATCH 03/66] Support power tap and hold; increase hold from .5 to .75 seconds --- tasmota/xdrv_35_pwm_dimmer.ino | 54 ++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index 823055c83..dc9fe0bfe 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -76,6 +76,7 @@ bool tap_handled = false; bool invert_power_button_bri_direction = false; bool button_pressed[3] = { false, false, false }; bool button_held[3]; +bool button_unprocessed[3] = { false, false, false }; #ifdef USE_PWM_DIMMER_REMOTE struct remote_pwm_dimmer remote_pwm_dimmers[MAX_PWM_DIMMER_KEYS]; struct remote_pwm_dimmer * active_remote_pwm_dimmer; @@ -284,8 +285,16 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed) if (pressed) { uint32_t now = millis(); - // If this is about the power button, ... - if (is_power_button) { + // If the button was pressed and released but was not processed by support_button because the + // button interval had not elapsed, + if (button_unprocessed[button_index]) { + mqtt_trigger = 5; + if (!active_remote_pwm_dimmer) mqtt_trigger += button_index; + button_hold_time[button_index] = now + 750; + } + + // Otherwise, if this is about the power button, ... + else if (is_power_button) { // If we're not ignoring the power button and no other buttons are pressed, ... if (!ignore_power_button && buttons_pressed == 1) { @@ -325,8 +334,8 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed) // If the up or down button was tapped while holding the power button before this, handle // the operation below. if (button_tapped) { - handle_tap = true; - button_hold_time[button_index] = now + 500; + handle_tap = ignore_power_button = true; + button_hold_time[button_index] = now + 750; } // Otherwise, if the power is on and remote mode is enabled, adjust the brightness. Set the @@ -568,7 +577,7 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed) // If the up or down button was tapped while holding the power button and the up or down button // is being held or was just released after not being held, handle the operation. if (handle_tap) { - ignore_power_button = tap_handled = true; + tap_handled = true; // If the down button was tapped while holding the power button, send a device group update to // select the previous/next fixed color. @@ -591,9 +600,10 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed) #endif // USE_DEVICE_GROUPS ; } - // If the down button was tapped while holding the power button, publish an MQTT Event Trigger#. + // If the up button was tapped while holding the power button, publish an MQTT Event Trigger#. else { - mqtt_trigger = (is_down_button ? 3 : 4); + mqtt_trigger = 3; + if (is_down_button) mqtt_trigger = 4; } } @@ -739,7 +749,15 @@ bool Xdrv35(uint8_t function) // increment the buttons pressed count. if (!button_pressed[button_index]) { button_pressed[button_index] = true; - button_hold_time[button_index] = now + (button_index == power_button_index ? 500 : 250); + uint32_t hold_delay = 250; + if (button_index == power_button_index) { +#ifdef USE_PWM_DIMMER_REMOTE + if (!(active_remote_pwm_dimmer ? active_remote_pwm_dimmer->power_on : TasmotaGlobal.power)) hold_delay = 500; +#else // USE_PWM_DIMMER_REMOTE + if (!TasmotaGlobal.power) hold_delay = 500; +#endif // USE_PWM_DIMMER_REMOTE + } + button_hold_time[button_index] = now + hold_delay; buttons_pressed++; if (buttons_pressed > 1) multibutton_in_progress = true; @@ -796,6 +814,16 @@ bool Xdrv35(uint8_t function) // Set a timer so FUNC_ANY_KEY ignores the button if support_button winds up sending a // key because of this. ignore_any_key_time = now + 500; + + // If a multi-button operation is in progress or the button was pressed, released and + // then held, tell support_button that we've handled it. + result = true; + Button.press_counter[button_index] = 0; + if (buttons_pressed == 0) multibutton_in_progress = false; + button_unprocessed[button_index] = false; + } + else { + button_unprocessed[button_index] = true; } // If the power button was just released, clear the flags associated with it. @@ -806,12 +834,6 @@ bool Xdrv35(uint8_t function) } button_held[button_index] = false; } - - // If a multi-button operation is in progress, tell support_button that we've handled it. - if (multibutton_in_progress) { - result = true; - if (buttons_pressed == 0) multibutton_in_progress = false; - } } break; @@ -819,7 +841,9 @@ bool Xdrv35(uint8_t function) { uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; // 0 = Off, 1 = On, 2 = Toggle, 3 = Hold, 10,11,12,13 and 14 for Button Multipress if ((state == 2 || state == 10) && ignore_any_key_time < millis()) { - PWMDimmerHandleButton((XdrvMailbox.payload & 0xFF) - 1, false); + uint32_t button_index = (XdrvMailbox.payload & 0xFF) - 1; + button_unprocessed[button_index] = false; + PWMDimmerHandleButton(button_index, false); } } break; From e1391acaba7cc8fdab34c350544583adc46de01f Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Sat, 12 Dec 2020 15:09:49 -0600 Subject: [PATCH 04/66] Wrap remote dimmer reference in ifdef --- tasmota/xdrv_35_pwm_dimmer.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/xdrv_35_pwm_dimmer.ino index dc9fe0bfe..c54d071f1 100644 --- a/tasmota/xdrv_35_pwm_dimmer.ino +++ b/tasmota/xdrv_35_pwm_dimmer.ino @@ -289,7 +289,9 @@ void PWMDimmerHandleButton(uint32_t button_index, bool pressed) // button interval had not elapsed, if (button_unprocessed[button_index]) { mqtt_trigger = 5; +#ifdef USE_PWM_DIMMER_REMOTE if (!active_remote_pwm_dimmer) mqtt_trigger += button_index; +#endif // USE_PWM_DIMMER_REMOTE button_hold_time[button_index] = now + 750; } From fe3bef037c284ef2c2cf9b2a8dc2f0f2009eb0fc Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 13 Dec 2020 07:31:10 +0100 Subject: [PATCH 05/66] fix pin overrides --- tasmota/xdrv_42_i2s_audio.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/xdrv_42_i2s_audio.ino index 568989fb3..6f82b63bc 100644 --- a/tasmota/xdrv_42_i2s_audio.ino +++ b/tasmota/xdrv_42_i2s_audio.ino @@ -90,16 +90,16 @@ AudioGeneratorTalkie *talkie = nullptr; #define TWATCH_DAC_IIS_DOUT 3 #endif // ESP8266 #ifdef ESP32 -#undef TWATCH_DAC_IIS_BCK -#undef TWATCH_DAC_IIS_WS -#undef TWATCH_DAC_IIS_DOUT #ifndef TWATCH_DAC_IIS_BCK +#undef TWATCH_DAC_IIS_BCK #define TWATCH_DAC_IIS_BCK 26 #endif #ifndef TWATCH_DAC_IIS_WS +#undef TWATCH_DAC_IIS_WS #define TWATCH_DAC_IIS_WS 25 #endif #ifndef TWATCH_DAC_IIS_DOUT +#undef TWATCH_DAC_IIS_DOUT #define TWATCH_DAC_IIS_DOUT 33 #endif #endif // ESP32 From c50256f2b30140d7d88d2f9ef134e113c1932fc6 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 13 Dec 2020 07:32:09 +0100 Subject: [PATCH 06/66] add pwm and alt_eeprom --- tasmota/xdrv_10_scripter.ino | 186 +++++++++++++++++++++++++++++------ 1 file changed, 157 insertions(+), 29 deletions(-) diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/xdrv_10_scripter.ino index 75bffaec3..d2049af81 100755 --- a/tasmota/xdrv_10_scripter.ino +++ b/tasmota/xdrv_10_scripter.ino @@ -108,7 +108,11 @@ uint32_t DecodeLightId(uint32_t hue_id); #pragma message "script 24c256 file option used" #else //#warning "EEP_SCRIPT_SIZE also needs USE_24C256" +#if EEP_SCRIPT_SIZE==SPI_FLASH_SEC_SIZE #pragma message "internal eeprom script buffer used" +#else +#pragma message "internal compressed eeprom script buffer used" +#endif //#define USE_24C256 #endif #endif // EEP_SCRIPT_SIZE @@ -119,7 +123,7 @@ uint32_t DecodeLightId(uint32_t hue_id); #endif // USE_UNISHOX_COMPRESSION -#ifdef USE_SCRIPT_COMPRESSION +//#ifdef USE_SCRIPT_COMPRESSION #include #define SCRIPT_COMPRESS compressor.unishox_compress @@ -127,7 +131,8 @@ uint32_t DecodeLightId(uint32_t hue_id); #ifndef UNISHOXRSIZE #define UNISHOXRSIZE 2560 #endif -#endif // USE_SCRIPT_COMPRESSION + +//#endif // USE_SCRIPT_COMPRESSION #ifndef STASK_PRIO #define STASK_PRIO 1 @@ -158,6 +163,51 @@ void Script_ticker4_end(void) { } #endif +// EEPROM MACROS +// i2c eeprom + +#if defined(ALT_EEPROM) && !defined(ESP32) +#undef EEP_WRITE +#undef EEP_READ +#undef EEP_INIT +#define EEP_WRITE(A,B,C) alt_eeprom_writeBytes(A, B, (uint8_t*)C); +#define EEP_READ(A,B,C) alt_eeprom_readBytes(A, B, (uint8_t*)C); +#define EEP_INIT(A) alt_eeprom_init(A) + +#if EEP_SCRIPT_SIZE>6500 +#undef EEP_SCRIPT_SIZE +#define EEP_SCRIPT_SIZE 6500 +#endif + +uint32_t eeprom_block; + +// these support only one 4 k block below EEPROM this steals 4k of application area +uint32_t alt_eeprom_init(uint32_t size) { + //EEPROM.begin(size); + eeprom_block = (uint32_t)&_FS_end - 0x40200000 - SPI_FLASH_SEC_SIZE; + return 1; +} + +void alt_eeprom_writeBytes(uint32_t adr, uint32_t len, uint8_t *buf) { + uint32_t *lwp=(uint32_t*)buf; + ESP.flashEraseSector(eeprom_block / SPI_FLASH_SEC_SIZE); + ESP.flashWrite(eeprom_block , lwp, SPI_FLASH_SEC_SIZE); +} + +void alt_eeprom_readBytes(uint32_t adr, uint32_t len, uint8_t *buf) { + uint32_t *lwp=(uint32_t*)buf; + ESP.flashRead(eeprom_block , lwp, SPI_FLASH_SEC_SIZE); +} +#else +#undef EEP_WRITE +#undef EEP_READ +#undef EEP_INIT +#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); +#define EEP_READ(A,B,C) eeprom_readBytes(A, B, (uint8_t*)C); +#define EEP_INIT(A) eeprom_init(A) +#endif // ALT_EEPROM + + #if defined(LITTLEFS_SCRIPT_SIZE) || (USE_SCRIPT_FATFS==-1) #ifdef ESP32 @@ -516,10 +566,7 @@ void RulesTeleperiod(void) { if (bitRead(Settings.rule_enabled, 0) && TasmotaGlobal.mqtt_data[0]) Run_Scripter(">T", 2, TasmotaGlobal.mqtt_data); } -// EEPROM MACROS -// i2c eeprom -#define EEP_WRITE(A,B,C) eeprom_writeBytes(A, B, (uint8_t*)C); -#define EEP_READ(A,B,C) eeprom_readBytes(A, B, (uint8_t*)C); + #define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; @@ -3734,22 +3781,45 @@ void esp32_beep(int32_t freq ,uint32_t len) { xTimerChangePeriod( beep_th, ticks, 10); } } +#endif // ESP32 -void esp32_pwm(int32_t value) { +uint8_t pwmpin[5]; + +void esp_pwm(int32_t value, uint32 freq, uint32_t channel) { + if (channel < 1 || channel > 3) channel = 1; +#ifdef ESP32 + channel+=7; if (value < 0) { if (value <= -64) value = 0; - ledcSetup(7, 4000, 10); - ledcAttachPin(-value, 7); - ledcWrite(7, 0); + // set range to 10 bit + ledcSetup(channel, freq, 10); + ledcAttachPin(-value, channel); + ledcWrite(channel, 0); } else { if (value > 1023) { value = 1023; } - ledcWrite(7, value); + ledcWrite(channel, value); } +#else + // esp8266 default to range 0-1023 + channel-=1; + if (value < 0) { + if (value <= -64) value = 0; + pwmpin[channel] = -value; + pinMode(pwmpin[channel], OUTPUT); + analogWriteFreq(freq); + analogWrite(pwmpin[channel], 0); + } else { + if (value > 1023) { + value = 1023; + } + analogWrite(pwmpin[channel],value); + } +#endif // ESP32 } -#endif // ESP32 + //#define IFTHEN_DEBUG @@ -4164,14 +4234,34 @@ int16_t Run_script_sub(const char *type, int8_t tlen, JsonParserObject *jo) { lp++; goto next_line; } - else if (!strncmp(lp, "pwm(", 4)) { - lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, 0); +#endif //ESP32 + + else if (!strncmp(lp, "pwm", 3)) { + lp += 3; + uint8_t channel = 1; + if (*(lp+1)=='(') { + channel = *lp & 7; + if (channel > 5) { + channel = 5; + } + lp += 2; + } else { + if (*lp=='(') { + lp++; + } else { + goto next_line; + } + } + lp = GetNumericArgument(lp, OPER_EQU, &fvar, 0); SCRIPT_SKIP_SPACES - esp32_pwm(fvar); + float fvar1=4000; + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &fvar1, 0); + } + esp_pwm(fvar, fvar1, channel); lp++; goto next_line; } -#endif //ESP32 else if (!strncmp(lp, "wcs", 3)) { lp+=4; // skip one space after cmd @@ -5116,7 +5206,16 @@ void SaveScript(void) { #ifdef EEP_SCRIPT_SIZE if (glob_script_mem.flags&1) { +#if EEP_SCRIPT_SIZE==SPI_FLASH_SEC_SIZE EEP_WRITE(0, EEP_SCRIPT_SIZE, glob_script_mem.script_ram); +#else + char *ucs; + ucs = (char*)calloc(SPI_FLASH_SEC_SIZE + 4, 1); + if (!script_compress(ucs,EEP_SCRIPT_SIZE-1)) { + EEP_WRITE(0, EEP_SCRIPT_SIZE, ucs); + } + if (ucs) free(ucs); +#endif } #endif // EEP_SCRIPT_SIZE @@ -5198,6 +5297,21 @@ void ScriptSaveSettings(void) { SaveScriptEnd(); } +// +uint32_t script_compress(char *dest, uint32_t size) { + //AddLog_P(LOG_LEVEL_INFO,PSTR("in string: %s len = %d"),glob_script_mem.script_ram,strlen(glob_script_mem.script_ram)); + uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), dest, size); + if (len_compressed > 0) { + dest[len_compressed] = 0; + AddLog_P(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram)); + return 0; + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed); + return 1; + } +} +//#endif // USE_SCRIPT_COMPRESSION + void SaveScriptEnd(void) { #ifdef USE_SCRIPT_GLOBVARS @@ -5212,20 +5326,11 @@ void SaveScriptEnd(void) { } #ifdef USE_SCRIPT_COMPRESSION - //AddLog_P(LOG_LEVEL_INFO,PSTR("in string: %s len = %d"),glob_script_mem.script_ram,strlen(glob_script_mem.script_ram)); - uint32_t len_compressed = SCRIPT_COMPRESS(glob_script_mem.script_ram, strlen(glob_script_mem.script_ram), Settings.rules[0], MAX_SCRIPT_SIZE-1); - if (len_compressed > 0) { - Settings.rules[0][len_compressed] = 0; - AddLog_P(LOG_LEVEL_INFO,PSTR("script compressed to %d bytes = %d %%"),len_compressed,len_compressed * 100 / strlen(glob_script_mem.script_ram)); - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("script compress error: %d"), len_compressed); - } + script_compress(Settings.rules[0],MAX_SCRIPT_SIZE-1); #endif // USE_SCRIPT_COMPRESSION if (bitRead(Settings.rule_enabled, 0)) { - - int16_t res = Init_Scripter(); if (res) { AddLog_P(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); @@ -6990,7 +7095,7 @@ exgc: WSContentSend_PD("['"); char lbl[16]; if (todflg>=0) { - sprintf(lbl, "%d", todflg / divflg); + sprintf(lbl, "%d:%02d", todflg / divflg, (todflg % divflg) * (60 / divflg) ); todflg++; if (todflg >= entries) { todflg = 0; @@ -7395,9 +7500,10 @@ bool Xdrv10(uint8_t function) #endif //USE_BUTTON_EVENT #ifdef EEP_SCRIPT_SIZE - if (eeprom_init(EEP_SCRIPT_SIZE)) { - // found 32kb eeprom + if (EEP_INIT(EEP_SCRIPT_SIZE)) { + // found 32kb eeprom, char *script; +#if EEP_SCRIPT_SIZE==SPI_FLASH_SEC_SIZE script = (char*)calloc(EEP_SCRIPT_SIZE + 4, 1); if (!script) break; glob_script_mem.script_ram = script; @@ -7407,6 +7513,28 @@ bool Xdrv10(uint8_t function) memset(script, EEP_SCRIPT_SIZE, 0); } script[EEP_SCRIPT_SIZE - 1] = 0; +#else + char *ucs; + ucs = (char*)calloc(SPI_FLASH_SEC_SIZE + 4, 1); + if (!ucs) break; + EEP_READ(0, SPI_FLASH_SEC_SIZE, ucs); + if (*ucs==0xff) { + memset(ucs, SPI_FLASH_SEC_SIZE, 0); + } + ucs[SPI_FLASH_SEC_SIZE - 1] = 0; + + script = (char*)calloc(EEP_SCRIPT_SIZE + 4, 1); + if (!script) break; + glob_script_mem.script_ram = script; + glob_script_mem.script_size = EEP_SCRIPT_SIZE; + + int32_t len_decompressed; + len_decompressed = SCRIPT_DECOMPRESS(ucs, strlen(ucs), glob_script_mem.script_ram, glob_script_mem.script_size); + if (len_decompressed>0) glob_script_mem.script_ram[len_decompressed] = 0; + + if (ucs) free(ucs); + +#endif // use rules storage for permanent vars glob_script_mem.script_pram = (uint8_t*)Settings.rules[0]; glob_script_mem.script_pram_size = MAX_SCRIPT_SIZE; From 5a57ec3dab2872e0586294f8ec36e2404084dab6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 13 Dec 2020 15:34:17 +0100 Subject: [PATCH 07/66] Add support for LE LampUX 907001-US Add support for LE LampUX 907001-US (#10114) --- tasmota/tasmota_template.h | 5 +-- tasmota/xlgt_04_sm2135.ino | 71 +++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/tasmota/tasmota_template.h b/tasmota/tasmota_template.h index dcd3bfb48..51063fb28 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/tasmota_template.h @@ -247,6 +247,7 @@ const char kSensorNamesFixed[] PROGMEM = #define MAX_A4988_MSS 3 #define MAX_WEBCAM_DATA 8 #define MAX_WEBCAM_HSD 3 +#define MAX_SM2135_DAT 4 const uint16_t kGpioNiceList[] PROGMEM = { GPIO_NONE, // Not used @@ -352,8 +353,8 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SM16716_SEL), // SM16716 SELECT #endif // USE_SM16716 #ifdef USE_SM2135 - AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK - AGPIO(GPIO_SM2135_DAT), // SM2135 DATA + AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK + AGPIO(GPIO_SM2135_DAT) + MAX_SM2135_DAT, // SM2135 DATA #endif // USE_SM2135 #ifdef USE_TUYA_MCU AGPIO(GPIO_TUYA_TX), // Tuya Serial interface diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/xlgt_04_sm2135.ino index 17cbc48ec..dd766ab33 100644 --- a/tasmota/xlgt_04_sm2135.ino +++ b/tasmota/xlgt_04_sm2135.ino @@ -24,8 +24,13 @@ * * Action LSC SmartLed (GreenRedBlue) * {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} + * {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,4064,0,4032,0,0,0],"FLAG":0,"BASE":18} * Polux E14 (BlueGreenRed) - Notice GPIO00 = 9 (Switch1) * {"NAME":"Polux RGBCW E14","GPIO":[9,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} + * Polux E14 (BlueGreenRed) + * {"NAME":"Polux RGBCW E14","GPIO":[0,0,0,0,0,0,0,0,4065,0,4032,0,0,0],"FLAG":0,"BASE":18} + * LE LampUX 907001-US + * {"NAME":"LE LampUX 907001-US","GPIO":[0,0,0,0,0,0,0,0,4066,0,4032,0,0,0],"FLAG":0,"BASE":18} \*********************************************************************************************/ #define XLGT_04 4 @@ -53,14 +58,12 @@ #define SM2135_55MA 0x09 #define SM2135_60MA 0x0A -enum Sm2135Color { SM2135_WCGRB, SM2135_WCBGR }; - -// RGB current CW current -const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; // See https://github.com/arendst/Tasmota/issues/6495#issuecomment-549121683 +enum Sm2135Color { SM2135_WCGRB, SM2135_WCBGR, SM2135_WCGRBHI, SM2135_WCBGRHI }; struct SM2135 { uint8_t clk = 0; uint8_t data = 0; + uint8_t current; uint8_t model = SM2135_WCGRB; } Sm2135; @@ -135,37 +138,49 @@ bool Sm2135SetChannels(void) { uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; uint8_t data[6]; - Sm2135Start(SM2135_ADDR_MC); - Sm2135Write(SM2135_CURRENT); - if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { - Sm2135Write(SM2135_CW); - Sm2135Stop(); - delay(1); - Sm2135Start(SM2135_ADDR_C); - Sm2135Write(cur_col[4]); // Warm - Sm2135Write(cur_col[3]); // Cold - } else { + uint32_t light_type = 3; // RGB and CW + if (Sm2135.model < 2) { + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { + light_type = 1; // CW only + } else { + light_type = 2; // RGB only + } + } + if (light_type &2) { // Set RGB + Sm2135Start(SM2135_ADDR_MC); + Sm2135Write(Sm2135.current); Sm2135Write(SM2135_RGB); - if (SM2135_WCBGR == Sm2135.model) { + if (Sm2135.model &1) { // SM2135_WCBGR Sm2135Write(cur_col[2]); // Blue Sm2135Write(cur_col[1]); // Green Sm2135Write(cur_col[0]); // Red - } else { + } else { // SM2135_WCGRB Sm2135Write(cur_col[1]); // Green Sm2135Write(cur_col[0]); // Red Sm2135Write(cur_col[2]); // Blue } + Sm2135Stop(); + } + if (light_type &1) { // Set CW + Sm2135Start(SM2135_ADDR_MC); + Sm2135Write(Sm2135.current); + Sm2135Write(SM2135_CW); + Sm2135Stop(); + delay(1); + Sm2135Start(SM2135_ADDR_C); + Sm2135Write(cur_col[4]); // Warm + Sm2135Write(cur_col[3]); // Cold + Sm2135Stop(); } - Sm2135Stop(); return true; } void Sm2135ModuleSelected(void) { - if (PinUsed(GPIO_SM2135_CLK) && PinUsed(GPIO_SM2135_DAT)) { + if (PinUsed(GPIO_SM2135_CLK) && PinUsed(GPIO_SM2135_DAT, GPIO_ANY)) { Sm2135.clk = Pin(GPIO_SM2135_CLK); - Sm2135.data = Pin(GPIO_SM2135_DAT); + Sm2135.data = Pin(GPIO_SM2135_DAT, GPIO_ANY); Sm2135.model = SM2135_WCGRB; if (PinUsed(GPIO_SWT1)) { @@ -173,12 +188,28 @@ void Sm2135ModuleSelected(void) pinMode(Pin(GPIO_SWT1), INPUT); // Discard GPIO_SWT functionality SetPin(Pin(GPIO_SWT1), AGPIO(GPIO_NONE)); } + if (PinUsed(GPIO_SM2135_DAT, 1)) { + Sm2135.model = SM2135_WCBGR; + } + if (PinUsed(GPIO_SM2135_DAT, 2)) { + Sm2135.model = SM2135_WCGRBHI; + } + if (PinUsed(GPIO_SM2135_DAT, 3)) { + Sm2135.model = SM2135_WCBGRHI; + } + +// RGB current CW current + Sm2135.current = (SM2135_20MA << 4) | SM2135_15MA; // See https://github.com/arendst/Tasmota/issues/6495#issuecomment-549121683 + if (Sm2135.model > SM2135_WCBGR) { + Sm2135.current = (SM2135_20MA << 4) | SM2135_30MA; + } Sm2135Init(); TasmotaGlobal.light_type = LT_RGBWC; TasmotaGlobal.light_driver = XLGT_04; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 (%s) Found"), (SM2135_WCBGR == Sm2135.model) ? PSTR("BGR") : PSTR("GRB")); + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LGT: SM2135 (%s-%s current) Found"), + (SM2135_WCBGR == (Sm2135.model &1)) ? PSTR("BGR") : PSTR("GRB"), (Sm2135.model > SM2135_WCBGR) ? PSTR("High") : PSTR("Low")); } } From 9c5162672b98964a0b37ff94bedfc6575fc53714 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 13 Dec 2020 17:31:46 +0100 Subject: [PATCH 08/66] Fix JSMN unescape function Fix JSMN unescape function (#10107) --- lib/default/jsmn-shadinger-1.0/src/jsmn.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/default/jsmn-shadinger-1.0/src/jsmn.cpp b/lib/default/jsmn-shadinger-1.0/src/jsmn.cpp index 905d102f4..bfd0e11bb 100644 --- a/lib/default/jsmn-shadinger-1.0/src/jsmn.cpp +++ b/lib/default/jsmn-shadinger-1.0/src/jsmn.cpp @@ -399,6 +399,7 @@ void json_unescape(char* string) { c = string[++i]; switch (c) { case 0: + string[outlength++] = 0; return; // end of stream case '\"': case '/': @@ -427,7 +428,10 @@ void json_unescape(char* string) { uint32_t hexval = 0; for (uint32_t j = 0; j < 4; ++j) { char val = string[++i]; - if (0 == val) { return; } // we reached end of string + if (0 == val) { + string[outlength++] = 0; + return; // we reached end of string + } uint32_t uival = 0; if ((val >= 'a') && (val <= 'f')) uival = 10 + (val - 'a'); @@ -450,4 +454,5 @@ void json_unescape(char* string) { string[outlength++] = c; } } + string[outlength++] = 0; } \ No newline at end of file From 990ebb3f346d6efde6410d31ececd423e0ef1804 Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 13 Dec 2020 18:41:12 +0100 Subject: [PATCH 09/66] more debug infos --- tasmota/xsns_36_mgc3130.ino | 55 +++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/tasmota/xsns_36_mgc3130.ino b/tasmota/xsns_36_mgc3130.ino index 036ecf009..c1001c3c8 100644 --- a/tasmota/xsns_36_mgc3130.ino +++ b/tasmota/xsns_36_mgc3130.ino @@ -69,7 +69,8 @@ const char HTTP_MGC_3130_SNS[] PROGMEM = "{s}" "%s" "{m}%s{e}" "{s}" "HwRev" "{m}%u.%u{e}" "{s}" "loaderVer" "{m}%u.%u{e}" - "{s}" "platVer" "{m}%u{e}"; // {s} = , {m} = , {e} = + "{s}" "platVer" "{m}%u{e}" + "{s}" "NoisePower" "{m}%s{e}"; // {s} = , {m} = , {e} = #endif // USE_WEBSERVER @@ -152,17 +153,18 @@ union MGC3130_Union{ float SDData[4]; // signal deviation } out; struct { - uint8_t header[3]; + uint8_t header[4]; // payload - uint8_t valid; + uint8_t valid; // 0xAA is valid uint8_t hwRev[2]; uint8_t parameterStartAddr; uint8_t loaderVersion[2]; uint8_t loaderPlatform; - uint8_t fwStartAddr; + uint8_t fwStartAddr; // should be 0x20 char fwVersion[120]; } fw; struct{ + uint8_t header[4]; uint8_t id; uint8_t size; uint16_t error; @@ -180,7 +182,7 @@ int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; -char MGC3130_firmwareInfo[20]; +float MGC3130_noisePower = -1; uint8_t MGC3130_touchTimeout = 0; uint16_t MGC3130_touchCounter = 1; // measure how long you touch the surface in loop cycles @@ -194,6 +196,7 @@ uint8_t MGC3130_mode = 1; // 1-gesture; 2-airwheel; 3-position uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; +uint8_t MGC3130enableAll[] = {0x10, 0x00, 0x00, 0xA2, 0xA0, 0x00 , 0x00, 0x00, 0x3f, 0x18, 0x00, 0x00, 0x3f, 0x18, 0x00, 0x00}; void MGC3130_handleSensorData(){ if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ @@ -220,6 +223,9 @@ void MGC3130_handleSensorData(){ MqttPublishSensor(); } } + if(MGC_data.out.systemInfo.noisePowerValid){ + MGC3130_noisePower = MGC_data.out.noisePower; + } } void MGC3130_sendMessage(uint8_t data[], uint8_t length){ @@ -394,7 +400,7 @@ void MGC3130_handleAirWheel(){ } void MGC3130_handleSystemStatus(){ - //Serial.println("Got System status"); + AddLog_P(LOG_LEVEL_DEBUG,PSTR("MGC3130: system_status: response to ID:%02x, error code: %04x"),MGC_data.status.id, MGC_data.status.error); } bool MGC3130_receiveMessage(){ @@ -407,16 +413,15 @@ bool MGC3130_receiveMessage(){ MGC3130_handleSystemStatus(); break; case MGC3130_FW_VERSION: - hwRev[0] = MGC_data.fw.hwRev[1]; - hwRev[1] = MGC_data.fw.hwRev[0]; - loaderVersion[0] = MGC_data.fw.loaderVersion[0]; - loaderVersion[1] = MGC_data.fw.loaderVersion[1]; + hwRev[1] = MGC_data.fw.hwRev[1]; + hwRev[0] = MGC_data.fw.hwRev[0]; + loaderVersion[1] = MGC_data.fw.loaderVersion[0]; + loaderVersion[0] = MGC_data.fw.loaderVersion[1]; loaderPlatform = MGC_data.fw.loaderPlatform; - snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); - MGC3130_firmwareInfo[20] = '\0'; - // Serial.print(MGC3130_firmwareInfo); + AddLog_P(LOG_LEVEL_INFO,PSTR("MGC3130: GestIC:%s"),MGC_data.fw.fwVersion); break; } + MGC_data.out.id = 0; return true; } return false; @@ -424,11 +429,12 @@ bool MGC3130_receiveMessage(){ bool MGC3130_readData() { + static uint8_t _lastCounter = 0; bool success = false; if (!digitalRead(MGC3130_xfer)){ pinMode(MGC3130_xfer, OUTPUT); digitalWrite(MGC3130_xfer, LOW); - Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); // request usual data output + Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)132); // request maximal data output MGC_data.buffer[0] = 4; // read at least header, but update after first read anyway unsigned char i = 0; @@ -438,6 +444,14 @@ bool MGC3130_readData() } digitalWrite(MGC3130_xfer, HIGH); pinMode(MGC3130_xfer, INPUT); + uint8_t _mismatch = MGC_data.out.counter - _lastCounter; + if(_mismatch != 1){ + if(i>4 && MGC_data.out.id != MGC3130_FW_VERSION){ + AddLog_P(LOG_LEVEL_DEBUG,PSTR("MGC3130: missed a packet, mismatch: %u"), _mismatch - 1); + AddLogBuffer(LOG_LEVEL_DEBUG,MGC_data.buffer,i); + } + } + _lastCounter = MGC_data.out.counter; success = true; } return success; @@ -537,7 +551,9 @@ void MGC3130_show(bool json) } #ifdef USE_WEBSERVER } else { - WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); + char _noise[FLOATSZ]; + dtostrfd(MGC3130_noisePower, 2, _noise); + WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform, _noise); #endif // USE_WEBSERVER } } @@ -552,6 +568,7 @@ void MGC3130_show(bool json) * Sensor36 | 1 | Gesture Mode * Sensor36 | 2 | Airwheel Mode * Sensor36 | 3 | Position Mode with x,y,z - z must be higher than half of the max. sensing height + * Sensor36 | 4 | Enable all data for debugging (noise level in web GUI) \*********************************************************************************************/ bool MGC3130CommandSensor() @@ -573,8 +590,12 @@ bool MGC3130CommandSensor() case 3: // position & touch MGC3130_mode = 3; MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; + break; + case 4: // enable all readings for noise level for web GUI + MGC3130_sendMessage(MGC3130enableAll,16); + break; } + Response_P(PSTR("{\"MGC3130\":{\"mode\":%d}}"), MGC3130_mode); return serviced; } @@ -614,4 +635,4 @@ bool Xsns36(uint8_t function) return result; } #endif // USE_MGC3130 -#endif // USE_I2C \ No newline at end of file +#endif // USE_I2C From af40f3c5dad416f7e127db3377a81781cb43e990 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 13 Dec 2020 19:18:36 +0100 Subject: [PATCH 10/66] Zigbee fix for visual map --- tasmota/xdrv_23_zigbee_8_parsers.ino | 6 +++++- tasmota/xdrv_23_zigbee_A_impl.ino | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index d97aa8335..172c85181 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -1059,6 +1059,7 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) // device is reachable zigbee_devices.deviceWasReached(shortaddr); + bool non_empty = false; // check whether the response contains relevant information const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr); @@ -1089,6 +1090,7 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) uint8_t m_lqi = buf.get8(idx + 21); idx += 22; + non_empty = true; if (i > 0) { ResponseAppend_P(PSTR(",")); } @@ -1161,6 +1163,7 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) break; // abort for any other value since we don't know the length of the field } + non_empty = true; if (i > 0) { ResponseAppend_P(PSTR(",")); } @@ -1180,7 +1183,8 @@ int32_t Z_Mgmt_Lqi_Bind_Rsp(int32_t res, const class SBuffer &buf, boolean lqi) MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_MAP)); // Check if there are more values waiting, if so re-send a new request to get other values - if (start + len < total) { + // Only send a new request if the current was non-empty. This avoids an infinite loop if the device announces more slots that it actually has. + if ((non_empty) && (start + len < total)) { // there are more values to read #ifdef USE_ZIGBEE_ZNP Z_Send_State_or_Map(shortaddr, start + len, lqi ? ZDO_MGMT_LQI_REQ : ZDO_MGMT_BIND_REQ); diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index f7d6fbdc7..9e35decee 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -1943,7 +1943,7 @@ void ZigbeeShowMap(void) { } else { WSContentSend_P(PSTR( "" - "
" + "
Unable to load vis.js
" "