From 46ad5166e1b02472f5ff26614b8154501d3569c0 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 19 Feb 2020 21:55:20 -0800 Subject: [PATCH 01/20] Add Wemos DC mottor shild (v1.0.0) support --- I2CDEVICES.md | 1 + tasmota/my_user_config.h | 3 + tasmota/support_features.ino | 4 +- tasmota/tasmota_post.h | 3 + tasmota/xdrv_34_wemos_motor_v1.ino | 188 +++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 tasmota/xdrv_34_wemos_motor_v1.ino diff --git a/I2CDEVICES.md b/I2CDEVICES.md index c7b42108f..57e906af7 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -64,4 +64,5 @@ Index | Define | Driver | Device | Address(es) | Description 41 | USE_DHT12 | xsns_58 | DHT12 | 0x5C | Temperature and humidity sensor 42 | USE_DS1624 | xsns_59 | DS1621 | 0x48 - 0x4F | Temperature sensor 42 | USE_DS1624 | xsns_59 | DS1624 | 0x48 - 0x4F | Temperature sensor + 43 | USE_WEMOS_MOTOR_V1 | xdrv_34 | | 0x30 | WEMOS motor shield v1.0.0 (6612FNG) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 67ab1cf1c..af3295928 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -501,6 +501,9 @@ #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module // #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) + #define USE_WEMOS_MOTOR_V1 + #define USE_WEMOS_MOTOR_V1_ADDR 0x30 + #define USE_WEMOS_MOTOR_V1_FREQ 1000 #endif // USE_I2C // -- SPI sensors --------------------------------- diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 2dc25b8e5..224ef86f6 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -513,8 +513,10 @@ void GetFeatures(void) #ifdef USE_LE01MR feature5 |= 0x08000000; // xnrg_13_fif_le01mr.ino #endif +#ifdef USE_WEMOS_MOTOR_V1 + feature5 |= 0x10000000; // xdrv_34_wemos_motor_v1.ino +#endif -// feature5 |= 0x10000000; // feature5 |= 0x20000000; // feature5 |= 0x40000000; // feature5 |= 0x80000000; diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index 615ff8919..33e9c200b 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -221,6 +221,9 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack //#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code +#define USE_WEMOS_MOTOR_V1 + #define USE_WEMOS_MOTOR_V1_ADDR 0x30 + #define USE_WEMOS_MOTOR_V1_FREQ 1000 #endif // FIRMWARE_SENSORS /*********************************************************************************************\ diff --git a/tasmota/xdrv_34_wemos_motor_v1.ino b/tasmota/xdrv_34_wemos_motor_v1.ino new file mode 100644 index 000000000..fbf03d66d --- /dev/null +++ b/tasmota/xdrv_34_wemos_motor_v1.ino @@ -0,0 +1,188 @@ +/* + xdrv_34_wemos_motor_v1.ino - Support for I2C WEMOS motor shield (6612FNG) + + Copyright (C) 2020 Andre Thomas and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_I2C +#ifdef USE_WEMOS_MOTOR_V1 +/*********************************************************************************************\ + * WEMOS_MOTOR_V1 - DC motor driver shield (6612FNG) v 1.0.0 + * + * I2C Address: 0x30 + * Command format: + * driver43 ,,{,} + * command: + * RESET - reset a motor shield + * SETMOTOR - seter motor state + * motor: + * 0 - motor A + * 1 - motor B + * direction: + * 0 - short break + * 1 - CCW + * 2 - CW + * 3 - stop + * 4 - standby + * duty (optional): + * 0 - 100% (100% by default) +\*********************************************************************************************/ + +#define XDRV_34 34 +#define XI2C_43 43 // See I2CDEVICES.md + +#define MOTOR_A 0 +#define MOTOR_B 1 + +#define SHORT_BRAKE 0 +#define CCW 1 +#define CW 2 +#define STOP 3 +#define STANDBY 4 + +bool wemos_driver_detected = false; + +uint8_t _motor; + +void Wemos_Driver_Detect(void) +{ + if (!I2cActive(USE_WEMOS_MOTOR_V1_ADDR)) + { + I2cSetActiveFound(USE_WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); + wemos_driver_detected = true; + Motor_Reset(); + return; + } +} + +void Motor_Reset(void) +{ + Wire.begin(); + Motor_SetFreq(USE_WEMOS_MOTOR_V1_FREQ); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); +} + +void Motor_SetFreq(uint32_t freq) +{ + Wire.beginTransmission(USE_WEMOS_MOTOR_V1_ADDR); + Wire.write(((byte)(freq >> 16)) & (byte)0x0f); + Wire.write((byte)(freq >> 16)); + Wire.write((byte)(freq >> 8)); + Wire.write((byte)freq); + Wire.endTransmission(); // stop transmitting + delay(100); +} + +void Motor_SetMotor(uint8_t motor, uint8_t dir, float pwm_val) +{ + uint16_t _pwm_val; + Wire.beginTransmission(USE_WEMOS_MOTOR_V1_ADDR); + Wire.write(motor | (byte)0x10); + Wire.write(dir); + + _pwm_val = uint16_t(pwm_val * 100); + + if (_pwm_val > 10000) + _pwm_val = 10000; + + Wire.write((byte)(_pwm_val >> 8)); + Wire.write((byte)_pwm_val); + Wire.endTransmission(); + + delay(100); + + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); +} + +bool Motor_Command(void) +{ + uint8_t args_count = 0; + if (XdrvMailbox.data_len > 0) + { + args_count = 1; + } + else + { + return false; + } + + for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) + { + if (' ' == XdrvMailbox.data[idx]) + { + XdrvMailbox.data[idx] = ','; + } + if (',' == XdrvMailbox.data[idx]) + { + args_count++; + } + } + UpperCase(XdrvMailbox.data, XdrvMailbox.data); + + char *command = strtok(XdrvMailbox.data, ","); + + if (strcmp(command, "RESET") == 0) + { + Motor_Reset(); + return true; + } + + if (strcmp(command, "SETMOTOR") == 0) + { + if (args_count >= 3) + { + + int motor = atoi(strtok(NULL, ",")); + int dir = atoi(strtok(NULL, ",")); + int duty = 100; + + if (args_count == 4) + { + duty = atoi(strtok(NULL, ",")); + } + + Motor_SetMotor(motor, dir, duty); + + return true; + } + } + return false; +} + +bool Xdrv34(uint8_t function) +{ + if (!I2cEnabled(XI2C_43)) + { + return false; + } + bool result = false; + if (FUNC_INIT == function) + { + Wemos_Driver_Detect(); + result = wemos_driver_detected; + } + else if (wemos_driver_detected) + { + if (FUNC_COMMAND_DRIVER == function && XI2C_43 == XdrvMailbox.index) + { + result = Motor_Command(); + } + } + return result; +} + +#endif // USE_WEMOS_MOTOR_V1 +#endif // USE_IC2 From a721e35449f7b9de6ac90d3b79009a4d746c943f Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Fri, 21 Feb 2020 21:47:33 -0600 Subject: [PATCH 02/20] Remove power from struct, use DGR log prefix --- tasmota/support_device_groups.ino | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tasmota/support_device_groups.ino b/tasmota/support_device_groups.ino index 3add948a7..336b13cad 100644 --- a/tasmota/support_device_groups.ino +++ b/tasmota/support_device_groups.ino @@ -36,7 +36,6 @@ struct device_group_member { struct device_group { uint32_t next_ack_check_time; - power_t power; uint16_t last_full_status_sequence; uint16_t message_length; uint8_t message_header_length; @@ -64,7 +63,7 @@ void DeviceGroupsInit(void) if (Settings.flag.device_groups_enabled) { device_groups = (struct device_group *)calloc(device_group_count, sizeof(struct device_group)); if (device_groups == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, "DeviceGroups: error allocating %u-element device group array", device_group_count); + AddLog_P2(LOG_LEVEL_ERROR, "DGR: error allocating %u-element device group array", device_group_count); Settings.flag.device_groups_enabled = false; return; } @@ -132,7 +131,7 @@ void SendDeviceGroupPacket(IPAddress ip, char * packet, int len, const char * la } delay(10); } - AddLog_P2(LOG_LEVEL_ERROR, "DeviceGroups: error sending %s packet", label); + AddLog_P2(LOG_LEVEL_ERROR, "DGR: error sending %s packet", label); } void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType message_type, ...) @@ -377,7 +376,7 @@ void _SendDeviceGroupMessage(uint8_t device_group_index, DeviceGroupMessageType // Broadcast the packet. #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, "DeviceGroups: sending %u-byte device group %s packet via broadcast, sequence=%u", device_group->message_length, device_group->group_name, device_group->message[device_group->message_header_length] | device_group->message[device_group->message_header_length + 1] << 8); + AddLog_P2(LOG_LEVEL_DEBUG, "DGR: sending %u-byte device group %s packet via broadcast, sequence=%u", device_group->message_length, device_group->group_name, device_group->message[device_group->message_header_length] | device_group->message[device_group->message_header_length + 1] << 8); #endif // DEVICE_GROUPS_DEBUG SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, "Broadcast"); device_group->next_ack_check_time = millis() + 100; @@ -419,11 +418,11 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) if (!device_group_member) { device_group_member = (struct device_group_member *)calloc(1, sizeof(struct device_group_member)); if (device_group_member == nullptr) { - AddLog_P2(LOG_LEVEL_ERROR, "DeviceGroups: error allocating device group member block"); + AddLog_P2(LOG_LEVEL_ERROR, "DGR: error allocating device group member block"); return; } #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, "DeviceGroups: adding member %s (%p)", IPAddressToString(remote_ip), device_group_member); + AddLog_P2(LOG_LEVEL_DEBUG, "DGR: adding member %s (%p)", IPAddressToString(remote_ip), device_group_member); #endif // DEVICE_GROUPS_DEBUG device_group_member->ip_address = remote_ip; *flink = device_group_member; @@ -538,7 +537,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) case DGR_ITEM_LIGHT_CHANNELS: break; default: - AddLog_P2(LOG_LEVEL_ERROR, "DeviceGroups: ********** invalid item=%u received from device group %s member %s", item, device_group->group_name, IPAddressToString(remote_ip)); + AddLog_P2(LOG_LEVEL_ERROR, "DGR: ********** invalid item=%u received from device group %s member %s", item, device_group->group_name, IPAddressToString(remote_ip)); } #endif // DEVICE_GROUPS_DEBUG @@ -607,7 +606,7 @@ void ProcessDeviceGroupMessage(char * packet, int packet_length) return; badmsg: - AddLog_P2(LOG_LEVEL_ERROR, "DeviceGroups: malformed message received from %s", IPAddressToString(remote_ip)); + AddLog_P2(LOG_LEVEL_ERROR, "DGR: malformed message received from %s", IPAddressToString(remote_ip)); #ifdef DEVICE_GROUPS_DEBUG AddLog_P2(LOG_LEVEL_DEBUG, "packet_length=%u, offset=%u", packet_length, message_ptr - packet); #endif // DEVICE_GROUPS_DEBUG @@ -650,7 +649,7 @@ void DeviceGroupsLoop(void) if (device_group->next_ack_check_time <= now) { if (device_group->initial_status_requests_remaining) { #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, "DeviceGroups: sending initial status request for group %s", device_group->group_name); + AddLog_P2(LOG_LEVEL_DEBUG, "DGR: sending initial status request for group %s", device_group->group_name); #endif // DEVICE_GROUPS_DEBUG if (--device_group->initial_status_requests_remaining) { SendDeviceGroupPacket(IPAddress(239,255,255,250), device_group->message, device_group->message_length, "Initial"); @@ -672,14 +671,14 @@ void DeviceGroupsLoop(void) if (device_group_member->timeout_time && device_group_member->timeout_time < now) { #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, "DeviceGroups: removing member %s (%p)", IPAddressToString(device_group_member->ip_address), device_group_member); + AddLog_P2(LOG_LEVEL_DEBUG, "DGR: removing member %s (%p)", IPAddressToString(device_group_member->ip_address), device_group_member); #endif // DEVICE_GROUPS_DEBUG *flink = device_group_member->flink; free(device_group_member); } else { #ifdef DEVICE_GROUPS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, "DeviceGroups: sending %u-byte device group %s packet via unicast to %s, sequence %u, last message acked=%u", device_group->message_length, device_group->group_name, IPAddressToString(device_group_member->ip_address), outgoing_sequence, device_group_member->acked_sequence); + AddLog_P2(LOG_LEVEL_DEBUG, "DGR: sending %u-byte device group %s packet via unicast to %s, sequence %u, last message acked=%u", device_group->message_length, device_group->group_name, IPAddressToString(device_group_member->ip_address), outgoing_sequence, device_group_member->acked_sequence); #endif // DEVICE_GROUPS_DEBUG SendDeviceGroupPacket(device_group_member->ip_address, device_group->message, device_group->message_length, "Unicast"); if (!device_group_member->timeout_time) device_group_member->timeout_time = now + 15000; From 5f1dac2119de6676f8cf410d9c40ffe3ed2042c4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Feb 2020 11:01:47 +0100 Subject: [PATCH 03/20] Update my_user_config.h --- tasmota/my_user_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 15f8e3a3f..8bf359605 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -502,7 +502,7 @@ #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module // #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) - #define USE_WEMOS_MOTOR_V1 +// #define USE_WEMOS_MOTOR_V1 #define USE_WEMOS_MOTOR_V1_ADDR 0x30 #define USE_WEMOS_MOTOR_V1_FREQ 1000 #endif // USE_I2C From f706b7c4900edc0820ed688ab6396647cd30a66d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 22 Feb 2020 12:18:20 +0100 Subject: [PATCH 04/20] Add support for Wemos Motor Shield V1 Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) --- BUILDS.md | 7 ++ RELEASENOTES.md | 3 +- tasmota/CHANGELOG.md | 1 + tasmota/my_user_config.h | 6 +- tasmota/tasmota_post.h | 6 +- tasmota/xdrv_34_wemos_motor_v1.ino | 130 ++++++++++++++--------------- tools/decode-status.py | 4 +- 7 files changed, 81 insertions(+), 76 deletions(-) diff --git a/BUILDS.md b/BUILDS.md index aa1834061..88ee58cd0 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -62,6 +62,7 @@ | USE_DDS2382 | - | - | - | - | x | - | - | | USE_DDSU666 | - | - | - | - | x | - | - | | USE_SOLAX_X1 | - | - | - | - | - | - | - | +| USE_LE01MR | - | - | - | - | - | - | - | | | | | | | | | | | USE_ADC_VCC | x | x | - | - | - | - | - | | USE_COUNTER | - | - | x | x | x | x | x | @@ -109,6 +110,8 @@ | USE_HIH6 | - | - | - | - | x | - | - | | USE_DHT12 | - | - | - | - | x | - | - | | USE_DS1624 | - | - | - | - | x | - | - | +| USE_AHT1x | - | - | - | - | - | - | - | +| USE_WEMOS_MOTOR_V1 | - | - | - | - | x | - | - | | | | | | | | | | | Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks | USE_SPI | - | - | - | - | - | - | x | @@ -124,6 +127,10 @@ | USE_RDM6300 | - | - | - | - | x | - | - | | USE_IBEACON | - | - | - | - | x | - | - | | USE_GPS | - | - | - | - | - | - | - | +| USE_HM10 | - | - | - | - | x | - | - | +| | | | | | | | | +| USE_NRF24 | - | - | - | - | - | - | - | +| USE_MIBLE | - | - | - | - | - | - | - | | USE_ZIGBEE | - | - | - | - | - | - | - | | | | | | | | | | | USE_IR_REMOTE | - | - | x | x | x | x | x | diff --git a/RELEASENOTES.md b/RELEASENOTES.md index dba7abeed..e5e08674c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -103,4 +103,5 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for FiF LE-01MR energy meter by saper-2 (#7584) - Add new DHT driver. The old driver can still be used using define USE_DHT_OLD (#7468) - Add another new DHT driver based on ESPEasy. The old driver can still be used using define USE_DHT_OLD. The previous new driver can be used with define USE_DHT_V2 (#7717) -- Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) \ No newline at end of file +- Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) +- Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 2689a7222..465bcde8f 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -4,6 +4,7 @@ - Revert most wifi connectivity changes introduced in 8.1.0.5 (#7746, #7602, #7621) - Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) +- Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) ### 8.1.0.8 20200212 diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 8bf359605..c2cf75fe0 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -487,6 +487,9 @@ // #define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) // #define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) +// #define USE_WEMOS_MOTOR_V1 // [I2cDriver44] Enable Wemos motor driver V1 (I2C addresses 0x2D - 0x30) (+0k7 code) +// #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 +// #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 @@ -502,9 +505,6 @@ #define MTX_ADDRESS7 0x00 // [DisplayAddress7] I2C address of seventh 8x8 matrix module #define MTX_ADDRESS8 0x00 // [DisplayAddress8] I2C address of eigth 8x8 matrix module // #define USE_DISPLAY_SH1106 // [DisplayModel 7] [I2cDriver6] Enable SH1106 Oled 128x64 display (I2C addresses 0x3C and 0x3D) -// #define USE_WEMOS_MOTOR_V1 - #define USE_WEMOS_MOTOR_V1_ADDR 0x30 - #define USE_WEMOS_MOTOR_V1_FREQ 1000 #endif // USE_I2C // -- SPI sensors --------------------------------- diff --git a/tasmota/tasmota_post.h b/tasmota/tasmota_post.h index a126ff6fc..a2b49e677 100644 --- a/tasmota/tasmota_post.h +++ b/tasmota/tasmota_post.h @@ -165,6 +165,9 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack //#define USE_PCF8574 // Enable PCF8574 I/O Expander (I2C addresses 0x20 - 0x26 and 0x39 - 0x3F) (+1k9 code) #define USE_HIH6 // Enable Honywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) //#define USE_AHT1x // Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) +#define USE_WEMOS_MOTOR_V1 // Enable Wemos motor driver V1 (I2C addresses 0x2D - 0x30) (+0k7 code) + #define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 + #define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency #define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) #define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) @@ -223,9 +226,6 @@ extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack //#define USE_ARDUINO_SLAVE // Add support for Arduino Uno/Pro Mini via serial interface including flashing (+2k3 code, 44 mem) #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code -#define USE_WEMOS_MOTOR_V1 - #define USE_WEMOS_MOTOR_V1_ADDR 0x30 - #define USE_WEMOS_MOTOR_V1_FREQ 1000 #endif // FIRMWARE_SENSORS /*********************************************************************************************\ diff --git a/tasmota/xdrv_34_wemos_motor_v1.ino b/tasmota/xdrv_34_wemos_motor_v1.ino index 5f744a914..9e6c0a967 100644 --- a/tasmota/xdrv_34_wemos_motor_v1.ino +++ b/tasmota/xdrv_34_wemos_motor_v1.ino @@ -1,7 +1,7 @@ /* xdrv_34_wemos_motor_v1.ino - Support for I2C WEMOS motor shield (6612FNG) - Copyright (C) 2020 Andre Thomas and Theo Arends + Copyright (C) 2020 Denis Sborets 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 @@ -23,6 +23,7 @@ * WEMOS_MOTOR_V1 - DC motor driver shield (6612FNG) v 1.0.0 * * I2C Address: 0x30 + * * Command format: * driver44 ,,{,} * command: @@ -41,8 +42,15 @@ * 0 - 100% (100% by default) \*********************************************************************************************/ -#define XDRV_34 34 -#define XI2C_44 44 // See I2CDEVICES.md +#define XDRV_34 34 +#define XI2C_44 44 // See I2CDEVICES.md + +#ifndef WEMOS_MOTOR_V1_ADDR +#define WEMOS_MOTOR_V1_ADDR 0x30 // Default I2C address 0x30 +#endif +#ifndef WEMOS_MOTOR_V1_FREQ +#define WEMOS_MOTOR_V1_FREQ 1000 // Default frequency +#endif #define MOTOR_A 0 #define MOTOR_B 1 @@ -53,80 +61,69 @@ #define STOP 3 #define STANDBY 4 -bool wemos_driver_detected = false; +struct WMOTORV1 { + bool detected = false; + uint8_t motor; +} WMotorV1; -uint8_t _motor; - -void Wemos_Driver_Detect(void) +void WMotorV1Detect(void) { - if (!I2cActive(USE_WEMOS_MOTOR_V1_ADDR)) - { - I2cSetActiveFound(USE_WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); - wemos_driver_detected = true; - Motor_Reset(); - return; + if (I2cSetDevice(WEMOS_MOTOR_V1_ADDR)) { + WMotorV1.detected = true; + I2cSetActiveFound(WEMOS_MOTOR_V1_ADDR, "WEMOS_MOTOR_V1"); + WMotorV1Reset(); } } -void Motor_Reset(void) +void WMotorV1Reset(void) { - Wire.begin(); - Motor_SetFreq(USE_WEMOS_MOTOR_V1_FREQ); - Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); +// Wire.begin(); + WMotorV1SetFrequency(WEMOS_MOTOR_V1_FREQ); } -void Motor_SetFreq(uint32_t freq) +void WMotorV1SetFrequency(uint32_t freq) { - Wire.beginTransmission(USE_WEMOS_MOTOR_V1_ADDR); + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); Wire.write(((byte)(freq >> 16)) & (byte)0x0f); Wire.write((byte)(freq >> 16)); Wire.write((byte)(freq >> 8)); Wire.write((byte)freq); Wire.endTransmission(); // stop transmitting - delay(100); +// delay(100); } -void Motor_SetMotor(uint8_t motor, uint8_t dir, float pwm_val) +void WMotorV1SetMotor(uint8_t motor, uint8_t dir, float pwm_val) { - uint16_t _pwm_val; - Wire.beginTransmission(USE_WEMOS_MOTOR_V1_ADDR); + Wire.beginTransmission(WEMOS_MOTOR_V1_ADDR); Wire.write(motor | (byte)0x10); Wire.write(dir); - _pwm_val = uint16_t(pwm_val * 100); - - if (_pwm_val > 10000) + uint16_t _pwm_val = uint16_t(pwm_val * 100); + if (_pwm_val > 10000) { _pwm_val = 10000; + } Wire.write((byte)(_pwm_val >> 8)); Wire.write((byte)_pwm_val); Wire.endTransmission(); - - delay(100); - - Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); +// delay(100); } -bool Motor_Command(void) +bool WMotorV1Command(void) { uint8_t args_count = 0; - if (XdrvMailbox.data_len > 0) - { + + if (XdrvMailbox.data_len > 0) { args_count = 1; - } - else - { + } else { return false; } - for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) - { - if (' ' == XdrvMailbox.data[idx]) - { + for (uint32_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + if (' ' == XdrvMailbox.data[idx]) { XdrvMailbox.data[idx] = ','; } - if (',' == XdrvMailbox.data[idx]) - { + if (',' == XdrvMailbox.data[idx]) { args_count++; } } @@ -134,51 +131,50 @@ bool Motor_Command(void) char *command = strtok(XdrvMailbox.data, ","); - if (strcmp(command, "RESET") == 0) - { - Motor_Reset(); + if (strcmp(command, "RESET") == 0) { + WMotorV1Reset(); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"RESET\":\"OK\"}}")); return true; } - if (strcmp(command, "SETMOTOR") == 0) - { - if (args_count >= 3) - { + if (strcmp(command, "SETMOTOR") == 0) { + if (args_count >= 3) { int motor = atoi(strtok(NULL, ",")); int dir = atoi(strtok(NULL, ",")); int duty = 100; - - if (args_count == 4) - { + if (args_count == 4) { duty = atoi(strtok(NULL, ",")); } - Motor_SetMotor(motor, dir, duty); - + WMotorV1SetMotor(motor, dir, duty); + Response_P(PSTR("{\"WEMOS_MOTOR_V1\":{\"SETMOTOR\":\"OK\"}}")); return true; } } return false; } +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + bool Xdrv34(uint8_t function) { - if (!I2cEnabled(XI2C_44)) - { - return false; - } + if (!I2cEnabled(XI2C_44)) { return false; } + bool result = false; - if (FUNC_INIT == function) - { - Wemos_Driver_Detect(); - result = wemos_driver_detected; + + if (FUNC_INIT == function) { + WMotorV1Detect(); } - else if (wemos_driver_detected) - { - if (FUNC_COMMAND_DRIVER == function && XI2C_44 == XdrvMailbox.index) - { - result = Motor_Command(); + else if (WMotorV1.detected) { + switch (function) { + case FUNC_COMMAND_DRIVER: + if (XI2C_44 == XdrvMailbox.index) { + result = WMotorV1Command(); + } + break; } } return result; diff --git a/tools/decode-status.py b/tools/decode-status.py index 210175de8..3838d8faf 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -193,7 +193,7 @@ a_features = [[ "USE_ARDUINO_SLAVE","USE_HIH6","USE_HPMA","USE_TSL2591", "USE_DHT12","USE_DS1624","USE_GPS","USE_HOTPLUG", "USE_NRF24","USE_MIBLE","USE_HM10","USE_LE01MR", - "USE_AHT1x","","","" + "USE_AHT1x","USE_WEMOS_MOTOR_V1","","" ],[ "","","","", "","","","", @@ -236,7 +236,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v20200220 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v20200222 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) From 13763f55a68361c13e81b56b279cdb0d798c76b3 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sat, 22 Feb 2020 20:53:55 +0100 Subject: [PATCH 05/20] Fix Zigbee auto-increment transaction number (#7757) --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 1 + tasmota/xdrv_23_zigbee_1_headers.ino | 2 +- tasmota/xdrv_23_zigbee_3_devices.ino | 23 ++++++++++++++++++++++- tasmota/xdrv_23_zigbee_6_commands.ino | 2 +- tasmota/xdrv_23_zigbee_8_parsers.ino | 2 ++ tasmota/xdrv_23_zigbee_9_impl.ino | 9 +++++---- 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 465bcde8f..c8cf0ab3a 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -5,6 +5,7 @@ - Revert most wifi connectivity changes introduced in 8.1.0.5 (#7746, #7602, #7621) - Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) - Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) +- Fix Zigbee auto-increment transaction number (#7757) ### 8.1.0.8 20200212 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index 00cda53b4..c7930e79b 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -489,6 +489,7 @@ #define D_CMND_ZIGBEE_FORGET "Forget" #define D_CMND_ZIGBEE_SAVE "Save" #define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality" + #define D_CMND_ZIGBEE_ENDPOINT "Endpoint" #define D_CMND_ZIGBEE_READ "Read" #define D_CMND_ZIGBEE_SEND "Send" #define D_JSON_ZIGBEE_ZCL_SENT "ZbZCLSent" diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/xdrv_23_zigbee_1_headers.ino index 101d5703d..0bc592266 100644 --- a/tasmota/xdrv_23_zigbee_1_headers.ino +++ b/tasmota/xdrv_23_zigbee_1_headers.ino @@ -21,7 +21,7 @@ // contains some definitions for functions used before their declarations -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1); +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId); // Get an JSON attribute, with case insensitive key search diff --git a/tasmota/xdrv_23_zigbee_3_devices.ino b/tasmota/xdrv_23_zigbee_3_devices.ino index 4814bdb8a..78c192cc1 100644 --- a/tasmota/xdrv_23_zigbee_3_devices.ino +++ b/tasmota/xdrv_23_zigbee_3_devices.ino @@ -49,6 +49,8 @@ typedef struct Z_Device { // json buffer used for attribute reporting DynamicJsonBuffer *json_buffer; JsonObject *json; + // sequence number for Zigbee frames + uint8_t seqNumber; } Z_Device; // All devices are stored in a Vector @@ -96,6 +98,9 @@ public: // device just seen on the network, update the lastSeen field void updateLastSeen(uint16_t shortaddr); + // get next sequence number for (increment at each all) + uint8_t getNextSeqNumber(uint16_t shortaddr); + // Dump json String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; @@ -133,6 +138,7 @@ public: private: std::vector _devices = {}; uint32_t _saveTimer = 0; + uint8_t _seqNumber = 0; // global seqNumber if device is unknown template < typename T> static bool findInVector(const std::vector & vecOfElements, const T & element); @@ -226,7 +232,9 @@ Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { std::vector(), 0,0,0,0, nullptr, - nullptr, nullptr }; + nullptr, nullptr, + 0, // seqNumber + }; device.json_buffer = new DynamicJsonBuffer(); _devices.push_back(device); dirty(); @@ -532,6 +540,19 @@ void Z_Devices::updateLastSeen(uint16_t shortaddr) { _updateLastSeen(device); } +// get the next sequance number for the device, or use the global seq number if device is unknown +uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { + int32_t short_found = findShortAddr(shortaddr); + if (short_found >= 0) { + Z_Device &device = getShortAddr(shortaddr); + device.seqNumber += 1; + return device.seqNumber; + } else { + _seqNumber += 1; + return _seqNumber; + } +} + // Per device timers // // Reset the timer for a specific device diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 073ab8e4c..9d6f38b95 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -78,7 +78,7 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoi break; } if (attrs) { - ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); + ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr)); } } diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 668f85471..7d84124ed 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -451,6 +451,8 @@ int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); zcl_received.postProcessAttributes(srcaddr, json); + // Add Endpoint + json[F(D_CMND_ZIGBEE_ENDPOINT)] = srcendpoint; // Add linkquality json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index 6ea08c4ac..eccfc56be 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -344,7 +344,7 @@ void ZigbeeZNPSend(const uint8_t *msg, size_t len) { ToHex_P(msg, len, hex_char, sizeof(hex_char))); } -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId) { +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool needResponse, uint8_t transacId) { SBuffer buf(25+len); buf.add8(Z_SREQ | Z_AF); // 24 buf.add8(AF_DATA_REQUEST); // 01 @@ -357,7 +357,7 @@ void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8 buf.add8(0x1E); // 1E radius buf.add8(3 + len); - buf.add8((disableDefResp ? 0x10 : 0x00) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field + buf.add8((needResponse ? 0x00 : 0x10) | (clusterSpecific ? 0x01 : 0x00)); // Frame Control Field buf.add8(transacId); // Transaction Sequance Number buf.add8(cmdId); if (len > 0) { @@ -438,7 +438,7 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { } // everything is good, we can send the command - ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len()); + ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len(), false, zigbee_devices.getNextSeqNumber(dstAddr)); // now set the timer, if any, to read back the state later if (clusterSpecific) { zigbeeSetCommandTimer(dstAddr, cluster, endpoint); @@ -469,6 +469,7 @@ void CmndZbSend(void) { uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint String cmd_str = ""; // the actual low-level command, either specified or computed + // parse JSON const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); if (nullptr != &val_device) { device = zigbee_devices.parseDeviceParam(val_device.as()); @@ -729,7 +730,7 @@ void CmndZbRead(void) { } if ((0 != endpoint) && (attrs_len > 0)) { - ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false /* we do want a response */); + ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device)); ResponseCmndDone(); } else { ResponseCmndChar("Missing parameters"); From 73b2fdd6f472891631be54233d0968597d5d3bbd Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 23 Feb 2020 09:21:54 +0100 Subject: [PATCH 06/20] fix OUI-bug --- tasmota/xsns_61_MI_NRF24.ino | 115 +++++++++++++++++++++++++---------- 1 file changed, 83 insertions(+), 32 deletions(-) diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index 8527c9749..cc865f6d6 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -21,6 +21,9 @@ Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 0.9.3.0 20200222 integrate - use now the correct id-word instead of MAC-OUI, + add CGG1 + --- 0.9.2.0 20200212 integrate - "backports" from MI-HM10, change reading pattern, add missing PDU-types, renaming driver --- @@ -58,18 +61,21 @@ #define MJ_HT_V1 2 #define LYWSD02 3 #define LYWSD03 4 +#define CGG1 5 -uint8_t kMINRFSlaveID[4][3] = { 0xC4,0x7C,0x8D, // Flora - 0x58,0x2D,0x34, // MJ_HT_V1 - 0xE7,0x2E,0x00, // LYWSD02 - 0xA4,0xC1,0x38, // LYWSD03 - }; +const uint16_t kMINRFSlaveID[5]={ 0x0098, // Flora + 0x01aa, // MJ_HT_V1 + 0x045b, // LYWSD02 + 0x055b, // LYWSD03 + 0x0347 // CGG1 + }; const char kMINRFSlaveType1[] PROGMEM = "Flora"; const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; -const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4}; +const char kMINRFSlaveType5[] PROGMEM = "CGG1"; +const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4,kMINRFSlaveType5}; // PDU's or different channels 37-39 const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; @@ -77,10 +83,11 @@ const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71da7646}; // 1 and 3 unsure // const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0xffffffff}; //encrypted - 58 58 const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; //unencrypted - 30 58 +const uint32_t kMINRFCGPDU[3] = {0x4760cd78,0xdbcc0ccd,0x33048deb}; // very unsure!!! // start-LSFR for different channels 37-39 const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02 -const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03 +const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03, ???CGG1???? #pragma pack(1) // important!! @@ -269,7 +276,7 @@ struct { } MINRF; struct mi_sensor_t{ - uint8_t type; //Flora = 1; MJ_HT_V1=2; LYWSD02=3; LYWSD03=4 + uint8_t type; //Flora = 1; MJ_HT_V1=2; LYWSD02=3; LYWSD03=4; ; CGG1=5 uint8_t serial[6]; uint8_t showedUp; float temp; //Flora, MJ_HT_V1, LYWSD0x @@ -362,6 +369,9 @@ bool MINRFreceivePacket(void) case 4: MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "LYWSD03" mode break; + case 5: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); // "CGG1" mode + break; } // DEBUG_SENSOR_LOG(PSTR("MINRF: LSFR:%x"),_lsfr); // if (_lsfr>254) _lsfr=0; @@ -470,6 +480,9 @@ void MINRFchangePacketModeTo(uint8_t _mode) { if(kMINRFL3PDU[_nextchannel]==0xffffffff) break; NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]);// 95 fe 58 30 -> LYWSD03 (= no data message) break; + case 5: // special CGG1 packet + NRF24radio.openReadingPipe(0,kMINRFCGPDU[_nextchannel]); // 95 fe 50 30 -> CGG1 + break; } // DEBUG_SENSOR_LOG(PSTR("MINRF: Change Mode to %u"),_mode); MINRF.packetMode = _mode; @@ -479,24 +492,25 @@ void MINRFchangePacketModeTo(uint8_t _mode) { * @brief Return the slot number of a known sensor or return create new sensor slot * * @param _serial BLE address of the sensor - * @param _type Type number of the sensor, 0xff for Auto-type + * @param _type Type number of the sensor * @return uint32_t Known or new slot in the sensors-vector */ -uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ - if(_type==0xff){ - DEBUG_SENSOR_LOG(PSTR("MINRF: will test MAC-type")); - for (uint32_t i=0;i<4;i++){ - if(memcmp(_serial,kMINRFSlaveID+i,3)==0){ - DEBUG_SENSOR_LOG(PSTR("MINRF: MAC is type %u"), i); - _type = i+1; - } - else { - DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-type is unknown")); - } +uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint16_t _type){ + + DEBUG_SENSOR_LOG(PSTR("MINRF: will test ID-type: %x"), _type); + bool _success = false; + for (uint32_t i=0;i<5;i++){ + if(_type == kMINRFSlaveID[i]){ + DEBUG_SENSOR_LOG(PSTR("MINRF: ID is type %u"), i); + _type = i+1; + _success = true; + } + else { + DEBUG_SENSOR_LOG(PSTR("MINRF: ID-type is not: %x"),kMINRFSlaveID[i]); } } - if(_type==0xff) return _type; // error - + if(!_success) return 0xff; + DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); for(uint32_t i=0; i6000){ // happens every 6000/20 = 300 seconds @@ -712,9 +762,10 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds else if (MINRF.packetMode == LYWSD03){ MINRFhandleLYWSD03Packet(); } - - // DEBUG_SENSOR_LOG(PSTR("MINRF: Change packet mode every 50 msec")); - if (MINRF.packetMode == LYWSD03){ + else if (MINRF.packetMode == CGG1){ + MINRFhandleCGG1Packet(); + } + if (MINRF.packetMode == CGG1){ MINRFinitBLE(1); // no real ble packets in release mode, otherwise for developing use 0 } else { From 11604e30d4fcb5a56f996b41f745e24f17730d8e Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sun, 23 Feb 2020 10:29:04 +0100 Subject: [PATCH 07/20] Moved 'Updated shadow' do DEBUG log level --- tasmota/xdrv_02_mqtt.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_02_mqtt.ino b/tasmota/xdrv_02_mqtt.ino index aadf44f71..1fda2a95a 100644 --- a/tasmota/xdrv_02_mqtt.ino +++ b/tasmota/xdrv_02_mqtt.ino @@ -398,7 +398,7 @@ void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retain free(mqtt_save); bool result = MqttClient.publish(romram, mqtt_data, false); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); yield(); // #3313 } #endif // USE_MQTT_AWS_IOT From 8411178dacef96ccf20b9536de0bb4540bbb5e20 Mon Sep 17 00:00:00 2001 From: Staars Date: Sun, 23 Feb 2020 14:52:26 +0100 Subject: [PATCH 08/20] use now the correct PDU-types --- tasmota/xsns_61_MI_NRF24.ino | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/xsns_61_MI_NRF24.ino index cc865f6d6..f1db19d74 100644 --- a/tasmota/xsns_61_MI_NRF24.ino +++ b/tasmota/xsns_61_MI_NRF24.ino @@ -83,11 +83,11 @@ const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71da7646}; // 1 and 3 unsure // const uint32_t kMINRFL3PDU[3] = {0x4760dd78,0xdbcc1ccd,0xffffffff}; //encrypted - 58 58 const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; //unencrypted - 30 58 -const uint32_t kMINRFCGPDU[3] = {0x4760cd78,0xdbcc0ccd,0x33048deb}; // very unsure!!! +const uint32_t kMINRFCGPDU[3] = {0x4760cd6e,0xdbcc0cdb,0x33048dfd}; // start-LSFR for different channels 37-39 const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; // Flora, LYWSD02 -const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03, ???CGG1???? +const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; // MJ_HT_V1, LYWSD03, CGG1 #pragma pack(1) // important!! @@ -720,6 +720,10 @@ void MINRFhandleCGG1Packet(void){ // we assume, that the packet structure is equ } } +/*********************************************************************************************\ + * Main loop of the driver +\*********************************************************************************************/ + void MINRF_EVERY_50_MSECOND() { // Every 50mseconds if(MINRF.timer>6000){ // happens every 6000/20 = 300 seconds @@ -771,7 +775,7 @@ void MINRF_EVERY_50_MSECOND() { // Every 50mseconds else { MINRFinitBLE(++MINRF.packetMode); } - + MINRFhopChannel(); NRF24radio.startListening(); } From d4dd0a39a5ab865055c99c2a8e045972d67b7da7 Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sun, 23 Feb 2020 16:46:00 +0100 Subject: [PATCH 09/20] Add Zigbee enhanced commands decoding, added ``ZbPing`` --- tasmota/CHANGELOG.md | 1 + tasmota/i18n.h | 2 + tasmota/xdrv_23_zigbee_3_devices.ino | 8 + tasmota/xdrv_23_zigbee_5_converters.ino | 12 +- tasmota/xdrv_23_zigbee_6_commands.ino | 261 +++++++++++++++++++--- tasmota/xdrv_23_zigbee_7_statemachine.ino | 1 + tasmota/xdrv_23_zigbee_8_parsers.ino | 51 ++++- tasmota/xdrv_23_zigbee_9_impl.ino | 114 +++++----- 8 files changed, 344 insertions(+), 106 deletions(-) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index c8cf0ab3a..ef9ce17f4 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -6,6 +6,7 @@ - Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) - Add support for Wemos Motor Shield V1 by Denis Sborets (#7764) - Fix Zigbee auto-increment transaction number (#7757) +- Add Zigbee enhanced commands decoding, added ``ZbPing`` ### 8.1.0.8 20200212 diff --git a/tasmota/i18n.h b/tasmota/i18n.h index c7930e79b..ab7d18ce0 100644 --- a/tasmota/i18n.h +++ b/tasmota/i18n.h @@ -496,6 +496,8 @@ #define D_JSON_ZIGBEE_RECEIVED "ZbReceived" #define D_JSON_ZIGBEE_RECEIVED_LEGACY "ZigbeeReceived" #define D_CMND_ZIGBEE_BIND "Bind" +#define D_CMND_ZIGBEE_PING "Ping" + #define D_JSON_ZIGBEE_PING "ZbPing" // Commands xdrv_25_A4988_Stepper.ino #define D_CMND_MOTOR "MOTOR" diff --git a/tasmota/xdrv_23_zigbee_3_devices.ino b/tasmota/xdrv_23_zigbee_3_devices.ino index 78c192cc1..71752f4e3 100644 --- a/tasmota/xdrv_23_zigbee_3_devices.ino +++ b/tasmota/xdrv_23_zigbee_3_devices.ino @@ -19,6 +19,10 @@ #ifdef USE_ZIGBEE +#ifndef ZIGBEERECEIVED +#define ZIGBEERECEIVED 1 +#endif + #include #include @@ -725,18 +729,22 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); +#if ZIGBEERECEIVED // DEPRECATED TODO Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); +#endif } else { Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); +#if ZIGBEERECEIVED // DEPRECATED TODO Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); XdrvRulesProcess(); +#endif } // MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); // XdrvRulesProcess(); diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/xdrv_23_zigbee_5_converters.ino index d97e5e7d4..4debd73fd 100644 --- a/tasmota/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/xdrv_23_zigbee_5_converters.ino @@ -486,18 +486,8 @@ void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { // Parse non-normalized attributes -// The key is "s_" followed by 16 bits clusterId, "_" followed by 8 bits command id void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - char attrid_str[12]; - snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cluster_id, _cmd_id); - - 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; + convertClusterSpecific(json, _cluster_id, _cmd_id, _frame_control.b.direction, _payload); } // return value: diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 9d6f38b95..83841f90c 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -19,34 +19,79 @@ #ifdef USE_ZIGBEE -//typedef int32_t (*Z_AttrConverter)(uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const char *new_name, void * param); typedef struct Z_CommandConverter { const char * tasmota_cmd; - const char * zcl_cmd; + uint16_t cluster; + uint8_t cmd; // normally 8 bits, 0xFF means it's a parameter + uint8_t direction; // direction of the command. 0x01 client->server, 0x02 server->client, 0x03 both + const char * param; } Z_CommandConverter; +typedef struct Z_XYZ_Var { // Holds values for vairables X, Y and Z + uint32_t x = 0; + uint32_t y = 0; + uint32_t z = 0; + uint8_t x_type = 0; // 0 = no value, 1 = 1 bytes, 2 = 2 bytes + uint8_t y_type = 0; + uint8_t z_type = 0; +} Z_XYZ_Var; + // list of post-processing directives const Z_CommandConverter Z_Commands[] = { - { "Power", "0006!xx" }, // 0=Off, 1=On, 2=Toggle - { "Dimmer", "0008!04/xx0A00" }, // Move to Level with On/Off, xx=0..254 (255 is invalid) - { "Dimmer+", "0008!06/001902" }, // Step up by 10%, 0.2 secs - { "Dimmer-", "0008!06/011902" }, // Step down by 10%, 0.2 secs - { "DimmerStop", "0008!03" }, // Stop any Dimmer animation - { "ResetAlarm", "0009!00/xxyyyy" }, // Reset alarm (alarm code + cluster identifier) - { "ResetAllAlarms","0009!01" }, // Reset all alarms - { "Hue", "0300!00/xx000A00" }, // Move to Hue, shortest time, 1s - { "Sat", "0300!03/xx0A00" }, // Move to Sat - { "HueSat", "0300!06/xxyy0A00" }, // Hue, Sat - { "Color", "0300!07/xxxxyyyy0A00" }, // x, y (uint16) - { "CT", "0300!0A/xxxx0A00" }, // Color Temperature Mireds (uint16) - { "Shutter", "0102!xx" }, - { "ShutterOpen", "0102!00" }, - { "ShutterClose", "0102!01" }, - { "ShutterStop", "0102!02" }, - { "ShutterLift", "0102!05xx" }, // Lift percentage, 0%=open, 100%=closed - { "ShutterTilt", "0102!08xx" }, // Tilt percentage + // Group adress commands + { "AddGroup", 0x0004, 0x00, 0x01, "xxxx00" }, // Add group id, group name is not supported + { "ViewGroup", 0x0004, 0x01, 0x01, "xxxx" }, // Ask for the group name + { "GetGroup", 0x0004, 0x02, 0x01, "01xxxx" }, // Get one group membership + { "GetAllGroups", 0x0004, 0x02, 0x01, "00" }, // Get all groups membership + { "RemoveGroup", 0x0004, 0x03, 0x01, "xxxx" }, // Remove one group + { "RemoveAllGroups",0x0004, 0x04, 0x01, "" }, // Remove all groups + // Light & Shutter commands + { "Power", 0x0006, 0xFF, 0x01, "" }, // 0=Off, 1=On, 2=Toggle + { "Dimmer", 0x0008, 0x04, 0x01, "xx0A00" }, // Move to Level with On/Off, xx=0..254 (255 is invalid) + { "Dimmer+", 0x0008, 0x06, 0x01, "001902" }, // Step up by 10%, 0.2 secs + { "Dimmer-", 0x0008, 0x06, 0x01, "011902" }, // Step down by 10%, 0.2 secs + { "DimmerStop", 0x0008, 0x03, 0x01, "" }, // Stop any Dimmer animation + { "ResetAlarm", 0x0009, 0x00, 0x01, "xxyyyy" }, // Reset alarm (alarm code + cluster identifier) + { "ResetAllAlarms", 0x0009, 0x01, 0x01, "" }, // Reset all alarms + { "Hue", 0x0300, 0x00, 0x01, "xx000A00" }, // Move to Hue, shortest time, 1s + { "Sat", 0x0300, 0x03, 0x01, "xx0A00" }, // Move to Sat + { "HueSat", 0x0300, 0x06, 0x01, "xxyy0A00" }, // Hue, Sat + { "Color", 0x0300, 0x07, 0x01, "xxxxyyyy0A00" }, // x, y (uint16) + { "CT", 0x0300, 0x0A, 0x01, "xxxx0A00" }, // Color Temperature Mireds (uint16) + { "ShutterOpen", 0x0102, 0x00, 0x01, "" }, + { "ShutterClose", 0x0102, 0x01, 0x01, "" }, + { "ShutterStop", 0x0102, 0x02, 0x01, "" }, + { "ShutterLift", 0x0102, 0x05, 0x01, "xx" }, // Lift percentage, 0%=open, 100%=closed + { "ShutterTilt", 0x0102, 0x08, 0x01, "xx" }, // Tilt percentage + { "Shutter", 0x0102, 0xFF, 0x01, "" }, + // Blitzwolf PIR + { "Occupancy", 0xEF00, 0x01, 0x01, "xx"}, // Specific decoder for Blitzwolf PIR, empty name means special treatment + // Decoders only - normally not used to send, and names may be masked by previous definitions + { "Dimmer", 0x0008, 0x00, 0x01, "xx" }, + { "DimmerMove", 0x0008, 0x01, 0x01, "xx0A" }, + { "DimmerStep", 0x0008, 0x02, 0x01, "xx190A00" }, + { "DimmerMove", 0x0008, 0x05, 0x01, "xx0A" }, + { "Dimmer+", 0x0008, 0x06, 0x01, "00" }, + { "Dimmer-", 0x0008, 0x06, 0x01, "01" }, + { "DimmerStop", 0x0008, 0x07, 0x01, "" }, + { "HueMove", 0x0300, 0x01, 0x01, "xx19" }, + { "HueStep", 0x0300, 0x02, 0x01, "xx190A00" }, + { "SatMove", 0x0300, 0x04, 0x01, "xx19" }, + { "SatStep", 0x0300, 0x05, 0x01, "xx190A" }, + { "ColorMove", 0x0300, 0x08, 0x01, "xxxxyyyy" }, + { "ColorStep", 0x0300, 0x09, 0x01, "xxxxyyyy0A00" }, + // Tradfri + { "ArrowClick", 0x0005, 0x07, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right + { "ArrowHold", 0x0005, 0x08, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right + { "ArrowRelease", 0x0005, 0x09, 0x01, "" }, + // responses for Group cluster commands + { "AddGroupResp", 0x0004, 0x00, 0x02, "xxyyyy" }, // xx = status, yy = group id + { "ViewGroupResp", 0x0004, 0x01, 0x02, "xxyyyy" }, // xx = status, yy = group id, name ignored + { "GetGroupResp", 0x0004, 0x02, 0x02, "xxyyzzzz" }, // xx = capacity, yy = count, zzzz = first group id, following groups ignored + { "RemoveGroup", 0x0004, 0x03, 0x02, "xxyyyy" }, // xx = status, yy = group id }; + #define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian // Below are the attributes we wand to read from each cluster @@ -55,6 +100,7 @@ const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007) }; // Hue, Sat, X, Y, CT +// This callback is registered after a cluster specific command and sends a read command for the same cluster int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { size_t attrs_len = 0; const uint8_t* attrs = nullptr; @@ -82,7 +128,6 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoi } } - // set a timer to read back the value in the future void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint) { uint32_t wait_ms = 0; @@ -105,22 +150,182 @@ void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoi } } -const __FlashStringHelper* zigbeeFindCommand(const char *command) { - char parm_uc[16]; // used to convert JSON keys to uppercase +// returns true if char is 'x', 'y' or 'z' +inline bool isXYZ(char c) { + return (c >= 'x') && (c <= 'z'); +} + +// returns the Hex value of a digit [0-9A-Fa-f] +// return: 0x00-0x0F +// or -1 if cannot be parsed +inline int8_t hexValue(char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + return -1; +} + +// Parse a Big Endian suite of max_len digits, or stops when a non-hex digit is found +uint32_t parseHex_P(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(pgm_read_byte(*data)); + if (v < 0) { break; } // non hex digit, we stop parsing + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + +// Parse a model like "xxyy00" +// and fill x, y and z values +// Little Endian encoding +// On exit, xyz is updated, and x_type, y_type, z_type contain the number of bytes read for each +void parseXYZ(const char *model, const SBuffer &payload, struct Z_XYZ_Var *xyz) { + const char *p = model; // pointer to the model character + uint32_t v = 0; // index in the payload bytes buffer + char c = pgm_read_byte(p); // cur char + while (c) { + char c1 = pgm_read_byte(p+1); // next char + if (!c1) { break; } // unexpected end of model + if (isXYZ(c) && (c == c1) && (v < payload.len())) { // if char is [x-z] and followed by same char + uint8_t val = payload.get8(v); + switch (c) { + case 'x': + xyz->x = xyz->x | (val << (xyz->x_type * 8)); + xyz->x_type++; + break; + case 'y': + xyz->y = xyz->y | (val << (xyz->y_type * 8)); + xyz->y_type++; + break; + case 'z': + xyz->z = xyz->z | (val << (xyz->z_type * 8)); + xyz->z_type++; + break; + } + } + p += 2; + v++; + c = pgm_read_byte(p); + } +} + +// works on big endiand hex only +// Returns if found: +// - cluster number +// - command number or 0xFF if command is part of the variable part +// - the payload in the form of a HEX string with x/y/z variables + + + +// Parse a cluster specific command, and try to convert into human readable +void convertClusterSpecific(JsonObject& json, uint16_t cluster, uint8_t cmd, bool direction, const SBuffer &payload) { + size_t hex_char_len = payload.len()*2+2; + char *hex_char = (char*) malloc(hex_char_len); + if (!hex_char) { return; } + ToHex_P((unsigned char*)payload.getBuffer(), payload.len(), hex_char, hex_char_len); + + const __FlashStringHelper* command_name = nullptr; + Z_XYZ_Var xyz; + +//AddLog_P2(LOG_LEVEL_INFO, PSTR(">>> len = %d - %02X%02X%02X"), payload.len(), payload.get8(0), payload.get8(1), payload.get8(2)); + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + if (conv->cluster == cluster) { + // cluster match + if ((0xFF == conv->cmd) || (cmd == conv->cmd)) { + // cmd match + if ((direction && (conv->direction & 0x02)) || (!direction && (conv->direction & 0x01))) { + // check if we have a match for params too + // Match if: + // - payload exactly matches conv->param (conv->param may be longer) + // - payload matches conv->param until 'x', 'y' or 'z' + const char * p = conv->param; + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">>>++1 param = %s"), p); + bool match = true; + for (uint8_t i = 0; i < payload.len(); i++) { + const char c1 = pgm_read_byte(p); + const char c2 = pgm_read_byte(p+1); + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">>>++2 c1 = %c, c2 = %c"), c1, c2); + if ((0x00 == c1) || isXYZ(c1)) { + break; + } + const char * p2 = p; + uint32_t nextbyte = parseHex_P(&p2, 2); + //AddLog_P2(LOG_LEVEL_INFO, PSTR(">>>++3 parseHex_P = %02X"), nextbyte); + if (nextbyte != payload.get8(i)) { + match = false; + break; + } + p += 2; + } + if (match) { + command_name = (const __FlashStringHelper*) conv->tasmota_cmd; + parseXYZ(conv->param, payload, &xyz); + if (0xFF == conv->cmd) { + // shift all values + xyz.z = xyz.y; + xyz.z_type = xyz.y_type; + xyz.y = xyz.x; + xyz.y_type = xyz.x_type; + xyz.x = cmd; + xyz.x_type = 1; // 1 byte + } + break; + } + } + } + } + } + + // always report attribute in raw format + // Format: "0001!06": "00" = "!": "" for commands to devices + // Format: "0004<00": "00" = "<": "" for commands to devices + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X%c%02X"), cluster, direction ? '<' : '!', cmd); + json[attrid_str] = hex_char; + free(hex_char); + + if (command_name) { + if (0 == xyz.x_type) { + json[command_name] = true; // no parameter + } else if (0 == xyz.y_type) { + json[command_name] = xyz.x; // 1 parameter + } else { + // multiple answers, create an array + JsonArray &arr = json.createNestedArray(command_name); + arr.add(xyz.x); + arr.add(xyz.y); + if (xyz.z_type) { + arr.add(xyz.z); + } + } + } +} + +// Find the command details by command name +// If not found: +// - returns nullptr +const __FlashStringHelper* zigbeeFindCommand(const char *command, uint16_t *cluster, uint16_t *cmd) { for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { const Z_CommandConverter *conv = &Z_Commands[i]; if (0 == strcasecmp_P(command, conv->tasmota_cmd)) { - return (const __FlashStringHelper*) conv->zcl_cmd; + *cluster = conv->cluster; + *cmd = conv->cmd; + return (const __FlashStringHelper*) conv->param; } } return nullptr; } -inline bool isXYZ(char c) { - return (c >= 'x') && (c <= 'z'); -} - // take the lower 4 bits and turn it to an hex char inline char hexDigit(uint32_t h) { uint32_t nybble = h & 0x0F; diff --git a/tasmota/xdrv_23_zigbee_7_statemachine.ino b/tasmota/xdrv_23_zigbee_7_statemachine.ino index 5a3d78e85..d0616a40b 100644 --- a/tasmota/xdrv_23_zigbee_7_statemachine.ino +++ b/tasmota/xdrv_23_zigbee_7_statemachine.ino @@ -33,6 +33,7 @@ const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; // Node descriptor const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; // Endpoints descriptor const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; // Simple Descriptor (clusters) const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; // Device announces its address +const uint8_t ZIGBEE_STATUS_DEVICE_IEEE = 35; // Request of device address const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; // Status: CC2530 ZNP Version const uint8_t ZIGBEE_STATUS_CC_INFO = 51; // Status: CC2530 Device Configuration const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; // Unsupported ZNP version diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/xdrv_23_zigbee_8_parsers.ino index 7d84124ed..b67add374 100644 --- a/tasmota/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/xdrv_23_zigbee_8_parsers.ino @@ -176,15 +176,24 @@ int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { return -1; } +// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address +void Z_SendIEEEAddrReq(uint16_t shortaddr) { + uint8_t IEEEAddrReq[] = { Z_SREQ | Z_ZDO, ZDO_IEEE_ADDR_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), 0x00, 0x00 }; + + ZigbeeZNPSend(IEEEAddrReq, sizeof(IEEEAddrReq)); +} + // Send ACTIVE_EP_REQ to collect active endpoints for this address void Z_SendActiveEpReq(uint16_t shortaddr) { uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); + + // uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, + // Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + //ZigbeeZNPSend(NodeDescReq, sizeof(NodeDescReq)); Not sure this is useful } @@ -335,6 +344,40 @@ int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) { return -1; } +int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) { + uint8_t status = buf.get8(2); + Z_IEEEAddress ieeeAddr = buf.get64(3); + Z_ShortAddress nwkAddr = buf.get16(11); + // uint8_t startIndex = buf.get8(13); + // uint8_t numAssocDev = buf.get8(14); + + if (0 == status) { // SUCCESS + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + "}}"), + ZIGBEE_STATUS_DEVICE_IEEE, hex, nwkAddr + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + // Ping response + const String * friendlyName = zigbee_devices.getFriendlyName(nwkAddr); + if (friendlyName) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"" + ",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, friendlyName->c_str()); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\"}}"), nwkAddr); + } + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + return -1; +} + int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { Z_ShortAddress srcAddr = buf.get16(2); Z_ShortAddress nwkAddr = buf.get16(4); @@ -484,6 +527,7 @@ ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) // 45CA ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) // 45CB ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) // 4585 ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) // 4584 +ZBM(AREQ_ZDO_IEEE_ADDR_RSP, Z_AREQ | Z_ZDO, ZDO_IEEE_ADDR_RSP) // 4581 const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, @@ -493,6 +537,7 @@ const Z_Dispatcher Z_DispatchTable[] PROGMEM = { { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, { AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc }, + { AREQ_ZDO_IEEE_ADDR_RSP, &Z_ReceiveIEEEAddr }, }; int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { diff --git a/tasmota/xdrv_23_zigbee_9_impl.ino b/tasmota/xdrv_23_zigbee_9_impl.ino index eccfc56be..08055ba9e 100644 --- a/tasmota/xdrv_23_zigbee_9_impl.ino +++ b/tasmota/xdrv_23_zigbee_9_impl.ino @@ -33,19 +33,22 @@ const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" - D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" + D_CMND_ZIGBEE_PING ; const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" // legacy prefix -- deprecated D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" - D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND "|" + D_CMND_ZIGBEE_PING ; void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZbZNPSend, &CmndZbPermitJoin, &CmndZbStatus, &CmndZbReset, &CmndZbSend, &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, - &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind + &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind, + &CmndZbPing, }; int32_t ZigbeeProcessInput(class SBuffer &buf) { @@ -367,61 +370,16 @@ void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8 ZigbeeZNPSend(buf.getBuffer(), buf.len()); } -inline int8_t hexValue(char c) { - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - if ((c >= 'A') && (c <= 'F')) { - return 10 + c - 'A'; - } - if ((c >= 'a') && (c <= 'f')) { - return 10 + c - 'a'; - } - return -1; -} - -uint32_t parseHex(const char **data, size_t max_len = 8) { - uint32_t ret = 0; - for (uint32_t i = 0; i < max_len; i++) { - int8_t v = hexValue(**data); - if (v < 0) { break; } // non hex digit, we stop parsing - ret = (ret << 4) | v; - *data += 1; - } - return ret; -} - -void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { - - uint16_t cluster = 0x0000; // 0x0000 is a valid default value - uint8_t cmd = ZCL_READ_ATTRIBUTES; // default command is READ_ATTRIBUTES - bool clusterSpecific = false; - // Parse 'cmd' in the form "AAAA_BB/CCCCCCCC" or "AAAA!BB/CCCCCCCC" - // where AA is the cluster number, BBBB the command number, CCCC... the payload - // First delimiter is '_' for a global command, or '!' for a cluster specific commanc - cluster = parseHex(&data, 4); - - // delimiter - if (('_' == *data) || ('!' == *data)) { - if ('!' == *data) { clusterSpecific = true; } - data++; - } else { - ResponseCmndChar("Wrong delimiter for payload"); - return; - } - // parse cmd number - cmd = parseHex(&data, 2); - - // move to end of payload - // delimiter is optional - if ('/' == *data) { data++; } // skip delimiter - - size_t size = strlen(data); +void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, bool clusterSpecific, + uint16_t cluster, uint8_t cmd, const char *param) { + size_t size = param ? strlen(param) : 0; SBuffer buf((size+2)/2); // actual bytes buffer for data - while (*data) { - uint8_t code = parseHex(&data, 2); - buf.add8(code); + if (param) { + while (*param) { + uint8_t code = parseHex_P(¶m, 2); + buf.add8(code); + } } if (0 == endpoint) { @@ -430,7 +388,7 @@ void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); } AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), - dstAddr, cluster, endpoint, cmd, data); + dstAddr, cluster, endpoint, cmd, param); if (0 == endpoint) { AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); @@ -467,6 +425,9 @@ void CmndZbSend(void) { static char delim[] = ", "; // delimiters for parameters uint16_t device = 0xFFFF; // 0xFFFF is broadcast, so considered valid uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint + // Command elements + uint16_t cluster = 0; + uint8_t cmd = 0; String cmd_str = ""; // the actual low-level command, either specified or computed // parse JSON @@ -497,8 +458,9 @@ void CmndZbSend(void) { String key = it->key; JsonVariant& value = it->value; uint32_t x = 0, y = 0, z = 0; + uint16_t cmd_var; - const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str()); + const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str(), &cluster, &cmd_var); if (tasmota_cmd) { cmd_str = tasmota_cmd; } else { @@ -534,9 +496,16 @@ void CmndZbSend(void) { } } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str()); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str()); + if (0xFF == cmd_var) { // if command number is a variable, replace it with x + cmd = x; + x = y; // and shift other variables + y = z; + } else { + cmd = cmd_var; // or simply copy the cmd number + } cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); // fill in parameters - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str()); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str()); } else { // we have zero command, pass through until last error for missing command } @@ -547,9 +516,9 @@ void CmndZbSend(void) { // we have an unsupported command type, just ignore it and fallback to missing command } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"), - device, endpoint, cmd_str.c_str()); - zigbeeZCLSendStr(device, endpoint, cmd_str.c_str()); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%04X!%02X/%s\"}"), + device, endpoint, cluster, cmd, cmd_str.c_str()); + zigbeeZCLSendStr(device, endpoint, true, cluster, cmd, cmd_str.c_str()); } else { Response_P(PSTR("Missing zigbee 'Send'")); return; @@ -615,16 +584,28 @@ void CmndZbBind(void) { // Probe a specific device to get its endpoints and supported clusters void CmndZbProbe(void) { + CmndZbProbeOrPing(true); +} + +void CmndZbProbeOrPing(boolean probe) { if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } // everything is good, we can send the command - Z_SendActiveEpReq(shortaddr); + Z_SendIEEEAddrReq(shortaddr); + if (probe) { + Z_SendActiveEpReq(shortaddr); + } ResponseCmndDone(); } +// Ping a device, actually a simplified version of ZbProbe +void CmndZbPing(void) { + CmndZbProbeOrPing(false); +} + // Specify, read or erase a Friendly Name void CmndZbName(void) { // Syntax is: @@ -729,6 +710,11 @@ void CmndZbRead(void) { } } + if (0 == endpoint) { // try to compute the endpoint + endpoint = zigbee_devices.findClusterEndpointIn(device, cluster); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); + } + if ((0 != endpoint) && (attrs_len > 0)) { ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(device)); ResponseCmndDone(); From 21976005e13e1161031f21519ab601f3011338cf Mon Sep 17 00:00:00 2001 From: Hadinger Date: Sun, 23 Feb 2020 17:11:51 +0100 Subject: [PATCH 10/20] Support for BW-IS5 leak detector --- tasmota/xdrv_23_zigbee_6_commands.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/xdrv_23_zigbee_6_commands.ino index 83841f90c..5c3e3fc1b 100644 --- a/tasmota/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/xdrv_23_zigbee_6_commands.ino @@ -84,6 +84,8 @@ const Z_CommandConverter Z_Commands[] = { { "ArrowClick", 0x0005, 0x07, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right { "ArrowHold", 0x0005, 0x08, 0x01, "xx" }, // xx == 0x01 = left, 0x00 = right { "ArrowRelease", 0x0005, 0x09, 0x01, "" }, + // IAS - Intruder Alarm System + leak/fire detection + { "ZoneStatusChange",0x0500, 0x00, 0x02, "xxxxyyzz" }, // xxxx = zone status, yy = extended status, zz = zone id, Delay is ignored // responses for Group cluster commands { "AddGroupResp", 0x0004, 0x00, 0x02, "xxyyyy" }, // xx = status, yy = group id { "ViewGroupResp", 0x0004, 0x01, 0x02, "xxyyyy" }, // xx = status, yy = group id, name ignored From 9b758350b580542f556e1815c56f7f679eb2f759 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Feb 2020 20:49:54 +0100 Subject: [PATCH 11/20] Fix Domoticz compile error Fix compile error when Domoticz is enabled but Lights is disabled --- tasmota/xdrv_07_domoticz.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/xdrv_07_domoticz.ino index 49e546b65..90563eeed 100644 --- a/tasmota/xdrv_07_domoticz.ino +++ b/tasmota/xdrv_07_domoticz.ino @@ -296,6 +296,7 @@ bool DomoticzMqttData(void) found = true; } else #endif // USE_SHUTTER +#ifdef USE_LIGHT if (iscolordimmer && 10 == nvalue) { // Color_SetColor // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Set_a_light_to_a_certain_color_or_color_temperature JsonObject& color = domoticz["Color"]; @@ -333,8 +334,9 @@ bool DomoticzMqttData(void) snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); found = true; - } - else if (1 == nvalue || 0 == nvalue) { + } else +#endif // USE_LIGHT + if (1 == nvalue || 0 == nvalue) { if (((power >> i) &1) == (power_t)nvalue) { return true; // Stop loop } From 8969877502503a536f4f2df15ae0ee9d1c33383b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 23 Feb 2020 21:26:58 +0100 Subject: [PATCH 12/20] Refactor DHT driver Refactor DHT driver (#7717) --- tasmota/xsns_06_dht_v5.ino | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tasmota/xsns_06_dht_v5.ino b/tasmota/xsns_06_dht_v5.ino index f08286b43..9c7003970 100644 --- a/tasmota/xsns_06_dht_v5.ino +++ b/tasmota/xsns_06_dht_v5.ino @@ -100,6 +100,7 @@ bool DhtRead(uint32_t sensor) break; } +/* bool error = false; noInterrupts(); if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { @@ -127,6 +128,22 @@ bool DhtRead(uint32_t sensor) } interrupts(); if (error) { return false; } +*/ + + uint32_t i = 0; + noInterrupts(); + if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { + for (i = 0; i < 40; i++) { + if (!DhtWaitState(sensor, 1)) { break; } + delayMicroseconds(35); // Was 30 + if (digitalRead(Dht[sensor].pin)) { + dht_data[i / 8] |= (1 << (7 - i % 8)); + } + if (!DhtWaitState(sensor, 0)) { break; } + } + } + interrupts(); + if (i < 40) { return false; } uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { From 18af25bb71ba612b6536aeb4b87c29353d16f4db Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 24 Feb 2020 09:57:51 +0100 Subject: [PATCH 13/20] Update platformio_tasmota_env.ini --- platformio_tasmota_env.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index 2a26ba4d2..cfcd53686 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -3,6 +3,7 @@ platform = ${common.platform} platform_packages = ${common.platform_packages} framework = ${common.framework} board = ${common.board} +board_build.ldscript = ${common.board_build.ldscript} board_build.flash_mode = ${common.board_build.flash_mode} board_build.f_cpu = ${common.board_build.f_cpu} build_unflags = ${common.build_unflags} From 3b0b9088e1e40962137493d7976edffe232498be Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 24 Feb 2020 10:01:26 +0100 Subject: [PATCH 14/20] Update platformio.ini --- platformio.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio.ini b/platformio.ini index 16553f0d6..552408790 100755 --- a/platformio.ini +++ b/platformio.ini @@ -56,6 +56,7 @@ default_envs = framework = arduino board = esp01_1m board_build.flash_mode = dout +board_build.ldscript = eagle.flash.1m.ld platform = ${core_active.platform} platform_packages = ${core_active.platform_packages} From 25301b8cdcbd5afd8d28ef6e4108d68ccefb9655 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 24 Feb 2020 10:04:48 +0100 Subject: [PATCH 15/20] Fix using new syntax for custom LD script --- platformio_override_sample.ini | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 93bbbb5ba..0c63c6e8f 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -135,10 +135,9 @@ build_flags = ${esp82xx_defaults.build_flags} [core_2_6_3] ; *** Esp8266 core for Arduino version 2.6.3 -platform = espressif8266@2.3.2 +platform = espressif8266@2.3.3 platform_packages = build_flags = ${esp82xx_defaults.build_flags} - -Wl,-Teagle.flash.1m.ld -DBEARSSL_SSL_BASIC ; NONOSDK221 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 @@ -180,12 +179,10 @@ build_flags = ${esp82xx_defaults.build_flags} [tasmota_core_stage] ; *** Esp8266 core for Arduino version stable beta -platform = espressif8266@2.3.2 +platform = espressif8266@2.3.3 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#6be561617f645f6a2ae82b8211f6af8c43e834cf build_flags = ${esp82xx_defaults.build_flags} - -Wl,-Teagle.flash.1m.ld -DBEARSSL_SSL_BASIC - -DNOPRINTFLOAT ; NONOSDK221 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 ; NONOSDK22x_190313 @@ -226,12 +223,10 @@ build_flags = ${esp82xx_defaults.build_flags} [core_stage] ; *** Esp8266 core for Arduino version latest beta -platform = espressif8266@2.3.2 +platform = espressif8266@2.3.3 platform_packages = framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git -board_build.ldscript = eagle.flash.1m.ld build_flags = ${esp82xx_defaults.build_flags} -DBEARSSL_SSL_BASIC - -DNOPRINTFLOAT ; NONOSDK221 ; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221 ; NONOSDK22x_190313 From 7a3a9bd364e332b8ffe3f37a98eb74ad17a5eb6c Mon Sep 17 00:00:00 2001 From: stefanbode Date: Mon, 24 Feb 2020 12:23:03 +0100 Subject: [PATCH 16/20] fixes and enhancements - shutterposition now reports actual position if the shutter is moving #7686 - new RuleEvent: Shutter1#Target to get upfront info where the shutter is moving - RuleExecution now every second to get an update of the current position - New functionality for Stepper Motors. Now supporting 4 Stepper shutters working simultanously --- tasmota/xdrv_27_shutter.ino | 129 +++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index c353a1dd4..0fd6d861b 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -48,7 +48,7 @@ void (* const ShutterCommand[])(void) PROGMEM = { &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime}; - const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}"; + const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d,\"Target\":%d}"; const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; #include @@ -70,7 +70,7 @@ struct SHUTTER { int8_t direction[MAX_SHUTTERS]; // 1 == UP , 0 == stop; -1 == down uint8_t mode = 0; // operation mode definition. see enum type above SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE int16_t motordelay[MAX_SHUTTERS]; // initial motorstarttime in 0.05sec. - int16_t pwm_frequency; // frequency of PWN for stepper motors + int16_t pwm_frequency[MAX_SHUTTERS]; // frequency of PWN for stepper motors uint16_t max_pwm_frequency = 1000; // maximum of PWM frequency for openig the shutter. depend on the motor and drivers uint16_t max_close_pwm_frequency[MAX_SHUTTERS];// maximum of PWM frequency for closeing the shutter. depend on the motor and drivers uint8_t skip_relay_change; // avoid overrun at endstops @@ -82,17 +82,18 @@ void ShutterLogPos(uint32_t i) char stemp2[10]; dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), - i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency); + i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency[i]); } void ShutterRtc50mS(void) { - for (uint32_t i = 0; i < shutters_present; i++) { + for (uint8_t i = 0; i < shutters_present; i++) { Shutter.time[i]++; if (Shutter.accelerator[i]) { - Shutter.pwm_frequency += Shutter.accelerator[i]; - Shutter.pwm_frequency = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency)); - analogWriteFreq(Shutter.pwm_frequency); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: accelerator i=%d -> %d"),i, Shutter.accelerator[i]); + Shutter.pwm_frequency[i] += Shutter.accelerator[i]; + Shutter.pwm_frequency[i] = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency[i])); + analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 50); } } @@ -102,8 +103,6 @@ void ShutterRtc50mS(void) int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) { - if (0 == percent) return 0; - if (100 == percent) return Shutter.open_max[index]; if (Settings.shutter_set50percent[index] != 50) { return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; } else { @@ -138,8 +137,6 @@ int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) { - if (0 >= realpos) return 0; - if (Shutter.open_max[index] <= realpos) return 100; if (Settings.shutter_set50percent[index] != 50) { return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]); } else { @@ -203,8 +200,9 @@ void ShutterInit(void) Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); + Shutter.pwm_frequency[i] = 0; + Shutter.accelerator[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 50); } } @@ -256,29 +254,24 @@ void ShutterInit(void) void ShutterReportPosition(bool always) { - uint32_t shutter_moving = 0; Response_P(PSTR("{")); for (uint32_t i = 0; i < shutters_present; i++) { //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d: Real Pos: %d"), i+1,Shutter.real_position[i]); uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); if (Shutter.direction[i] != 0) { - shutter_moving = 1; + rules_flag.shutter_moving = 1; ShutterLogPos(i); } if (i) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i]); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i], ShutterRealToPercentPosition(Shutter.target_position[i], i)); } ResponseJsonEnd(); - if (always || (1 == shutter_moving)) { + if (always || (rules_flag.shutter_moving)) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); + XdrvRulesProcess(); } - if (rules_flag.shutter_moving > shutter_moving) { - rules_flag.shutter_moved = 1; - } else { - rules_flag.shutter_moved = 0; - } - rules_flag.shutter_moving = shutter_moving; - //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved); + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: rules_flag.shutter_moving: %d, moved %d"), rules_flag.shutter_moving, rules_flag.shutter_moved); + } void ShutterLimitRealAndTargetPositions(uint32_t i) { @@ -304,22 +297,22 @@ void ShutterUpdatePosition(void) int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_frequency*1000 / max_freq_change_per_sec; + int32_t min_runtime_ms = Shutter.pwm_frequency[i]*1000 / max_freq_change_per_sec; int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency / max_frequency * Shutter.direction[i] ; + int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency[i] / max_frequency * Shutter.direction[i] ; int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta =200 * Shutter.pwm_frequency/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + stop_position_delta =200 * Shutter.pwm_frequency[i]/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); //Shutter.accelerator[i] = tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*11/200); //int32_t act_freq_change = max_freq_change_per_sec/20; AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, - Shutter.pwm_frequency,max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); + Shutter.pwm_frequency[i],max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > Shutter.target_position[i] * Shutter.direction[i] ) { Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*12/200); //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Ramp down: acc: %d"), Shutter.accelerator[i]); - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency == max_frequency) { + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency[i] == max_frequency) { Shutter.accelerator[i] = 0; } } else { @@ -343,13 +336,13 @@ void ShutterUpdatePosition(void) case SHT_OFF_ON__OPEN_CLOSE_STEPPER: missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; //prepare for stop PWM - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency[i]); Shutter.accelerator[i] = 0; - Shutter.pwm_frequency = Shutter.pwm_frequency > 250 ? 250 : Shutter.pwm_frequency; - analogWriteFreq(Shutter.pwm_frequency); + Shutter.pwm_frequency[i] = Shutter.pwm_frequency[i] > 250 ? 250 : Shutter.pwm_frequency[i]; + analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 50); - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); + Shutter.pwm_frequency[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { delay(1); } @@ -391,6 +384,7 @@ void ShutterUpdatePosition(void) Shutter.direction[i] = 0; ShutterReportPosition(true); + rules_flag.shutter_moved = 1; XdrvRulesProcess(); } } @@ -413,8 +407,8 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) Shutter.skip_relay_change = 1; } else { if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); + Shutter.pwm_frequency[i] = 0; + analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 0); // can be operated without counter, but then not that acurate. if (pin[GPIO_CNTR1+i] < 99) { @@ -435,14 +429,16 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) void ShutterWaitForMotorStop(uint32_t i) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..dir %d, akt freq: %d, max %d, maxclose %d"),Shutter.direction[i],Shutter.pwm_frequency[i], Shutter.max_pwm_frequency, Shutter.max_close_pwm_frequency[i]); if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency); - while (Shutter.pwm_frequency > 0) { - Shutter.accelerator[i] = 0; - Shutter.pwm_frequency = tmax(Shutter.pwm_frequency-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); - analogWriteFreq(Shutter.pwm_frequency); + while (Shutter.pwm_frequency[i] > 0) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld, delta: %d"), Shutter.pwm_frequency[i], (int32_t)((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) ); + + Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]); + analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 50); delay(50); } @@ -532,7 +528,10 @@ void ShutterButtonHandler(void) uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; uint16_t loops_per_second = 1000 / Settings.button_debounce; // ButtonDebounce (50) - + if ( button != Button.last_state[button_index]) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d, presstate %d, lasttate %d, Settings.flag.button_single %d)"), shutter_index+1, button_index+1, button, Button.last_state[button_index], Settings.flag.button_single); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Button.press_counter[button_index] %d, Button.hold_timer[button_index] %d, Button.window_timer[button_index] %d "), Button.press_counter[button_index], Button.hold_timer[button_index], Button.window_timer[button_index] ); + } if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action buttonState = SHT_PRESSED_MULTI; @@ -542,9 +541,10 @@ void ShutterButtonHandler(void) buttonState = SHT_PRESSED_IMMEDIATE; press_index = 1; Button.press_counter[button_index] = 99; // Remember to discard further action for press & hold within button timings - } else + } else { Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window + Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window + } } blinks = 201; } @@ -596,14 +596,18 @@ void ShutterButtonHandler(void) // check for simultaneous shutter button press uint32 min_shutterbutton_press_counter = -1; for (uint32_t i = 0; i < MAX_KEYS; i++) { - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter); + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { min_shutterbutton_press_counter = Button.press_counter[i]; + } + } if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { // simultaneous shutter button press detected + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:simultanous presss deteced")); press_index = Button.press_counter[button_index]; for (uint32_t i = 0; i < MAX_KEYS; i++) - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index)) Button.press_counter[i] = 99; // Remember to discard further action for press & hold within button timings buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; } @@ -617,9 +621,13 @@ void ShutterButtonHandler(void) } } } + if (buttonState) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Final buttonState %d"), buttonState); + } if (buttonState != SHT_NOT_PRESSED) { if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_MULTI_SIMULTANEOUS")); if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { // 5x..7x && no SetOption1 (0) // simultaneous shutter button press 5x, 6x, 7x detected char scmnd[20]; @@ -628,7 +636,7 @@ void ShutterButtonHandler(void) return; } } else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) { - // simultaneous shutter button extend hold detected + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_EXT_HOLD_SIMULTANEOUS"));// simultaneous shutter button extend hold detected if (!Settings.flag.button_restrict) { // no SetOption1 (0) char scmnd[20]; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); @@ -636,6 +644,7 @@ void ShutterButtonHandler(void) return; } } else if (buttonState <= SHT_PRESSED_IMMEDIATE) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_IMMEDIATE")); if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); if (pos_press_index>3) pos_press_index=3; @@ -647,14 +656,15 @@ void ShutterButtonHandler(void) XdrvMailbox.data = databuf; XdrvMailbox.command = NULL; if (buttonState == SHT_PRESSED_IMMEDIATE) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterStop(); - } - else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_IMMEDIATE stop")); + //XdrvMailbox.payload = XdrvMailbox.index; + //CmndShutterStop(); + } else { uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; if (position) { if (Shutter.direction[shutter_index]) { XdrvMailbox.payload = XdrvMailbox.index; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: immegency stop")); CmndShutterStop(); } else { XdrvMailbox.payload = position = (position-1)<<1; @@ -670,12 +680,12 @@ void ShutterButtonHandler(void) Response_P("%d", position); MqttPublish(stopic, false); } - } - } - } - } - } - } + } // for (uint32_t) + } // if (Settings.shutter) + } // ende else + } // if (position) + } // end else + } // if if (Settings.shutter_startrelay[shutter_index] } Response_P(PSTR("{")); ResponseAppend_P(JSON_SHUTTER_BUTTON, shutter_index+1, (buttonState <= SHT_PRESSED_IMMEDIATE) ? (button_index+1) : 0, press_index); @@ -756,6 +766,7 @@ void CmndShutterPosition(void) AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); // value 0 with data_len > 0 can mean Open + // special handling fo UP,DOWN,TOGGLE,STOP command comming with payload -99 if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { //UpperCase(XdrvMailbox.data, XdrvMailbox.data); if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEUP))) { @@ -773,13 +784,13 @@ void CmndShutterPosition(void) } } - int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? 0 : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); + int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? (XdrvMailbox.payload == -99 ? ShutterRealToPercentPosition(Shutter.real_position[index], index) : 0) : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); // webgui still send also on inverted shutter the native position. target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; if (XdrvMailbox.payload != -99) { //target_pos_percent = (Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent; Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); + //Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); //Shutter.target_position[index] = XdrvMailbox.payload < 5 ? Settings.shuttercoeff[2][index] * XdrvMailbox.payload : Settings.shuttercoeff[1][index] * XdrvMailbox.payload + Settings.shuttercoeff[0,index]; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); } From 66a1dc957336b1a4d9a86f910d45fc4cdee3a5bf Mon Sep 17 00:00:00 2001 From: stefanbode Date: Mon, 24 Feb 2020 12:33:52 +0100 Subject: [PATCH 17/20] Update xdrv_27_shutter.ino - fixed shutter#moved rule not triggered --- tasmota/xdrv_27_shutter.ino | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 0fd6d861b..4c8ae1d52 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -429,15 +429,14 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) void ShutterWaitForMotorStop(uint32_t i) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..dir %d, akt freq: %d, max %d, maxclose %d"),Shutter.direction[i],Shutter.pwm_frequency[i], Shutter.max_pwm_frequency, Shutter.max_close_pwm_frequency[i]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { //AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Frequency change %d"), Shutter.pwm_frequency); while (Shutter.pwm_frequency[i] > 0) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld, delta: %d"), Shutter.pwm_frequency[i], (int32_t)((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) ); - + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld, delta: %d"), Shutter.pwm_frequency[i], (int32_t)((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) ); Shutter.pwm_frequency[i] = tmax(Shutter.pwm_frequency[i]-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]); + //AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Frequency: %ld"), Shutter.pwm_frequency[i]); analogWriteFreq(Shutter.pwm_frequency[i]); analogWrite(pin[GPIO_PWM1+i], 50); delay(50); @@ -528,10 +527,6 @@ void ShutterButtonHandler(void) uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; uint16_t loops_per_second = 1000 / Settings.button_debounce; // ButtonDebounce (50) - if ( button != Button.last_state[button_index]) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d, presstate %d, lasttate %d, Settings.flag.button_single %d)"), shutter_index+1, button_index+1, button, Button.last_state[button_index], Settings.flag.button_single); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Button.press_counter[button_index] %d, Button.hold_timer[button_index] %d, Button.window_timer[button_index] %d "), Button.press_counter[button_index], Button.hold_timer[button_index], Button.window_timer[button_index] ); - } if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { if (Settings.flag.button_single) { // SetOption13 (0) - Allow only single button press for immediate action buttonState = SHT_PRESSED_MULTI; @@ -543,7 +538,6 @@ void ShutterButtonHandler(void) Button.press_counter[button_index] = 99; // Remember to discard further action for press & hold within button timings } else { Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - Button.window_timer[button_index] = loops_per_second / 2; // 0.5 second multi press window } } blinks = 201; @@ -597,7 +591,7 @@ void ShutterButtonHandler(void) uint32 min_shutterbutton_press_counter = -1; for (uint32_t i = 0; i < MAX_KEYS; i++) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter); - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) != shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) min_shutterbutton_press_counter = Button.press_counter[i]; } @@ -621,13 +615,9 @@ void ShutterButtonHandler(void) } } } - if (buttonState) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Final buttonState %d"), buttonState); - } if (buttonState != SHT_NOT_PRESSED) { if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_MULTI_SIMULTANEOUS")); if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { // 5x..7x && no SetOption1 (0) // simultaneous shutter button press 5x, 6x, 7x detected char scmnd[20]; @@ -636,7 +626,6 @@ void ShutterButtonHandler(void) return; } } else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_EXT_HOLD_SIMULTANEOUS"));// simultaneous shutter button extend hold detected if (!Settings.flag.button_restrict) { // no SetOption1 (0) char scmnd[20]; snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); @@ -644,7 +633,6 @@ void ShutterButtonHandler(void) return; } } else if (buttonState <= SHT_PRESSED_IMMEDIATE) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_IMMEDIATE")); if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); if (pos_press_index>3) pos_press_index=3; @@ -656,15 +644,13 @@ void ShutterButtonHandler(void) XdrvMailbox.data = databuf; XdrvMailbox.command = NULL; if (buttonState == SHT_PRESSED_IMMEDIATE) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: SHT_PRESSED_IMMEDIATE stop")); - //XdrvMailbox.payload = XdrvMailbox.index; - //CmndShutterStop(); + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); } else { uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; if (position) { if (Shutter.direction[shutter_index]) { XdrvMailbox.payload = XdrvMailbox.index; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: immegency stop")); CmndShutterStop(); } else { XdrvMailbox.payload = position = (position-1)<<1; From b54f2d406eca8853dbaaffef33d89897fdc5437d Mon Sep 17 00:00:00 2001 From: stefanbode Date: Mon, 24 Feb 2020 12:38:00 +0100 Subject: [PATCH 18/20] Update xdrv_27_shutter.ino --- tasmota/xdrv_27_shutter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/xdrv_27_shutter.ino index 4c8ae1d52..6f4d711d7 100644 --- a/tasmota/xdrv_27_shutter.ino +++ b/tasmota/xdrv_27_shutter.ino @@ -591,7 +591,7 @@ void ShutterButtonHandler(void) uint32 min_shutterbutton_press_counter = -1; for (uint32_t i = 0; i < MAX_KEYS; i++) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Settings.shutter_button[i] %ld, shutter_index %d, Button.press_counter[i] %d, min_shutterbutton_press_counter %d"), Settings.shutter_button[i], shutter_index, Button.press_counter[i] , min_shutterbutton_press_counter); - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) { min_shutterbutton_press_counter = Button.press_counter[i]; } From 94c9072815e0aab9397bcf6871f9cd24d4068cbb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 24 Feb 2020 13:27:22 +0100 Subject: [PATCH 19/20] Add wifi debug code --- tasmota/settings.ino | 42 +++++++++++++++++++++++++++++++++++++--- tasmota/support_wifi.ino | 5 +++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/tasmota/settings.ino b/tasmota/settings.ino index 33da93562..c2ddc6607 100644 --- a/tasmota/settings.ino +++ b/tasmota/settings.ino @@ -277,6 +277,23 @@ bool RtcRebootValid(void) /*********************************************************************************************\ * Config - Flash + * + * Tasmota 1M flash usage + * 0x00000000 - Unzipped binary bootloader + * 0x00001000 - Unzipped binary code start + * :::: + * 0x000xxxxx - Unzipped binary code end + * 0x000x1000 - First page used by Core OTA + * :::: + * 0x000F3000 - Tasmota Quick Power Cycle counter (SETTINGS_LOCATION - CFG_ROTATES) - First four bytes only + * 0x000F4000 - First Tasmota rotating settings page + * :::: + * 0x000FA000 - Last Tasmota rotating settings page = Last page used by Core OTA + * 0x000FB000 - Core SPIFFS end = Core EEPROM = Tasmota settings page during OTA and when no flash rotation is active (SETTINGS_LOCATION) + * 0x000FC000 - SDK - Uses first 128 bytes for phy init data mirrored by Core in RAM. See core_esp8266_phy.cpp phy_init_data[128] = Core user_rf_cal_sector + * 0x000FD000 - SDK - Uses scattered bytes from 0x340 (iTead use as settings storage from 0x000FD000) + * 0x000FE000 - SDK - Uses scattered bytes from 0x340 (iTead use as mirrored settings storage from 0x000FE000) + * 0x000FF000 - SDK - Uses at least first 32 bytes of this page - Tasmota Zigbee persistence from 0x000FF800 to 0x000FFFFF \*********************************************************************************************/ extern "C" { @@ -745,8 +762,17 @@ void SettingsErase(uint8_t type) _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; // Tasmota and SDK parameter area (0x0F3xxx - 0x0FFFFF) _sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; // Flash size as seen by SDK } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " %d " D_UNIT_SECTORS), _sectorEnd - _sectorStart); +#ifdef USE_WIFI_SDK_ERASE + else if (4 == type) { + _sectorStart = SETTINGS_LOCATION +1; // SDK phy area and Core calibration sector (0x0FC000) + _sectorEnd = _sectorStart +1; // SDK end of phy area and Core calibration sector (0x0FCFFF) + } + else if (5 == type) { + _sectorStart = (ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE) -4; // SDK phy area and Core calibration sector (0xxFC000) + _sectorEnd = _sectorStart +1; // SDK end of phy area and Core calibration sector (0xxFCFFF) + } +#endif // USE_WIFI_SDK_ERASE + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " from 0x%08X to 0x%08X"), _sectorStart * SPI_FLASH_SEC_SIZE, (_sectorEnd * SPI_FLASH_SEC_SIZE) -1); // EspErase(_sectorStart, _sectorEnd); // Arduino core and SDK - erases flash as seen by SDK EsptoolErase(_sectorStart, _sectorEnd); // Esptool - erases flash completely @@ -755,11 +781,21 @@ void SettingsErase(uint8_t type) void SettingsSdkErase(void) { - WiFi.disconnect(true); // Delete SDK wifi config + WiFi.disconnect(false); // Delete SDK wifi config SettingsErase(1); delay(1000); } +#ifdef USE_WIFI_SDK_ERASE +void SettingsSdkWifiErase(void) +{ + WiFi.disconnect(false); // Delete SDK wifi config + SettingsErase(4); + SettingsErase(5); + delay(200); +} +#endif // USE_WIFI_SDK_ERASE + /********************************************************************************************/ void SettingsDefault(void) diff --git a/tasmota/support_wifi.ino b/tasmota/support_wifi.ino index df509b9d3..866ca4eee 100644 --- a/tasmota/support_wifi.ino +++ b/tasmota/support_wifi.ino @@ -620,12 +620,17 @@ void WifiConnect(void) // Re-enabled from 6.3.0.7 with ESP.restart replaced by ESP.reset void WifiDisconnect(void) { +#ifdef USE_WIFI_SDK_ERASE // Do not enable with DeepSleep as it will wear out flash + SettingsSdkWifiErase(); +#else // Courtesy of EspEasy WiFi.persistent(true); // use SDK storage of SSID/WPA parameters ETS_UART_INTR_DISABLE(); wifi_station_disconnect(); // this will store empty ssid/wpa into sdk storage ETS_UART_INTR_ENABLE(); WiFi.persistent(false); // Do not use SDK storage of SSID/WPA parameters + delay(100); // Flush anything in the network buffers. +#endif // USE_WIFI_SDK_ERASE } void WifiShutdown(void) From 9fdd5e25de2789a376d749fcd0615c2478d29a91 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Mon, 24 Feb 2020 07:15:19 -0600 Subject: [PATCH 20/20] Add Device_Groups.md --- Device_Groups.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Device_Groups.md diff --git a/Device_Groups.md b/Device_Groups.md new file mode 100644 index 000000000..c5990136f --- /dev/null +++ b/Device_Groups.md @@ -0,0 +1,45 @@ +# Device Groups + +The device groups module provides a framework to allow multiple devices to be in a group with values such as power, light color/temperature/brightness, PWM values, sensor values, etc. shared with other devices in the group. For example, with multiple light modules in a device group, the light settings can be changed on one module and the settings will automatically be changed on the other light modules. Dimmer switch modules could be in a device group with light modules and the dimmer switch could control the power, brightness and colors of all the lights in the group. Multiple dimmer switches could be in a device group to form a 3-way/4-way dimmer switch. + +UDP broadcasts, followed by UDP unicasts if necessary, are used to send updates to all devices so updates are fast. There is no need for an MQTT server but all the devices in a group must be on the same IP network. + +To include device groups support in the build, define USE_DEVICE_GROUPS in your user_config_override. This adds 3.5K to the code size. All devices in a group must be running firmware with device group support and have device groups enabled. + +To enable device groups, set Option16 to 1 and **restart the device**. + + +## Device Groups Operation + +The device group name is the MQTT group topic set with the GroupTopic command. All devices in the same IP network with the same group topic are in the same group. Some modules may define additional device groups. For example, if Remote Device Mode is enabled, the PWM Dimmer module defines three devices groups. + +The items that are sent to the group and the items that are received from the group are selected with the DevGroupShare command. By default all items are sent and received from the group. An example of when the DevGroupShare command would be used is when you have a group of lights that you control with a dimmer switch and home automation software. You want the dimmer switch to be able to control all items. The home automation software controls each light individually. When it controls the whole group, it actually sends command to each light in the group. If you use the home automation software to turn an individual light on or off or change it’s brightness, color or scheme, you do not want the change to be replicated to the other lights. In this case, you would set the incoming and outgoing item masks to 255 on the dimmer switch (DevGroupShare 255,255) and set the incoming item mask to 255 and outgoing item mask to 0 on all the lights (DevGroupShare 255,0). + + +### Commands + + + + + + + + + + + + + + + +
Command + Parameters +
DevGroupShare + ,<out> = set incoming and outgoing shared item mask (default = 255,255) +

+1 = Power, 2 = Light brightness, 4 = Light fade/speed, 8 = Light scheme, 16 = Light color, 32 = Minimum brightness +

GroupTopic<x> + 1 = reset device group <x> MQTT group topic to firmware default (MQTT_GRPTOPIC) and restart +

+ = set device group <x> MQTT group topic (32 chars max) and restart +