2018-10-18 12:01:31 +01:00
/*
2019-09-03 12:23:05 +01:00
xdrv_16_tuyamcu . ino - Tuya MCU support for Sonoff - Tasmota
2018-10-18 12:01:31 +01:00
2019-01-01 12:55:01 +00:00
Copyright ( C ) 2019 digiblur , Joel Stein and Theo Arends
2018-10-18 12:01:31 +01:00
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 < http : //www.gnu.org/licenses/>.
*/
2019-06-16 15:43:23 +01:00
# ifdef USE_LIGHT
2019-09-03 12:23:05 +01:00
# ifdef USE_TUYA_MCU
2018-10-18 12:01:31 +01:00
2018-11-07 09:30:03 +00:00
# define XDRV_16 16
2019-08-27 10:50:34 +01:00
# define XNRG_08 8
2018-11-07 09:30:03 +00:00
2018-10-18 12:01:31 +01:00
# ifndef TUYA_DIMMER_ID
2018-11-03 21:34:29 +00:00
# define TUYA_DIMMER_ID 0
2018-10-18 12:01:31 +01:00
# endif
2018-11-03 21:34:29 +00:00
# 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
2018-10-18 12:01:31 +01:00
2018-10-27 02:01:09 +01:00
# include <TasmotaSerial.h>
2018-10-27 03:11:30 +01:00
TasmotaSerial * TuyaSerial = nullptr ;
2018-10-27 02:01:09 +01:00
2019-08-17 14:00:57 +01:00
struct TUYA {
uint8_t new_dim = 0 ; // Tuya dimmer value temp
bool ignore_dim = false ; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction
uint8_t cmd_status = 0 ; // Current status of serial-read
uint8_t cmd_checksum = 0 ; // Checksum of tuya command
uint8_t data_len = 0 ; // Data lenght of command
int8_t wifi_state = - 2 ; // Keep MCU wifi-status in sync with WifiState()
uint8_t heartbeat_timer = 0 ; // 10 second heartbeat timer for tuya module
2019-08-27 14:40:43 +01:00
# ifdef USE_ENERGY_SENSOR
2019-08-27 10:50:34 +01:00
uint32_t lastPowerCheckTime = 0 ; // Time when last power was checked
2019-08-27 14:40:43 +01:00
# endif // USE_ENERGY_SENSOR
2019-08-17 14:00:57 +01:00
char * buffer = nullptr ; // Serial receive buffer
int byte_counter = 0 ; // Index in serial receive buffer
} Tuya ;
2018-10-27 10:37:42 +01:00
2019-09-03 11:23:44 +01:00
enum TuyaSupportedFunctions {
TUYA_MCU_FUNC_NONE ,
2019-09-03 11:57:13 +01:00
TUYA_MCU_FUNC_SWT1 = 1 , // Buttons
TUYA_MCU_FUNC_SWT2 ,
TUYA_MCU_FUNC_SWT3 ,
TUYA_MCU_FUNC_SWT4 ,
2019-09-03 11:23:44 +01:00
TUYA_MCU_FUNC_REL1 = 11 , // Relays
TUYA_MCU_FUNC_REL2 ,
TUYA_MCU_FUNC_REL3 ,
TUYA_MCU_FUNC_REL4 ,
TUYA_MCU_FUNC_REL5 ,
TUYA_MCU_FUNC_REL6 ,
TUYA_MCU_FUNC_REL7 ,
TUYA_MCU_FUNC_REL8 ,
TUYA_MCU_FUNC_DIMMER = 21 ,
TUYA_MCU_FUNC_POWER = 31 ,
TUYA_MCU_FUNC_CURRENT ,
TUYA_MCU_FUNC_VOLTAGE ,
2019-09-03 14:04:07 +01:00
TUYA_MCU_FUNC_REL1_INV = 41 , // Inverted Relays
TUYA_MCU_FUNC_REL2_INV ,
TUYA_MCU_FUNC_REL3_INV ,
TUYA_MCU_FUNC_REL4_INV ,
TUYA_MCU_FUNC_REL5_INV ,
TUYA_MCU_FUNC_REL6_INV ,
TUYA_MCU_FUNC_REL7_INV ,
TUYA_MCU_FUNC_REL8_INV ,
2019-09-03 11:23:44 +01:00
TUYA_MCU_FUNC_LAST = 255
} ;
const char kTuyaCommand [ ] PROGMEM = " | " // No prefix
D_CMND_TUYA_MCU ;
void ( * const TuyaCommand [ ] ) ( void ) PROGMEM = {
& CmndTuyaMcu
} ;
/*
2019-09-03 14:04:07 +01:00
TuyaMcu fnid , dpid
2019-09-03 11:23:44 +01:00
*/
void CmndTuyaMcu ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
char * p ;
uint8_t i = 0 ;
uint8_t parm [ 3 ] = { 0 } ;
for ( char * str = strtok_r ( XdrvMailbox . data , " , " , & p ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & p ) ) {
parm [ i ] = strtoul ( str , nullptr , 0 ) ;
i + + ;
}
if ( TuyaFuncIdValid ( parm [ 0 ] ) ) {
TuyaAddMcuFunc ( parm [ 0 ] , parm [ 1 ] ) ;
restart_flag = 2 ;
} else {
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " TYA: TuyaMcu Invalid function id=%d " ) , parm [ 0 ] ) ;
}
}
Response_P ( PSTR ( " [ " ) ) ;
bool added = false ;
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( Settings . tuya_fnid_map [ i ] . fnid ! = 0 ) {
if ( added ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " { \" fnId \" :%d, \" dpId \" :%d} " ) , Settings . tuya_fnid_map [ i ] . fnid , Settings . tuya_fnid_map [ i ] . dpid ) ;
added = true ;
}
}
ResponseAppend_P ( PSTR ( " ] " ) ) ;
}
2018-11-04 05:56:36 +00:00
/*********************************************************************************************\
* Internal Functions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-03 11:23:44 +01:00
void TuyaAddMcuFunc ( uint8_t fnId , uint8_t dpId ) {
bool added = false ;
if ( fnId = = 0 | | dpId = = 0 ) { // Delete entry
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( ( dpId > 0 & & Settings . tuya_fnid_map [ i ] . dpid = = dpId ) | | ( fnId > TUYA_MCU_FUNC_NONE & & Settings . tuya_fnid_map [ i ] . fnid = = fnId ) ) {
Settings . tuya_fnid_map [ i ] . fnid = TUYA_MCU_FUNC_NONE ;
Settings . tuya_fnid_map [ i ] . dpid = 0 ;
break ;
}
}
} else { // Add or update
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( Settings . tuya_fnid_map [ i ] . dpid = = dpId | | Settings . tuya_fnid_map [ i ] . dpid = = 0 | | Settings . tuya_fnid_map [ i ] . fnid = = fnId | | Settings . tuya_fnid_map [ i ] . fnid = = 0 ) {
if ( ! added ) { // Update entry if exisiting entry or add
Settings . tuya_fnid_map [ i ] . fnid = fnId ;
Settings . tuya_fnid_map [ i ] . dpid = dpId ;
added = true ;
} else if ( Settings . tuya_fnid_map [ i ] . dpid = = dpId | | Settings . tuya_fnid_map [ i ] . fnid = = fnId ) { // Remove existing entry if added to empty place
Settings . tuya_fnid_map [ i ] . fnid = TUYA_MCU_FUNC_NONE ;
Settings . tuya_fnid_map [ i ] . dpid = 0 ;
}
}
}
}
2019-09-03 14:04:07 +01:00
UpdateDevices ( ) ;
}
void UpdateDevices ( ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
uint8_t fnId = Settings . tuya_fnid_map [ i ] . fnid ;
if ( fnId > TUYA_MCU_FUNC_NONE & & Settings . tuya_fnid_map [ i ] . dpid > 0 ) {
if ( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) { //Relay
bitClear ( rel_inverted , fnId - TUYA_MCU_FUNC_REL1 ) ;
} else if ( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) { // Inverted Relay
bitSet ( rel_inverted , fnId - TUYA_MCU_FUNC_REL1_INV ) ;
}
}
}
2019-09-03 11:23:44 +01:00
}
inline bool TuyaFuncIdValid ( uint8_t fnId ) {
2019-09-03 11:57:13 +01:00
return ( fnId > = TUYA_MCU_FUNC_SWT1 & & fnId < = TUYA_MCU_FUNC_SWT4 ) | |
( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) | |
2019-09-03 11:23:44 +01:00
fnId = = TUYA_MCU_FUNC_DIMMER | |
2019-09-03 14:04:07 +01:00
( fnId > = TUYA_MCU_FUNC_POWER & & fnId < = TUYA_MCU_FUNC_VOLTAGE ) | |
( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) ;
2019-09-03 11:23:44 +01:00
}
uint8_t TuyaGetFuncId ( uint8_t dpid ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( Settings . tuya_fnid_map [ i ] . dpid = = dpid ) {
return Settings . tuya_fnid_map [ i ] . fnid ;
}
}
return TUYA_MCU_FUNC_NONE ;
}
uint8_t TuyaGetDpId ( uint8_t fnId ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( Settings . tuya_fnid_map [ i ] . fnid = = fnId ) {
return Settings . tuya_fnid_map [ i ] . dpid ;
}
}
return 0 ;
}
2019-07-02 14:49:08 +01:00
void TuyaSendCmd ( uint8_t cmd , uint8_t payload [ ] = nullptr , uint16_t payload_len = 0 )
{
2018-11-04 02:40:14 +00:00
uint8_t checksum = ( 0xFF + cmd + ( payload_len > > 8 ) + ( payload_len & 0xFF ) ) ;
2018-11-04 05:40:55 +00:00
TuyaSerial - > write ( 0x55 ) ; // Tuya header 55AA
TuyaSerial - > write ( 0xAA ) ;
2018-11-04 05:56:36 +00:00
TuyaSerial - > write ( ( uint8_t ) 0x00 ) ; // version 00
2018-11-04 05:40:55 +00:00
TuyaSerial - > write ( cmd ) ; // Tuya command
TuyaSerial - > write ( payload_len > > 8 ) ; // following data length (Hi)
TuyaSerial - > write ( payload_len & 0xFF ) ; // following data length (Lo)
2019-07-02 14:49:08 +01:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " TYA: Send \" 55aa00%02x%02x%02x " ) , cmd , payload_len > > 8 , payload_len & 0xFF ) ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < payload_len ; + + i ) {
2018-11-03 21:34:29 +00:00
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 ) ;
}
2019-07-02 14:49:08 +01:00
void TuyaSendState ( uint8_t id , uint8_t type , uint8_t * value )
{
2018-11-03 21:34:29 +00:00
uint16_t payload_len = 4 ;
uint8_t payload_buffer [ 8 ] ;
payload_buffer [ 0 ] = id ;
payload_buffer [ 1 ] = type ;
2019-07-02 14:49:08 +01:00
switch ( type ) {
2018-11-03 21:34:29 +00:00
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 ) ;
}
2019-07-02 14:49:08 +01:00
void TuyaSendBool ( uint8_t id , bool value )
{
TuyaSendState ( id , TUYA_TYPE_BOOL , ( uint8_t * ) & value ) ;
2018-11-03 21:34:29 +00:00
}
2019-07-02 14:49:08 +01:00
void TuyaSendValue ( uint8_t id , uint32_t value )
{
TuyaSendState ( id , TUYA_TYPE_VALUE , ( uint8_t * ) ( & value ) ) ;
2018-11-03 21:34:29 +00:00
}
2019-01-28 13:08:33 +00:00
bool TuyaSetPower ( void )
2018-10-18 12:01:31 +01:00
{
2019-01-28 13:08:33 +00:00
bool status = false ;
2018-10-18 12:01:31 +01:00
uint8_t rpower = XdrvMailbox . index ;
int16_t source = XdrvMailbox . payload ;
2018-10-27 03:11:30 +01:00
if ( source ! = SRC_SWITCH & & TuyaSerial ) { // ignore to prevent loop from pushing state from faceplate interaction
2019-09-03 14:04:07 +01:00
TuyaSendBool ( active_device , bitRead ( rpower , active_device - 1 ) ^ bitRead ( rel_inverted , active_device - 1 ) ) ;
2018-10-18 12:01:31 +01:00
status = true ;
}
return status ;
}
2019-01-28 13:08:33 +00:00
bool TuyaSetChannels ( void )
2018-12-17 16:34:55 +00:00
{
LightSerialDuty ( ( ( uint8_t * ) XdrvMailbox . data ) [ 0 ] ) ;
2019-08-21 15:31:31 +01:00
delay ( 20 ) ; // Hack when power is off and dimmer is set then both commands go too soon to Serial out.
2018-12-17 16:34:55 +00:00
return true ;
}
2018-10-18 12:01:31 +01:00
void LightSerialDuty ( uint8_t duty )
{
2019-09-03 11:23:44 +01:00
uint8_t dpid = TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) ;
if ( duty > 0 & & ! Tuya . ignore_dim & & TuyaSerial & & dpid > 0 ) {
2019-08-14 14:52:37 +01:00
if ( Settings . flag3 . tuya_dimmer_min_limit ) { // Enable dimming limit SetOption69: Enabled by default
if ( duty < 25 ) { duty = 25 ; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
}
2019-09-05 11:32:39 +01:00
duty = changeUIntScale ( duty , 0 , 255 , 0 , Settings . param [ P_TUYA_DIMMER_MAX ] ) ;
if ( Tuya . new_dim ! = duty ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Send dim value=%d (id=%d) " ) , duty , dpid ) ;
TuyaSendValue ( dpid , duty ) ;
2019-07-02 14:49:08 +01:00
}
2019-09-03 11:23:44 +01:00
} else if ( dpid > 0 ) {
2019-08-17 14:00:57 +01:00
Tuya . ignore_dim = false ; // reset flag
2019-09-05 11:32:39 +01:00
duty = changeUIntScale ( duty , 0 , 255 , 0 , Settings . param [ P_TUYA_DIMMER_MAX ] ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Send dim skipped value=%d " ) , duty ) ; // due to 0 or already set
2019-09-03 11:23:44 +01:00
} else {
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Cannot set dimmer. Dimmer Id unknown " ) ) ; //
2018-10-18 12:01:31 +01:00
}
}
2019-07-02 14:49:08 +01:00
void TuyaRequestState ( void )
{
if ( TuyaSerial ) {
2018-11-04 05:56:36 +00:00
// Get current status of MCU
2019-07-02 14:49:08 +01:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Read MCU state " ) ) ;
2018-11-04 05:56:36 +00:00
TuyaSendCmd ( TUYA_CMD_QUERY_STATE ) ;
}
}
2018-11-14 13:32:09 +00:00
void TuyaResetWifi ( void )
2018-11-04 05:56:36 +00:00
{
if ( ! Settings . flag . button_restrict ) {
char scmnd [ 20 ] ;
snprintf_P ( scmnd , sizeof ( scmnd ) , D_CMND_WIFICONFIG " %d " , 2 ) ;
ExecuteCommand ( scmnd , SRC_BUTTON ) ;
}
}
2018-11-14 13:32:09 +00:00
void TuyaPacketProcess ( void )
2018-10-18 12:01:31 +01:00
{
char scmnd [ 20 ] ;
2019-09-03 11:23:44 +01:00
uint8_t fnId = TUYA_MCU_FUNC_NONE ;
2018-10-18 12:01:31 +01:00
2019-08-17 14:00:57 +01:00
switch ( Tuya . buffer [ 3 ] ) {
2018-10-18 12:01:31 +01:00
2018-11-07 09:30:03 +00:00
case TUYA_CMD_HEARTBEAT :
2018-11-03 21:34:29 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Heartbeat " ) ) ;
2019-08-17 14:00:57 +01:00
if ( Tuya . buffer [ 6 ] = = 0 ) {
2018-11-03 21:34:29 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Detected MCU restart " ) ) ;
2019-08-17 14:00:57 +01:00
Tuya . wifi_state = - 2 ;
2018-11-03 21:34:29 +00:00
}
break ;
2018-10-18 12:01:31 +01:00
2018-11-03 21:34:29 +00:00
case TUYA_CMD_STATE :
2019-09-03 11:23:44 +01:00
fnId = TuyaGetFuncId ( Tuya . buffer [ 6 ] ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: FnId=%d is set for dpId=%d " ) , fnId , Tuya . buffer [ 6 ] ) ;
// if (TuyaFuncIdValid(fnId)) {
if ( Tuya . buffer [ 5 ] = = 5 ) { // on/off packet
if ( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) {
2019-09-03 11:57:13 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Relay-%d --> MCU State: %s Current State:%s " ) , fnId - TUYA_MCU_FUNC_REL1 + 1 , Tuya . buffer [ 10 ] ? " On " : " Off " , bitRead ( power , fnId - TUYA_MCU_FUNC_REL1 ) ? " On " : " Off " ) ;
2019-09-03 11:23:44 +01:00
if ( ( power | | Settings . light_dimmer > 0 ) & & ( Tuya . buffer [ 10 ] ! = bitRead ( power , fnId - TUYA_MCU_FUNC_REL1 ) ) ) {
ExecuteCommandPower ( fnId - TUYA_MCU_FUNC_REL1 + 1 , Tuya . buffer [ 10 ] , SRC_SWITCH ) ; // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
2019-08-14 13:53:46 +01:00
}
2019-09-03 14:04:07 +01:00
} else if ( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s " ) , fnId - TUYA_MCU_FUNC_REL1_INV + 1 , Tuya . buffer [ 10 ] ? " Off " : " On " , bitRead ( power , fnId - TUYA_MCU_FUNC_REL1_INV ) ^ 1 ? " Off " : " On " ) ;
if ( Tuya . buffer [ 10 ] ! = bitRead ( power , fnId - TUYA_MCU_FUNC_REL1_INV ) ^ 1 ) {
ExecuteCommandPower ( fnId - TUYA_MCU_FUNC_REL1_INV + 1 , Tuya . buffer [ 10 ] ^ 1 , SRC_SWITCH ) ; // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
}
2019-09-03 11:57:13 +01:00
} else if ( fnId > = TUYA_MCU_FUNC_SWT1 & & fnId < = TUYA_MCU_FUNC_SWT4 ) {
2019-09-03 14:00:51 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Switch-%d --> MCU State: %d Current State:%d " ) , fnId - TUYA_MCU_FUNC_SWT1 + 1 , Tuya . buffer [ 10 ] , SwitchGetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 ) ) ;
2019-09-03 11:57:13 +01:00
2019-09-03 14:00:51 +01:00
if ( SwitchGetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 ) ! = Tuya . buffer [ 10 ] ) {
SwitchSetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 , Tuya . buffer [ 10 ] ) ;
2019-09-03 11:57:13 +01:00
SwitchHandler ( 1 ) ;
}
2019-05-22 07:28:38 +01:00
}
2019-09-03 11:23:44 +01:00
2018-11-03 21:34:29 +00:00
}
2019-09-03 11:23:44 +01:00
else if ( Tuya . buffer [ 5 ] = = 8 ) { // Long value packet
if ( fnId = = TUYA_MCU_FUNC_DIMMER ) {
2019-09-05 11:32:39 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Dim State=%d " ) , Tuya . buffer [ 13 ] ) ;
Tuya . new_dim = changeUIntScale ( ( uint8_t ) Tuya . buffer [ 13 ] , 0 , Settings . param [ P_TUYA_DIMMER_MAX ] , 0 , 100 ) ;
if ( ( power | | Settings . flag3 . tuya_apply_o20 ) & & ( Tuya . new_dim > 0 ) & & ( abs ( Tuya . new_dim - Settings . light_dimmer ) > 1 ) ) {
Tuya . ignore_dim = true ;
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_DIMMER " %d " ) , Tuya . new_dim ) ;
ExecuteCommand ( scmnd , SRC_SWITCH ) ;
}
2019-09-03 11:23:44 +01:00
}
2019-08-27 10:50:34 +01:00
2019-09-03 11:23:44 +01:00
# ifdef USE_ENERGY_SENSOR
else if ( fnId = = TUYA_MCU_FUNC_VOLTAGE ) {
Energy . voltage = ( float ) ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) / 10 ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Voltage=%d " ) , Tuya . buffer [ 6 ] , ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) ) ;
} else if ( fnId = = TUYA_MCU_FUNC_CURRENT ) {
Energy . current = ( float ) ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) / 1000 ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Current=%d " ) , Tuya . buffer [ 6 ] , ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) ) ;
} else if ( fnId = = TUYA_MCU_FUNC_POWER ) {
Energy . active_power = ( float ) ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) / 10 ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Active_Power=%d " ) , Tuya . buffer [ 6 ] , ( Tuya . buffer [ 12 ] < < 8 | Tuya . buffer [ 13 ] ) ) ;
if ( Tuya . lastPowerCheckTime ! = 0 & & Energy . active_power > 0 ) {
Energy . kWhtoday + = ( float ) Energy . active_power * ( Rtc . utc_time - Tuya . lastPowerCheckTime ) / 36 ;
EnergyUpdateToday ( ) ;
}
Tuya . lastPowerCheckTime = Rtc . utc_time ;
2019-08-27 10:50:34 +01:00
}
2019-09-03 11:23:44 +01:00
# endif // USE_ENERGY_SENSOR
2019-08-27 14:40:43 +01:00
2019-09-03 11:23:44 +01:00
}
// } else {
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Unknown FnId=%s for dpId=%s"), fnId, Tuya.buffer[6]);
// }
2018-11-03 21:34:29 +00:00
break ;
case TUYA_CMD_WIFI_RESET :
case TUYA_CMD_WIFI_SELECT :
2018-11-10 17:08:31 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX WiFi Reset " ) ) ;
2018-11-03 21:34:29 +00:00
TuyaResetWifi ( ) ;
break ;
case TUYA_CMD_WIFI_STATE :
2018-11-10 17:08:31 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX WiFi LED set ACK " ) ) ;
2019-08-17 14:00:57 +01:00
Tuya . wifi_state = WifiState ( ) ;
2018-11-03 21:34:29 +00:00
break ;
case TUYA_CMD_MCU_CONF :
2019-09-03 11:23:44 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX MCU configuration Mode=%d " ) , Tuya . buffer [ 5 ] ) ;
2018-11-03 21:34:29 +00:00
2019-09-03 11:23:44 +01:00
if ( Tuya . buffer [ 5 ] = = 2 ) { // Processing by ESP module mode
2019-08-17 14:00:57 +01:00
uint8_t led1_gpio = Tuya . buffer [ 6 ] ;
uint8_t key1_gpio = Tuya . buffer [ 7 ] ;
2019-01-28 13:08:33 +00:00
bool key1_set = false ;
bool led1_set = false ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < sizeof ( Settings . my_gp ) ; i + + ) {
2018-11-03 21:34:29 +00:00
if ( Settings . my_gp . io [ i ] = = GPIO_LED1 ) led1_set = true ;
else if ( Settings . my_gp . io [ i ] = = GPIO_KEY1 ) key1_set = true ;
}
2019-07-02 14:49:08 +01:00
if ( ! Settings . my_gp . io [ led1_gpio ] & & ! led1_set ) {
2018-11-03 21:34:29 +00:00
Settings . my_gp . io [ led1_gpio ] = GPIO_LED1 ;
restart_flag = 2 ;
}
2019-07-02 14:49:08 +01:00
if ( ! Settings . my_gp . io [ key1_gpio ] & & ! key1_set ) {
2018-11-03 21:34:29 +00:00
Settings . my_gp . io [ key1_gpio ] = GPIO_KEY1 ;
restart_flag = 2 ;
}
2018-11-01 01:55:16 +00:00
}
2018-11-03 21:34:29 +00:00
TuyaRequestState ( ) ;
break ;
2018-11-04 02:40:14 +00:00
default :
2018-11-10 17:08:31 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX unknown command " ) ) ;
2018-11-01 01:55:16 +00:00
}
2018-10-18 12:01:31 +01:00
}
2018-11-04 05:56:36 +00:00
/*********************************************************************************************\
* API Functions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool TuyaModuleSelected ( void )
2018-11-04 05:56:36 +00:00
{
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 ;
}
2019-09-03 11:23:44 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) = = 0 & & TUYA_DIMMER_ID > 0 ) {
TuyaAddMcuFunc ( TUYA_MCU_FUNC_DIMMER , TUYA_DIMMER_ID ) ;
}
2019-09-03 14:04:07 +01:00
bool relaySet = false ;
devices_present - - ;
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
if ( ( Settings . tuya_fnid_map [ i ] . fnid > = TUYA_MCU_FUNC_REL1 & & Settings . tuya_fnid_map [ i ] . fnid < = TUYA_MCU_FUNC_REL8 ) | |
( Settings . tuya_fnid_map [ i ] . fnid > = TUYA_MCU_FUNC_REL1_INV & & Settings . tuya_fnid_map [ i ] . fnid < = TUYA_MCU_FUNC_REL8_INV ) ) {
relaySet = true ;
devices_present + + ;
}
}
if ( ! relaySet ) {
2019-09-03 11:23:44 +01:00
TuyaAddMcuFunc ( TUYA_MCU_FUNC_REL1 , 1 ) ;
2019-09-03 14:04:07 +01:00
devices_present + + ;
2019-09-03 11:23:44 +01:00
SettingsSaveAll ( ) ;
}
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) ! = 0 ) {
2019-08-20 11:13:37 +01:00
light_type = LT_SERIAL1 ;
} else {
light_type = LT_BASIC ;
}
2019-09-03 14:04:07 +01:00
UpdateDevices ( ) ;
2018-11-04 05:56:36 +00:00
return true ;
}
2018-11-14 13:32:09 +00:00
void TuyaInit ( void )
2018-11-04 05:56:36 +00:00
{
2019-08-17 14:00:57 +01:00
Tuya . buffer = ( char * ) ( malloc ( TUYA_BUFFER_SIZE ) ) ;
if ( Tuya . buffer ! = nullptr ) {
2018-11-27 10:55:54 +00:00
TuyaSerial = new TasmotaSerial ( pin [ GPIO_TUYA_RX ] , pin [ GPIO_TUYA_TX ] , 2 ) ;
if ( TuyaSerial - > begin ( 9600 ) ) {
if ( TuyaSerial - > hardwareSerial ( ) ) { ClaimSerial ( ) ; }
// Get MCU Configuration
2019-03-08 14:15:42 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Request MCU configuration " ) ) ;
2018-11-27 10:55:54 +00:00
TuyaSendCmd ( TUYA_CMD_MCU_CONF ) ;
}
2018-11-04 05:56:36 +00:00
}
2019-08-17 14:00:57 +01:00
Tuya . heartbeat_timer = 0 ; // init heartbeat timer when dimmer init is done
2018-11-04 05:56:36 +00:00
}
2018-11-14 13:32:09 +00:00
void TuyaSerialInput ( void )
2018-10-18 12:01:31 +01:00
{
2018-10-27 10:37:42 +01:00
while ( TuyaSerial - > available ( ) ) {
2018-10-18 12:01:31 +01:00
yield ( ) ;
2019-01-28 13:08:33 +00:00
uint8_t serial_in_byte = TuyaSerial - > read ( ) ;
2018-10-18 12:01:31 +01:00
if ( serial_in_byte = = 0x55 ) { // Start TUYA Packet
2019-08-17 14:00:57 +01:00
Tuya . cmd_status = 1 ;
Tuya . buffer [ Tuya . byte_counter + + ] = serial_in_byte ;
Tuya . cmd_checksum + = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
else if ( Tuya . cmd_status = = 1 & & serial_in_byte = = 0xAA ) { // Only packtes with header 0x55AA are valid
Tuya . cmd_status = 2 ;
2018-10-18 12:01:31 +01:00
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . buffer [ Tuya . byte_counter + + ] = 0x55 ;
Tuya . buffer [ Tuya . byte_counter + + ] = 0xAA ;
Tuya . cmd_checksum = 0xFF ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
else if ( Tuya . cmd_status = = 2 ) {
if ( Tuya . byte_counter = = 5 ) { // Get length of data
Tuya . cmd_status = 3 ;
Tuya . data_len = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
Tuya . cmd_checksum + = serial_in_byte ;
Tuya . buffer [ Tuya . byte_counter + + ] = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
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 ;
2018-10-18 12:01:31 +01:00
2018-11-10 17:08:31 +00:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " TYA: RX Packet: \" " ) ) ;
2019-08-17 14:00:57 +01:00
for ( uint32_t i = 0 ; i < Tuya . byte_counter ; i + + ) {
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " %s%02x " ) , log_data , Tuya . buffer [ i ] ) ;
2018-10-18 12:01:31 +01:00
}
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " %s \" " ) , log_data ) ;
AddLog ( LOG_LEVEL_DEBUG ) ;
TuyaPacketProcess ( ) ;
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . cmd_status = 0 ;
Tuya . cmd_checksum = 0 ;
Tuya . data_len = 0 ;
2018-10-18 12:01:31 +01:00
} // read additional packets from TUYA
2019-08-17 14:00:57 +01:00
else if ( Tuya . byte_counter < TUYA_BUFFER_SIZE - 1 ) { // add char to string if it still fits
Tuya . buffer [ Tuya . byte_counter + + ] = serial_in_byte ;
Tuya . cmd_checksum + = serial_in_byte ;
2018-10-18 12:01:31 +01:00
} else {
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . cmd_status = 0 ;
Tuya . cmd_checksum = 0 ;
Tuya . data_len = 0 ;
2018-10-18 12:01:31 +01:00
}
}
}
2019-01-28 13:08:33 +00:00
bool TuyaButtonPressed ( void )
2018-10-18 12:01:31 +01:00
{
2019-08-17 15:19:58 +01:00
if ( ! XdrvMailbox . index & & ( ( PRESSED = = XdrvMailbox . payload ) & & ( NOT_PRESSED = = Button . last_state [ XdrvMailbox . index ] ) ) ) {
2019-03-08 14:15:42 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Reset GPIO triggered " ) ) ;
2018-11-04 05:56:36 +00:00
TuyaResetWifi ( ) ;
return true ; // Reset GPIO served here
2018-10-27 10:37:42 +01:00
}
2018-11-04 05:56:36 +00:00
return false ; // Don't serve other buttons
2018-10-18 12:01:31 +01:00
}
2019-07-02 14:49:08 +01:00
void TuyaSetWifiLed ( void )
{
uint8_t wifi_state = 0x02 ;
switch ( WifiState ( ) ) {
case WIFI_SMARTCONFIG :
wifi_state = 0x00 ;
break ;
case WIFI_MANAGER :
case WIFI_WPSCONFIG :
wifi_state = 0x01 ;
break ;
case WIFI_RESTART :
wifi_state = 0x03 ;
break ;
}
2018-10-29 22:35:00 +00:00
2019-07-02 14:49:08 +01:00
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Set WiFi LED %d (%d) " ) , wifi_state , WifiState ( ) ) ;
2018-10-29 00:10:57 +00:00
2019-07-02 14:49:08 +01:00
TuyaSendCmd ( TUYA_CMD_WIFI_STATE , & wifi_state , 1 ) ;
2018-10-29 00:10:57 +00:00
}
2019-08-28 10:08:23 +01:00
# ifdef USE_ENERGY_SENSOR
/*********************************************************************************************\
* Energy Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int Xnrg08 ( uint8_t function )
{
int result = 0 ;
if ( TUYA_DIMMER = = my_module_type ) {
if ( FUNC_PRE_INIT = = function ) {
if ( ! energy_flg ) {
2019-09-03 11:23:44 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_POWER ) ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_CURRENT ) = = 0 ) {
2019-08-28 10:08:23 +01:00
Energy . current_available = false ;
}
2019-09-03 11:23:44 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_VOLTAGE ) = = 0 ) {
2019-08-28 10:08:23 +01:00
Energy . voltage_available = false ;
}
energy_flg = XNRG_08 ;
}
}
}
}
return result ;
}
# endif // USE_ENERGY_SENSOR
2018-10-18 12:01:31 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool Xdrv16 ( uint8_t function )
2018-10-18 12:01:31 +01:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-10-18 12:01:31 +01:00
2019-02-11 18:21:49 +00:00
if ( TUYA_DIMMER = = my_module_type ) {
2018-10-18 12:01:31 +01:00
switch ( function ) {
2019-03-30 12:03:45 +00:00
case FUNC_LOOP :
if ( TuyaSerial ) { TuyaSerialInput ( ) ; }
break ;
2018-10-18 12:01:31 +01:00
case FUNC_MODULE_INIT :
result = TuyaModuleSelected ( ) ;
break ;
case FUNC_INIT :
TuyaInit ( ) ;
break ;
2018-10-19 11:53:22 +01:00
case FUNC_SET_DEVICE_POWER :
2018-10-18 12:01:31 +01:00
result = TuyaSetPower ( ) ;
break ;
2018-10-19 11:53:22 +01:00
case FUNC_BUTTON_PRESSED :
result = TuyaButtonPressed ( ) ;
break ;
2018-10-29 00:10:57 +00:00
case FUNC_EVERY_SECOND :
2019-08-17 14:00:57 +01:00
if ( TuyaSerial & & Tuya . wifi_state ! = WifiState ( ) ) { TuyaSetWifiLed ( ) ; }
Tuya . heartbeat_timer + + ;
if ( Tuya . heartbeat_timer > 10 ) {
Tuya . heartbeat_timer = 0 ;
2019-07-02 14:49:08 +01:00
TuyaSendCmd ( TUYA_CMD_HEARTBEAT ) ;
2019-05-19 00:13:57 +01:00
}
2018-10-29 00:27:45 +00:00
break ;
2018-12-17 16:34:55 +00:00
case FUNC_SET_CHANNELS :
result = TuyaSetChannels ( ) ;
break ;
2019-09-03 11:23:44 +01:00
case FUNC_COMMAND :
result = DecodeCommand ( kTuyaCommand , TuyaCommand ) ;
break ;
2018-10-18 12:01:31 +01:00
}
}
return result ;
}
2019-09-03 12:23:05 +01:00
# endif // USE_TUYA_MCU
2019-06-16 15:43:23 +01:00
# endif // USE_LIGHT