2019-10-20 19:12:05 +01:00
/*
2019-10-26 21:58:04 +01:00
xdrv_31_tasmota_slave . ino - Support for external microcontroller slave on serial
2019-10-20 19:12:05 +01:00
2019-12-31 13:23:34 +00:00
Copyright ( C ) 2020 Andre Thomas and Theo Arends
2019-10-20 19:12:05 +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-10-26 21:58:04 +01:00
# ifdef USE_TASMOTA_SLAVE
2019-10-21 11:25:22 +01:00
/*********************************************************************************************\
2019-10-26 21:58:04 +01:00
* Tasmota slave
2019-10-21 11:25:22 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-10-20 19:12:05 +01:00
2019-10-21 11:25:22 +01:00
# define XDRV_31 31
2019-10-20 19:12:05 +01:00
# define CONST_STK_CRC_EOP 0x20
# define CMND_STK_GET_SYNC 0x30
# define CMND_STK_SET_DEVICE 0x42
# define CMND_STK_SET_DEVICE_EXT 0x45
# define CMND_STK_ENTER_PROGMODE 0x50
# define CMND_STK_LEAVE_PROGMODE 0x51
# define CMND_STK_LOAD_ADDRESS 0x55
# define CMND_STK_PROG_PAGE 0x64
2019-10-26 21:58:04 +01:00
/*************************************************\
* Tasmota Slave Specific Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define CMND_START 0xFC
# define CMND_END 0xFD
# define CMND_FEATURES 0x01
# define CMND_JSON 0x02
2019-11-02 11:14:38 +00:00
# define CMND_FUNC_EVERY_SECOND 0x03
# define CMND_FUNC_EVERY_100_MSECOND 0x04
# define CMND_SLAVE_SEND 0x05
# define CMND_PUBLISH_TELE 0x06
2019-11-29 19:20:33 +00:00
# define CMND_EXECUTE_CMND 0x07
2019-10-26 21:58:04 +01:00
# define PARAM_DATA_START 0xFE
# define PARAM_DATA_END 0xFF
2019-10-21 11:25:22 +01:00
# include <TasmotaSerial.h>
2019-10-20 19:12:05 +01:00
2019-10-26 21:58:04 +01:00
/*
* Embedding class in here since its rather specific to Arduino bootloader
*/
class SimpleHexParse {
public :
SimpleHexParse ( void ) ;
uint8_t parseLine ( char * hexline ) ;
uint8_t ptr_l = 0 ;
uint8_t ptr_h = 0 ;
bool PageIsReady = false ;
bool firstrun = true ;
bool EndOfFile = false ;
uint8_t FlashPage [ 128 ] ;
uint8_t FlashPageIdx = 0 ;
uint8_t layoverBuffer [ 16 ] ;
uint8_t layoverIdx = 0 ;
uint8_t getByte ( char * hexline , uint8_t idx ) ;
} ;
SimpleHexParse : : SimpleHexParse ( void )
{
}
uint8_t SimpleHexParse : : parseLine ( char * hexline )
{
if ( layoverIdx ) {
memcpy ( & FlashPage [ 0 ] , & layoverBuffer [ 0 ] , layoverIdx ) ;
FlashPageIdx = layoverIdx ;
layoverIdx = 0 ;
}
uint8_t len = getByte ( hexline , 1 ) ;
uint8_t addr_h = getByte ( hexline , 2 ) ;
uint8_t addr_l = getByte ( hexline , 3 ) ;
uint8_t rectype = getByte ( hexline , 4 ) ;
for ( uint8_t idx = 0 ; idx < len ; idx + + ) {
if ( FlashPageIdx < 128 ) {
FlashPage [ FlashPageIdx ] = getByte ( hexline , idx + 5 ) ;
FlashPageIdx + + ;
} else { // We have layover bytes
layoverBuffer [ layoverIdx ] = getByte ( hexline , idx + 5 ) ;
layoverIdx + + ;
}
}
if ( 1 = = rectype ) {
EndOfFile = true ;
while ( FlashPageIdx < 128 ) {
FlashPage [ FlashPageIdx ] = 0xFF ;
FlashPageIdx + + ;
}
}
if ( FlashPageIdx = = 128 ) {
if ( firstrun ) {
firstrun = false ;
} else {
ptr_l + = 0x40 ;
if ( ptr_l = = 0 ) {
ptr_l = 0 ;
ptr_h + + ;
}
}
firstrun = false ;
PageIsReady = true ;
}
return 0 ;
}
uint8_t SimpleHexParse : : getByte ( char * hexline , uint8_t idx )
{
char buff [ 3 ] ;
buff [ 3 ] = ' \0 ' ;
memcpy ( & buff , & hexline [ ( idx * 2 ) - 1 ] , 2 ) ;
return strtol ( buff , 0 , 16 ) ;
}
/*
* End of embedded class SimpleHexParse
*/
struct TSLAVE {
2019-10-21 11:25:22 +01:00
uint32_t spi_hex_size = 0 ;
uint32_t spi_sector_counter = 0 ;
uint8_t spi_sector_cursor = 0 ;
2019-10-27 18:10:25 +00:00
uint8_t inverted = LOW ;
2019-10-21 11:25:22 +01:00
bool type = false ;
bool flashing = false ;
2019-10-26 21:58:04 +01:00
bool SerialEnabled = false ;
uint8_t waitstate = 0 ; // We use this so that features detection does not slow down other stuff on startup
2019-11-29 19:20:33 +00:00
bool unsupported = false ;
2019-10-26 21:58:04 +01:00
} TSlave ;
typedef union {
2019-11-02 11:14:38 +00:00
uint32_t data ;
2019-10-26 21:58:04 +01:00
struct {
2019-11-02 11:14:38 +00:00
uint32_t func_json_append : 1 ; // Slave supports providing a JSON for TELEPERIOD
uint32_t func_every_second : 1 ; // Slave supports receiving a FUNC_EVERY_SECOND callback with no response
uint32_t func_every_100_msecond : 1 ; // Slave supports receiving a FUNC_EVERY_100_MSECOND callback with no response
uint32_t func_slave_send : 1 ; // Slave supports receiving commands with "slave send xxx"
uint32_t spare4 : 1 ;
uint32_t spare5 : 1 ;
uint32_t spare6 : 1 ;
uint32_t spare7 : 1 ;
uint32_t spare8 : 1 ;
uint32_t spare9 : 1 ;
uint32_t spare10 : 1 ;
uint32_t spare11 : 1 ;
uint32_t spare12 : 1 ;
uint32_t spare13 : 1 ;
uint32_t spare14 : 1 ;
uint32_t spare15 : 1 ;
uint32_t spare16 : 1 ;
uint32_t spare17 : 1 ;
uint32_t spare18 : 1 ;
uint32_t spare19 : 1 ;
uint32_t spare20 : 1 ;
uint32_t spare21 : 1 ;
uint32_t spare22 : 1 ;
uint32_t spare23 : 1 ;
uint32_t spare24 : 1 ;
uint32_t spare25 : 1 ;
uint32_t spare26 : 1 ;
uint32_t spare27 : 1 ;
uint32_t spare28 : 1 ;
uint32_t spare29 : 1 ;
uint32_t spare30 : 1 ;
uint32_t spare31 : 1 ;
2019-10-26 21:58:04 +01:00
} ;
} TSlaveFeatureCfg ;
2019-10-20 19:12:05 +01:00
2019-10-26 21:58:04 +01:00
/*
* The structure below must remain 4 byte aligned to be compatible with
* Tasmota as master
*/
2019-11-02 11:14:38 +00:00
struct TSLAVE_FEATURES {
2019-10-26 21:58:04 +01:00
uint32_t features_version ;
TSlaveFeatureCfg features ;
} TSlaveSettings ;
2019-11-02 11:14:38 +00:00
struct TSLAVE_COMMAND {
2019-10-26 21:58:04 +01:00
uint8_t command ;
uint8_t parameter ;
uint8_t unused2 ;
uint8_t unused3 ;
2019-11-02 11:14:38 +00:00
} TSlaveCommand ;
2019-10-26 21:58:04 +01:00
TasmotaSerial * TasmotaSlave_Serial ;
uint32_t TasmotaSlave_FlashStart ( void )
2019-10-21 11:25:22 +01:00
{
return ( ESP . getSketchSize ( ) / SPI_FLASH_SEC_SIZE ) + 2 ; // Stay on the safe side
}
2019-10-20 19:12:05 +01:00
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_UpdateInit ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
TSlave . spi_hex_size = 0 ;
TSlave . spi_sector_counter = TasmotaSlave_FlashStart ( ) ; // Reset the pre-defined write address where firmware will temporarily be stored
TSlave . spi_sector_cursor = 0 ;
2019-10-20 19:12:05 +01:00
return 0 ;
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_Reset ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
if ( TSlave . SerialEnabled ) {
2020-04-26 16:33:27 +01:00
digitalWrite ( Pin ( GPIO_TASMOTASLAVE_RST ) , ! TSlave . inverted ) ;
2019-10-20 19:12:05 +01:00
delay ( 1 ) ;
2020-04-26 16:33:27 +01:00
digitalWrite ( Pin ( GPIO_TASMOTASLAVE_RST ) , TSlave . inverted ) ;
2019-10-27 18:10:25 +00:00
delay ( 1 ) ;
2020-04-26 16:33:27 +01:00
digitalWrite ( Pin ( GPIO_TASMOTASLAVE_RST ) , ! TSlave . inverted ) ;
2019-10-20 19:12:05 +01:00
delay ( 5 ) ;
}
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_waitForSerialData ( int dataCount , int timeout )
2019-10-21 11:25:22 +01:00
{
2019-10-20 19:12:05 +01:00
int timer = 0 ;
while ( timer < timeout ) {
2019-10-26 21:58:04 +01:00
if ( TasmotaSlave_Serial - > available ( ) > = dataCount ) {
2019-10-20 19:12:05 +01:00
return 1 ;
}
delay ( 1 ) ;
timer + + ;
}
return 0 ;
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_sendBytes ( uint8_t * bytes , int count )
2019-10-21 11:25:22 +01:00
{
2019-10-26 21:58:04 +01:00
TasmotaSlave_Serial - > write ( bytes , count ) ;
2019-10-27 21:07:20 +00:00
TasmotaSlave_waitForSerialData ( 2 , 250 ) ;
2019-10-26 21:58:04 +01:00
uint8_t sync = TasmotaSlave_Serial - > read ( ) ;
uint8_t ok = TasmotaSlave_Serial - > read ( ) ;
if ( ( sync = = 0x14 ) & & ( ok = = 0x10 ) ) {
2019-10-20 19:12:05 +01:00
return 1 ;
}
return 0 ;
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_execCmd ( uint8_t cmd )
2019-10-21 11:25:22 +01:00
{
uint8_t bytes [ ] = { cmd , CONST_STK_CRC_EOP } ;
2019-10-26 21:58:04 +01:00
return TasmotaSlave_sendBytes ( bytes , 2 ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_execParam ( uint8_t cmd , uint8_t * params , int count )
2019-10-21 11:25:22 +01:00
{
uint8_t bytes [ 32 ] ;
2019-10-20 19:12:05 +01:00
bytes [ 0 ] = cmd ;
int i = 0 ;
while ( i < count ) {
bytes [ i + 1 ] = params [ i ] ;
i + + ;
}
bytes [ i + 1 ] = CONST_STK_CRC_EOP ;
2019-10-26 21:58:04 +01:00
return TasmotaSlave_sendBytes ( bytes , i + 2 ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_exitProgMode ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
return TasmotaSlave_execCmd ( CMND_STK_LEAVE_PROGMODE ) ; // Exit programming mode
2019-10-20 19:12:05 +01:00
}
2019-10-27 21:07:20 +00:00
uint8_t TasmotaSlave_SetupFlash ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
uint8_t ProgParams [ ] = { 0x86 , 0x00 , 0x00 , 0x01 , 0x01 , 0x01 , 0x01 , 0x03 , 0xff , 0xff , 0xff , 0xff , 0x00 , 0x80 , 0x04 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 } ;
uint8_t ExtProgParams [ ] = { 0x05 , 0x04 , 0xd7 , 0xc2 , 0x00 } ;
TasmotaSlave_Serial - > begin ( USE_TASMOTA_SLAVE_FLASH_SPEED ) ;
if ( TasmotaSlave_Serial - > hardwareSerial ( ) ) {
2019-10-20 19:12:05 +01:00
ClaimSerial ( ) ;
}
2019-10-27 21:07:20 +00:00
2019-10-26 21:58:04 +01:00
TasmotaSlave_Reset ( ) ;
2019-10-27 21:07:20 +00:00
uint8_t timeout = 0 ;
uint8_t no_error = 0 ;
while ( 50 > timeout ) {
2019-10-26 21:58:04 +01:00
if ( TasmotaSlave_execCmd ( CMND_STK_GET_SYNC ) ) {
2019-10-27 21:07:20 +00:00
timeout = 200 ;
no_error = 1 ;
2019-10-26 21:58:04 +01:00
}
2019-10-27 21:07:20 +00:00
timeout + + ;
2019-10-26 21:58:04 +01:00
delay ( 1 ) ;
}
if ( no_error ) {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Found bootloader " ) ) ;
} else {
2019-10-27 21:07:20 +00:00
no_error = 0 ;
2019-10-26 21:58:04 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Bootloader could not be found " ) ) ;
}
if ( no_error ) {
if ( TasmotaSlave_execParam ( CMND_STK_SET_DEVICE , ProgParams , sizeof ( ProgParams ) ) ) {
} else {
2019-10-27 21:07:20 +00:00
no_error = 0 ;
2019-10-26 21:58:04 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Could not configure device for programming (1) " ) ) ;
}
}
if ( no_error ) {
if ( TasmotaSlave_execParam ( CMND_STK_SET_DEVICE_EXT , ExtProgParams , sizeof ( ExtProgParams ) ) ) {
} else {
2019-10-27 21:07:20 +00:00
no_error = 0 ;
2019-10-26 21:58:04 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Could not configure device for programming (2) " ) ) ;
}
}
if ( no_error ) {
if ( TasmotaSlave_execCmd ( CMND_STK_ENTER_PROGMODE ) ) {
} else {
2019-10-27 21:07:20 +00:00
no_error = 0 ;
2019-10-26 21:58:04 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Failed to put bootloader into programming mode " ) ) ;
}
}
2019-10-27 21:07:20 +00:00
return no_error ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
uint8_t TasmotaSlave_loadAddress ( uint8_t adrHi , uint8_t adrLo )
2019-10-21 11:25:22 +01:00
{
2019-10-26 21:58:04 +01:00
uint8_t params [ ] = { adrLo , adrHi } ;
return TasmotaSlave_execParam ( CMND_STK_LOAD_ADDRESS , params , sizeof ( params ) ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_FlashPage ( uint8_t addr_h , uint8_t addr_l , uint8_t * data )
2019-10-20 19:12:05 +01:00
{
2019-10-21 11:25:22 +01:00
uint8_t Header [ ] = { CMND_STK_PROG_PAGE , 0x00 , 0x80 , 0x46 } ;
2019-10-26 21:58:04 +01:00
TasmotaSlave_loadAddress ( addr_h , addr_l ) ;
TasmotaSlave_Serial - > write ( Header , 4 ) ;
2019-10-20 19:12:05 +01:00
for ( int i = 0 ; i < 128 ; i + + ) {
2019-10-26 21:58:04 +01:00
TasmotaSlave_Serial - > write ( data [ i ] ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
TasmotaSlave_Serial - > write ( CONST_STK_CRC_EOP ) ;
2019-10-27 21:07:20 +00:00
TasmotaSlave_waitForSerialData ( 2 , 250 ) ;
2019-10-26 21:58:04 +01:00
TasmotaSlave_Serial - > read ( ) ;
TasmotaSlave_Serial - > read ( ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_Flash ( void )
2019-10-20 19:12:05 +01:00
{
bool reading = true ;
uint32_t read = 0 ;
uint32_t processed = 0 ;
char thishexline [ 50 ] ;
uint8_t position = 0 ;
char * flash_buffer ;
2019-10-27 21:07:20 +00:00
2019-10-26 21:58:04 +01:00
SimpleHexParse hexParse = SimpleHexParse ( ) ;
2019-10-20 19:12:05 +01:00
2019-10-27 21:07:20 +00:00
if ( ! TasmotaSlave_SetupFlash ( ) ) {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Flashing aborted! " ) ) ;
TSlave . flashing = false ;
restart_flag = 2 ;
return ;
}
2019-10-20 19:12:05 +01:00
flash_buffer = new char [ SPI_FLASH_SEC_SIZE ] ;
2019-10-26 21:58:04 +01:00
uint32_t flash_start = TasmotaSlave_FlashStart ( ) * SPI_FLASH_SEC_SIZE ;
2019-10-20 19:12:05 +01:00
while ( reading ) {
2019-10-21 11:25:22 +01:00
ESP . flashRead ( flash_start + read , ( uint32_t * ) flash_buffer , SPI_FLASH_SEC_SIZE ) ;
read = read + SPI_FLASH_SEC_SIZE ;
2019-10-26 21:58:04 +01:00
if ( read > = TSlave . spi_hex_size ) {
2019-10-20 19:12:05 +01:00
reading = false ;
}
2019-10-21 11:25:22 +01:00
for ( uint32_t ca = 0 ; ca < SPI_FLASH_SEC_SIZE ; ca + + ) {
2019-10-20 19:12:05 +01:00
processed + + ;
2019-10-26 21:58:04 +01:00
if ( ( processed < = TSlave . spi_hex_size ) & & ( ! hexParse . EndOfFile ) ) {
2019-10-20 19:12:05 +01:00
if ( ' : ' = = flash_buffer [ ca ] ) {
position = 0 ;
}
if ( 0x0D = = flash_buffer [ ca ] ) {
thishexline [ position ] = 0 ;
2019-10-26 21:58:04 +01:00
hexParse . parseLine ( thishexline ) ;
if ( hexParse . PageIsReady ) {
TasmotaSlave_FlashPage ( hexParse . ptr_h , hexParse . ptr_l , hexParse . FlashPage ) ;
hexParse . PageIsReady = false ;
hexParse . FlashPageIdx = 0 ;
2019-10-20 19:12:05 +01:00
}
} else {
if ( 0x0A ! = flash_buffer [ ca ] ) {
thishexline [ position ] = flash_buffer [ ca ] ;
position + + ;
}
}
}
}
}
2019-10-26 21:58:04 +01:00
TasmotaSlave_exitProgMode ( ) ;
2019-10-28 19:19:52 +00:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " TasmotaSlave: Flash done! " ) ) ;
2019-10-26 21:58:04 +01:00
TSlave . flashing = false ;
2019-10-20 19:12:05 +01:00
restart_flag = 2 ;
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_SetFlagFlashing ( bool value )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
TSlave . flashing = value ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
bool TasmotaSlave_GetFlagFlashing ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
return TSlave . flashing ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_WriteBuffer ( uint8_t * buf , size_t size )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
if ( 0 = = TSlave . spi_sector_cursor ) { // Starting a new sector write so we need to erase it first
ESP . flashEraseSector ( TSlave . spi_sector_counter ) ;
2019-10-20 19:12:05 +01:00
}
2019-10-26 21:58:04 +01:00
TSlave . spi_sector_cursor + + ;
ESP . flashWrite ( ( TSlave . spi_sector_counter * SPI_FLASH_SEC_SIZE ) + ( ( TSlave . spi_sector_cursor - 1 ) * 2048 ) , ( uint32_t * ) buf , size ) ;
TSlave . spi_hex_size = TSlave . spi_hex_size + size ;
if ( 2 = = TSlave . spi_sector_cursor ) { // The web upload sends 2048 bytes at a time so keep track of the cursor position to reset it for the next flash sector erase
TSlave . spi_sector_cursor = 0 ;
TSlave . spi_sector_counter + + ;
2019-10-20 19:12:05 +01:00
}
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_Init ( void )
2019-10-20 19:12:05 +01:00
{
2019-10-26 21:58:04 +01:00
if ( TSlave . type ) {
2019-10-20 19:12:05 +01:00
return ;
}
2019-10-26 21:58:04 +01:00
if ( 10 > TSlave . waitstate ) {
TSlave . waitstate + + ;
return ;
}
if ( ! TSlave . SerialEnabled ) {
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_TASMOTASLAVE_RXD ) & & PinUsed ( GPIO_TASMOTASLAVE_TXD ) & &
( PinUsed ( GPIO_TASMOTASLAVE_RST ) | | PinUsed ( GPIO_TASMOTASLAVE_RST_INV ) ) ) {
2020-04-26 16:33:27 +01:00
TasmotaSlave_Serial = new TasmotaSerial ( Pin ( GPIO_TASMOTASLAVE_RXD ) , Pin ( GPIO_TASMOTASLAVE_TXD ) , 1 , 0 , 200 ) ;
2019-10-26 21:58:04 +01:00
if ( TasmotaSlave_Serial - > begin ( USE_TASMOTA_SLAVE_SERIAL_SPEED ) ) {
if ( TasmotaSlave_Serial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
}
2020-05-02 16:25:58 +01:00
TasmotaSlave_Serial - > setTimeout ( 100 ) ; // Theo 20200502 - increase from 50
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_TASMOTASLAVE_RST_INV ) ) {
2020-04-26 16:33:27 +01:00
SetPin ( Pin ( GPIO_TASMOTASLAVE_RST_INV ) , GPIO_TASMOTASLAVE_RST ) ;
2019-10-27 18:10:25 +00:00
TSlave . inverted = HIGH ;
}
2020-04-26 16:33:27 +01:00
pinMode ( Pin ( GPIO_TASMOTASLAVE_RST ) , OUTPUT ) ;
2019-10-26 21:58:04 +01:00
TSlave . SerialEnabled = true ;
2019-10-27 18:10:25 +00:00
TasmotaSlave_Reset ( ) ;
2019-10-26 21:58:04 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " Tasmota Slave Enabled " ) ) ;
2019-10-21 11:25:22 +01:00
}
2019-10-26 21:58:04 +01:00
}
}
2020-05-02 16:25:58 +01:00
if ( TSlave . SerialEnabled ) { // All go for hardware now we need to detect features if there are any
2019-10-26 21:58:04 +01:00
TasmotaSlave_sendCmnd ( CMND_FEATURES , 0 ) ;
2020-05-02 16:25:58 +01:00
char buffer [ 32 ] = { 0 } ;
2019-10-26 21:58:04 +01:00
TasmotaSlave_Serial - > readBytesUntil ( char ( PARAM_DATA_START ) , buffer , sizeof ( buffer ) ) ;
uint8_t len = TasmotaSlave_Serial - > readBytesUntil ( char ( PARAM_DATA_END ) , buffer , sizeof ( buffer ) ) ;
2020-05-02 16:25:58 +01:00
if ( len ) { AddLogBuffer ( LOG_LEVEL_DEBUG_MORE , ( uint8_t * ) buffer , len ) ; } // Theo 20200502 - DMP: 99 17 34 01 02 00 00 00
2019-10-26 21:58:04 +01:00
memcpy ( & TSlaveSettings , & buffer , sizeof ( TSlaveSettings ) ) ;
2019-11-29 19:20:33 +00:00
if ( 20191129 = = TSlaveSettings . features_version ) {
2019-10-26 21:58:04 +01:00
TSlave . type = true ;
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " Tasmota Slave Version %u " ) , TSlaveSettings . features_version ) ;
2019-11-29 19:20:33 +00:00
} else {
if ( ( ! TSlave . unsupported ) & & ( TSlaveSettings . features_version > 0 ) ) {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " Tasmota Slave Version %u not supported! " ) , TSlaveSettings . features_version ) ;
TSlave . unsupported = true ;
}
2019-10-20 19:12:05 +01:00
}
}
}
2019-10-26 21:58:04 +01:00
void TasmotaSlave_Show ( void )
2019-10-20 19:12:05 +01:00
{
2019-11-02 11:14:38 +00:00
if ( ( TSlave . type ) & & ( TSlaveSettings . features . func_json_append ) ) {
2019-10-21 11:25:22 +01:00
char buffer [ 100 ] ;
2019-10-26 21:58:04 +01:00
TasmotaSlave_sendCmnd ( CMND_JSON , 0 ) ;
TasmotaSlave_Serial - > readBytesUntil ( char ( PARAM_DATA_START ) , buffer , sizeof ( buffer ) - 1 ) ;
uint8_t len = TasmotaSlave_Serial - > readBytesUntil ( char ( PARAM_DATA_END ) , buffer , sizeof ( buffer ) - 1 ) ;
buffer [ len ] = ' \0 ' ;
ResponseAppend_P ( PSTR ( " , \" TasmotaSlave \" :%s " ) , buffer ) ;
}
}
void TasmotaSlave_sendCmnd ( uint8_t cmnd , uint8_t param )
{
2019-11-02 11:14:38 +00:00
TSlaveCommand . command = cmnd ;
TSlaveCommand . parameter = param ;
char buffer [ sizeof ( TSlaveCommand ) + 2 ] ;
2019-10-26 21:58:04 +01:00
buffer [ 0 ] = CMND_START ;
2019-11-02 11:14:38 +00:00
memcpy ( & buffer [ 1 ] , & TSlaveCommand , sizeof ( TSlaveCommand ) ) ;
buffer [ sizeof ( TSlaveCommand ) + 1 ] = CMND_END ;
2020-05-02 16:25:58 +01:00
TasmotaSlave_Serial - > flush ( ) ; // Theo 20200502
2019-10-26 21:58:04 +01:00
for ( uint8_t ca = 0 ; ca < sizeof ( buffer ) ; ca + + ) {
TasmotaSlave_Serial - > write ( buffer [ ca ] ) ;
2019-10-20 19:12:05 +01:00
}
}
2019-11-03 13:52:53 +00:00
# define D_PRFX_SLAVE "Slave"
# define D_CMND_SLAVE_RESET "Reset"
# define D_CMND_SLAVE_SEND "Send"
const char kTasmotaSlaveCommands [ ] PROGMEM = D_PRFX_SLAVE " | "
D_CMND_SLAVE_RESET " | " D_CMND_SLAVE_SEND ;
2019-11-02 11:14:38 +00:00
void ( * const TasmotaSlaveCommand [ ] ) ( void ) PROGMEM = {
2019-11-03 13:52:53 +00:00
& CmndTasmotaSlaveReset , & CmndTasmotaSlaveSend } ;
2019-11-02 11:14:38 +00:00
2019-11-03 13:52:53 +00:00
void CmndTasmotaSlaveReset ( void )
2019-11-02 11:14:38 +00:00
{
2019-11-03 13:52:53 +00:00
TasmotaSlave_Reset ( ) ;
2019-11-29 19:20:33 +00:00
TSlave . type = false ; // Force redetection
TSlave . waitstate = 7 ; // give it at least 3 seconds to restart from bootloader
TSlave . unsupported = false ; // Reset unsupported flag
2019-11-03 13:52:53 +00:00
ResponseCmndDone ( ) ;
}
2019-11-02 11:14:38 +00:00
2019-11-03 13:52:53 +00:00
void CmndTasmotaSlaveSend ( void )
{
if ( 0 < XdrvMailbox . data_len ) {
TasmotaSlave_sendCmnd ( CMND_SLAVE_SEND , XdrvMailbox . data_len ) ;
TasmotaSlave_Serial - > write ( char ( PARAM_DATA_START ) ) ;
for ( uint8_t idx = 0 ; idx < XdrvMailbox . data_len ; idx + + ) {
TasmotaSlave_Serial - > write ( XdrvMailbox . data [ idx ] ) ;
2019-11-02 11:14:38 +00:00
}
2019-11-03 13:52:53 +00:00
TasmotaSlave_Serial - > write ( char ( PARAM_DATA_END ) ) ;
2019-11-02 11:14:38 +00:00
}
ResponseCmndDone ( ) ;
}
void TasmotaSlave_ProcessIn ( void )
{
uint8_t cmnd = TasmotaSlave_Serial - > read ( ) ;
switch ( cmnd ) {
case CMND_START :
TasmotaSlave_waitForSerialData ( sizeof ( TSlaveCommand ) , 50 ) ;
uint8_t buffer [ sizeof ( TSlaveCommand ) ] ;
for ( uint8_t idx = 0 ; idx < sizeof ( TSlaveCommand ) ; idx + + ) {
buffer [ idx ] = TasmotaSlave_Serial - > read ( ) ;
}
TasmotaSlave_Serial - > read ( ) ; // read trailing byte of command
memcpy ( & TSlaveCommand , & buffer , sizeof ( TSlaveCommand ) ) ;
2019-11-29 19:20:33 +00:00
char inbuf [ TSlaveCommand . parameter + 1 ] ;
TasmotaSlave_waitForSerialData ( TSlaveCommand . parameter , 50 ) ;
TasmotaSlave_Serial - > read ( ) ; // Read leading byte
for ( uint8_t idx = 0 ; idx < TSlaveCommand . parameter ; idx + + ) {
inbuf [ idx ] = TasmotaSlave_Serial - > read ( ) ;
}
TasmotaSlave_Serial - > read ( ) ; // Read trailing byte
inbuf [ TSlaveCommand . parameter ] = ' \0 ' ;
2019-11-02 11:14:38 +00:00
if ( CMND_PUBLISH_TELE = = TSlaveCommand . command ) { // We need to publish stat/ with incoming stream as content
Response_P ( PSTR ( " { \" TasmotaSlave \" : " ) ) ;
ResponseAppend_P ( " %s " , inbuf ) ;
ResponseJsonEnd ( ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , mqtt_data ) ;
XdrvRulesProcess ( ) ;
}
2019-11-29 19:20:33 +00:00
if ( CMND_EXECUTE_CMND = = TSlaveCommand . command ) { // We need to execute the incoming command
ExecuteCommand ( inbuf , SRC_IGNORE ) ;
}
2019-11-02 11:14:38 +00:00
break ;
default :
break ;
}
}
2019-10-21 11:25:22 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-10-20 19:12:05 +01:00
bool Xdrv31 ( uint8_t function )
{
bool result = false ;
2019-10-21 11:25:22 +01:00
2019-10-20 19:12:05 +01:00
switch ( function ) {
2019-11-02 11:14:38 +00:00
case FUNC_EVERY_100_MSECOND :
2019-11-02 12:45:45 +00:00
if ( TSlave . type ) {
2019-11-02 11:14:38 +00:00
if ( TasmotaSlave_Serial - > available ( ) ) {
TasmotaSlave_ProcessIn ( ) ;
2019-10-28 19:19:52 +00:00
}
2019-11-02 12:45:45 +00:00
if ( TSlaveSettings . features . func_every_100_msecond ) {
TasmotaSlave_sendCmnd ( CMND_FUNC_EVERY_100_MSECOND , 0 ) ;
}
2019-11-02 11:17:39 +00:00
}
2019-11-02 11:14:38 +00:00
break ;
case FUNC_EVERY_SECOND :
if ( ( TSlave . type ) & & ( TSlaveSettings . features . func_every_second ) ) {
TasmotaSlave_sendCmnd ( CMND_FUNC_EVERY_SECOND , 0 ) ;
2019-10-28 19:19:52 +00:00
}
2019-11-02 11:14:38 +00:00
TasmotaSlave_Init ( ) ;
2019-10-20 19:12:05 +01:00
break ;
case FUNC_JSON_APPEND :
2019-11-02 11:14:38 +00:00
if ( ( TSlave . type ) & & ( TSlaveSettings . features . func_json_append ) ) {
TasmotaSlave_Show ( ) ;
}
break ;
case FUNC_COMMAND :
result = DecodeCommand ( kTasmotaSlaveCommands , TasmotaSlaveCommand ) ;
2019-10-20 19:12:05 +01:00
break ;
}
2019-10-21 11:25:22 +01:00
return result ;
2019-10-20 19:12:05 +01:00
}
2019-11-02 11:17:39 +00:00
# endif // USE_TASMOTA_SLAVE