2017-07-30 16:55:37 +01:00
/*
2019-10-27 10:13:24 +00:00
xdrv_06_snfbridge . ino - sonoff RF bridge 433 support for Tasmota
2017-07-30 16:55:37 +01:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends and Erik Andrén Zachrisson ( fw update )
2017-07-30 16:55:37 +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-15 14:37:22 +01:00
# ifdef USE_SONOFF_RF
2017-07-30 16:55:37 +01:00
/*********************************************************************************************\
Sonoff RF Bridge 433
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-11-07 09:30:03 +00:00
# define XDRV_06 6
2024-03-14 16:21:09 +00:00
# define SFB_TIME_AVOID_DUPLICATE 2000 // Milliseconds
2017-08-29 15:35:45 +01:00
2019-08-13 15:10:47 +01:00
enum SonoffBridgeCommands {
CMND_RFSYNC , CMND_RFLOW , CMND_RFHIGH , CMND_RFHOST , CMND_RFCODE } ;
2019-08-01 15:38:52 +01:00
2024-03-14 16:21:09 +00:00
const char kSonoffBridgeCommands [ ] PROGMEM = D_CMND_PREFIX_RF " | " // Prefix
D_CMND_RFSYNC " | " D_CMND_RFLOW " | " D_CMND_RFHIGH " | " D_CMND_RFHOST " | " D_CMND_RFCODE " | " D_CMND_RFKEY " | " D_CMND_RFRAW " | " D_CMND_RFTIMEOUT ;
2017-10-29 17:18:46 +00:00
2019-08-01 15:38:52 +01:00
void ( * const SonoffBridgeCommand [ ] ) ( void ) PROGMEM = {
2024-03-14 16:21:09 +00:00
& CmndRfBridge , & CmndRfBridge , & CmndRfBridge , & CmndRfBridge , & CmndRfBridge , & CmndRfKey , & CmndRfRaw , & CmndRfTimeout } ;
2019-08-13 15:10:47 +01:00
struct SONOFFBRIDGE {
uint32_t last_received_id = 0 ;
uint32_t last_send_code = 0 ;
uint32_t last_time = 0 ;
uint32_t last_learn_time = 0 ;
uint8_t receive_flag = 0 ;
uint8_t receive_raw_flag = 0 ;
uint8_t learn_key = 1 ;
uint8_t learn_active = 0 ;
uint8_t expected_bytes = 0 ;
} SnfBridge ;
2017-11-17 16:52:31 +00:00
2018-06-04 17:10:38 +01:00
# ifdef USE_RF_FLASH
/*********************************************************************************************\
* EFM8BB1 RF microcontroller in - situ firmware update
*
* Enables upload of EFM8BB1 firmware provided by https : //github.com/Portisch/RF-Bridge-EFM8BB1 using the web gui.
2019-10-27 10:13:24 +00:00
* Based on source by Erik Andrén Zachrisson ( https : //github.com/arendst/Tasmota/pull/2886)
2018-06-04 17:10:38 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "ihx.h"
# include "c2.h"
2020-11-20 16:51:16 +00:00
uint32_t rf_decode_and_write ( uint8_t * record , size_t size ) {
2018-06-04 17:10:38 +01:00
uint8_t err = ihx_decode ( record , size ) ;
2020-11-20 16:51:16 +00:00
if ( err ! = IHX_SUCCESS ) { return 13 ; } // Failed to decode RF firmware
2018-06-04 17:10:38 +01:00
ihx_t * h = ( ihx_t * ) record ;
if ( h - > record_type = = IHX_RT_DATA ) {
int retries = 5 ;
uint16_t address = h - > address_high * 0x100 + h - > address_low ;
do {
2020-09-15 20:49:03 +01:00
err = c2_programming_init ( C2_DEVID_EFM8BB1 ) ;
2018-06-04 17:10:38 +01:00
err = c2_block_write ( address , h - > data , h - > len ) ;
} while ( err ! = C2_SUCCESS & & retries - - ) ;
} else if ( h - > record_type = = IHX_RT_END_OF_FILE ) {
// RF firmware upgrade done, restarting RF chip
err = c2_reset ( ) ;
}
2020-11-20 16:51:16 +00:00
if ( err ! = C2_SUCCESS ) { return 12 ; } // Failed to write to RF chip
2018-06-04 17:10:38 +01:00
return 0 ;
}
2020-11-20 16:51:16 +00:00
uint32_t rf_search_and_write ( uint8_t * data , size_t size ) {
2018-06-04 17:10:38 +01:00
// Binary contains a set of commands, decode and program each one
2020-11-20 16:51:16 +00:00
uint8_t buf [ 64 ] ;
uint8_t * p_data = data ;
uint32_t addr = 0 ;
uint32_t rec_end ;
uint32_t rec_start ;
uint32_t rec_size ;
uint32_t err ;
while ( addr < size ) {
memcpy ( buf , p_data , sizeof ( buf ) ) ; // Must load flash using memcpy on 4-byte boundary
2018-06-04 17:10:38 +01:00
// Find starts and ends of commands
2020-11-20 16:51:16 +00:00
for ( rec_start = 0 ; rec_start < 8 ; rec_start + + ) {
if ( ' : ' = = buf [ rec_start ] ) { break ; }
2018-06-04 17:10:38 +01:00
}
2020-11-20 16:51:16 +00:00
if ( rec_start > 7 ) { return 8 ; } // File invalid - RF Remnant data did not start with a start token
for ( rec_end = rec_start ; rec_end < sizeof ( buf ) ; rec_end + + ) {
if ( ' \n ' = = buf [ rec_end ] ) { break ; }
2018-06-04 17:10:38 +01:00
}
2020-11-20 16:51:16 +00:00
if ( rec_end = = sizeof ( buf ) ) { return 9 ; } // File too large - Failed to decode RF firmware
rec_size = rec_end - rec_start ;
2018-06-04 17:10:38 +01:00
2022-11-11 10:47:11 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: %*_H"), rec_size, (uint8_t*)&buf + rec_start);
2018-06-04 17:10:38 +01:00
2020-11-20 16:51:16 +00:00
err = rf_decode_and_write ( buf + rec_start , rec_size ) ;
if ( err ! = 0 ) { return err ; }
addr + = rec_size + 1 ;
p_data + = ( rec_end & 0xFFFC ) ; // Stay on 4-byte boundary
delay ( 0 ) ;
2018-06-04 17:10:38 +01:00
}
// Buffer was perfectly aligned, start and end found without any remaining trailing characters
return 0 ;
}
2020-11-20 16:51:16 +00:00
uint8_t rf_erase_flash ( void ) {
2018-06-04 17:10:38 +01:00
uint8_t err ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < 4 ; i + + ) { // HACK: Try multiple times as the command sometimes fails (unclear why)
2020-09-15 20:49:03 +01:00
err = c2_programming_init ( C2_DEVID_EFM8BB1 ) ;
2018-06-04 17:10:38 +01:00
if ( err ! = C2_SUCCESS ) {
return 10 ; // Failed to init RF chip
}
err = c2_device_erase ( ) ;
if ( err ! = C2_SUCCESS ) {
2022-11-11 09:44:56 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("RFB: Device erase error %d"), err);
2018-06-04 17:10:38 +01:00
if ( i < 3 ) {
c2_reset ( ) ; // Reset RF chip and try again
} else {
return 11 ; // Failed to erase RF chip
}
} else {
break ;
}
}
return 0 ;
}
2020-11-21 13:45:58 +00:00
uint32_t SnfBrUpdateFirmware ( uint8_t * data , uint32_t size ) {
2018-06-04 17:10:38 +01:00
pinMode ( PIN_C2CK , OUTPUT ) ;
pinMode ( PIN_C2D , INPUT ) ;
2020-11-20 16:51:16 +00:00
uint32_t error = rf_erase_flash ( ) ; // 10, 11
if ( error ) { return error ; }
2021-01-23 16:24:54 +00:00
// AddLog(LOG_LEVEL_DEBUG, PSTR("RFB: Erased"));
2020-11-20 16:51:16 +00:00
return rf_search_and_write ( data , size ) ;
}
2018-06-04 17:10:38 +01:00
# endif // USE_RF_FLASH
/********************************************************************************************/
2018-11-14 13:32:09 +00:00
void SonoffBridgeReceivedRaw ( void )
2018-06-04 17:10:38 +01:00
{
// Decoding according to https://github.com/Portisch/RF-Bridge-EFM8BB1
uint8_t buckets = 0 ;
2020-10-30 11:29:48 +00:00
if ( 0xB1 = = TasmotaGlobal . serial_in_buffer [ 1 ] ) { buckets = TasmotaGlobal . serial_in_buffer [ 2 ] < < 1 ; } // Bucket sniffing
2018-06-04 17:10:38 +01:00
2024-03-14 16:21:09 +00:00
ResponseTime_P ( PSTR ( " , \" " D_CMND_PREFIX_RF D_CMND_RFRAW " \" :{ \" " D_JSON_DATA " \" : \" " ) ) ;
2020-10-29 11:21:24 +00:00
for ( uint32_t i = 0 ; i < TasmotaGlobal . serial_in_byte_counter ; i + + ) {
2020-10-30 11:29:48 +00:00
ResponseAppend_P ( PSTR ( " %02X " ) , TasmotaGlobal . serial_in_buffer [ i ] ) ;
if ( 0xB1 = = TasmotaGlobal . serial_in_buffer [ 1 ] ) {
2018-06-04 17:10:38 +01:00
if ( ( i > 3 ) & & buckets ) { buckets - - ; }
2020-10-29 11:21:24 +00:00
if ( ( i < 3 ) | | ( buckets % 2 ) | | ( i = = TasmotaGlobal . serial_in_byte_counter - 2 ) ) {
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " " ) ) ;
2018-06-04 17:10:38 +01:00
}
}
}
2019-03-23 16:00:59 +00:00
ResponseAppend_P ( PSTR ( " \" }} " ) ) ;
2024-03-14 16:21:09 +00:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_CMND_PREFIX_RF D_CMND_RFRAW ) ) ;
2018-06-04 17:10:38 +01:00
}
/********************************************************************************************/
2018-11-14 13:32:09 +00:00
void SonoffBridgeLearnFailed ( void )
2017-11-17 16:52:31 +00:00
{
2019-08-13 15:10:47 +01:00
SnfBridge . learn_active = 0 ;
2024-03-14 16:21:09 +00:00
Response_P ( S_JSON_COMMAND_INDEX_SVALUE , D_CMND_PREFIX_RF D_CMND_RFKEY , SnfBridge . learn_key , D_JSON_LEARN_FAILED ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_CMND_PREFIX_RF D_CMND_RFKEY ) ) ;
2017-11-17 16:52:31 +00:00
}
2017-07-30 16:55:37 +01:00
2018-11-14 13:32:09 +00:00
void SonoffBridgeReceived ( void )
2017-07-30 16:55:37 +01:00
{
2017-10-18 17:22:34 +01:00
uint16_t sync_time = 0 ;
uint16_t low_time = 0 ;
uint16_t high_time = 0 ;
uint32_t received_id = 0 ;
2017-08-05 14:11:50 +01:00
char rfkey [ 8 ] ;
2018-06-25 11:33:23 +01:00
char stemp [ 16 ] ;
2017-07-30 16:55:37 +01:00
2022-11-11 13:34:58 +00:00
AddLogSerial ( ) ;
2017-07-30 16:55:37 +01:00
2020-10-30 11:29:48 +00:00
if ( 0xA2 = = TasmotaGlobal . serial_in_buffer [ 0 ] ) { // Learn timeout
2017-11-17 16:52:31 +00:00
SonoffBridgeLearnFailed ( ) ;
2017-07-30 16:55:37 +01:00
}
2020-10-30 11:29:48 +00:00
else if ( 0xA3 = = TasmotaGlobal . serial_in_buffer [ 0 ] ) { // Learned A3 20 F8 01 18 03 3E 2E 1A 22 55
2019-08-13 15:10:47 +01:00
SnfBridge . learn_active = 0 ;
2020-10-30 11:29:48 +00:00
low_time = TasmotaGlobal . serial_in_buffer [ 3 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 4 ] ; // Low time in uSec
high_time = TasmotaGlobal . serial_in_buffer [ 5 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 6 ] ; // High time in uSec
2017-10-18 17:22:34 +01:00
if ( low_time & & high_time ) {
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < 9 ; i + + ) {
2021-06-11 17:14:12 +01:00
Settings - > rf_code [ SnfBridge . learn_key ] [ i ] = TasmotaGlobal . serial_in_buffer [ i + 1 ] ;
2017-08-05 14:11:50 +01:00
}
2024-03-14 16:21:09 +00:00
Response_P ( S_JSON_COMMAND_INDEX_SVALUE , D_CMND_PREFIX_RF D_CMND_RFKEY , SnfBridge . learn_key , D_JSON_LEARNED ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_STAT , PSTR ( D_CMND_PREFIX_RF D_CMND_RFKEY ) ) ;
2017-08-05 14:11:50 +01:00
} else {
2017-11-17 16:52:31 +00:00
SonoffBridgeLearnFailed ( ) ;
2017-07-30 16:55:37 +01:00
}
}
2020-10-30 11:29:48 +00:00
else if ( 0xA4 = = TasmotaGlobal . serial_in_buffer [ 0 ] ) { // Received RF data A4 20 EE 01 18 03 3E 2E 1A 22 55
2019-08-13 15:10:47 +01:00
if ( SnfBridge . learn_active ) {
2017-11-17 16:52:31 +00:00
SonoffBridgeLearnFailed ( ) ;
} else {
2020-10-30 11:29:48 +00:00
sync_time = TasmotaGlobal . serial_in_buffer [ 1 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 2 ] ; // Sync time in uSec
low_time = TasmotaGlobal . serial_in_buffer [ 3 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 4 ] ; // Low time in uSec
high_time = TasmotaGlobal . serial_in_buffer [ 5 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 6 ] ; // High time in uSec
received_id = TasmotaGlobal . serial_in_buffer [ 7 ] < < 16 | TasmotaGlobal . serial_in_buffer [ 8 ] < < 8 | TasmotaGlobal . serial_in_buffer [ 9 ] ;
2017-08-29 15:35:45 +01:00
2017-11-17 16:52:31 +00:00
unsigned long now = millis ( ) ;
2024-03-14 16:21:09 +00:00
if ( ! ( ( received_id = = SnfBridge . last_received_id ) & & ( now - SnfBridge . last_time < Settings - > rf_duplicate_time ) ) ) {
2019-08-13 15:10:47 +01:00
SnfBridge . last_received_id = received_id ;
SnfBridge . last_time = now ;
2018-01-06 16:34:42 +00:00
strncpy_P ( rfkey , PSTR ( " \" " D_JSON_NONE " \" " ) , sizeof ( rfkey ) ) ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 1 ; i < = 16 ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > rf_code [ i ] [ 0 ] ) {
uint32_t send_id = Settings - > rf_code [ i ] [ 6 ] < < 16 | Settings - > rf_code [ i ] [ 7 ] < < 8 | Settings - > rf_code [ i ] [ 8 ] ;
2017-11-17 16:52:31 +00:00
if ( send_id = = received_id ) {
snprintf_P ( rfkey , sizeof ( rfkey ) , PSTR ( " %d " ) , i ) ;
break ;
}
2017-08-29 15:35:45 +01:00
}
2017-08-05 14:11:50 +01:00
}
2021-06-11 17:14:12 +01:00
if ( Settings - > flag . rf_receive_decimal ) { // SetOption28 - RF receive data format
2018-06-25 11:33:23 +01:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %u " ) , received_id ) ;
} else {
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " \" %06X \" " ) , received_id ) ;
}
2024-03-14 16:21:09 +00:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_RFRECEIVED " \" :{ \" " D_JSON_SYNC " \" :%d, \" " D_JSON_LOW " \" :%d, \" " D_JSON_HIGH " \" :%d, \" " D_JSON_DATA " \" :%s, \" " D_CMND_PREFIX_RF D_CMND_RFKEY " \" :%s}} " ) ,
2018-06-25 11:33:23 +01:00
sync_time , low_time , high_time , stemp , rfkey ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_RFRECEIVED ) ) ;
2017-11-17 16:52:31 +00:00
# ifdef USE_DOMOTICZ
DomoticzSensor ( DZ_COUNT , received_id ) ; // Send rid as Domoticz Counter value
# endif // USE_DOMOTICZ
2017-08-05 14:11:50 +01:00
}
}
2017-09-02 13:37:02 +01:00
}
2017-07-30 16:55:37 +01:00
}
2019-01-28 13:08:33 +00:00
bool SonoffBridgeSerialInput ( void )
2017-07-30 16:55:37 +01:00
{
2017-12-03 14:00:31 +00:00
// iTead Rf Universal Transceiver Module Serial Protocol Version 1.0 (20170420)
2018-10-16 08:53:48 +01:00
static int8_t receive_len = 0 ;
2018-08-27 17:16:28 +01:00
2019-08-13 15:10:47 +01:00
if ( SnfBridge . receive_flag ) {
if ( SnfBridge . receive_raw_flag ) {
2020-10-29 11:21:24 +00:00
if ( ! TasmotaGlobal . serial_in_byte_counter ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = 0xAA ;
2018-06-04 17:10:38 +01:00
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = TasmotaGlobal . serial_in_byte ;
2020-10-29 11:21:24 +00:00
if ( TasmotaGlobal . serial_in_byte_counter = = 3 ) {
2020-10-30 11:29:48 +00:00
if ( ( 0xA6 = = TasmotaGlobal . serial_in_buffer [ 1 ] ) | | ( 0xAB = = TasmotaGlobal . serial_in_buffer [ 1 ] ) ) { // AA A6 06 023908010155 55 - 06 is receive_len
receive_len = TasmotaGlobal . serial_in_buffer [ 2 ] + 4 ; // Get at least receive_len bytes
2018-08-27 17:16:28 +01:00
}
}
2020-10-30 11:29:48 +00:00
if ( ( ! receive_len & & ( 0x55 = = TasmotaGlobal . serial_in_byte ) ) | | ( receive_len & & ( TasmotaGlobal . serial_in_byte_counter = = receive_len ) ) ) { // 0x55 - End of text
2018-06-04 17:10:38 +01:00
SonoffBridgeReceivedRaw ( ) ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_flag = 0 ;
2018-06-04 17:10:38 +01:00
return 1 ;
}
}
2020-10-30 11:29:48 +00:00
else if ( ! ( ( 0 = = TasmotaGlobal . serial_in_byte_counter ) & & ( 0 = = TasmotaGlobal . serial_in_byte ) ) ) { // Skip leading 0
2020-10-29 11:21:24 +00:00
if ( 0 = = TasmotaGlobal . serial_in_byte_counter ) {
2019-08-13 15:10:47 +01:00
SnfBridge . expected_bytes = 2 ; // 0xA0, 0xA1, 0xA2
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . serial_in_byte > = 0xA3 ) {
2019-08-13 15:10:47 +01:00
SnfBridge . expected_bytes = 11 ; // 0xA3, 0xA4, 0xA5
2017-12-03 14:00:31 +00:00
}
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . serial_in_byte = = 0xA6 ) {
2019-08-13 15:10:47 +01:00
SnfBridge . expected_bytes = 0 ; // 0xA6 and up supported by Portisch firmware only
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = 0xAA ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_raw_flag = 1 ;
2018-06-04 17:10:38 +01:00
}
2017-12-03 14:00:31 +00:00
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_buffer [ TasmotaGlobal . serial_in_byte_counter + + ] = TasmotaGlobal . serial_in_byte ;
if ( ( SnfBridge . expected_bytes = = TasmotaGlobal . serial_in_byte_counter ) & & ( 0x55 = = TasmotaGlobal . serial_in_byte ) ) { // 0x55 - End of text
2017-10-18 17:22:34 +01:00
SonoffBridgeReceived ( ) ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_flag = 0 ;
2017-07-30 16:55:37 +01:00
return 1 ;
}
}
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_byte = 0 ;
2017-07-30 16:55:37 +01:00
}
2020-10-30 11:29:48 +00:00
if ( 0xAA = = TasmotaGlobal . serial_in_byte ) { // 0xAA - Start of text
2020-10-29 11:21:24 +00:00
TasmotaGlobal . serial_in_byte_counter = 0 ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . serial_in_byte = 0 ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_flag = 1 ;
2018-10-16 10:21:44 +01:00
receive_len = 0 ;
2017-07-30 16:55:37 +01:00
}
return 0 ;
}
2019-01-28 13:08:33 +00:00
void SonoffBridgeSendCommand ( uint8_t code )
2018-06-04 17:10:38 +01:00
{
Serial . write ( 0xAA ) ; // Start of Text
Serial . write ( code ) ; // Command or Acknowledge
Serial . write ( 0x55 ) ; // End of Text
}
2018-11-14 13:32:09 +00:00
void SonoffBridgeSendAck ( void )
2017-07-30 16:55:37 +01:00
{
Serial . write ( 0xAA ) ; // Start of Text
Serial . write ( 0xA0 ) ; // Acknowledge
Serial . write ( 0x55 ) ; // End of Text
}
2017-10-29 17:18:46 +00:00
void SonoffBridgeSendCode ( uint32_t code )
{
Serial . write ( 0xAA ) ; // Start of Text
Serial . write ( 0xA5 ) ; // Send following code
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < 6 ; i + + ) {
2021-06-11 17:14:12 +01:00
Serial . write ( Settings - > rf_code [ 0 ] [ i ] ) ;
2017-10-29 17:18:46 +00:00
}
2017-10-30 10:42:34 +00:00
Serial . write ( ( code > > 16 ) & 0xff ) ;
Serial . write ( ( code > > 8 ) & 0xff ) ;
2017-10-29 17:18:46 +00:00
Serial . write ( code & 0xff ) ;
Serial . write ( 0x55 ) ; // End of Text
Serial . flush ( ) ;
}
2017-10-18 17:22:34 +01:00
void SonoffBridgeSend ( uint8_t idx , uint8_t key )
2017-07-30 16:55:37 +01:00
{
uint8_t code ;
2017-09-02 13:37:02 +01:00
2017-07-30 16:55:37 +01:00
key - - ; // Support 1 to 16
Serial . write ( 0xAA ) ; // Start of Text
Serial . write ( 0xA5 ) ; // Send following code
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < 8 ; i + + ) {
2021-06-11 17:14:12 +01:00
Serial . write ( Settings - > rf_code [ idx ] [ i ] ) ;
2017-07-30 16:55:37 +01:00
}
if ( 0 = = idx ) {
2017-10-10 14:40:02 +01:00
code = ( 0x10 < < ( key > > 2 ) ) | ( 1 < < ( key & 3 ) ) ; // 11,12,14,18,21,22,24,28,41,42,44,48,81,82,84,88
2017-07-30 16:55:37 +01:00
} else {
2021-06-11 17:14:12 +01:00
code = Settings - > rf_code [ idx ] [ 8 ] ;
2017-07-30 16:55:37 +01:00
}
Serial . write ( code ) ;
Serial . write ( 0x55 ) ; // End of Text
Serial . flush ( ) ;
2017-10-02 15:45:03 +01:00
# ifdef USE_DOMOTICZ
2021-06-11 17:14:12 +01:00
// uint32_t rid = Settings->rf_code[idx][6] << 16 | Settings->rf_code[idx][7] << 8 | code;
2017-10-18 17:22:34 +01:00
// DomoticzSensor(DZ_COUNT, rid); // Send rid as Domoticz Counter value
2017-10-02 15:45:03 +01:00
# endif // USE_DOMOTICZ
2017-07-30 16:55:37 +01:00
}
2017-10-18 17:22:34 +01:00
void SonoffBridgeLearn ( uint8_t key )
2017-07-30 16:55:37 +01:00
{
2019-08-13 15:10:47 +01:00
SnfBridge . learn_key = key ;
SnfBridge . learn_active = 1 ;
SnfBridge . last_learn_time = millis ( ) ;
2017-07-30 16:55:37 +01:00
Serial . write ( 0xAA ) ; // Start of Text
Serial . write ( 0xA1 ) ; // Start learning
Serial . write ( 0x55 ) ; // End of Text
}
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-08-13 16:33:35 +01:00
void CmndRfBridge ( void ) // RfSync, RfLow, RfHigh, RfHost and RfCode
2017-07-30 16:55:37 +01:00
{
2019-08-01 15:38:52 +01:00
char * p ;
char stemp [ 10 ] ;
uint32_t code = 0 ;
uint8_t radix = 10 ;
2017-10-29 17:18:46 +00:00
2019-08-13 15:10:47 +01:00
uint32_t set_index = XdrvMailbox . command_code * 2 ;
2019-08-01 15:38:52 +01:00
if ( XdrvMailbox . data [ 0 ] = = ' # ' ) {
XdrvMailbox . data + + ;
XdrvMailbox . data_len - - ;
radix = 16 ;
2018-05-09 09:49:43 +01:00
}
2017-10-29 17:18:46 +00:00
2019-08-01 15:38:52 +01:00
if ( XdrvMailbox . data_len ) {
code = strtol ( XdrvMailbox . data , & p , radix ) ;
if ( code ) {
2019-08-13 15:10:47 +01:00
if ( CMND_RFCODE = = XdrvMailbox . command_code ) {
SnfBridge . last_send_code = code ;
2019-08-01 15:38:52 +01:00
SonoffBridgeSendCode ( code ) ;
} else {
if ( 1 = = XdrvMailbox . payload ) {
code = pgm_read_byte ( kDefaultRfCode + set_index ) < < 8 | pgm_read_byte ( kDefaultRfCode + set_index + 1 ) ;
}
uint8_t msb = code > > 8 ;
uint8_t lsb = code & 0xFF ;
if ( ( code > 0 ) & & ( code < 0x7FFF ) & & ( msb ! = 0x55 ) & & ( lsb ! = 0x55 ) ) { // Check for End of Text codes
2021-06-11 17:14:12 +01:00
Settings - > rf_code [ 0 ] [ set_index ] = msb ;
Settings - > rf_code [ 0 ] [ set_index + 1 ] = lsb ;
2017-10-29 17:18:46 +00:00
}
}
2017-10-30 10:42:34 +00:00
}
2017-07-30 16:55:37 +01:00
}
2019-08-13 15:10:47 +01:00
if ( CMND_RFCODE = = XdrvMailbox . command_code ) {
code = SnfBridge . last_send_code ;
2019-08-01 15:38:52 +01:00
} else {
2021-06-11 17:14:12 +01:00
code = Settings - > rf_code [ 0 ] [ set_index ] < < 8 | Settings - > rf_code [ 0 ] [ set_index + 1 ] ;
2019-08-01 15:38:52 +01:00
}
if ( 10 = = radix ) {
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " %d " ) , code ) ;
} else {
2019-11-06 19:48:26 +00:00
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " \" #%06X \" " ) , code ) ;
2019-08-01 15:38:52 +01:00
}
Response_P ( S_JSON_COMMAND_XVALUE , XdrvMailbox . command , stemp ) ;
}
void CmndRfKey ( void )
{
if ( ( XdrvMailbox . index > 0 ) & & ( XdrvMailbox . index < = 16 ) ) {
2017-11-17 16:52:31 +00:00
unsigned long now = millis ( ) ;
2019-08-13 15:10:47 +01:00
if ( ( ! SnfBridge . learn_active ) | | ( now - SnfBridge . last_learn_time > 60100 ) ) {
SnfBridge . learn_active = 0 ;
2018-01-05 11:26:19 +00:00
if ( 2 = = XdrvMailbox . payload ) { // Learn RF data
SonoffBridgeLearn ( XdrvMailbox . index ) ;
2020-05-04 19:00:05 +01:00
ResponseCmndIdxChar ( PSTR ( D_JSON_START_LEARNING ) ) ;
2017-07-30 16:55:37 +01:00
}
2018-01-05 11:26:19 +00:00
else if ( 3 = = XdrvMailbox . payload ) { // Unlearn RF data
2021-06-11 17:14:12 +01:00
Settings - > rf_code [ XdrvMailbox . index ] [ 0 ] = 0 ; // Reset sync_time MSB
2020-05-04 19:00:05 +01:00
ResponseCmndIdxChar ( PSTR ( D_JSON_SET_TO_DEFAULT ) ) ;
2017-10-30 10:42:34 +00:00
}
2018-01-05 11:26:19 +00:00
else if ( 4 = = XdrvMailbox . payload ) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < 6 ; i + + ) {
2021-06-11 17:14:12 +01:00
Settings - > rf_code [ XdrvMailbox . index ] [ i ] = Settings - > rf_code [ 0 ] [ i ] ;
2017-10-30 10:42:34 +00:00
}
2021-06-11 17:14:12 +01:00
Settings - > rf_code [ XdrvMailbox . index ] [ 6 ] = ( SnfBridge . last_send_code > > 16 ) & 0xff ;
Settings - > rf_code [ XdrvMailbox . index ] [ 7 ] = ( SnfBridge . last_send_code > > 8 ) & 0xff ;
Settings - > rf_code [ XdrvMailbox . index ] [ 8 ] = SnfBridge . last_send_code & 0xff ;
2020-05-04 19:00:05 +01:00
ResponseCmndIdxChar ( PSTR ( D_JSON_SAVED ) ) ;
2018-02-18 15:47:26 +00:00
} else if ( 5 = = XdrvMailbox . payload ) { // Show default or learned RF data
uint8_t key = XdrvMailbox . index ;
2021-06-11 17:14:12 +01:00
uint8_t index = ( 0 = = Settings - > rf_code [ key ] [ 0 ] ) ? 0 : key ; // Use default if sync_time MSB = 0
uint16_t sync_time = ( Settings - > rf_code [ index ] [ 0 ] < < 8 ) | Settings - > rf_code [ index ] [ 1 ] ;
uint16_t low_time = ( Settings - > rf_code [ index ] [ 2 ] < < 8 ) | Settings - > rf_code [ index ] [ 3 ] ;
uint16_t high_time = ( Settings - > rf_code [ index ] [ 4 ] < < 8 ) | Settings - > rf_code [ index ] [ 5 ] ;
uint32_t code = ( Settings - > rf_code [ index ] [ 6 ] < < 16 ) | ( Settings - > rf_code [ index ] [ 7 ] < < 8 ) ;
2018-02-18 15:47:26 +00:00
if ( 0 = = index ) {
key - - ;
code | = ( uint8_t ) ( ( 0x10 < < ( key > > 2 ) ) | ( 1 < < ( key & 3 ) ) ) ;
} else {
2021-06-11 17:14:12 +01:00
code | = Settings - > rf_code [ index ] [ 8 ] ;
2018-02-18 15:47:26 +00:00
}
2019-03-23 16:00:59 +00:00
Response_P ( PSTR ( " { \" %s%d \" :{ \" " D_JSON_SYNC " \" :%d, \" " D_JSON_LOW " \" :%d, \" " D_JSON_HIGH " \" :%d, \" " D_JSON_DATA " \" : \" %06X \" }} " ) ,
2019-08-01 15:38:52 +01:00
XdrvMailbox . command , XdrvMailbox . index , sync_time , low_time , high_time , code ) ;
2017-07-30 16:55:37 +01:00
} else {
2021-06-11 17:14:12 +01:00
if ( ( 1 = = XdrvMailbox . payload ) | | ( 0 = = Settings - > rf_code [ XdrvMailbox . index ] [ 0 ] ) ) { // Test sync_time MSB
2018-01-05 11:26:19 +00:00
SonoffBridgeSend ( 0 , XdrvMailbox . index ) ; // Send default RF data
2020-05-04 19:00:05 +01:00
ResponseCmndIdxChar ( PSTR ( D_JSON_DEFAULT_SENT ) ) ;
2017-07-30 16:55:37 +01:00
} else {
2018-01-05 11:26:19 +00:00
SonoffBridgeSend ( XdrvMailbox . index , 0 ) ; // Send learned RF data
2020-05-04 19:00:05 +01:00
ResponseCmndIdxChar ( PSTR ( D_JSON_LEARNED_SENT ) ) ;
2017-07-30 16:55:37 +01:00
}
}
} else {
2019-08-13 15:10:47 +01:00
Response_P ( S_JSON_COMMAND_INDEX_SVALUE , XdrvMailbox . command , SnfBridge . learn_key , D_JSON_LEARNING_ACTIVE ) ;
2017-07-30 16:55:37 +01:00
}
2018-06-04 17:10:38 +01:00
}
2019-08-01 15:38:52 +01:00
}
void CmndRfRaw ( void )
{
if ( XdrvMailbox . data_len ) {
if ( XdrvMailbox . data_len < 6 ) { // On, Off
switch ( XdrvMailbox . payload ) {
case 0 : // Receive Raw Off
SonoffBridgeSendCommand ( 0xA7 ) ; // Stop reading RF signals enabling iTead default RF handling
case 1 : // Receive Raw On
2019-08-13 15:10:47 +01:00
SnfBridge . receive_raw_flag = XdrvMailbox . payload ;
2019-08-01 15:38:52 +01:00
break ;
case 166 : // 0xA6 - Start reading RF signals disabling iTead default RF handling
case 167 : // 0xA7 - Stop reading RF signals enabling iTead default RF handling
case 169 : // 0xA9 - Start learning predefined protocols
case 176 : // 0xB0 - Stop sniffing
case 177 : // 0xB1 - Start sniffing
case 255 : // 0xFF - Show firmware version
SonoffBridgeSendCommand ( XdrvMailbox . payload ) ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_raw_flag = 1 ;
2019-08-01 15:38:52 +01:00
break ;
case 192 : // 0xC0 - Beep
char beep [ ] = " AAC000C055 \0 " ;
SerialSendRaw ( beep ) ;
break ;
2018-06-04 17:10:38 +01:00
}
2019-08-01 15:38:52 +01:00
} else {
SerialSendRaw ( RemoveSpace ( XdrvMailbox . data ) ) ;
2019-08-13 15:10:47 +01:00
SnfBridge . receive_raw_flag = 1 ;
2018-06-04 17:10:38 +01:00
}
2019-08-01 15:38:52 +01:00
}
2019-08-13 15:10:47 +01:00
ResponseCmndStateText ( SnfBridge . receive_raw_flag ) ;
2018-06-04 17:10:38 +01:00
}
2024-03-14 16:21:09 +00:00
void CmndRfTimeout ( void ) {
if ( XdrvMailbox . payload > = 10 ) {
Settings - > rf_duplicate_time = XdrvMailbox . payload ;
}
ResponseCmndNumber ( Settings - > rf_duplicate_time ) ;
}
2021-12-15 11:14:52 +00:00
# ifdef USE_WEBSERVER
2021-12-15 11:30:48 +00:00
void SonoffBridgeAddButton ( void ) {
2021-12-15 11:14:52 +00:00
WSContentSend_P ( HTTP_TABLE100 ) ;
WSContentSend_P ( PSTR ( " <tr> " ) ) ;
char number [ 4 ] ;
uint32_t idx = 0 ;
for ( uint32_t i = 0 ; i < 4 ; i + + ) {
if ( idx > 0 ) { WSContentSend_P ( PSTR ( " </tr><tr> " ) ) ; }
for ( uint32_t j = 0 ; j < 4 ; j + + ) {
idx + + ;
WSContentSend_P ( PSTR ( " <td style='width:25%%'><button onclick='la( \" &k=%d \" );'>%s</button></td> " ) , idx , // &k is related to WebGetArg("k", tmp, sizeof(tmp));
2023-12-18 11:31:14 +00:00
( strlen ( GetWebButton ( idx - 1 ) ) ) ? HtmlEscape ( GetWebButton ( idx - 1 ) ) . c_str ( ) : itoa ( idx , number , 10 ) ) ;
2021-12-15 11:14:52 +00:00
}
}
WSContentSend_P ( PSTR ( " </tr></table> " ) ) ;
}
void SonoffBridgeWebGetArg ( void ) {
char tmp [ 8 ] ; // WebGetArg numbers only
WebGetArg ( PSTR ( " k " ) , tmp , sizeof ( tmp ) ) ; // 1 - 16 Pre defined RF keys
if ( strlen ( tmp ) ) {
char command [ 20 ] ;
2024-03-14 16:21:09 +00:00
snprintf_P ( command , sizeof ( command ) , PSTR ( D_CMND_PREFIX_RF D_CMND_RFKEY " %s " ) , tmp ) ;
2021-12-15 11:14:52 +00:00
ExecuteWebCommand ( command ) ;
}
}
# endif // USE_WEBSERVER
2018-01-05 11:26:19 +00:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-11-11 09:44:56 +00:00
bool Xdrv06 ( uint32_t function )
2018-01-05 11:26:19 +00:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-01-05 11:26:19 +00:00
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-10-30 11:29:48 +00:00
if ( SONOFF_BRIDGE = = TasmotaGlobal . module_type ) {
2018-01-05 11:26:19 +00:00
switch ( function ) {
2019-08-01 15:38:52 +01:00
case FUNC_SERIAL :
result = SonoffBridgeSerialInput ( ) ;
break ;
2018-01-05 11:26:19 +00:00
case FUNC_COMMAND :
2019-08-01 15:38:52 +01:00
result = DecodeCommand ( kSonoffBridgeCommands , SonoffBridgeCommand ) ;
2018-09-04 15:22:34 +01:00
break ;
2021-12-15 11:14:52 +00:00
# ifdef USE_WEBSERVER
case FUNC_WEB_ADD_MAIN_BUTTON :
2021-12-15 11:30:48 +00:00
SonoffBridgeAddButton ( ) ;
2021-12-15 11:14:52 +00:00
break ;
case FUNC_WEB_GET_ARG :
SonoffBridgeWebGetArg ( ) ;
break ;
# endif // USE_WEBSERVER
2019-08-13 15:10:47 +01:00
case FUNC_INIT :
2024-03-14 16:21:09 +00:00
if ( Settings - > rf_duplicate_time < 10 ) {
Settings - > rf_duplicate_time = SFB_TIME_AVOID_DUPLICATE ;
}
2019-08-13 15:10:47 +01:00
SnfBridge . receive_raw_flag = 0 ;
SonoffBridgeSendCommand ( 0xA7 ) ; // Stop reading RF signals enabling iTead default RF handling
break ;
case FUNC_PRE_INIT :
2019-12-29 12:27:48 +00:00
SetSerial ( 19200 , TS_SERIAL_8N1 ) ;
2019-08-13 15:10:47 +01:00
break ;
2023-12-27 21:03:56 +00:00
case FUNC_ACTIVE :
result = true ;
break ;
2018-01-05 11:26:19 +00:00
}
}
2020-04-10 17:24:08 +01:00
# endif // ESP8266
2018-01-05 11:26:19 +00:00
return result ;
}
2019-10-15 10:25:00 +01:00
2019-10-15 14:37:22 +01:00
# endif // USE_SONOFF_RF