From 64d6ef86ceebcfdab82f2bec9946fc66a548c656 Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Sat, 24 Nov 2018 03:29:32 +0100 Subject: [PATCH] PS_16_DZ: initial support --- sonoff/language/tuya.patch | 13 ++ sonoff/my_user_config.h | 1 + sonoff/sonoff.h | 2 +- sonoff/sonoff_post.h | 1 + sonoff/sonoff_template.h | 22 +++- sonoff/xdrv_04_light.ino | 5 + sonoff/xdrv_19_ps16dz_dimmer.ino | 211 +++++++++++++++++++++++++++++++ 7 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 sonoff/language/tuya.patch create mode 100644 sonoff/xdrv_19_ps16dz_dimmer.ino diff --git a/sonoff/language/tuya.patch b/sonoff/language/tuya.patch new file mode 100644 index 000000000..685f4458c --- /dev/null +++ b/sonoff/language/tuya.patch @@ -0,0 +1,13 @@ +diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h +index 4e3aa00..925080d 100644 +--- a/sonoff/language/en-GB.h ++++ b/sonoff/language/en-GB.h +@@ -527,6 +527,8 @@ + #define D_SENSOR_TX20_TX "TX20" + #define D_SENSOR_RFSEND "RFSend" + #define D_SENSOR_RFRECV "RFrecv" ++#define D_SENSOR_TUYA_TX "Tuya Tx" ++#define D_SENSOR_TUYA_RX "Tuya Rx" + + // Units + #define D_UNIT_AMPERE "A" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 91b764688..dc23003cd 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -360,6 +360,7 @@ #define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) +#define USE_PS_16_DZ // ADD support for PS-16-DZ Dimmer // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index e53a7c83d..07b87661c 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -231,7 +231,7 @@ enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; enum LightSubtypes { LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC }; // Do not insert new fields enum LightTypes { LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, - LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_WS2812, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields + LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_WS2812, LT_RGBW, LT_RGBWC, LT_SERIAL3, LT_NU15 }; // Do not insert new fields enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 1e52a8641..b6fc2c77f 100644 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -111,6 +111,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifndef TUYA_DIMMER_ID #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif +#define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) #define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 3135dd339..98e329bdf 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -256,6 +256,7 @@ enum SupportedModules { GOSUND, ARMTRONIX_DIMMERS, SK03_TUYA, + PS_16_DZ, MAXMODULE }; /********************************************************************************************/ @@ -494,7 +495,8 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { AILIGHT, // Light Bulbs PHILIPS, WITTY, // Development Devices - WEMOS + WEMOS, + PS_16_DZ }; // Default module settings @@ -1270,7 +1272,23 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO14 Blue Led (0 = On, 1 = Off) GPIO_REL1, // GPIO15 Relay (0 = Off, 1 = On) 0, 0 - } + }, + { "PS-16-DZ", // PS-16-DZ Dimmer (ESP8266 w/ separate Nuvoton MCU dimmer) + // https://www.aliexpress.com/item/SM-Smart-WIFI-Wall-Dimmer-Light-Switch-US-Ewelink-APP-Remote-Control-Wi-Fi-Wirele-Work/32871151902.html + GPIO_USER, + GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, + GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, + GPIO_USER, + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + GPIO_USER, + 0 + } }; /* diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 92e19fb57..10caf2472 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -841,6 +841,11 @@ void LightAnimate(void) LightSerial2Duty(cur_col[0],cur_col[1]); } #endif // USE_ARMTRONIX_DIMMERS +#ifdef USE_PS16DZ_DIMMERS + if (light_type == LT_SERIAL3) { + PS16DZSerialDuty(cur_col[0]); + } +#endif // USE_PS16DZ_DIMMERS } } diff --git a/sonoff/xdrv_19_ps16dz_dimmer.ino b/sonoff/xdrv_19_ps16dz_dimmer.ino new file mode 100644 index 000000000..82f81eefa --- /dev/null +++ b/sonoff/xdrv_19_ps16dz_dimmer.ino @@ -0,0 +1,211 @@ +/* + xdrv_16_ps16dzdimmer.ino - PS16DZ dimmer support for Sonoff-Tasmota + + Copyright (C) 2018 Joel Stein 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_PS_16_DZ + +#define XDRV_19 19 + +#define PS16DZ_BUFFER_SIZE 256 + +#include + +TasmotaSerial *PS16DZSerial = nullptr; + +boolean ps16dz_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction +int8_t ps16dz_wifi_state = -2; // Keep MCU wifi-status in sync with WifiState() + +boolean ps16dz_power = false; +uint8_t ps16dz_bright = 0; +uint64_t ps16dz_seq = 0; + +char ps16dz_buffer[PS16DZ_BUFFER_SIZE]; // Serial receive buffer +int ps16dz_byte_counter = 0; // Index in serial receive buffer + +/*********************************************************************************************\ + * Internal Functions +\*********************************************************************************************/ + + +boolean PS16DZSetPower(void) +{ + boolean status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + if (source != SRC_SWITCH && PS16DZSerial) { // ignore to prevent loop from pushing state from faceplate interaction + + snprintf_P(ps16dz_buffer, sizeof(ps16dz_buffer), PSTR( "AT+UPDATE=\"sequence\":\"%lld\",\"switch\":\"%s\""), ps16dz_seq++, rpower?"on":"off"); + snprintf_P(log_data, sizeof(log_data), PSTR( "PSD: Send serial command: %s"), ps16dz_buffer ); + AddLog(LOG_LEVEL_DEBUG); + + PS16DZSerial->print(ps16dz_buffer); + PS16DZSerial->write(0x1B); + + status = true; + } + return status; +} + +void PS16DZSerialDuty(uint8_t duty) +{ + if (duty > 0 && !ps16dz_ignore_dim && PS16DZSerial) { + if (duty < 25) { + duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself + } + + snprintf_P(ps16dz_buffer, sizeof(ps16dz_buffer), PSTR( "AT+UPDATE=\"sequence\":\"%lld\",\"bright\":\"%d\""), ps16dz_seq++, duty/(255./100.)); + snprintf_P(log_data, sizeof(log_data), PSTR( "PSD: Send serial command: %s"), ps16dz_buffer ); + AddLog(LOG_LEVEL_DEBUG); + + PS16DZSerial->print(ps16dz_buffer); + PS16DZSerial->write(0x1B); + + } else { + ps16dz_ignore_dim = false; // reset flag + + snprintf_P(log_data, sizeof(log_data), PSTR( "PSD: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); + AddLog(LOG_LEVEL_DEBUG); + + } +} + +void PS16DZResetWifi(void) +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +/*********************************************************************************************\ + * API Functions +\*********************************************************************************************/ + +boolean PS16DZModuleSelected(void) +{ + light_type = LT_SERIAL3; + return true; +} + +void PS16DZInit(void) +{ + PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (PS16DZSerial->begin(19200)) { + if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } + } +} + +void PS16DZSerialInput(void) +{ + char scmnd[20]; + String ps16dz_command; + while (PS16DZSerial->available()) { + yield(); + ps16dz_command = PS16DZSerial->readStringUntil(0x1B); + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: command received: %s"), ps16dz_command.c_str()); + AddLog(LOG_LEVEL_DEBUG); + if(ps16dz_command.substring(3,6) == "UPDATE" || ps16dz_command.substring(3,6) == "RESULT"){ + char *end_str; + char *string = strdup(ps16dz_command.substring(10).c_str()); + char* token = strtok_r(string, ",", &end_str); + while (token != NULL) { + char* end_token; + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: token = %s"), token); + AddLog(LOG_LEVEL_DEBUG); + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(NULL, ":", &end_token); + if(!strncmp(token2, "\"switch\"", 8)){ + ps16dz_power = !strncmp(token3, "\"on\"", 4)?true:false; + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: power received: %s"), token3); + AddLog(LOG_LEVEL_DEBUG); + if((power || Settings.light_dimmer > 0) && (power !=ps16dz_power)) { + ExecuteCommandPower(1, ps16dz_power, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + } + else if(!strncmp(token2, "\"bright\"", 8)){ + ps16dz_bright = atoi(token3); + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: brightness received: %d"), ps16dz_bright); + AddLog(LOG_LEVEL_DEBUG); + if(power && ps16dz_bright > 0) { + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), ps16dz_bright ); + + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: Send CMND_DIMMER_STR=%s"), scmnd ); + AddLog(LOG_LEVEL_DEBUG); + + ps16dz_ignore_dim = true; + ExecuteCommand(scmnd, SRC_SWITCH); + } + + } + else if(!strncmp(token2, "\"sequence\"", 10)){ + ps16dz_seq = strtoull(token3, NULL, 10); + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: sequence received: %lld"), ps16dz_seq); + AddLog(LOG_LEVEL_DEBUG); + } + + token = strtok_r(NULL, ",", &end_str); + } + } + else if(ps16dz_command.substring(3,7) == "SETTING"){ + snprintf_P(log_data, sizeof(log_data), PSTR("PSD: Reset")); + AddLog(LOG_LEVEL_DEBUG); + PS16DZResetWifi(); + } + snprintf_P(ps16dz_buffer, sizeof(ps16dz_buffer), PSTR( "AT+SEND=ok")); + snprintf_P(log_data, sizeof(log_data), PSTR( "PSD: Send serial command: %s"), ps16dz_buffer ); + AddLog(LOG_LEVEL_DEBUG); + + PS16DZSerial->print(ps16dz_buffer); + PS16DZSerial->write(0x1B); + } +} + + + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +boolean Xdrv19(byte function) +{ + boolean result = false; + + if (PS_16_DZ == Settings.module) { + switch (function) { + case FUNC_MODULE_INIT: + result = PS16DZModuleSelected(); + break; + case FUNC_INIT: + PS16DZInit(); + break; + case FUNC_LOOP: + if (PS16DZSerial) { PS16DZSerialInput(); } + break; + case FUNC_SET_DEVICE_POWER: + result = PS16DZSetPower(); + break; + } + } + return result; +} + +#endif // USE_PS_16_DZ