Merge pull request #4273 from chaosmaster/tuya-refactor

tuya: refactor code
This commit is contained in:
Theo Arends 2018-11-04 16:58:56 +01:00 committed by GitHub
commit 27e540d101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 218 additions and 189 deletions

View File

@ -20,9 +20,25 @@
#ifdef USE_TUYA_DIMMER
#ifndef TUYA_DIMMER_ID
#define TUYA_DIMMER_ID 0
#define TUYA_DIMMER_ID 0
#endif
#define TUYA_BUFFER_SIZE 256
#define TUYA_POWER_ID 1
#define TUYA_CMD_HEARTBEAT 0x00
#define TUYA_CMD_QUERY_PRODUCT 0x01
#define TUYA_CMD_MCU_CONF 0x02
#define TUYA_CMD_WIFI_STATE 0x03
#define TUYA_CMD_WIFI_RESET 0x04
#define TUYA_CMD_WIFI_SELECT 0x05
#define TUYA_CMD_SET_DP 0x06
#define TUYA_CMD_STATE 0x07
#define TUYA_CMD_QUERY_STATE 0x08
#define TUYA_TYPE_BOOL 0x01
#define TUYA_TYPE_VALUE 0x02
#define TUYA_BUFFER_SIZE 256
#include <TasmotaSerial.h>
@ -38,6 +54,64 @@ int8_t tuya_wifi_state = -2; // Keep MCU wifi-status in sync with
char tuya_buffer[TUYA_BUFFER_SIZE]; // Serial receive buffer
int tuya_byte_counter = 0; // Index in serial receive buffer
/*********************************************************************************************\
* Internal Functions
\*********************************************************************************************/
void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0){
uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF));
TuyaSerial->write(0x55); // Tuya header 55AA
TuyaSerial->write(0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write(cmd); // Tuya command
TuyaSerial->write(payload_len >> 8); // following data length (Hi)
TuyaSerial->write(payload_len & 0xFF); // following data length (Lo)
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Sent Packet: \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF);
for(int i = 0; i < payload_len; ++i) {
TuyaSerial->write(payload[i]);
checksum += payload[i];
snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]);
}
TuyaSerial->write(checksum);
TuyaSerial->flush();
snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum);
AddLog(LOG_LEVEL_DEBUG);
}
void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value){
uint16_t payload_len = 4;
uint8_t payload_buffer[8];
payload_buffer[0] = id;
payload_buffer[1] = type;
switch(type){
case TUYA_TYPE_BOOL:
payload_len += 1;
payload_buffer[2] = 0x00;
payload_buffer[3] = 0x01;
payload_buffer[4] = value[0];
break;
case TUYA_TYPE_VALUE:
payload_len += 4;
payload_buffer[2] = 0x00;
payload_buffer[3] = 0x04;
payload_buffer[4] = value[3];
payload_buffer[5] = value[2];
payload_buffer[6] = value[1];
payload_buffer[7] = value[0];
break;
}
TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len);
}
void TuyaSendBool(uint8_t id, boolean value){
TuyaSendState(id, TUYA_TYPE_BOOL, &value);
}
void TuyaSendValue(uint8_t id, uint32_t value){
TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value));
}
boolean TuyaSetPower()
{
boolean status = false;
@ -50,19 +124,7 @@ boolean TuyaSetPower()
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: SetDevicePower.rpower=%d"), rpower);
AddLog(LOG_LEVEL_DEBUG);
TuyaSerial->write((uint8_t)0x55); // Tuya header 55AA
TuyaSerial->write((uint8_t)0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write((uint8_t)0x06); // Tuya command 06
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x05); // following data length 0x05
TuyaSerial->write((uint8_t)0x01); // relay number 1,2,3
TuyaSerial->write((uint8_t)0x01);
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x01);
TuyaSerial->write((uint8_t)rpower); // status
TuyaSerial->write((uint8_t)0x0D + rpower); // checksum sum of all bytes in packet mod 256
TuyaSerial->flush();
TuyaSendBool(TUYA_POWER_ID, rpower);
status = true;
}
@ -75,22 +137,8 @@ void LightSerialDuty(uint8_t duty)
if (duty < 25) {
duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
}
TuyaSerial->write((uint8_t)0x55); // Tuya header 55AA
TuyaSerial->write((uint8_t)0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write((uint8_t)0x06); // Tuya command 06 - send order
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x08); // following data length 0x08
TuyaSerial->write((uint8_t)Settings.param[P_TUYA_DIMMER_ID]); // dimmer id
TuyaSerial->write((uint8_t)0x02); // type=value
TuyaSerial->write((uint8_t)0x00); // length hi
TuyaSerial->write((uint8_t)0x04); // length low
TuyaSerial->write((uint8_t)0x00); //
TuyaSerial->write((uint8_t)0x00); //
TuyaSerial->write((uint8_t)0x00); //
TuyaSerial->write((uint8_t) duty ); // dim value (0-255)
TuyaSerial->write((uint8_t) byte(Settings.param[P_TUYA_DIMMER_ID] + 19 + duty) ); // checksum:sum of all bytes in packet mod 256
TuyaSerial->flush();
TuyaSendValue(Settings.param[P_TUYA_DIMMER_ID], duty);
snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Serial Packet Dim Value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]);
AddLog(LOG_LEVEL_DEBUG);
@ -104,88 +152,144 @@ void LightSerialDuty(uint8_t duty)
}
}
void TuyaRequestState(){
if(TuyaSerial) {
// Get current status of MCU
snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state");
AddLog(LOG_LEVEL_DEBUG);
TuyaSendCmd(TUYA_CMD_QUERY_STATE);
}
}
void TuyaResetWifi()
{
if (!Settings.flag.button_restrict) {
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2);
ExecuteCommand(scmnd, SRC_BUTTON);
}
}
void TuyaPacketProcess()
{
char scmnd[20];
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Packet Size=%d"), tuya_byte_counter);
AddLog(LOG_LEVEL_DEBUG);
switch(tuya_buffer[3]) {
if (tuya_byte_counter == 7 && tuya_buffer[3] == 14 ) { // heartbeat packet
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat"));
if(tuya_buffer[6] == 0){
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart"));
tuya_wifi_state = -2;
}
case TUYA_CMD_HEARTBEAT:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat"));
if(tuya_buffer[6] == 0){
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart"));
tuya_wifi_state = -2;
}
break;
case TUYA_CMD_STATE:
if (tuya_buffer[5] == 5) { // on/off packet
snprintf_P(log_data, sizeof(log_data),PSTR("TYA: Rcvd - %s State"),tuya_buffer[10]?"On":"Off");
AddLog(LOG_LEVEL_DEBUG);
if((power || Settings.light_dimmer > 0) && (power != tuya_buffer[10])) {
ExecuteCommandPower(1, tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
}
}
else if (tuya_buffer[5] == 8) { // dim packet
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Rcvd Dim State=%d"), tuya_buffer[13]);
AddLog(LOG_LEVEL_DEBUG);
if (!Settings.param[P_TUYA_DIMMER_ID]) {
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Autoconfiguring Dimmer ID %d"), tuya_buffer[6]);
AddLog(LOG_LEVEL_DEBUG);
Settings.param[P_TUYA_DIMMER_ID] = tuya_buffer[6];
}
tuya_new_dim = round(tuya_buffer[13] * (100. / 255.));
if((power) && (tuya_new_dim > 0) && (abs(tuya_new_dim - Settings.light_dimmer) > 2)) {
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);
}
}
break;
case TUYA_CMD_WIFI_RESET:
case TUYA_CMD_WIFI_SELECT:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd WiFi Reset"));
TuyaResetWifi();
break;
case TUYA_CMD_WIFI_STATE:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd WiFi LED set ACK"));
tuya_wifi_state = WifiState();
break;
case TUYA_CMD_MCU_CONF:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd MCU configuration"));
if (tuya_buffer[5] == 2) {
uint8_t led1_gpio = tuya_buffer[6];
uint8_t key1_gpio = tuya_buffer[7];
boolean key1_set = false;
boolean led1_set = false;
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true;
else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true;
}
if(!Settings.my_gp.io[led1_gpio] && !led1_set){
Settings.my_gp.io[led1_gpio] = GPIO_LED1;
restart_flag = 2;
}
if(!Settings.my_gp.io[key1_gpio] && !key1_set){
Settings.my_gp.io[key1_gpio] = GPIO_KEY1;
restart_flag = 2;
}
}
TuyaRequestState();
break;
default:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Rcvd unknown command"));
}
else if (tuya_byte_counter == 12 && tuya_buffer[3] == 7 && tuya_buffer[5] == 5) { // on/off packet
}
snprintf_P(log_data, sizeof(log_data),PSTR("TYA: Rcvd - %s State"),tuya_buffer[10]?"On":"Off");
/*********************************************************************************************\
* API Functions
\*********************************************************************************************/
boolean TuyaModuleSelected()
{
if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected
pin[GPIO_TUYA_TX] = 1;
pin[GPIO_TUYA_RX] = 3;
Settings.my_gp.io[1] = GPIO_TUYA_TX;
Settings.my_gp.io[3] = GPIO_TUYA_RX;
restart_flag = 2;
}
light_type = LT_SERIAL;
return true;
}
void TuyaInit()
{
if (!Settings.param[P_TUYA_DIMMER_ID]) {
Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID;
}
TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1);
if (TuyaSerial->begin(9600)) {
if (TuyaSerial->hardwareSerial()) { ClaimSerial(); }
// Get MCU Configuration
snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU configuration");
AddLog(LOG_LEVEL_DEBUG);
if((power || Settings.light_dimmer > 0) && (power != tuya_buffer[10])) {
ExecuteCommandPower(1, tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
}
}
else if (tuya_byte_counter == 15 && tuya_buffer[3] == 7 && tuya_buffer[5] == 8) { // dim packet
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Rcvd Dim State=%d"), tuya_buffer[13]);
AddLog(LOG_LEVEL_DEBUG);
if (!Settings.param[P_TUYA_DIMMER_ID]) {
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Autoconfiguring Dimmer ID %d"), tuya_buffer[6]);
AddLog(LOG_LEVEL_DEBUG);
Settings.param[P_TUYA_DIMMER_ID] = tuya_buffer[6];
}
tuya_new_dim = round(tuya_buffer[13] * (100. / 255.));
if((power) && (tuya_new_dim > 0) && (abs(tuya_new_dim - Settings.light_dimmer) > 2)) {
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);
}
}
else if (tuya_byte_counter == 8 && tuya_buffer[3] == 5 && tuya_buffer[5] == 1) { // reset WiFi settings packet
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: WiFi Reset Rcvd"));
TuyaResetWifi();
}
else if (tuya_byte_counter == 7 && tuya_buffer[3] == 3 && tuya_buffer[6] == 2) { // WiFi LED has been sucessfully set.
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: WiFi LED set ACK Rcvd"));
tuya_wifi_state = WifiState();
}
else if (tuya_buffer[3] == 2) { // MCU configuration packet
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: MCU configuration Rcvd"));
if (tuya_buffer[5] == 2) {
uint8_t led1_gpio = tuya_buffer[6];
uint8_t key1_gpio = tuya_buffer[7];
boolean key1_set = false;
boolean led1_set = false;
for (byte i = 0; i < MAX_GPIO_PIN; i++) {
if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true;
else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true;
}
if(!Settings.my_gp.io[led1_gpio] && !led1_set){
Settings.my_gp.io[led1_gpio] = GPIO_LED1;
restart_flag = 2;
}
if(!Settings.my_gp.io[key1_gpio] && !key1_set){
Settings.my_gp.io[key1_gpio] = GPIO_KEY1;
restart_flag = 2;
}
}
TuyaRequestState();
TuyaSendCmd(TUYA_CMD_MCU_CONF);
}
}
@ -195,9 +299,6 @@ void TuyaSerialInput()
yield();
byte serial_in_byte = TuyaSerial->read();
//snprintf_P(log_data, sizeof(log_data), PSTR("TYA: serial_in_byte %d, tuya_cmd_status %d, tuya_cmd_checksum %d, tuya_data_len %d, tuya_byte_counter %d"), serial_in_byte, tuya_cmd_status, tuya_cmd_checksum, tuya_data_len, tuya_byte_counter);
//AddLog(LOG_LEVEL_DEBUG);
if (serial_in_byte == 0x55) { // Start TUYA Packet
tuya_cmd_status = 1;
tuya_buffer[tuya_byte_counter++] = serial_in_byte;
@ -206,8 +307,6 @@ void TuyaSerialInput()
else if (tuya_cmd_status == 1 && serial_in_byte == 0xAA){ // Only packtes with header 0x55AA are valid
tuya_cmd_status = 2;
AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: 0x55AA Packet Start"));
tuya_byte_counter = 0;
tuya_buffer[tuya_byte_counter++] = 0x55;
tuya_buffer[tuya_byte_counter++] = 0xAA;
@ -224,7 +323,7 @@ void TuyaSerialInput()
else if ((tuya_cmd_status == 3) && (tuya_byte_counter == (6 + tuya_data_len)) && (tuya_cmd_checksum == serial_in_byte)){ // Compare checksum and process packet
tuya_buffer[tuya_byte_counter++] = serial_in_byte;
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: 0x55 Packet End: \""));
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Received Packet: \""));
for (int i = 0; i < tuya_byte_counter; i++) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, tuya_buffer[i]);
}
@ -249,17 +348,16 @@ void TuyaSerialInput()
}
}
boolean TuyaModuleSelected()
boolean TuyaButtonPressed()
{
if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected
pin[GPIO_TUYA_TX] = 1;
pin[GPIO_TUYA_RX] = 3;
Settings.my_gp.io[1] = GPIO_TUYA_TX;
Settings.my_gp.io[3] = GPIO_TUYA_RX;
restart_flag = 2;
if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == lastbutton[XdrvMailbox.index]))) {
snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Reset GPIO triggered"));
AddLog(LOG_LEVEL_DEBUG);
TuyaResetWifi();
return true; // Reset GPIO served here
}
light_type = LT_SERIAL;
return true;
return false; // Don't serve other buttons
}
void TuyaSetWifiLed(){
@ -280,78 +378,9 @@ void TuyaSetWifiLed(){
snprintf_P(log_data, sizeof(log_data), "TYA: Set WiFi LED to state %d (%d)", wifi_state, WifiState());
AddLog(LOG_LEVEL_DEBUG);
TuyaSerial->write((uint8_t)0x55); // header 55AA
TuyaSerial->write((uint8_t)0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write((uint8_t)0x03); // command 03 - set wifi state
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x01); // following data length 0x01
TuyaSerial->write((uint8_t)wifi_state); // wifi state (0x00 == smartconfig, 0x01 == AP, 0x02 == not connected, 0x03 == connected)
TuyaSerial->write((uint8_t)(0x03 + wifi_state)); // checksum:sum of all bytes in packet mod 256
TuyaSerial->flush();
TuyaSendCmd(TUYA_CMD_WIFI_STATE, &wifi_state, 1);
}
void TuyaRequestState(){
if(TuyaSerial) {
// Get current status of MCU
snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state");
AddLog(LOG_LEVEL_DEBUG);
TuyaSerial->write((uint8_t)0x55); // header 55AA
TuyaSerial->write((uint8_t)0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write((uint8_t)0x08); // command 08 - get status
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x00); // following data length 0x00
TuyaSerial->write((uint8_t)0x07); // checksum:sum of all bytes in packet mod 256
TuyaSerial->flush();
}
}
void TuyaInit()
{
if (!Settings.param[P_TUYA_DIMMER_ID]) {
Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID;
}
TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1);
if (TuyaSerial->begin(9600)) {
if (TuyaSerial->hardwareSerial()) { ClaimSerial(); }
// Get MCU Configuration
snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU configuration");
AddLog(LOG_LEVEL_DEBUG);
TuyaSerial->write((uint8_t)0x55); // header 55AA
TuyaSerial->write((uint8_t)0xAA);
TuyaSerial->write((uint8_t)0x00); // version 00
TuyaSerial->write((uint8_t)0x02); // command 02 - get configuration
TuyaSerial->write((uint8_t)0x00);
TuyaSerial->write((uint8_t)0x00); // following data length 0x00
TuyaSerial->write((uint8_t)0x01); // checksum:sum of all bytes in packet mod 256
TuyaSerial->flush();
}
}
void TuyaResetWifi()
{
if (!Settings.flag.button_restrict) {
char scmnd[20];
tuya_wifi_state = -1;
snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2);
ExecuteCommand(scmnd, SRC_BUTTON);
}
}
boolean TuyaButtonPressed()
{
if ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == lastbutton[XdrvMailbox.index])) {
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), XdrvMailbox.index +1);
AddLog(LOG_LEVEL_DEBUG);
TuyaResetWifi();
}
return true; // Serviced here
}
/*********************************************************************************************\
* Interface