From 5894cd97fcc1bdbf9efdbb187c4f503df268638f Mon Sep 17 00:00:00 2001 From: digiblur <3240875+digiblur@users.noreply.github.com> Date: Mon, 15 Oct 2018 18:32:14 -0500 Subject: [PATCH] Initial TuyaDimmer Support --- sonoff/sonoff.h | 2 +- sonoff/sonoff.ino | 172 +++++++++++++++++++++++++++++++-------- sonoff/sonoff_template.h | 13 ++- sonoff/xdrv_04_light.ino | 39 ++++++++- 4 files changed, 187 insertions(+), 39 deletions(-) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index a573849d3..eb99cc2a1 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -208,7 +208,7 @@ enum DomoticzSensors {DZ_TEMP, DZ_TEMP_HUM, DZ_TEMP_HUM_BARO, DZ_POWER_ENERGY, D enum Ws2812ClockIndex { WS_SECOND, WS_MINUTE, WS_HOUR, WS_MARKER }; enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; -enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, LT_NU8, LT_NU9, LT_NU10, LT_WS2812, LT_RGBW, LT_RGBWC}; +enum LightTypes {LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, LT_SERIAL, LT_NU9, LT_NU10, LT_WS2812, LT_RGBW, LT_RGBWC}; enum LichtSubtypes {LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC}; enum LichtSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index f03b9973b..898aef66c 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -200,6 +200,8 @@ char mqtt_data[MESSZ]; // MQTT publish buffer and web page char log_data[LOGSZ]; // Logging char web_log[WEB_LOG_SIZE] = {'\0'}; // Web log buffer String backlog[MAX_BACKLOG]; // Command backlog +uint8_t tuya_new_dim = 0; // Tuya dimmer value temp +boolean tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction /********************************************************************************************/ @@ -350,6 +352,23 @@ void SetDevicePower(power_t rpower, int source) Serial.write('\n'); Serial.flush(); } + else if (TUYA_DIMMER == Settings.module && source != SRC_SWITCH ) { // ignore to prevent loop from pushing state from faceplate interaction + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: SetDevicePower.rpower=%d"), rpower); + AddLog(LOG_LEVEL_DEBUG); + Serial.write(0x55); // Tuya header 55AA + Serial.write(0xAA); + Serial.write(0x00); // version 00 + Serial.write(0x06); // Tuya command 06 + Serial.write(0x00); + Serial.write(0x05); // following data length 0x05 + Serial.write(0x01); // relay number 1,2,3 + Serial.write(0x01); + Serial.write(0x00); + Serial.write(0x01); + Serial.write(rpower); // status + Serial.write(0x0D + rpower); // checksum sum of all bytes in packet mod 256 + Serial.flush(); + } else if (EXS_RELAY == Settings.module) { SetLatchingRelay(rpower, 1); } @@ -2214,6 +2233,48 @@ void ArduinoOTAInit() /********************************************************************************************/ +void TuyaPacketProcess() +{ + char scmnd[20]; + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Packet Size=%d"), serial_in_byte_counter); + AddLog(LOG_LEVEL_DEBUG); + if (serial_in_byte_counter == 7 && serial_in_buffer[3] == 14 ) { // heartbeat packet + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); + } + if (serial_in_byte_counter == 12 && serial_in_buffer[3] == 7 && serial_in_buffer[5] == 5) { // on/off packet + if (serial_in_buffer[10] == 0) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd - Off State")); + ExecuteCommandPower(1, 0, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } else + { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd - On State")); + ExecuteCommandPower(1, 1, SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + } + serial_in_byte_counter = 0; + serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + } + if (serial_in_byte_counter == 15 && serial_in_buffer[3] == 7 && serial_in_buffer[5] == 8) { // dim packet + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Rcvd Dim State=%d"), serial_in_buffer[13]); + AddLog(LOG_LEVEL_DEBUG); + tuya_new_dim = round(serial_in_buffer[13] * (100. / 255.)); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send CMND_DIMMER=%d"), tuya_new_dim ); + AddLog(LOG_LEVEL_DEBUG); + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), tuya_new_dim ); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send CMND_DIMMER_STR=%s"), scmnd ); + AddLog(LOG_LEVEL_DEBUG); + tuya_ignore_dim = true; + ExecuteCommand(scmnd, SRC_SWITCH); + serial_in_byte_counter = 0; + serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + } + if (serial_in_byte_counter == 8 && serial_in_buffer[3] == 5 && serial_in_buffer[5] == 1 && serial_in_buffer[7] == 5 ) { // reset WiFi settings packet - to do: reset red MCU LED after WiFi is up + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: WiFi Reset Rcvd")); + serial_in_byte_counter = 0; + serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " 2"); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} void SerialInput() { while (Serial.available()) { @@ -2242,6 +2303,29 @@ void SerialInput() } } +/*-------------------------------------------------------------------------------------------*\ + * Tuya based Dimmer with Serial Communications to MCU dimmer +\*-------------------------------------------------------------------------------------------*/ + if (TUYA_DIMMER == Settings.module) { + if (serial_in_byte == '\x55') { // Start TUYA Packet + if (serial_in_byte_counter > 0 && serial_in_byte_counter < 11) { + TuyaPacketProcess(); + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: 0x55 Packet Start")); + serial_in_byte_counter = 0; + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; +// return; // test to see if we need this + } else { // read additional packets from TUYA + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + serial_polling_window = millis(); +// return; // test to see if we need this + } else { + serial_in_byte_counter = 0; + } + } + } + /*-------------------------------------------------------------------------------------------*/ if (XdrvCall(FUNC_SERIAL)) { @@ -2251,33 +2335,33 @@ void SerialInput() } /*-------------------------------------------------------------------------------------------*/ - - if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // binary data... - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - if (!Settings.flag.mqtt_serial) { - if (isprint(serial_in_byte)) { - if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - } else { - serial_in_byte_counter = 0; - } + if (TUYA_DIMMER != Settings.module) { + if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { // binary data... + serial_in_byte_counter = 0; + Serial.flush(); + return; } - } else { - if (serial_in_byte || Settings.flag.mqtt_serial_raw) { - if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && - ((serial_in_byte != Settings.serial_delimiter) || Settings.flag.mqtt_serial_raw)) { // add char to string if it still fits - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - serial_polling_window = millis(); - } else { - serial_polling_window = 0; - break; + if (!Settings.flag.mqtt_serial) { + if (isprint(serial_in_byte)) { + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } else { + serial_in_byte_counter = 0; + } + } + } else { + if (serial_in_byte || Settings.flag.mqtt_serial_raw) { + if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && + ((serial_in_byte != Settings.serial_delimiter) || Settings.flag.mqtt_serial_raw)) { // add char to string if it still fits + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + serial_polling_window = millis(); + } else { + serial_polling_window = 0; + break; + } } } } - /*-------------------------------------------------------------------------------------------*\ * Sonoff SC 19200 baud serial interface \*-------------------------------------------------------------------------------------------*/ @@ -2306,23 +2390,34 @@ void SerialInput() } } - if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { - serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed - if (!Settings.flag.mqtt_serial_raw) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); - } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); - for (int i = 0; i < serial_in_byte_counter; i++) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02x"), mqtt_data, serial_in_buffer[i]); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + if (TUYA_DIMMER == Settings.module && serial_in_byte_counter > 6 && (millis() > (serial_polling_window + SERIAL_POLLING))) { + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: 0x55 Packet End: \"")); + for (int i = 0; i < serial_in_byte_counter; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, serial_in_buffer[i]); } - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); -// XdrvRulesProcess(); + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG); + TuyaPacketProcess(); + serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed serial_in_byte_counter = 0; + } else { + if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { + serial_in_buffer[serial_in_byte_counter] = 0; // serial data completed + if (!Settings.flag.mqtt_serial_raw) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"%s\"}"), serial_in_buffer); + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_SERIALRECEIVED "\":\"")); + for (int i = 0; i < serial_in_byte_counter; i++) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02x"), mqtt_data, serial_in_buffer[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}"), mqtt_data); + } + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); +// XdrvRulesProcess(); + serial_in_byte_counter = 0; + } } } - /********************************************************************************************/ void GpioSwitchPinMode(uint8_t index) @@ -2464,6 +2559,11 @@ void GpioInit() devices_present = 0; baudrate = 19200; } + else if (TUYA_DIMMER == Settings.module) { + Settings.flag.mqtt_serial = 0; + baudrate = 9600; + light_type = LT_SERIAL; + } else if (SONOFF_BN == Settings.module) { // PWM Single color led (White) light_type = LT_PWM1; } diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index bac97a65c..134778d2c 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -241,6 +241,7 @@ enum SupportedModules { OBI, TECKIN, APLIC_WDP303075, + TUYA_DIMMER, MAXMODULE }; /********************************************************************************************/ @@ -418,7 +419,8 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { AILIGHT, // Light Bulbs PHILIPS, WITTY, // Development Devices - WEMOS + WEMOS, + TUYA_DIMMER }; // Default module settings @@ -1133,6 +1135,15 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1_INV, // GPIO13 LED (0 = On, 1 = Off) GPIO_REL1, // GPIO14 Relay SRU 5VDC SDA (0 = Off, 1 = On ) 0, 0, 0 + }, + { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) + 0, + GPIO_TXD, // TX to dimmer MCU + 0, + GPIO_RXD, // RX from dimmer MCU + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0 } }; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 6c8aa938d..a4f58ae67 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -343,6 +343,37 @@ void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t dut os_delay_us(12); // TStop > 12us. } +// *************** Tuya Dimmer Serial Comms +void LightSerialDuty(uint8_t duty) +{ + if (duty > 0 && !tuya_ignore_dim ) { + if (duty < 25) { + duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself + } + Serial.write(0x55); // Tuya header 55AA + Serial.write(0xAA); + Serial.write(0x00); // version 00 + Serial.write(0x06); // Tuya command 06 - send order + Serial.write(0x00); + Serial.write(0x08); // following data length 0x08 + Serial.write(0x03); // dimmer id + Serial.write(0x02); // type=value + Serial.write(0x00); // length hi + Serial.write(0x04); // length low + Serial.write(0x00); // + Serial.write(0x00); // + Serial.write(0x00); // + Serial.write( duty ); // dim value (0-255) + Serial.write( byte(22 + duty) ); // checksum:sum of all bytes in packet mod 256 + Serial.flush(); + snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Serial Packet Dim Value=%d"), duty); + AddLog(LOG_LEVEL_DEBUG); + } else { + tuya_ignore_dim = false; // reset flag + snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Dim Level skipped due to 0 or already set. Value=%d"), duty); + AddLog(LOG_LEVEL_DEBUG); + } +} /********************************************************************************************/ void LightInit() @@ -359,7 +390,7 @@ void LightInit() pinMode(pin[GPIO_PWM1 +i], OUTPUT); } } - if (LT_PWM1 == light_type) { + if (LT_PWM1 == light_type || LT_SERIAL == light_type) { Settings.light_color[0] = 255; // One PWM channel only supports Dimmer but needs max color } if (SONOFF_LED == Settings.module) { // Fix Sonoff Led instabilities @@ -391,6 +422,9 @@ void LightInit() max_scheme = LS_MAX + WS2812_SCHEMES; } #endif // USE_WS2812 ************************************************************************ + else if (LT_SERIAL == light_type) { + light_subtype = LST_SINGLE; + } else { light_pdi_pin = pin[GPIO_DI]; light_pdcki_pin = pin[GPIO_DCKI]; @@ -821,6 +855,9 @@ void LightAnimate() if (light_type > LT_WS2812) { LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); } + if (light_type == LT_SERIAL) { + LightSerialDuty(cur_col[0]); + } } } }