2018-03-20 13:31:11 +00:00
/*
2019-10-27 10:13:24 +00:00
xdrv_08_serial_bridge . ino - serial bridge support for Tasmota
2018-03-20 13:31:11 +00:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends and Dániel Zoltán Tolnai
2018-03-20 13:31:11 +00: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/>.
*/
# ifdef USE_SERIAL_BRIDGE
/*********************************************************************************************\
* Serial Bridge using Software Serial library ( TasmotaSerial )
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-11-07 09:30:03 +00:00
# define XDRV_08 8
2020-08-30 11:09:18 +01:00
# define HARDWARE_FALLBACK 2
2018-11-07 09:30:03 +00:00
2022-06-06 17:08:02 +01:00
# define USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
# ifdef ESP8266
2022-09-24 16:11:20 +01:00
const uint16_t SERIAL_BRIDGE_BUFFER_SIZE = MIN_INPUT_BUFFER_SIZE ;
2022-06-06 16:48:40 +01:00
# else
const uint16_t SERIAL_BRIDGE_BUFFER_SIZE = INPUT_BUFFER_SIZE ;
# endif
2018-03-20 13:31:11 +00:00
2019-08-11 17:12:18 +01:00
const char kSerialBridgeCommands [ ] PROGMEM = " | " // No prefix
2021-12-25 11:58:24 +00:00
D_CMND_SSERIALSEND " | " D_CMND_SBAUDRATE " | " D_CMND_SSERIALCONFIG ;
2018-03-20 13:31:11 +00:00
2019-11-24 11:24:35 +00:00
void ( * const SerialBridgeCommand [ ] ) ( void ) PROGMEM = {
2021-12-25 11:58:24 +00:00
& CmndSSerialSend , & CmndSBaudrate , & CmndSSerialConfig } ;
2018-03-20 13:31:11 +00:00
2019-08-11 17:12:18 +01:00
# include <TasmotaSerial.h>
2019-08-01 16:33:44 +01:00
2019-03-26 17:26:50 +00:00
TasmotaSerial * SerialBridgeSerial = nullptr ;
2018-03-20 13:31:11 +00:00
unsigned long serial_bridge_polling_window = 0 ;
2019-03-26 17:26:50 +00:00
char * serial_bridge_buffer = nullptr ;
2019-02-09 15:08:09 +00:00
int serial_bridge_in_byte_counter = 0 ;
bool serial_bridge_raw = false ;
2018-03-20 13:31:11 +00:00
2021-12-25 11:58:24 +00:00
/********************************************************************************************/
bool SetSSerialBegin ( void ) {
2021-12-30 13:39:22 +00:00
return SerialBridgeSerial - > begin ( Settings - > sbaudrate * 300 , ConvertSerialConfig ( Settings - > sserial_config ) ) ; // Reinitialize serial port with new baud rate
2021-12-25 11:58:24 +00:00
}
void SetSSerialConfig ( uint32_t serial_config ) {
if ( serial_config > TS_SERIAL_8O2 ) {
serial_config = TS_SERIAL_8N1 ;
}
if ( serial_config ! = Settings - > sserial_config ) {
Settings - > sserial_config = serial_config ;
SetSSerialBegin ( ) ;
}
}
2022-06-07 09:03:12 +01:00
void SerialBridgePrintf ( PGM_P formatP , . . . ) {
2022-06-06 17:08:02 +01:00
# ifdef USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
if ( Settings - > sbflag1 . serbridge_console & & serial_bridge_buffer ) {
2022-06-07 09:03:12 +01:00
va_list arg ;
va_start ( arg , formatP ) ;
char * data = ext_vsnprintf_malloc_P ( formatP , arg ) ;
va_end ( arg ) ;
if ( data = = nullptr ) { return ; }
SerialBridgeSerial - > printf ( data ) ;
free ( data ) ;
2022-06-06 16:48:40 +01:00
}
2022-06-06 17:08:02 +01:00
# endif // USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
}
2021-12-25 11:58:24 +00:00
/********************************************************************************************/
void SerialBridgeInput ( void ) {
2018-03-20 13:31:11 +00:00
while ( SerialBridgeSerial - > available ( ) ) {
yield ( ) ;
2019-02-05 13:13:53 +00:00
uint8_t serial_in_byte = SerialBridgeSerial - > read ( ) ;
2020-05-26 11:35:21 +01:00
2022-06-06 17:08:02 +01:00
# ifdef USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
if ( Settings - > sbflag1 . serbridge_console ) {
static bool serial_bridge_overrun = false ;
if ( isprint ( serial_in_byte ) ) { // Any char between 32 and 127
if ( serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE - 1 ) { // Add char to string if it still fits
serial_bridge_buffer [ serial_bridge_in_byte_counter + + ] = serial_in_byte ;
} else {
serial_bridge_overrun = true ; // Signal overrun but continue reading input to flush until '\n' (EOL)
}
}
else if ( serial_in_byte = = ' \n ' ) {
serial_bridge_buffer [ serial_bridge_in_byte_counter ] = 0 ; // Serial data completed
TasmotaGlobal . seriallog_level = ( Settings - > seriallog_level < LOG_LEVEL_INFO ) ? ( uint8_t ) LOG_LEVEL_INFO : Settings - > seriallog_level ;
if ( serial_bridge_overrun ) {
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " SSerial buffer overrun " ) ) ;
} else {
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_COMMAND " %s " ) , serial_bridge_buffer ) ;
2022-06-07 09:35:51 +01:00
ExecuteCommand ( serial_bridge_buffer , SRC_SSERIAL ) ;
2022-06-06 16:48:40 +01:00
}
serial_bridge_in_byte_counter = 0 ;
serial_bridge_overrun = false ;
SerialBridgeSerial - > flush ( ) ;
return ;
}
} else {
2022-06-06 17:08:02 +01:00
# endif // USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
serial_bridge_raw = ( 254 = = Settings - > serial_delimiter ) ;
if ( ( serial_in_byte > 127 ) & & ! serial_bridge_raw ) { // Discard binary data above 127 if no raw reception allowed
serial_bridge_in_byte_counter = 0 ;
SerialBridgeSerial - > flush ( ) ;
return ;
2018-03-20 13:31:11 +00:00
}
2022-06-06 16:48:40 +01:00
if ( serial_in_byte | | serial_bridge_raw ) { // Any char between 1 and 127 or any char (0 - 255)
bool in_byte_is_delimiter = // Char is delimiter when...
( ( ( Settings - > serial_delimiter < 128 ) & & ( serial_in_byte = = Settings - > serial_delimiter ) ) | | // Any char between 1 and 127 and being delimiter
( ( Settings - > serial_delimiter = = 128 ) & & ! isprint ( serial_in_byte ) ) ) & & // Any char not between 32 and 127
! serial_bridge_raw ; // In raw mode (CMND_SERIALSEND3) there is never a delimiter
if ( ( serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE - 1 ) & & // Add char to string if it still fits and ...
! in_byte_is_delimiter ) { // Char is not a delimiter
serial_bridge_buffer [ serial_bridge_in_byte_counter + + ] = serial_in_byte ;
}
2020-05-23 16:30:48 +01:00
2022-06-06 16:48:40 +01:00
if ( ( serial_bridge_in_byte_counter > = SERIAL_BRIDGE_BUFFER_SIZE - 1 ) | | // Send message when buffer is full or ...
in_byte_is_delimiter ) { // Char is delimiter
serial_bridge_polling_window = 0 ; // Publish now
break ;
}
}
serial_bridge_polling_window = millis ( ) ; // Wait for more data
2022-06-06 17:08:02 +01:00
# ifdef USE_SERIAL_BRIDGE_TEE
2018-03-20 13:31:11 +00:00
}
2022-06-06 17:08:02 +01:00
# endif // USE_SERIAL_BRIDGE_TEE
}
# ifdef USE_SERIAL_BRIDGE_TEE
if ( Settings - > sbflag1 . serbridge_console ) {
return ;
2018-03-20 13:31:11 +00:00
}
2022-06-06 17:08:02 +01:00
# endif // USE_SERIAL_BRIDGE_TEE
2018-03-20 13:31:11 +00:00
2022-06-06 17:08:02 +01:00
if ( serial_bridge_in_byte_counter & & ( millis ( ) > ( serial_bridge_polling_window + SERIAL_POLLING ) ) ) {
serial_bridge_buffer [ serial_bridge_in_byte_counter ] = 0 ; // Serial data completed
bool assume_json = ( ! serial_bridge_raw & & ( serial_bridge_buffer [ 0 ] = = ' { ' ) ) ;
2020-05-24 16:15:06 +01:00
2022-06-06 17:08:02 +01:00
Response_P ( PSTR ( " { \" " D_JSON_SSERIALRECEIVED " \" : " ) ) ;
if ( assume_json ) {
ResponseAppend_P ( serial_bridge_buffer ) ;
} else {
ResponseAppend_P ( PSTR ( " \" " ) ) ;
if ( serial_bridge_raw ) {
char hex_char [ ( serial_bridge_in_byte_counter * 2 ) + 2 ] ;
ResponseAppend_P ( ToHex_P ( ( unsigned char * ) serial_bridge_buffer , serial_bridge_in_byte_counter , hex_char , sizeof ( hex_char ) ) ) ;
2020-05-24 16:15:06 +01:00
} else {
2022-06-06 17:08:02 +01:00
ResponseAppend_P ( EscapeJSONString ( serial_bridge_buffer ) . c_str ( ) ) ;
2020-05-24 16:15:06 +01:00
}
2022-06-06 17:08:02 +01:00
ResponseAppend_P ( PSTR ( " \" " ) ) ;
2022-06-06 16:48:40 +01:00
}
2022-06-06 17:08:02 +01:00
ResponseJsonEnd ( ) ;
2022-09-21 20:44:17 +01:00
if ( Settings - > flag6 . mqtt_disable_sserialrec ) { // SetOption147 If it is activated, Tasmota will not publish SSerialReceived MQTT messages, but it will proccess event trigger rules
XdrvRulesProcess ( 0 ) ;
2022-09-20 22:38:59 +01:00
} else {
2022-09-21 20:44:17 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_SSERIALRECEIVED ) ) ;
}
2022-06-06 17:08:02 +01:00
serial_bridge_in_byte_counter = 0 ;
2018-03-20 13:31:11 +00:00
}
}
/********************************************************************************************/
2022-06-06 16:48:40 +01:00
void SerialBridgeInit ( void ) {
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_SBR_RX ) & & PinUsed ( GPIO_SBR_TX ) ) {
2020-08-30 11:09:18 +01:00
SerialBridgeSerial = new TasmotaSerial ( Pin ( GPIO_SBR_RX ) , Pin ( GPIO_SBR_TX ) , HARDWARE_FALLBACK ) ;
2021-12-25 11:58:24 +00:00
if ( SetSSerialBegin ( ) ) {
2019-02-09 15:08:09 +00:00
if ( SerialBridgeSerial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
2020-10-30 11:29:48 +00:00
serial_bridge_buffer = TasmotaGlobal . serial_in_buffer ; // Use idle serial buffer to save RAM
2019-02-09 15:08:09 +00:00
} else {
serial_bridge_buffer = ( char * ) ( malloc ( SERIAL_BRIDGE_BUFFER_SIZE ) ) ;
2018-11-27 11:09:36 +00:00
}
2019-02-09 15:08:09 +00:00
SerialBridgeSerial - > flush ( ) ;
2022-06-07 13:11:23 +01:00
SerialBridgePrintf ( " \r \n " ) ;
2018-03-20 13:31:11 +00:00
}
}
}
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-06-06 16:48:40 +01:00
void CmndSSerialSend ( void ) {
2020-07-21 11:42:18 +01:00
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 6 ) ) {
2019-02-09 15:08:09 +00:00
serial_bridge_raw = ( XdrvMailbox . index > 3 ) ;
2022-06-06 16:48:40 +01:00
Settings - > sbflag1 . serbridge_console = 0 ; // Disable console Tee
2019-02-09 15:08:09 +00:00
if ( XdrvMailbox . data_len > 0 ) {
2018-03-20 13:31:11 +00:00
if ( 1 = = XdrvMailbox . index ) {
2019-02-09 15:08:09 +00:00
SerialBridgeSerial - > write ( XdrvMailbox . data , XdrvMailbox . data_len ) ; // "Hello Tiger"
SerialBridgeSerial - > write ( " \n " ) ; // "\n"
2018-03-20 13:31:11 +00:00
}
2019-02-09 15:08:09 +00:00
else if ( ( 2 = = XdrvMailbox . index ) | | ( 4 = = XdrvMailbox . index ) ) {
SerialBridgeSerial - > write ( XdrvMailbox . data , XdrvMailbox . data_len ) ; // "Hello Tiger" or "A0"
2018-03-20 13:31:11 +00:00
}
2019-02-09 15:08:09 +00:00
else if ( 3 = = XdrvMailbox . index ) { // "Hello\f"
2018-03-20 13:31:11 +00:00
SerialBridgeSerial - > write ( Unescape ( XdrvMailbox . data , & XdrvMailbox . data_len ) , XdrvMailbox . data_len ) ;
}
2019-02-09 15:08:09 +00:00
else if ( 5 = = XdrvMailbox . index ) {
2019-01-17 16:48:34 +00:00
char * p ;
char stemp [ 3 ] ;
uint8_t code ;
char * codes = RemoveSpace ( XdrvMailbox . data ) ;
int size = strlen ( XdrvMailbox . data ) ;
2019-10-15 07:27:32 +01:00
while ( size > 1 ) {
2019-03-08 18:24:02 +00:00
strlcpy ( stemp , codes , sizeof ( stemp ) ) ;
2019-01-17 16:48:34 +00:00
code = strtol ( stemp , & p , 16 ) ;
2019-02-09 15:08:09 +00:00
SerialBridgeSerial - > write ( code ) ; // "AA004566" as hex values
2019-01-17 16:48:34 +00:00
size - = 2 ;
codes + = 2 ;
}
}
2020-07-21 11:42:18 +01:00
else if ( 6 = = XdrvMailbox . index ) {
char * p ;
uint8_t code ;
char * values = XdrvMailbox . data ;
for ( char * str = strtok_r ( values , " , " , & p ) ; str ; str = strtok_r ( nullptr , " , " , & p ) ) {
code = ( uint8_t ) atoi ( str ) ;
SerialBridgeSerial - > write ( code ) ; // "72,101,108,108"
}
}
2019-08-03 12:01:34 +01:00
ResponseCmndDone ( ) ;
2018-03-20 13:31:11 +00:00
}
}
2022-06-06 17:08:02 +01:00
# ifdef USE_SERIAL_BRIDGE_TEE
2022-06-06 16:48:40 +01:00
if ( 9 = = XdrvMailbox . index ) {
2022-06-06 17:08:02 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 1 ) ) {
2022-06-07 09:03:12 +01:00
Settings - > sbflag1 . serbridge_console = XdrvMailbox . payload ;
2022-06-06 16:48:40 +01:00
}
ResponseCmndStateText ( Settings - > sbflag1 . serbridge_console ) ;
}
2022-06-06 17:08:02 +01:00
# endif // USE_SERIAL_BRIDGE_TEE
2019-08-01 16:33:44 +01:00
}
2018-11-27 11:09:36 +00:00
2021-12-25 11:58:24 +00:00
void CmndSBaudrate ( void ) {
2019-08-28 11:02:27 +01:00
if ( XdrvMailbox . payload > = 300 ) {
XdrvMailbox . payload / = 300 ; // Make it a valid baudrate
2021-06-11 17:14:12 +01:00
Settings - > sbaudrate = XdrvMailbox . payload ;
2021-12-25 11:58:24 +00:00
SetSSerialBegin ( ) ;
2019-08-01 16:33:44 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > sbaudrate * 300 ) ;
2018-03-20 13:31:11 +00:00
}
2021-12-25 11:58:24 +00:00
void CmndSSerialConfig ( void ) {
// See TasmotaSerialConfig for possible options
// SSerialConfig 0..23 where 3 equals 8N1
// SSerialConfig 8N1
if ( XdrvMailbox . data_len > 0 ) {
if ( XdrvMailbox . data_len < 3 ) { // Use 0..23 as serial config option
if ( ( XdrvMailbox . payload > = TS_SERIAL_5N1 ) & & ( XdrvMailbox . payload < = TS_SERIAL_8O2 ) ) {
SetSSerialConfig ( XdrvMailbox . payload ) ;
}
}
else if ( ( XdrvMailbox . payload > = 5 ) & & ( XdrvMailbox . payload < = 8 ) ) {
int8_t serial_config = ParseSerialConfig ( XdrvMailbox . data ) ;
if ( serial_config > = 0 ) {
SetSSerialConfig ( serial_config ) ;
}
}
}
ResponseCmndChar ( GetSerialConfig ( Settings - > sserial_config ) . c_str ( ) ) ;
}
2018-03-20 13:31:11 +00:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-06-06 16:48:40 +01:00
bool Xdrv08 ( uint8_t function ) {
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-03-20 13:31:11 +00:00
2022-06-06 16:48:40 +01:00
if ( FUNC_PRE_INIT = = function ) {
SerialBridgeInit ( ) ;
}
else if ( serial_bridge_buffer ) {
2018-03-20 13:31:11 +00:00
switch ( function ) {
case FUNC_LOOP :
2022-06-06 16:48:40 +01:00
SerialBridgeInput ( ) ;
2019-03-30 12:03:45 +00:00
break ;
2018-03-20 13:31:11 +00:00
case FUNC_COMMAND :
2019-08-01 16:33:44 +01:00
result = DecodeCommand ( kSerialBridgeCommands , SerialBridgeCommand ) ;
2018-03-20 13:31:11 +00:00
break ;
}
}
return result ;
}
# endif // USE_SERIAL_BRIDGE