2020-06-19 19:54:37 +01:00
/*
xdrv_23_zigbee_9_serial . ino - zigbee : serial communication with MCU
Copyright ( C ) 2020 Theo Arends and Stephan Hadinger
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_ZIGBEE
# ifdef USE_ZIGBEE_ZNP
const uint32_t ZIGBEE_BUFFER_SIZE = 256 ; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255
const uint8_t ZIGBEE_SOF = 0xFE ;
const uint8_t ZIGBEE_SOF_ALT = 0xFF ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
const uint32_t ZIGBEE_BUFFER_SIZE = 256 ;
const uint8_t ZIGBEE_EZSP_CANCEL = 0x1A ; // cancel byte
const uint8_t ZIGBEE_EZSP_EOF = 0x7E ; // end of frame
const uint8_t ZIGBEE_EZSP_ESCAPE = 0x7D ; // escape byte
2020-07-04 18:59:59 +01:00
const uint32_t ZIGBEE_LED_RECEIVE = 0 ; // LED<1> blinks when receiving
const uint32_t ZIGBEE_LED_SEND = 0 ; // LED<2> blinks when receiving
2020-06-19 19:54:37 +01:00
class EZSP_Serial_t {
public :
2020-07-22 18:29:16 +01:00
uint8_t to_send = 0 ; // 0..7, frame number of next packet to send, nothing to send if equal to to_end
uint8_t to_end = 0 ; // 0..7, frame number of next packet to send
uint8_t to_ack = 0 ; // 0..7, frame number of last packet acknowledged + 1
2020-06-19 19:54:37 +01:00
uint8_t from_ack = 0 ; // 0..7, frame to ack
uint8_t ezsp_seq = 0 ; // 0..255, EZSP sequence number
2020-07-22 18:29:16 +01:00
SBuffer * to_packets [ 8 ] = { nullptr , nullptr , nullptr , nullptr , nullptr , nullptr , nullptr , nullptr } ;
2020-06-19 19:54:37 +01:00
} ;
2020-07-22 18:29:16 +01:00
2020-06-19 19:54:37 +01:00
EZSP_Serial_t EZSP_Serial ;
2020-08-08 19:56:09 +01:00
//
// Blink Led Status
//
const uint32_t Z_LED_STATUS_ON_MILLIS = 50 ; // keep led on at least 50 ms
bool Z_LedStatusSet ( bool onoff ) {
static bool led_status_on = false ;
static uint32_t led_on_time = 0 ;
if ( onoff ) {
SetLedPowerIdx ( ZIGBEE_LED_RECEIVE , 1 ) ;
led_status_on = true ;
led_on_time = millis ( ) ;
} else if ( ( led_status_on ) & & ( TimePassedSince ( led_on_time ) > = Z_LED_STATUS_ON_MILLIS ) ) {
SetLedPowerIdx ( ZIGBEE_LED_RECEIVE , 0 ) ;
led_status_on = false ;
}
return led_status_on ;
}
2020-08-08 19:59:35 +01:00
# endif // USE_ZIGBEE_EZSP
# include <TasmotaSerial.h>
TasmotaSerial * ZigbeeSerial = nullptr ;
2020-06-19 19:54:37 +01:00
/********************************************************************************************/
//
// Called at event loop, checks for incoming data from the CC2530
//
void ZigbeeInputLoop ( void ) {
# ifdef USE_ZIGBEE_ZNP
static uint32_t zigbee_polling_window = 0 ; // number of milliseconds since first byte
static uint8_t fcs = ZIGBEE_SOF ;
static uint32_t zigbee_frame_len = 5 ; // minimal zigbee frame length, will be updated when buf[1] is read
// Receive only valid ZNP frames:
// 00 - SOF = 0xFE
// 01 - Length of Data Field - 0..250
// 02 - CMD1 - first byte of command
// 03 - CMD2 - second byte of command
// 04..FD - Data Field
// FE (or last) - FCS Checksum
while ( ZigbeeSerial - > available ( ) ) {
yield ( ) ;
uint8_t zigbee_in_byte = ZigbeeSerial - > read ( ) ;
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZbInput byte=%d len=%d"), zigbee_in_byte, zigbee_buffer->len());
2020-06-19 19:54:37 +01:00
if ( 0 = = zigbee_buffer - > len ( ) ) { // make sure all variables are correctly initialized
zigbee_frame_len = 5 ;
fcs = ZIGBEE_SOF ;
// there is a rare race condition when an interrupt occurs when receiving the first byte
// in this case the first bit (lsb) is missed and Tasmota receives 0xFF instead of 0xFE
// We forgive this mistake, and next bytes are automatically resynchronized
if ( ZIGBEE_SOF_ALT = = zigbee_in_byte ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( " ZbInput forgiven first byte %02X (only for statistics) " ) , zigbee_in_byte ) ;
2020-06-19 19:54:37 +01:00
zigbee_in_byte = ZIGBEE_SOF ;
}
}
if ( ( 0 = = zigbee_buffer - > len ( ) ) & & ( ZIGBEE_SOF ! = zigbee_in_byte ) ) {
// waiting for SOF (Start Of Frame) byte, discard anything else
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( " ZbInput discarding byte %02X " ) , zigbee_in_byte ) ;
2020-06-19 19:54:37 +01:00
continue ; // discard
}
if ( zigbee_buffer - > len ( ) < zigbee_frame_len ) {
zigbee_buffer - > add8 ( zigbee_in_byte ) ;
zigbee_polling_window = millis ( ) ; // Wait for more data
fcs ^ = zigbee_in_byte ;
}
if ( zigbee_buffer - > len ( ) > = zigbee_frame_len ) {
zigbee_polling_window = 0 ; // Publish now
break ;
}
// recalculate frame length
if ( 02 = = zigbee_buffer - > len ( ) ) {
// We just received the Lenght byte
uint8_t len_byte = zigbee_buffer - > get8 ( 1 ) ;
if ( len_byte > 250 ) len_byte = 250 ; // ZNP spec says len is 250 max
zigbee_frame_len = len_byte + 5 ; // SOF + LEN + CMD1 + CMD2 + FCS = 5 bytes overhead
}
}
if ( zigbee_buffer - > len ( ) & & ( millis ( ) > ( zigbee_polling_window + ZIGBEE_POLLING ) ) ) {
char hex_char [ ( zigbee_buffer - > len ( ) * 2 ) + 2 ] ;
ToHex_P ( ( unsigned char * ) zigbee_buffer - > getBuffer ( ) , zigbee_buffer - > len ( ) , hex_char , sizeof ( hex_char ) ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
2020-06-19 19:54:37 +01:00
// buffer received, now check integrity
if ( zigbee_buffer - > len ( ) ! = zigbee_frame_len ) {
// Len is not correct, log and reject frame
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_JSON_ZIGBEEZNPRECEIVED " : received frame of wrong size %s, len %d, expected %d " ) , hex_char , zigbee_buffer - > len ( ) , zigbee_frame_len ) ;
2020-06-19 19:54:37 +01:00
} else if ( 0x00 ! = fcs ) {
// FCS is wrong, packet is corrupt, log and reject frame
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_JSON_ZIGBEEZNPRECEIVED " : received bad FCS frame %s, %d " ) , hex_char , fcs ) ;
2020-06-19 19:54:37 +01:00
} else {
// frame is correct
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received correct frame %s"), hex_char);
2020-06-19 19:54:37 +01:00
SBuffer znp_buffer = zigbee_buffer - > subBuffer ( 2 , zigbee_frame_len - 3 ) ; // remove SOF, LEN and FCS
ToHex_P ( ( unsigned char * ) znp_buffer . getBuffer ( ) , znp_buffer . len ( ) , hex_char , sizeof ( hex_char ) ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEEZNPRECEIVED " \" : \" %s \" } " ) , hex_char ) ;
if ( Settings . flag3 . tuya_serial_mqtt_publish ) {
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( TELE , PSTR ( D_RSLT_SENSOR ) ) ;
2020-06-19 19:54:37 +01:00
} else {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE " %s " ) , TasmotaGlobal . mqtt_data ) ;
2020-06-19 19:54:37 +01:00
}
// now process the message
ZigbeeProcessInput ( znp_buffer ) ;
}
zigbee_buffer - > setLen ( 0 ) ; // empty buffer
}
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
static uint32_t zigbee_polling_window = 0 ; // number of milliseconds since first byte
static bool escape = false ; // was the previous byte an escape?
bool frame_complete = false ; // frame is ready and complete
// Receive only valid EZSP frames:
// 1A - Cancel - cancel all previous bytes
// 7D - Escape byte - following byte is escaped
// 7E - end of frame
2020-08-08 19:56:09 +01:00
Z_LedStatusSet ( false ) ;
2020-06-19 19:54:37 +01:00
while ( ZigbeeSerial - > available ( ) ) {
2020-08-08 19:56:09 +01:00
Z_LedStatusSet ( true ) ; // turn on receive LED<1>
2020-07-04 18:59:59 +01:00
2020-06-19 19:54:37 +01:00
yield ( ) ;
uint8_t zigbee_in_byte = ZigbeeSerial - > read ( ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x%02X len=%d"), zigbee_in_byte, zigbee_buffer->len());
2020-06-19 19:54:37 +01:00
// if (0 == zigbee_buffer->len()) { // make sure all variables are correctly initialized
// escape = false;
// frame_complete = false;
// }
if ( ( 0x11 = = zigbee_in_byte ) | | ( 0x13 = = zigbee_in_byte ) ) {
continue ; // ignore reserved bytes XON/XOFF
}
if ( ZIGBEE_EZSP_ESCAPE = = zigbee_in_byte ) {
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: Escape byte received"));
2020-06-19 19:54:37 +01:00
escape = true ;
continue ;
}
if ( ZIGBEE_EZSP_CANCEL = = zigbee_in_byte ) {
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: ZbInput byte=0x1A, cancel byte received, discarding %d bytes"), zigbee_buffer->len());
2020-06-19 19:54:37 +01:00
zigbee_buffer - > setLen ( 0 ) ; // empty buffer
escape = false ;
frame_complete = false ;
continue ; // re-loop
}
if ( ZIGBEE_EZSP_EOF = = zigbee_in_byte ) {
// end of frame
frame_complete = true ;
break ;
}
if ( zigbee_buffer - > len ( ) < ZIGBEE_BUFFER_SIZE ) {
if ( escape ) {
// invert bit 5
2020-07-20 16:24:51 +01:00
zigbee_in_byte ^ = 0x20 ;
2020-06-19 19:54:37 +01:00
escape = false ;
}
zigbee_buffer - > add8 ( zigbee_in_byte ) ;
zigbee_polling_window = millis ( ) ; // Wait for more data
} // adding bytes
} // while (ZigbeeSerial->available())
uint32_t frame_len = zigbee_buffer - > len ( ) ;
if ( frame_complete | | ( frame_len & & ( millis ( ) > ( zigbee_polling_window + ZIGBEE_POLLING ) ) ) ) {
char hex_char [ frame_len * 2 + 2 ] ;
ToHex_P ( ( unsigned char * ) zigbee_buffer - > getBuffer ( ) , zigbee_buffer - > len ( ) , hex_char , sizeof ( hex_char ) ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric());
2020-06-19 19:54:37 +01:00
if ( ( frame_complete ) & & ( frame_len > = 3 ) ) {
// frame received and has at least 3 bytes (without EOF), checking CRC
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEE_EZSP_RECEIVED ": received raw frame %s"), hex_char);
2020-06-19 19:54:37 +01:00
uint16_t crc = 0xFFFF ; // frame CRC
// compute CRC
for ( uint32_t i = 0 ; i < frame_len - 2 ; i + + ) {
crc = crc ^ ( ( uint16_t ) zigbee_buffer - > get8 ( i ) < < 8 ) ;
for ( uint32_t i = 0 ; i < 8 ; i + + ) {
if ( crc & 0x8000 ) {
crc = ( crc < < 1 ) ^ 0x1021 ; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard
} else {
crc < < = 1 ;
}
}
}
uint16_t crc_received = zigbee_buffer - > get8 ( frame_len - 2 ) < < 8 | zigbee_buffer - > get8 ( frame_len - 1 ) ;
// remove 2 last bytes
if ( crc_received ! = crc ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_JSON_ZIGBEE_EZSP_RECEIVED " : bad crc (received 0x%04X, computed 0x%04X) %s " ) , crc_received , crc , hex_char ) ;
2020-06-19 19:54:37 +01:00
} else {
// copy buffer
SBuffer ezsp_buffer = zigbee_buffer - > subBuffer ( 0 , frame_len - 2 ) ; // CRC
// CRC is correct, apply de-stuffing if DATA frame
if ( 0 = = ( ezsp_buffer . get8 ( 0 ) & 0x80 ) ) {
// DATA frame
uint8_t rand = 0x42 ;
for ( uint32_t i = 1 ; i < ezsp_buffer . len ( ) ; i + + ) {
ezsp_buffer . set8 ( i , ezsp_buffer . get8 ( i ) ^ rand ) ;
if ( rand & 1 ) { rand = ( rand > > 1 ) ^ 0xB8 ; }
else { rand = ( rand > > 1 ) ; }
}
}
ToHex_P ( ( unsigned char * ) ezsp_buffer . getBuffer ( ) , ezsp_buffer . len ( ) , hex_char , sizeof ( hex_char ) ) ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_ZIGBEE " { \" " D_JSON_ZIGBEE_EZSP_RECEIVED " 2 \" : \" %s \" } " ) , hex_char ) ;
2020-06-19 19:54:37 +01:00
// now process the message
ZigbeeProcessInputRaw ( ezsp_buffer ) ;
}
} else {
// the buffer timed-out, print error and discard
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( D_JSON_ZIGBEE_EZSP_RECEIVED " : time-out, discarding %s, %d " ) , hex_char ) ;
2020-06-19 19:54:37 +01:00
}
zigbee_buffer - > setLen ( 0 ) ; // empty buffer
escape = false ;
frame_complete = false ;
}
# endif // USE_ZIGBEE_EZSP
}
/********************************************************************************************/
// Initialize internal structures
void ZigbeeInitSerial ( void )
{
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem1 = %d"), ESP_getFreeHeap());
2020-06-19 19:54:37 +01:00
zigbee . active = false ;
if ( PinUsed ( GPIO_ZIGBEE_RX ) & & PinUsed ( GPIO_ZIGBEE_TX ) ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_ZIGBEE " GPIOs Rx:%d Tx:%d " ) , Pin ( GPIO_ZIGBEE_RX ) , Pin ( GPIO_ZIGBEE_TX ) ) ;
2020-10-30 11:29:48 +00:00
// if TasmotaGlobal.seriallog_level is 0, we allow GPIO 13/15 to switch to Hardware Serial
ZigbeeSerial = new TasmotaSerial ( Pin ( GPIO_ZIGBEE_RX ) , Pin ( GPIO_ZIGBEE_TX ) , TasmotaGlobal . seriallog_level ? 1 : 2 , 0 , 256 ) ; // set a receive buffer of 256 bytes
2020-06-19 19:54:37 +01:00
ZigbeeSerial - > begin ( 115200 ) ;
if ( ZigbeeSerial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
2020-10-30 11:29:48 +00:00
uint32_t aligned_buffer = ( ( uint32_t ) TasmotaGlobal . serial_in_buffer + 3 ) & ~ 3 ;
zigbee_buffer = new PreAllocatedSBuffer ( sizeof ( TasmotaGlobal . serial_in_buffer ) - 3 , ( char * ) aligned_buffer ) ;
2020-06-19 19:54:37 +01:00
} else {
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem2 = %d"), ESP_getFreeHeap());
2020-06-19 19:54:37 +01:00
zigbee_buffer = new SBuffer ( ZIGBEE_BUFFER_SIZE ) ;
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem3 = %d"), ESP_getFreeHeap());
2020-06-19 19:54:37 +01:00
}
2020-07-22 09:07:07 +01:00
if ( PinUsed ( GPIO_ZIGBEE_RST ) ) {
pinMode ( Pin ( GPIO_ZIGBEE_RST ) , OUTPUT ) ;
digitalWrite ( Pin ( GPIO_ZIGBEE_RST ) , 1 ) ;
}
2020-06-19 19:54:37 +01:00
zigbee . active = true ;
zigbee . init_phase = true ; // start the state machine
zigbee . state_machine = true ; // start the state machine
ZigbeeSerial - > flush ( ) ;
}
2020-11-06 16:09:13 +00:00
// AddLog_P(LOG_LEVEL_INFO, PSTR("ZigbeeInit Mem9 = %d"), ESP_getFreeHeap());
2020-06-19 19:54:37 +01:00
}
# ifdef USE_ZIGBEE_ZNP
2020-07-21 21:39:39 +01:00
// flush any ongoing frame, sending 256 times 0xFF
void ZigbeeZNPFlush ( void ) {
if ( ZigbeeSerial ) {
for ( uint32_t i = 0 ; i < 256 ; i + + ) {
ZigbeeSerial - > write ( 0xFF ) ;
}
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " 0xFF x 255 " ) ) ;
2020-07-21 21:39:39 +01:00
}
}
2020-06-19 19:54:37 +01:00
void ZigbeeZNPSend ( const uint8_t * msg , size_t len ) {
if ( ( len < 2 ) | | ( len > 252 ) ) {
// abort, message cannot be less than 2 bytes for CMD1 and CMD2
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_JSON_ZIGBEEZNPSENT " : bad message len %d " ) , len ) ;
2020-06-19 19:54:37 +01:00
return ;
}
uint8_t data_len = len - 2 ; // removing CMD1 and CMD2
if ( ZigbeeSerial ) {
uint8_t fcs = data_len ;
ZigbeeSerial - > write ( ZIGBEE_SOF ) ; // 0xFE
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend SOF %02X"), ZIGBEE_SOF);
2020-06-19 19:54:37 +01:00
ZigbeeSerial - > write ( data_len ) ;
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend LEN %02X"), data_len);
2020-06-19 19:54:37 +01:00
for ( uint32_t i = 0 ; i < len ; i + + ) {
uint8_t b = pgm_read_byte ( msg + i ) ;
ZigbeeSerial - > write ( b ) ;
fcs ^ = b ;
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend byt %02X"), b);
2020-06-19 19:54:37 +01:00
}
ZigbeeSerial - > write ( fcs ) ; // finally send fcs checksum byte
2020-11-06 16:09:13 +00:00
//AddLog_P(LOG_LEVEL_DEBUG_MORE, PSTR("ZNPSend FCS %02X"), fcs);
2020-06-19 19:54:37 +01:00
}
// Now send a MQTT message to report the sent message
char hex_char [ ( len * 2 ) + 2 ] ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s " ) ,
2020-06-19 19:54:37 +01:00
ToHex_P ( msg , len , hex_char , sizeof ( hex_char ) ) ) ;
}
//
// Same code for `ZbZNPSend` and `ZbZNPReceive`
// building the complete message (intro, length)
//
void CmndZbZNPSendOrReceive ( bool send )
{
if ( ZigbeeSerial & & ( XdrvMailbox . data_len > 0 ) ) {
uint8_t code ;
char * codes = RemoveSpace ( XdrvMailbox . data ) ;
int32_t size = strlen ( XdrvMailbox . data ) ;
SBuffer buf ( ( size + 1 ) / 2 ) ;
while ( size > 1 ) {
char stemp [ 3 ] ;
strlcpy ( stemp , codes , sizeof ( stemp ) ) ;
code = strtol ( stemp , nullptr , 16 ) ;
buf . add8 ( code ) ;
size - = 2 ;
codes + = 2 ;
}
if ( send ) {
// Command was `ZbZNPSend`
ZigbeeZNPSend ( buf . getBuffer ( ) , buf . len ( ) ) ;
} else {
// Command was `ZbZNPReceive`
ZigbeeProcessInput ( buf ) ;
}
}
ResponseCmndDone ( ) ;
}
// For debug purposes only, simulates a message received
void CmndZbZNPReceive ( void )
{
CmndZbZNPSendOrReceive ( false ) ;
}
void CmndZbZNPSend ( void )
{
CmndZbZNPSendOrReceive ( true ) ;
}
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
// internal function to output a byte, and escape it (stuffing) if needed
void ZigbeeEZSPSend_Out ( uint8_t out_byte ) {
switch ( out_byte ) {
case 0x7E : // Flag byte
case 0x11 : // XON
case 0x13 : // XOFF
case 0x18 : // Substitute byte
case 0x1A : // Cancel byte
case 0x7D : // Escape byte
// case 0xFF: // special wake-up
ZigbeeSerial - > write ( ZIGBEE_EZSP_ESCAPE ) ; // send Escape byte 0x7D
ZigbeeSerial - > write ( out_byte ^ 0x20 ) ; // send with bit 5 inverted
break ;
default :
ZigbeeSerial - > write ( out_byte ) ; // send unchanged
break ;
}
}
// Send low-level EZSP frames
//
// The frame should contain the Control Byte and Data Field
// The frame shouldn't be escaped, nor randomized
//
// Before sending:
// - send Cancel byte (0x1A) if requested
// - randomize Data Field if DATA Frame
// - compute CRC16
// - escape (stuff) reserved bytes
// - add EOF (0x7E)
// - send frame
// send_cancel: should we first send a EZSP_CANCEL (0x1A) before the message to clear any leftover
void ZigbeeEZSPSendRaw ( const uint8_t * msg , size_t len , bool send_cancel ) {
if ( ( len < 1 ) | | ( len > 252 ) ) {
// abort, message cannot be less than 2 bytes for CMD1 and CMD2
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_JSON_ZIGBEE_EZSP_SENT " : bad message len %d " ) , len ) ;
2020-06-19 19:54:37 +01:00
return ;
}
2020-07-04 18:59:59 +01:00
// turn send led on
2020-08-08 19:56:09 +01:00
Z_LedStatusSet ( true ) ;
2020-07-04 18:59:59 +01:00
2020-06-19 19:54:37 +01:00
if ( ZigbeeSerial ) {
if ( send_cancel ) {
ZigbeeSerial - > write ( ZIGBEE_EZSP_CANCEL ) ; // 0x1A
}
bool data_frame = ( 0 = = ( msg [ 0 ] & 0x80 ) ) ;
uint8_t rand = 0x42 ; // pseudo-randomizer initial value
uint16_t crc = 0xFFFF ; // CRC16 CCITT initialization
2020-07-20 16:24:51 +01:00
2020-06-19 19:54:37 +01:00
for ( uint32_t i = 0 ; i < len ; i + + ) {
uint8_t out_byte = msg [ i ] ;
// apply randomization if DATA field
if ( data_frame & & ( i > 0 ) ) {
out_byte ^ = rand ;
if ( rand & 1 ) { rand = ( rand > > 1 ) ^ 0xB8 ; }
else { rand = ( rand > > 1 ) ; }
}
// compute CRC
crc = crc ^ ( ( uint16_t ) out_byte < < 8 ) ;
for ( uint32_t i = 0 ; i < 8 ; i + + ) {
if ( crc & 0x8000 ) {
crc = ( crc < < 1 ) ^ 0x1021 ; // polynom is x^16 + x^12 + x^5 + 1, CCITT standard
} else {
crc < < = 1 ;
}
}
// output byte
ZigbeeEZSPSend_Out ( out_byte ) ;
}
// send CRC16 in big-endian
ZigbeeEZSPSend_Out ( crc > > 8 ) ;
ZigbeeEZSPSend_Out ( crc & 0xFF ) ;
// finally send End of Frame
ZigbeeSerial - > write ( ZIGBEE_EZSP_EOF ) ; // 0x1A
}
2020-07-04 18:59:59 +01:00
2020-06-19 19:54:37 +01:00
// Now send a MQTT message to report the sent message
char hex_char [ ( len * 2 ) + 2 ] ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( D_LOG_ZIGBEE D_JSON_ZIGBEE_EZSP_SENT_RAW " %s " ) ,
2020-06-19 19:54:37 +01:00
ToHex_P ( msg , len , hex_char , sizeof ( hex_char ) ) ) ;
}
// Send an EZSP command and data
// Ex: Version with min v8 = 000008
2020-07-22 18:29:16 +01:00
void ZigbeeEZSPSendCmd ( const uint8_t * msg , size_t len ) {
2020-06-19 19:54:37 +01:00
char hex_char [ len * 2 + 2 ] ;
ToHex_P ( msg , len , hex_char , sizeof ( hex_char ) ) ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE " ZbEZSPSend %s " ) , hex_char ) ;
2020-06-19 19:54:37 +01:00
SBuffer cmd ( len + 3 ) ; // prefix with seq number (1 byte) and frame control bytes (2 bytes)
cmd . add8 ( EZSP_Serial . ezsp_seq + + ) ;
cmd . add8 ( 0x00 ) ; // Low byte of Frame Control
cmd . add8 ( 0x01 ) ; // High byte of Frame Control, frameFormatVersion = 1
cmd . addBuffer ( msg , len ) ;
// send
2020-07-22 18:29:16 +01:00
ZigbeeEZSPSendDATA ( cmd . getBuffer ( ) , cmd . len ( ) ) ;
2020-06-19 19:54:37 +01:00
}
// Send an EZSP DATA frame, automatically calculating the correct frame numbers
2020-07-22 18:29:16 +01:00
void ZigbeeEZSPSendDATA_frm ( bool send_cancel , uint8_t to_frm , uint8_t from_ack ) {
SBuffer * buf = EZSP_Serial . to_packets [ to_frm ] ;
if ( ! buf ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: Buffer for packet %d is not allocated " ) , EZSP_Serial . to_send ) ;
2020-07-22 18:29:16 +01:00
return ;
}
uint8_t control_byte = ( ( to_frm & 0x07 ) < < 4 ) + ( from_ack & 0x07 ) ;
buf - > set8 ( 0 , control_byte ) ; // change control_byte
2020-06-19 19:54:37 +01:00
// send
2020-07-22 18:29:16 +01:00
ZigbeeEZSPSendRaw ( buf - > getBuffer ( ) , buf - > len ( ) , send_cancel ) ;
}
// Send an EZSP DATA frame, automatically calculating the correct frame numbers
void ZigbeeEZSPSendDATA ( const uint8_t * msg , size_t len ) {
// prepare buffer by adding 1 byte prefix
SBuffer * buf = new SBuffer ( len + 1 ) ; // prepare for control_byte prefix
buf - > add8 ( 0x00 ) ; // placeholder for control_byte
buf - > addBuffer ( msg , len ) ;
//
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: adding packet to_send, to_ack:%d, to_send:%d, to_end:%d " ) ,
2020-07-22 18:29:16 +01:00
EZSP_Serial . to_ack , EZSP_Serial . to_send , EZSP_Serial . to_end ) ;
uint8_t to_frm = EZSP_Serial . to_end ;
if ( EZSP_Serial . to_packets [ to_frm ] ) {
delete EZSP_Serial . to_packets [ to_frm ] ;
EZSP_Serial . to_packets [ to_frm ] = nullptr ;
}
EZSP_Serial . to_packets [ to_frm ] = buf ;
EZSP_Serial . to_end = ( to_frm + 1 ) & 0x07 ; // move cursor
2020-10-29 11:21:24 +00:00
2020-07-22 18:29:16 +01:00
// ZigbeeEZSPSendDATA_frm(send_cancel, to_frm, EZSP_Serial.from_ack);
// increment to_frame
//EZSP_Serial.to_ack = (EZSP_Serial.to_ack + 1) & 0x07;
//EZSP_Serial.to_frm = (EZSP_Serial.to_frm + 1) & 0x07;
2020-06-19 19:54:37 +01:00
}
2020-07-20 16:24:51 +01:00
// Receive a high-level EZSP command/response, starting with 16-bits frame ID
2020-11-12 18:38:21 +00:00
void ZigbeeProcessInputEZSP ( class SBuffer & buf ) {
2020-06-19 19:54:37 +01:00
// verify errors in first 2 bytes.
// TODO
// uint8_t sequence_num = buf.get8(0);
uint16_t frame_control = buf . get16 ( 1 ) ;
bool truncated = frame_control & 0x02 ;
bool overflow = frame_control & 0x01 ;
2020-11-12 18:38:21 +00:00
// bool callbackPending = frame_control & 0x04;
2020-06-19 19:54:37 +01:00
bool security_enabled = frame_control & 0x8000 ;
2020-06-29 21:21:32 +01:00
if ( truncated | | overflow | | security_enabled ) {
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_INFO , PSTR ( " ZIG: specific frame_control 0x%04X " ) , frame_control ) ;
2020-06-19 19:54:37 +01:00
}
// remove first 2 bytes, be
for ( uint32_t i = 0 ; i < buf . len ( ) - 3 ; i + + ) {
buf . set8 ( i , buf . get8 ( i + 3 ) ) ;
}
buf . setLen ( buf . len ( ) - 3 ) ;
char hex_char [ buf . len ( ) * 2 + 2 ] ;
// log message
ToHex_P ( ( unsigned char * ) buf . getBuffer ( ) , buf . len ( ) , hex_char , sizeof ( hex_char ) ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_EZSP_RECEIVED " \" : \" %s \" } " ) , hex_char ) ;
if ( Settings . flag3 . tuya_serial_mqtt_publish ) {
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( TELE , PSTR ( D_RSLT_SENSOR ) ) ;
2020-06-19 19:54:37 +01:00
} else {
2020-07-25 14:40:42 +01:00
// demote less interesting messages to LOG_LEVEL_DEBUG
uint32_t log_level = LOG_LEVEL_INFO ;
switch ( buf . get16 ( 0 ) ) {
case EZSP_version : // 0000
case EZSP_addEndpoint : // 0200
case EZSP_setConcentrator : // 1000
case EZSP_networkInit : // 1700
case EZSP_stackStatusHandler : // 1900
2020-10-10 15:19:37 +01:00
case EZSP_formNetwork : // 1E00
2020-07-25 14:40:42 +01:00
case EZSP_permitJoining : // 2200
case EZSP_getEui64 : // 2600
case EZSP_getNodeId : // 2700
2020-07-29 09:02:04 +01:00
case EZSP_getNetworkParameters : // 2800
2020-07-25 14:40:42 +01:00
case EZSP_sendUnicast : // 3400
case EZSP_sendBroadcast : // 3600
2020-09-14 21:06:19 +01:00
case EZSP_sendMulticast : // 3800
2020-07-25 14:40:42 +01:00
case EZSP_messageSentHandler : // 3F00
2020-09-14 21:06:19 +01:00
case EZSP_incomingMessageHandler : // 4500
2020-07-25 14:40:42 +01:00
case EZSP_setConfigurationValue : // 5300
case EZSP_setPolicy : // 5500
2020-09-08 20:10:24 +01:00
case 0x0059 : // 5900 - supposedly removed by still happening
2020-07-25 14:40:42 +01:00
case EZSP_setMulticastTableEntry : // 6400
2020-07-29 09:02:04 +01:00
case EZSP_setInitialSecurityState : // 6800
case EZSP_getCurrentSecurityState : // 6900
2020-08-05 19:49:07 +01:00
case EZSP_getKey : // 6A00
2020-07-25 14:40:42 +01:00
log_level = LOG_LEVEL_DEBUG ;
break ;
}
2020-11-06 16:09:13 +00:00
AddLog_P ( log_level , PSTR ( D_LOG_ZIGBEE " %s " ) , TasmotaGlobal . mqtt_data ) ; // TODO move to LOG_LEVEL_DEBUG when stable
2020-06-19 19:54:37 +01:00
}
// Pass message to state machine
ZigbeeProcessInput ( buf ) ;
}
2020-07-22 18:29:16 +01:00
// Check if we advanced in the ACKed frames, and free from memory packets acknowledged
void EZSP_HandleAck ( uint8_t new_ack ) {
if ( EZSP_Serial . to_ack ! = new_ack ) { // new ack receveid
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: new ack/data received, was %d now %d " ) , EZSP_Serial . to_ack , new_ack ) ;
2020-07-22 18:29:16 +01:00
uint32_t i = EZSP_Serial . to_ack ;
do {
if ( EZSP_Serial . to_packets [ i ] ) {
delete EZSP_Serial . to_packets [ i ] ;
EZSP_Serial . to_packets [ i ] = nullptr ;
}
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: freeing packet %d from memory " ) , i ) ;
2020-07-22 18:29:16 +01:00
i = ( i + 1 ) & 0x07 ;
} while ( i ! = new_ack ) ;
EZSP_Serial . to_ack = new_ack ;
}
}
2020-06-19 19:54:37 +01:00
// Receive raw ASH frame (CRC was removed, data unstuffed) but still contains frame numbers
2020-11-12 18:38:21 +00:00
void ZigbeeProcessInputRaw ( class SBuffer & buf ) {
2020-06-19 19:54:37 +01:00
uint8_t control_byte = buf . get8 ( 0 ) ;
uint8_t ack_num = control_byte & 0x07 ; // keep 3 LSB
2020-07-22 18:29:16 +01:00
if ( control_byte & 0x80 ) { // non DATA frame
2020-06-19 19:54:37 +01:00
uint8_t frame_type = control_byte & 0xE0 ; // keep 3 MSB
if ( frame_type = = 0x80 ) {
// ACK
2020-07-22 18:29:16 +01:00
EZSP_HandleAck ( ack_num ) ;
2020-06-19 19:54:37 +01:00
} else if ( frame_type = = 0xA0 ) {
// NAK
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: Received NAK %d, to_ack:%d, to_send:%d, to_end:%d " ) ,
2020-07-22 18:29:16 +01:00
ack_num , EZSP_Serial . to_ack , EZSP_Serial . to_send , EZSP_Serial . to_end ) ;
EZSP_Serial . to_send = ack_num ;
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: NAK, resending packet %d " ) , ack_num ) ;
2020-06-19 19:54:37 +01:00
} else if ( control_byte = = 0xC1 ) {
2020-07-20 16:24:51 +01:00
2020-06-19 19:54:37 +01:00
// RSTACK
// received just after boot, either because of Power up, hardware reset or RST
2020-07-02 21:56:37 +01:00
EZ_RSTACK ( buf . get8 ( 2 ) ) ;
2020-06-19 19:54:37 +01:00
EZSP_Serial . from_ack = 0 ;
EZSP_Serial . to_ack = 0 ;
2020-07-22 18:29:16 +01:00
EZSP_Serial . to_end = 0 ;
EZSP_Serial . to_send = 0 ;
2020-06-19 19:54:37 +01:00
// pass it to state machine with a special 0xFFFE frame code (EZSP_RSTACK_ID)
buf . set8 ( 0 , Z_B0 ( EZSP_rstAck ) ) ;
buf . set8 ( 1 , Z_B1 ( EZSP_rstAck ) ) ;
// keep byte #2 with code
buf . setLen ( 3 ) ;
ZigbeeProcessInput ( buf ) ;
} else if ( control_byte = = 0xC2 ) {
2020-07-20 16:24:51 +01:00
2020-06-19 19:54:37 +01:00
// ERROR
2020-07-02 21:56:37 +01:00
EZ_ERROR ( buf . get8 ( 2 ) ) ;
2020-06-19 19:54:37 +01:00
zigbee . active = false ; // stop all zigbee activities
2020-10-29 11:58:22 +00:00
TasmotaGlobal . restart_flag = 2 ; // there is nothing more we can do except restart
2020-06-19 19:54:37 +01:00
} else {
// Unknown
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: Received unknown control byte 0x%02X " ) , control_byte ) ;
2020-06-19 19:54:37 +01:00
}
2020-07-22 18:29:16 +01:00
} else { // DATA Frame
2020-10-29 11:21:24 +00:00
2020-07-22 18:29:16 +01:00
// adjust to latest acked packet
uint8_t new_ack = control_byte & 0x07 ;
EZSP_HandleAck ( new_ack ) ;
2020-06-19 19:54:37 +01:00
// MCU acknowledged the correct frame
// we acknowledge the frame too
EZSP_Serial . from_ack = ( ( control_byte > > 4 ) + 1 ) & 0x07 ;
uint8_t ack_byte = 0x80 | EZSP_Serial . from_ack ;
ZigbeeEZSPSendRaw ( & ack_byte , 1 , false ) ; // send a 1-byte ACK
// build the EZSP frame
// remove first byte
for ( uint8_t i = 0 ; i < buf . len ( ) - 1 ; i + + ) {
buf . set8 ( i , buf . get8 ( i + 1 ) ) ;
}
buf . setLen ( buf . len ( ) - 1 ) ;
// pass to next level
ZigbeeProcessInputEZSP ( buf ) ;
}
}
//
// Same code for `ZbEZSPSend` and `ZbEZSPReceive`
// building the complete message (intro, length)
//
// ZbEZSPSend1 = high level EZSP command
// ZbEZSPSend2 = low level EZSP DATA frame (with sequence numbers)
// ZbEZSPSend3 = low level ASH frame
//
void CmndZbEZSPSendOrReceive ( bool send )
{
if ( ZigbeeSerial & & ( XdrvMailbox . data_len > 0 ) ) {
uint8_t code ;
char * codes = RemoveSpace ( XdrvMailbox . data ) ;
int32_t size = strlen ( XdrvMailbox . data ) ;
SBuffer buf ( ( size + 1 ) / 2 ) ;
while ( size > 1 ) {
char stemp [ 3 ] ;
strlcpy ( stemp , codes , sizeof ( stemp ) ) ;
code = strtol ( stemp , nullptr , 16 ) ;
buf . add8 ( code ) ;
size - = 2 ;
codes + = 2 ;
}
if ( send ) {
// Command was `ZbEZSPSend`
2020-07-22 18:29:16 +01:00
if ( 2 = = XdrvMailbox . index ) { ZigbeeEZSPSendDATA ( buf . getBuffer ( ) , buf . len ( ) ) ; }
2020-06-19 19:54:37 +01:00
else if ( 3 = = XdrvMailbox . index ) { ZigbeeEZSPSendRaw ( buf . getBuffer ( ) , buf . len ( ) , true ) ; }
2020-07-22 18:29:16 +01:00
else { ZigbeeEZSPSendCmd ( buf . getBuffer ( ) , buf . len ( ) ) ; }
2020-07-20 16:24:51 +01:00
2020-06-19 19:54:37 +01:00
} else {
// Command was `ZbEZSPReceive`
if ( 2 = = XdrvMailbox . index ) { ZigbeeProcessInput ( buf ) ; }
else if ( 3 = = XdrvMailbox . index ) { ZigbeeProcessInputRaw ( buf ) ; }
else { ZigbeeProcessInputEZSP ( buf ) ; } // TODO
}
}
ResponseCmndDone ( ) ;
}
// Variants with managed ASH frame numbers
// For debug purposes only, simulates a message received
void CmndZbEZSPReceive ( void )
{
CmndZbEZSPSendOrReceive ( false ) ;
}
void CmndZbEZSPSend ( void )
{
CmndZbEZSPSendOrReceive ( true ) ;
}
# endif // USE_ZIGBEE_EZSP
//
// Internal function, send the low-level frame
// Input:
// - shortaddr: 16-bits short address, or 0x0000 if group address
// - groupaddr: 16-bits group address, or 0x0000 if unicast using shortaddr
// - clusterIf: 16-bits cluster number
// - endpoint: 8-bits target endpoint (source is always 0x01), unused for group addresses. Should not be 0x00 except when sending to group address.
2020-10-13 21:45:11 +01:00
// - cmd: 8-bits ZCL command number
2020-06-19 19:54:37 +01:00
// - clusterSpecific: boolean, is the message general cluster or cluster specific, used to create the FC byte of ZCL
// - msg: pointer to byte array, payload of ZCL message (len is following), ignored if nullptr
// - len: length of the 'msg' payload
// - needResponse: boolean, true = we ask the target to respond, false = the target should not respond
// - transacId: 8-bits, transation id of message (should be incremented at each message), used both for Zigbee message number and ZCL message number
// Returns: None
//
2020-09-06 19:51:20 +01:00
void ZigbeeZCLSend_Raw ( const ZigbeeZCLSendMessage & zcl ) {
2020-06-19 19:54:37 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-09-06 19:51:20 +01:00
SBuffer buf ( 32 + zcl . len ) ;
2020-06-19 19:54:37 +01:00
buf . add8 ( Z_SREQ | Z_AF ) ; // 24
buf . add8 ( AF_DATA_REQUEST_EXT ) ; // 02
2020-09-06 19:51:20 +01:00
if ( BAD_SHORTADDR = = zcl . shortaddr ) { // if no shortaddr we assume group address
2020-06-19 19:54:37 +01:00
buf . add8 ( Z_Addr_Group ) ; // 01
2020-09-06 19:51:20 +01:00
buf . add64 ( zcl . groupaddr ) ; // group address, only 2 LSB, upper 6 MSB are discarded
2020-06-19 19:54:37 +01:00
buf . add8 ( 0xFF ) ; // dest endpoint is not used for group addresses
} else {
buf . add8 ( Z_Addr_ShortAddress ) ; // 02
2020-09-06 19:51:20 +01:00
buf . add64 ( zcl . shortaddr ) ; // dest address, only 2 LSB, upper 6 MSB are discarded
buf . add8 ( zcl . endpoint ) ; // dest endpoint
2020-06-19 19:54:37 +01:00
}
buf . add16 ( 0x0000 ) ; // dest Pan ID, 0x0000 = intra-pan
buf . add8 ( 0x01 ) ; // source endpoint
2020-10-13 21:45:11 +01:00
buf . add16 ( zcl . cluster ) ;
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . transacId ) ; // transacId
2020-06-19 19:54:37 +01:00
buf . add8 ( 0x30 ) ; // 30 options
buf . add8 ( 0x1E ) ; // 1E radius
2020-09-06 19:51:20 +01:00
buf . add16 ( 3 + zcl . len + ( zcl . manuf ? 2 : 0 ) ) ;
buf . add8 ( ( zcl . needResponse ? 0x00 : 0x10 ) | ( zcl . clusterSpecific ? 0x01 : 0x00 ) | ( zcl . manuf ? 0x04 : 0x00 ) ) ; // Frame Control Field
if ( zcl . manuf ) {
buf . add16 ( zcl . manuf ) ; // add Manuf Id if not null
2020-06-19 19:54:37 +01:00
}
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . transacId ) ; // Transaction Sequence Number
2020-10-13 21:45:11 +01:00
buf . add8 ( zcl . cmd ) ;
2020-09-06 19:51:20 +01:00
if ( zcl . len > 0 ) {
buf . addBuffer ( zcl . msg , zcl . len ) ; // add the payload
2020-06-19 19:54:37 +01:00
}
ZigbeeZNPSend ( buf . getBuffer ( ) , buf . len ( ) ) ;
# endif // USE_ZIGBEE_ZNP
2020-07-02 21:56:37 +01:00
# ifdef USE_ZIGBEE_EZSP
2020-09-06 19:51:20 +01:00
SBuffer buf ( 32 + zcl . len ) ;
2020-07-02 21:56:37 +01:00
2020-09-06 19:51:20 +01:00
if ( BAD_SHORTADDR ! = zcl . shortaddr ) {
2020-07-05 20:01:26 +01:00
// send unicast message to an address
buf . add16 ( EZSP_sendUnicast ) ; // 3400
buf . add8 ( EMBER_OUTGOING_DIRECT ) ; // 00
2020-09-06 19:51:20 +01:00
buf . add16 ( zcl . shortaddr ) ; // dest addr
2020-07-05 20:01:26 +01:00
// ApsFrame
buf . add16 ( Z_PROF_HA ) ; // Home Automation profile
2020-10-13 21:45:11 +01:00
buf . add16 ( zcl . cluster ) ; // cluster
2020-07-05 20:01:26 +01:00
buf . add8 ( 0x01 ) ; // srcEp
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . endpoint ) ; // dstEp
2020-10-19 19:34:40 +01:00
if ( zcl . direct ) {
buf . add16 ( 0x0000 ) ; // APS frame
} else {
buf . add16 ( EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY ) ; // APS frame
}
2020-09-06 19:51:20 +01:00
buf . add16 ( zcl . groupaddr ) ; // groupId
buf . add8 ( zcl . transacId ) ;
2020-07-05 20:01:26 +01:00
// end of ApsFrame
buf . add8 ( 0x01 ) ; // tag TODO
2020-09-06 19:51:20 +01:00
buf . add8 ( 3 + zcl . len + ( zcl . manuf ? 2 : 0 ) ) ;
buf . add8 ( ( zcl . needResponse ? 0x00 : 0x10 ) | ( zcl . clusterSpecific ? 0x01 : 0x00 ) | ( zcl . manuf ? 0x04 : 0x00 ) ) ; // Frame Control Field
if ( zcl . manuf ) {
buf . add16 ( zcl . manuf ) ; // add Manuf Id if not null
2020-07-05 20:01:26 +01:00
}
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . transacId ) ; // Transaction Sequance Number
2020-10-13 21:45:11 +01:00
buf . add8 ( zcl . cmd ) ;
2020-09-06 19:51:20 +01:00
if ( zcl . len > 0 ) {
buf . addBuffer ( zcl . msg , zcl . len ) ; // add the payload
2020-07-05 20:01:26 +01:00
}
} else {
// send broadcast group address, aka groupcast
buf . add16 ( EZSP_sendMulticast ) ; // 3800
// ApsFrame
buf . add16 ( Z_PROF_HA ) ; // Home Automation profile
2020-10-13 21:45:11 +01:00
buf . add16 ( zcl . cluster ) ; // cluster
2020-07-05 20:01:26 +01:00
buf . add8 ( 0x01 ) ; // srcEp
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . endpoint ) ; // broadcast endpoint for groupcast
2020-10-19 19:34:40 +01:00
if ( zcl . direct ) {
buf . add16 ( 0x0000 ) ; // APS frame
} else {
buf . add16 ( EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY ) ; // APS frame
}
2020-09-06 19:51:20 +01:00
buf . add16 ( zcl . groupaddr ) ; // groupId
buf . add8 ( zcl . transacId ) ;
2020-07-05 20:01:26 +01:00
// end of ApsFrame
buf . add8 ( 0 ) ; // hops, 0x00 = EMBER_MAX_HOPS
buf . add8 ( 7 ) ; // nonMemberRadius, 7 = infinite
buf . add8 ( 0x01 ) ; // tag TODO
2020-09-06 19:51:20 +01:00
buf . add8 ( 3 + zcl . len + ( zcl . manuf ? 2 : 0 ) ) ;
buf . add8 ( ( zcl . needResponse ? 0x00 : 0x10 ) | ( zcl . clusterSpecific ? 0x01 : 0x00 ) | ( zcl . manuf ? 0x04 : 0x00 ) ) ; // Frame Control Field
if ( zcl . manuf ) {
buf . add16 ( zcl . manuf ) ; // add Manuf Id if not null
2020-07-05 20:01:26 +01:00
}
2020-09-06 19:51:20 +01:00
buf . add8 ( zcl . transacId ) ; // Transaction Sequance Number
2020-10-13 21:45:11 +01:00
buf . add8 ( zcl . cmd ) ;
2020-09-06 19:51:20 +01:00
if ( zcl . len > 0 ) {
buf . addBuffer ( zcl . msg , zcl . len ) ; // add the payload
2020-07-05 20:01:26 +01:00
}
2020-07-02 21:56:37 +01:00
}
2020-07-22 18:29:16 +01:00
ZigbeeEZSPSendCmd ( buf . buf ( ) , buf . len ( ) ) ;
# endif // USE_ZIGBEE_EZSP
}
//
// Send any buffered data to the NCP
//
// Used only with EZSP, as there is no replay of procotol control with ZNP
void ZigbeeOutputLoop ( void ) {
# ifdef USE_ZIGBEE_EZSP
// while (EZSP_Serial.to_send != EZSP_Serial.to_end) {
if ( EZSP_Serial . to_send ! = EZSP_Serial . to_end ) { // we send only one packet per tick to lower the chance of NAK
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: Something to_send, to_ack:%d, to_send:%d, to_end:%d " ) ,
2020-07-22 18:29:16 +01:00
EZSP_Serial . to_ack , EZSP_Serial . to_send , EZSP_Serial . to_end ) ;
// we have a frame waiting to be sent
ZigbeeEZSPSendDATA_frm ( true , EZSP_Serial . to_send , EZSP_Serial . from_ack ) ;
// increment sent counter
EZSP_Serial . to_send = ( EZSP_Serial . to_send + 1 ) & 0x07 ;
}
2020-07-02 21:56:37 +01:00
# endif // USE_ZIGBEE_EZSP
2020-06-19 19:54:37 +01:00
}
# endif // USE_ZIGBEE