Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Tuya Dimmer Support #4075

Merged
merged 1 commit into from Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion sonoff/sonoff.h
Expand Up @@ -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};

Expand Down
170 changes: 135 additions & 35 deletions sonoff/sonoff.ino
Expand Up @@ -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

/********************************************************************************************/

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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
\*-------------------------------------------------------------------------------------------*/
Expand Down Expand Up @@ -2306,23 +2390,34 @@ void SerialInput()
}
}

if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) {
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]);
}
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
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]);
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);
}
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;
}
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED));
// XdrvRulesProcess();
serial_in_byte_counter = 0;
}
}

/********************************************************************************************/

void GpioSwitchPinMode(uint8_t index)
Expand Down Expand Up @@ -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;
}
Expand Down
13 changes: 12 additions & 1 deletion sonoff/sonoff_template.h
Expand Up @@ -241,6 +241,7 @@ enum SupportedModules {
OBI,
TECKIN,
APLIC_WDP303075,
TUYA_DIMMER,
MAXMODULE };

/********************************************************************************************/
Expand Down Expand Up @@ -418,7 +419,8 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = {
AILIGHT, // Light Bulbs
PHILIPS,
WITTY, // Development Devices
WEMOS
WEMOS,
TUYA_DIMMER
};

// Default module settings
Expand Down Expand Up @@ -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
}
};

Expand Down
39 changes: 38 additions & 1 deletion sonoff/xdrv_04_light.ino
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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]);
}
}
}
}
Expand Down