2019-08-31 20:23:32 +01:00
/*
xdrv_23_zigbee . ino - zigbee support for Sonoff - Tasmota
Copyright ( C ) 2019 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
# define XDRV_23 23
const uint32_t ZIGBEE_BUFFER_SIZE = 256 ; // Max ZNP frame is SOF+LEN+CMD1+CMD2+250+FCS = 255
const uint8_t ZIGBEE_SOF = 0xFE ;
2019-09-15 10:10:59 +01:00
// Status code used for ZigbeeStatus MQTT message
// Ex: {"ZigbeeStatus":{"code": 3,"message":"Configured, starting coordinator"}}
const uint8_t ZIGBEE_STATUS_OK = 0 ; // Zigbee started and working
const uint8_t ZIGBEE_STATUS_BOOT = 1 ; // CC2530 booting
const uint8_t ZIGBEE_STATUS_RESET_CONF = 2 ; // Resetting CC2530 configuration
const uint8_t ZIGBEE_STATUS_STARTING = 3 ; // Starting CC2530 as coordinator
const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20 ; // Disable PermitJoin
const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21 ; // Enable PermitJoin for 60 seconds
const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22 ; // Enable PermitJoin until next boot
const uint8_t ZIGBEE_STATUS_DEVICE_VERSION = 50 ; // Status: CC2530 ZNP Version
const uint8_t ZIGBEE_STATUS_DEVICE_INFO = 51 ; // Status: CC2530 Device Configuration
const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98 ; // Unsupported ZNP version
const uint8_t ZIGBEE_STATUS_ABORT = 99 ; // Fatal error, Zigbee not working
//#define Z_USE_SOFTWARE_SERIAL
# ifdef Z_USE_SOFTWARE_SERIAL
# include <SoftwareSerial.h>
SoftwareSerial * ZigbeeSerial = nullptr ;
# else
2019-08-31 20:23:32 +01:00
# include <TasmotaSerial.h>
TasmotaSerial * ZigbeeSerial = nullptr ;
2019-09-15 10:10:59 +01:00
# endif
2019-08-31 20:23:32 +01:00
2019-09-15 10:10:59 +01:00
const char kZigbeeCommands [ ] PROGMEM = " | " D_CMND_ZIGBEEZNPSEND " | " D_CMND_ZIGBEE_PERMITJOIN ;
void ( * const ZigbeeCommand [ ] ) ( void ) PROGMEM = { & CmndZigbeeZNPSend , & CmndZigbeePermitJoin } ;
2019-08-31 20:23:32 +01:00
typedef int32_t ( * ZB_Func ) ( uint8_t value ) ;
typedef int32_t ( * ZB_RecvMsgFunc ) ( int32_t res , class SBuffer & buf ) ;
typedef union Zigbee_Instruction {
struct {
uint8_t i ; // instruction
uint8_t d8 ; // 8 bits data
uint16_t d16 ; // 16 bits data
} i ;
const void * p ; // pointer
// const void *m; // for type checking only, message
// const ZB_Func f;
// const ZB_RecvMsgFunc fr;
} Zigbee_Instruction ;
//
// Zigbee_Instruction z1 = { .i = {1,2,3}};
// Zigbee_Instruction z3 = { .p = nullptr };
typedef struct Zigbee_Instruction_Type {
uint8_t instr ;
uint8_t data ;
} Zigbee_Instruction_Type ;
enum Zigbee_StateMachine_Instruction_Set {
// 2 bytes instructions
ZGB_INSTR_4_BYTES = 0 ,
ZGB_INSTR_NOOP = 0 , // do nothing
ZGB_INSTR_LABEL , // define a label
ZGB_INSTR_GOTO , // goto label
ZGB_INSTR_ON_ERROR_GOTO , // goto label if error
ZGB_INSTR_ON_TIMEOUT_GOTO , // goto label if timeout
ZGB_INSTR_WAIT , // wait for x ms (in chunks of 100ms)
ZGB_INSTR_WAIT_FOREVER , // wait forever but state machine still active
ZGB_INSTR_STOP , // stop state machine with optional error code
// 6 bytes instructions
ZGB_INSTR_8_BYTES = 0x80 ,
ZGB_INSTR_CALL = 0x80 , // call a function
ZGB_INSTR_LOG , // log a message, if more detailed logging required, call a function
2019-09-15 10:10:59 +01:00
ZGB_INSTR_MQTT_STATUS , // send MQTT status string with code
2019-08-31 20:23:32 +01:00
ZGB_INSTR_SEND , // send a ZNP message
ZGB_INSTR_WAIT_UNTIL , // wait until the specified message is received, ignore all others
ZGB_INSTR_WAIT_RECV , // wait for a message according to the filter
ZGB_ON_RECV_UNEXPECTED , // function to handle unexpected messages, or nullptr
// 10 bytes instructions
ZGB_INSTR_12_BYTES = 0xF0 ,
ZGB_INSTR_WAIT_RECV_CALL , // wait for a filtered message and call function upon receive
} ;
# define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} },
# define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} },
# define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} },
# define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} },
# define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} },
# define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} },
# define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} },
# define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} },
# define ZI_CALL(f, x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) },
# define ZI_LOG(x, m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) },
2019-09-15 10:10:59 +01:00
# define ZI_MQTT_STATUS(x, m) { .i = { ZGB_INSTR_MQTT_STATUS, (x), 0x0000 } }, { .p = ((const void*)(m)) },
2019-08-31 20:23:32 +01:00
# define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) },
# define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) },
# define ZI_WAIT_RECV(x, m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) },
# define ZI_WAIT_UNTIL(x, m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) },
# define ZI_WAIT_RECV_FUNC(x, m, f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) },
2019-09-15 10:10:59 +01:00
// Labels used in the State Machine -- internal only
const uint8_t ZIGBEE_LABEL_START = 10 ; // Start ZNP
const uint8_t ZIGBEE_LABEL_READY = 20 ; // goto label 20 for main loop
const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21 ; // main loop
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30 ; // disable permit join
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31 ; // enable permit join for 60 seconds
const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32 ; // enable permit join for 60 seconds
// errors
const uint8_t ZIGBEE_LABEL_ABORT = 99 ; // goto label 99 in case of fatal error
const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98 ; // Unsupported ZNP version
2019-08-31 20:23:32 +01:00
struct ZigbeeStatus {
bool active = true ; // is Zigbee active for this device, i.e. GPIOs configured
bool state_machine = false ; // the state machine is running
bool state_waiting = false ; // the state machine is waiting for external event or timeout
bool state_no_timeout = false ; // the current wait loop does not generate a timeout but only continues running
bool ready = false ; // cc2530 initialization is complet, ready to operate
uint8_t on_error_goto = ZIGBEE_LABEL_ABORT ; // on error goto label, 99 default to abort
uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT ; // on timeout goto label, 99 default to abort
int16_t pc = 0 ; // program counter, -1 means abort
uint32_t next_timeout = 0 ; // millis for the next timeout
uint8_t * recv_filter = nullptr ; // receive filter message
bool recv_until = false ; // ignore all messages until the received frame fully matches
size_t recv_filter_len = 0 ;
ZB_RecvMsgFunc recv_func = nullptr ; // function to call when message is expected
ZB_RecvMsgFunc recv_unexpected = nullptr ; // function called when unexpected message is received
bool init_phase = true ; // initialization phase, before accepting zigbee traffic
} ;
struct ZigbeeStatus zigbee ;
SBuffer * zigbee_buffer = nullptr ;
/*********************************************************************************************\
* State Machine
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define Z_B0(a) (uint8_t)( ((a) ) & 0xFF )
# define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF )
# define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF )
# define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF )
# define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF )
# define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF )
# define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF )
# define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF )
// Macro to define message to send and receive
# define ZBM(n, x...) const uint8_t n[] PROGMEM = { x };
// ZBS_* Zigbee Send
// ZBR_* Zigbee Recv
2019-09-15 10:10:59 +01:00
ZBM ( ZBS_RESET , Z_AREQ | Z_SYS , SYS_RESET , 0x00 ) // 410001 SYS_RESET_REQ Hardware reset
ZBM ( ZBR_RESET , Z_AREQ | Z_SYS , SYS_RESET_IND ) // 4180 SYS_RESET_REQ Hardware reset response
2019-08-31 20:23:32 +01:00
ZBM ( ZBS_VERSION , Z_SREQ | Z_SYS , SYS_VERSION ) // 2102 Z_SYS:version
ZBM ( ZBR_VERSION , Z_SRSP | Z_SYS , SYS_VERSION ) // 6102 Z_SYS:version
// Check if ZNP_HAS_CONFIGURED is set
ZBM ( ZBS_ZNPHC , Z_SREQ | Z_SYS , SYS_OSAL_NV_READ , ZNP_HAS_CONFIGURED & 0xFF , ZNP_HAS_CONFIGURED > > 8 , 0x00 /* offset */ ) // 2108000F00 - 6108000155
ZBM ( ZBR_ZNPHC , Z_SRSP | Z_SYS , SYS_OSAL_NV_READ , Z_Success , 0x01 /* len */ , 0x55 ) // 6108000155
// If not set, the response is 61-08-02-00 = Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_InvalidParameter, 0x00 /* len */
ZBM ( ZBS_PAN , Z_SREQ | Z_SAPI , SAPI_READ_CONFIGURATION , CONF_PANID ) // 260483
ZBM ( ZBR_PAN , Z_SRSP | Z_SAPI , SAPI_READ_CONFIGURATION , Z_Success , CONF_PANID , 0x02 /* len */ ,
Z_B0 ( USE_ZIGBEE_PANID ) , Z_B1 ( USE_ZIGBEE_PANID ) ) // 6604008302xxxx
ZBM ( ZBS_EXTPAN , Z_SREQ | Z_SAPI , SAPI_READ_CONFIGURATION , CONF_EXTENDED_PAN_ID ) // 26042D
ZBM ( ZBR_EXTPAN , Z_SRSP | Z_SAPI , SAPI_READ_CONFIGURATION , Z_Success , CONF_EXTENDED_PAN_ID ,
0x08 /* len */ ,
Z_B0 ( USE_ZIGBEE_EXTPANID ) , Z_B1 ( USE_ZIGBEE_EXTPANID ) , Z_B2 ( USE_ZIGBEE_EXTPANID ) , Z_B3 ( USE_ZIGBEE_EXTPANID ) ,
Z_B4 ( USE_ZIGBEE_EXTPANID ) , Z_B5 ( USE_ZIGBEE_EXTPANID ) , Z_B6 ( USE_ZIGBEE_EXTPANID ) , Z_B7 ( USE_ZIGBEE_EXTPANID ) ,
) // 6604002D08xxxxxxxxxxxxxxxx
ZBM ( ZBS_CHANN , Z_SREQ | Z_SAPI , SAPI_READ_CONFIGURATION , CONF_CHANLIST ) // 260484
ZBM ( ZBR_CHANN , Z_SRSP | Z_SAPI , SAPI_READ_CONFIGURATION , Z_Success , CONF_CHANLIST ,
0x04 /* len */ ,
Z_B0 ( USE_ZIGBEE_CHANNEL ) , Z_B1 ( USE_ZIGBEE_CHANNEL ) , Z_B2 ( USE_ZIGBEE_CHANNEL ) , Z_B3 ( USE_ZIGBEE_CHANNEL ) ,
) // 6604008404xxxxxxxx
ZBM ( ZBS_PFGK , Z_SREQ | Z_SAPI , SAPI_READ_CONFIGURATION , CONF_PRECFGKEY ) // 260462
ZBM ( ZBR_PFGK , Z_SRSP | Z_SAPI , SAPI_READ_CONFIGURATION , Z_Success , CONF_PRECFGKEY ,
0x10 /* len */ ,
Z_B0 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B1 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B2 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B3 ( USE_ZIGBEE_PRECFGKEY_L ) ,
Z_B4 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B5 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B6 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B7 ( USE_ZIGBEE_PRECFGKEY_L ) ,
Z_B0 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B1 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B2 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B3 ( USE_ZIGBEE_PRECFGKEY_H ) ,
Z_B4 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B5 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B6 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B7 ( USE_ZIGBEE_PRECFGKEY_H ) ,
/*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0D */ ) // 660400621001030507090B0D0F00020406080A0C0D
ZBM ( ZBS_PFGKEN , Z_SREQ | Z_SAPI , SAPI_READ_CONFIGURATION , CONF_PRECFGKEYS_ENABLE ) // 260463
ZBM ( ZBR_PFGKEN , Z_SRSP | Z_SAPI , SAPI_READ_CONFIGURATION , Z_Success , CONF_PRECFGKEYS_ENABLE ,
0x01 /* len */ , 0x00 ) // 660400630100
// commands to "format" the device
// Write configuration - write success
ZBM ( ZBR_W_OK , Z_SRSP | Z_SAPI , SAPI_WRITE_CONFIGURATION , Z_Success ) // 660500 - Write Configuration
ZBM ( ZBR_WNV_OK , Z_SRSP | Z_SYS , SYS_OSAL_NV_WRITE , Z_Success ) // 610900 - NV Write
// Factory reset
ZBM ( ZBS_FACTRES , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_STARTUP_OPTION , 0x01 /* len */ , 0x02 ) // 2605030102
// Write PAN ID
ZBM ( ZBS_W_PAN , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_PANID , 0x02 /* len */ , Z_B0 ( USE_ZIGBEE_PANID ) , Z_B1 ( USE_ZIGBEE_PANID ) ) // 26058302xxxx
// Write EXT PAN ID
ZBM ( ZBS_W_EXTPAN , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_EXTENDED_PAN_ID , 0x08 /* len */ ,
Z_B0 ( USE_ZIGBEE_EXTPANID ) , Z_B1 ( USE_ZIGBEE_EXTPANID ) , Z_B2 ( USE_ZIGBEE_EXTPANID ) , Z_B3 ( USE_ZIGBEE_EXTPANID ) ,
Z_B4 ( USE_ZIGBEE_EXTPANID ) , Z_B5 ( USE_ZIGBEE_EXTPANID ) , Z_B6 ( USE_ZIGBEE_EXTPANID ) , Z_B7 ( USE_ZIGBEE_EXTPANID )
) // 26052D086263151D004B1200
// Write Channel ID
ZBM ( ZBS_W_CHANN , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_CHANLIST , 0x04 /* len */ ,
Z_B0 ( USE_ZIGBEE_CHANNEL ) , Z_B1 ( USE_ZIGBEE_CHANNEL ) , Z_B2 ( USE_ZIGBEE_CHANNEL ) , Z_B3 ( USE_ZIGBEE_CHANNEL ) ,
/*0x00, 0x08, 0x00, 0x00*/ ) // 26058404xxxxxxxx
// Write Logical Type = 00 = coordinator
ZBM ( ZBS_W_LOGTYP , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_LOGICAL_TYPE , 0x01 /* len */ , 0x00 ) // 2605870100
// Write precfgkey
ZBM ( ZBS_W_PFGK , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_PRECFGKEY ,
0x10 /* len */ ,
Z_B0 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B1 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B2 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B3 ( USE_ZIGBEE_PRECFGKEY_L ) ,
Z_B4 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B5 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B6 ( USE_ZIGBEE_PRECFGKEY_L ) , Z_B7 ( USE_ZIGBEE_PRECFGKEY_L ) ,
Z_B0 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B1 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B2 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B3 ( USE_ZIGBEE_PRECFGKEY_H ) ,
Z_B4 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B5 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B6 ( USE_ZIGBEE_PRECFGKEY_H ) , Z_B7 ( USE_ZIGBEE_PRECFGKEY_H ) ,
/*0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0D */ ) // 2605621001030507090B0D0F00020406080A0C0D
// Write precfgkey enable
ZBM ( ZBS_W_PFGKEN , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_PRECFGKEYS_ENABLE , 0x01 /* len */ , 0x00 ) // 2605630100
// Write Security Mode
ZBM ( ZBS_WNV_SECMODE , Z_SREQ | Z_SYS , SYS_OSAL_NV_WRITE , Z_B0 ( CONF_TCLK_TABLE_START ) , Z_B1 ( CONF_TCLK_TABLE_START ) ,
0x00 /* offset */ , 0x20 /* len */ ,
0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF ,
0x5a , 0x69 , 0x67 , 0x42 , 0x65 , 0x65 , 0x41 , 0x6c ,
0x6c , 0x69 , 0x61 , 0x6e , 0x63 , 0x65 , 0x30 , 0x39 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ) // 2109010100200FFFFFFFFFFFFFFFF5A6967426565416C6C69616E636530390000000000000000
// Write Z_ZDO Direct CB
ZBM ( ZBS_W_ZDODCB , Z_SREQ | Z_SAPI , SAPI_WRITE_CONFIGURATION , CONF_ZDO_DIRECT_CB , 0x01 /* len */ , 0x01 ) // 26058F0101
// NV Init ZNP Has Configured
ZBM ( ZBS_WNV_INITZNPHC , Z_SREQ | Z_SYS , SYS_OSAL_NV_ITEM_INIT , ZNP_HAS_CONFIGURED & 0xFF , ZNP_HAS_CONFIGURED > > 8 ,
0x01 , 0x00 /* InitLen 16 bits */ , 0x01 /* len */ , 0x00 ) // 2107000F01000100 - 610709
// Init succeeded
2019-09-15 10:10:59 +01:00
ZBM ( ZBR_WNV_INIT_OK , Z_SRSP | Z_SYS , SYS_OSAL_NV_ITEM_INIT , Z_Created ) // 610709 - NV Write
2019-08-31 20:23:32 +01:00
// Write ZNP Has Configured
ZBM ( ZBS_WNV_ZNPHC , Z_SREQ | Z_SYS , SYS_OSAL_NV_WRITE , Z_B0 ( ZNP_HAS_CONFIGURED ) , Z_B1 ( ZNP_HAS_CONFIGURED ) ,
0x00 /* offset */ , 0x01 /* len */ , 0x55 ) // 2109000F000155 - 610900
// Z_ZDO:startupFromApp
ZBM ( ZBS_STARTUPFROMAPP , Z_SREQ | Z_ZDO , ZDO_STARTUP_FROM_APP , 100 , 0 /* delay */ ) // 25406400
ZBM ( ZBR_STARTUPFROMAPP , Z_SRSP | Z_ZDO , ZDO_STARTUP_FROM_APP ) // 6540 + 01 for new network, 00 for exisitng network, 02 for error
ZBM ( AREQ_STARTUPFROMAPP , Z_AREQ | Z_ZDO , ZDO_STATE_CHANGE_IND , ZDO_DEV_ZB_COORD ) // 45C009 + 08 = starting, 09 = started
// GetDeviceInfo
ZBM ( ZBS_GETDEVICEINFO , Z_SREQ | Z_UTIL , Z_UTIL_GET_DEVICE_INFO ) // 2700
ZBM ( ZBR_GETDEVICEINFO , Z_SRSP | Z_UTIL , Z_UTIL_GET_DEVICE_INFO , Z_Success ) // Ex= 6700.00.6263151D004B1200.0000.07.09.00
// IEEE Adr (8 bytes) = 6263151D004B1200
// Short Addr (2 bytes) = 0000
// Device Type (1 byte) = 07 (coord?)
// Device State (1 byte) = 09 (coordinator started)
// NumAssocDevices (1 byte) = 00
// Read Pan ID
//ZBM(ZBS_READ_NV_PANID, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, PANID & 0xFF, PANID >> 8, 0x00 /* offset */ ) // 2108830000
// Z_ZDO:nodeDescReq
ZBM ( ZBS_ZDO_NODEDESCREQ , Z_SREQ | Z_ZDO , ZDO_NODE_DESC_REQ , 0x00 , 0x00 /* dst addr */ , 0x00 , 0x00 /* NWKAddrOfInterest */ ) // 250200000000
ZBM ( ZBR_ZDO_NODEDESCREQ , Z_SRSP | Z_ZDO , ZDO_NODE_DESC_REQ , Z_Success ) // 650200
// Async resp ex: 4582.0000.00.0000.00.40.8F.0000.50.A000.0100.A000.00
ZBM ( AREQ_ZDO_NODEDESCREQ , Z_AREQ | Z_ZDO , ZDO_NODE_DESC_RSP ) // 4582
// SrcAddr (2 bytes) 0000
// Status (1 byte) 00 Success
// NwkAddr (2 bytes) 0000
// LogicalType (1 byte) - 00 Coordinator
// APSFlags (1 byte) - 40 0=APSFlags 4=NodeFreqBands
// MACCapabilityFlags (1 byte) - 8F ALL
// ManufacturerCode (2 bytes) - 0000
// MaxBufferSize (1 byte) - 50 NPDU
// MaxTransferSize (2 bytes) - A000 = 160
// ServerMask (2 bytes) - 0100 - Primary Trust Center
// MaxOutTransferSize (2 bytes) - A000 = 160
// DescriptorCapabilities (1 byte) - 00
// Z_ZDO:activeEpReq
ZBM ( ZBS_ZDO_ACTIVEEPREQ , Z_SREQ | Z_ZDO , ZDO_ACTIVE_EP_REQ , 0x00 , 0x00 , 0x00 , 0x00 ) // 250500000000
ZBM ( ZBR_ZDO_ACTIVEEPREQ , Z_SRSP | Z_ZDO , ZDO_ACTIVE_EP_REQ , Z_Success ) // 65050000
ZBM ( ZBR_ZDO_ACTIVEEPRSP_NONE , Z_AREQ | Z_ZDO , ZDO_ACTIVE_EP_RSP , 0x00 , 0x00 /* srcAddr */ , Z_Success ,
0x00 , 0x00 /* nwkaddr */ , 0x00 /* activeepcount */ ) // 45050000 - no Ep running
ZBM ( ZBR_ZDO_ACTIVEEPRSP_OK , Z_AREQ | Z_ZDO , ZDO_ACTIVE_EP_RSP , 0x00 , 0x00 /* srcAddr */ , Z_Success ,
0x00 , 0x00 /* nwkaddr */ , 0x02 /* activeepcount */ , 0x0B , 0x01 /* the actual endpoints */ ) // 25050000 - no Ep running
// Z_AF:register profile:104, ep:01
ZBM ( ZBS_AF_REGISTER01 , Z_SREQ | Z_AF , AF_REGISTER , 0x01 /* endpoint */ , Z_B0 ( Z_PROF_HA ) , Z_B1 ( Z_PROF_HA ) , // 24000401050000000000
0x05 , 0x00 /* AppDeviceId */ , 0x00 /* AppDevVer */ , 0x00 /* LatencyReq */ ,
0x00 /* AppNumInClusters */ , 0x00 /* AppNumInClusters */ )
ZBM ( ZBR_AF_REGISTER , Z_SRSP | Z_AF , AF_REGISTER , Z_Success ) // 640000
ZBM ( ZBS_AF_REGISTER0B , Z_SREQ | Z_AF , AF_REGISTER , 0x0B /* endpoint */ , Z_B0 ( Z_PROF_HA ) , Z_B1 ( Z_PROF_HA ) , // 2400040B050000000000
0x05 , 0x00 /* AppDeviceId */ , 0x00 /* AppDevVer */ , 0x00 /* LatencyReq */ ,
0x00 /* AppNumInClusters */ , 0x00 /* AppNumInClusters */ )
// Z_ZDO:mgmtPermitJoinReq
ZBM ( ZBS_PERMITJOINREQ_CLOSE , Z_SREQ | Z_ZDO , ZDO_MGMT_PERMIT_JOIN_REQ , 0x02 /* AddrMode */ , // 25360200000000
0x00 , 0x00 /* DstAddr */ , 0x00 /* Duration */ , 0x00 /* TCSignificance */ )
2019-09-15 10:10:59 +01:00
ZBM ( ZBS_PERMITJOINREQ_OPEN_60 , Z_SREQ | Z_ZDO , ZDO_MGMT_PERMIT_JOIN_REQ , 0x0F /* AddrMode */ , // 25360FFFFC3C00
0xFC , 0xFF /* DstAddr */ , 60 /* Duration */ , 0x00 /* TCSignificance */ )
ZBM ( ZBS_PERMITJOINREQ_OPEN_XX , Z_SREQ | Z_ZDO , ZDO_MGMT_PERMIT_JOIN_REQ , 0x0F /* AddrMode */ , // 25360FFFFCFF00
2019-08-31 20:23:32 +01:00
0xFC , 0xFF /* DstAddr */ , 0xFF /* Duration */ , 0x00 /* TCSignificance */ )
ZBM ( ZBR_PERMITJOINREQ , Z_SRSP | Z_ZDO , ZDO_MGMT_PERMIT_JOIN_REQ , Z_Success ) // 653600
2019-09-15 10:10:59 +01:00
ZBM ( ZBR_PERMITJOIN_AREQ_CLOSE , Z_AREQ | Z_ZDO , ZDO_PERMIT_JOIN_IND , 0x00 /* Duration */ ) // 45CB00
ZBM ( ZBR_PERMITJOIN_AREQ_OPEN_60 , Z_AREQ | Z_ZDO , ZDO_PERMIT_JOIN_IND , 60 /* Duration */ ) // 45CB3C
ZBM ( ZBR_PERMITJOIN_AREQ_OPEN_XX , Z_AREQ | Z_ZDO , ZDO_PERMIT_JOIN_IND , 0xFF /* Duration */ ) // 45CBFF
2019-08-31 20:23:32 +01:00
ZBM ( ZBR_PERMITJOIN_AREQ_RSP , Z_AREQ | Z_ZDO , ZDO_MGMT_PERMIT_JOIN_RSP , 0x00 , 0x00 /* srcAddr*/ , Z_Success ) // 45B6000000
// Filters for ZCL frames
ZBM ( ZBR_AF_INCOMING_MESSAGE , Z_AREQ | Z_AF , AF_INCOMING_MSG ) // 4481
static const Zigbee_Instruction zb_prog [ ] PROGMEM = {
ZI_LABEL ( 0 )
ZI_NOOP ( )
ZI_ON_ERROR_GOTO ( ZIGBEE_LABEL_ABORT )
ZI_ON_TIMEOUT_GOTO ( ZIGBEE_LABEL_ABORT )
ZI_ON_RECV_UNEXPECTED ( & Z_Recv_Default )
ZI_WAIT ( 15000 ) // wait for 15 seconds for Tasmota to stabilize
ZI_ON_ERROR_GOTO ( 50 )
2019-09-15 10:10:59 +01:00
ZI_MQTT_STATUS ( ZIGBEE_STATUS_BOOT , " Booting " )
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: rebooting device")
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_RESET ) // reboot cc2530 just in case we rebooted ESP8266 but not cc2530
ZI_WAIT_RECV ( 5000 , ZBR_RESET ) // timeout 5s
ZI_LOG ( LOG_LEVEL_INFO , " ZIG: checking device configuration " )
ZI_SEND ( ZBS_ZNPHC ) // check value of ZNP Has Configured
ZI_WAIT_RECV ( 2000 , ZBR_ZNPHC )
ZI_SEND ( ZBS_VERSION ) // check ZNP software version
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV_FUNC ( 1000 , ZBR_VERSION , & Z_ReceiveCheckVersion ) // Check version
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_PAN ) // check PAN ID
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV ( 1000 , ZBR_PAN )
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_EXTPAN ) // check EXT PAN ID
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV ( 1000 , ZBR_EXTPAN )
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_CHANN ) // check CHANNEL
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV ( 1000 , ZBR_CHANN )
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_PFGK ) // check PFGK
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV ( 1000 , ZBR_PFGK )
2019-08-31 20:23:32 +01:00
ZI_SEND ( ZBS_PFGKEN ) // check PFGKEN
2019-09-15 10:10:59 +01:00
ZI_WAIT_RECV ( 1000 , ZBR_PFGKEN )
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee configuration ok")
2019-08-31 20:23:32 +01:00
// all is good, we can start
2019-09-15 10:10:59 +01:00
ZI_LABEL ( ZIGBEE_LABEL_START ) // START ZNP App
ZI_MQTT_STATUS ( ZIGBEE_STATUS_STARTING , " Configured, starting coordinator " )
//ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
2019-08-31 20:23:32 +01:00
ZI_ON_ERROR_GOTO ( ZIGBEE_LABEL_ABORT )
// Z_ZDO:startupFromApp
2019-09-15 10:10:59 +01:00
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: starting zigbee coordinator")
ZI_SEND ( ZBS_STARTUPFROMAPP ) // start coordinator
ZI_WAIT_RECV ( 2000 , ZBR_STARTUPFROMAPP ) // wait for sync ack of command
ZI_WAIT_UNTIL ( 5000 , AREQ_STARTUPFROMAPP ) // wait for async message that coordinator started
ZI_SEND ( ZBS_GETDEVICEINFO ) // GetDeviceInfo
ZI_WAIT_RECV_FUNC ( 2000 , ZBR_GETDEVICEINFO , & Z_ReceiveDeviceInfo )
//ZI_WAIT_RECV(2000, ZBR_GETDEVICEINFO) // TODO memorize info
ZI_SEND ( ZBS_ZDO_NODEDESCREQ ) // Z_ZDO:nodeDescReq
ZI_WAIT_RECV ( 1000 , ZBR_ZDO_NODEDESCREQ )
2019-08-31 20:23:32 +01:00
ZI_WAIT_UNTIL ( 5000 , AREQ_ZDO_NODEDESCREQ )
2019-09-15 10:10:59 +01:00
ZI_SEND ( ZBS_ZDO_ACTIVEEPREQ ) // Z_ZDO:activeEpReq
ZI_WAIT_RECV ( 1000 , ZBR_ZDO_ACTIVEEPREQ )
ZI_WAIT_UNTIL ( 1000 , ZBR_ZDO_ACTIVEEPRSP_NONE )
ZI_SEND ( ZBS_AF_REGISTER01 ) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
ZI_WAIT_RECV ( 1000 , ZBR_AF_REGISTER )
ZI_SEND ( ZBS_AF_REGISTER0B ) // Z_AF register for endpoint 0B, profile 0x0104 Home Automation
ZI_WAIT_RECV ( 1000 , ZBR_AF_REGISTER )
2019-08-31 20:23:32 +01:00
// Z_ZDO:nodeDescReq ?? Is is useful to redo it? TODO
// redo Z_ZDO:activeEpReq to check that Ep are available
2019-09-15 10:10:59 +01:00
ZI_SEND ( ZBS_ZDO_ACTIVEEPREQ ) // Z_ZDO:activeEpReq
ZI_WAIT_RECV ( 1000 , ZBR_ZDO_ACTIVEEPREQ )
ZI_WAIT_UNTIL ( 1000 , ZBR_ZDO_ACTIVEEPRSP_OK )
ZI_SEND ( ZBS_PERMITJOINREQ_CLOSE ) // Closing the Permit Join
ZI_WAIT_RECV ( 1000 , ZBR_PERMITJOINREQ )
ZI_WAIT_UNTIL ( 1000 , ZBR_PERMITJOIN_AREQ_RSP ) // not sure it's useful
2019-08-31 20:23:32 +01:00
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE)
2019-09-15 10:10:59 +01:00
//ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) // Opening Permit Join, normally through command
//ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ)
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX)
2019-08-31 20:23:32 +01:00
ZI_LABEL ( ZIGBEE_LABEL_READY )
2019-09-15 10:10:59 +01:00
ZI_MQTT_STATUS ( ZIGBEE_STATUS_OK , " Started " )
2019-08-31 20:23:32 +01:00
ZI_LOG ( LOG_LEVEL_INFO , " ZIG: zigbee device ready, listening... " )
2019-09-15 10:10:59 +01:00
ZI_CALL ( & Z_State_Ready , 1 ) // Now accept incoming messages
ZI_LABEL ( ZIGBEE_LABEL_MAIN_LOOP )
2019-08-31 20:23:32 +01:00
ZI_WAIT_FOREVER ( )
ZI_GOTO ( ZIGBEE_LABEL_READY )
2019-09-15 10:10:59 +01:00
ZI_LABEL ( ZIGBEE_LABEL_PERMIT_JOIN_CLOSE )
ZI_MQTT_STATUS ( ZIGBEE_STATUS_PERMITJOIN_CLOSE , " Disable Pairing mode " )
ZI_SEND ( ZBS_PERMITJOINREQ_CLOSE ) // Closing the Permit Join
ZI_WAIT_RECV ( 1000 , ZBR_PERMITJOINREQ )
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_CLOSE)
ZI_GOTO ( ZIGBEE_LABEL_MAIN_LOOP )
ZI_LABEL ( ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 )
ZI_MQTT_STATUS ( ZIGBEE_STATUS_PERMITJOIN_OPEN_60 , " Enable Pairing mode for 60 seconds " )
ZI_SEND ( ZBS_PERMITJOINREQ_OPEN_60 )
ZI_WAIT_RECV ( 1000 , ZBR_PERMITJOINREQ )
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_60)
ZI_GOTO ( ZIGBEE_LABEL_MAIN_LOOP )
ZI_LABEL ( ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX )
ZI_MQTT_STATUS ( ZIGBEE_STATUS_PERMITJOIN_OPEN_XX , " Enable Pairing mode until next boot " )
ZI_SEND ( ZBS_PERMITJOINREQ_OPEN_XX )
ZI_WAIT_RECV ( 1000 , ZBR_PERMITJOINREQ )
//ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) // not sure it's useful
//ZI_WAIT_UNTIL(500, ZBR_PERMITJOIN_AREQ_OPEN_XX)
ZI_GOTO ( ZIGBEE_LABEL_MAIN_LOOP )
ZI_LABEL ( 50 ) // reformat device
ZI_MQTT_STATUS ( ZIGBEE_STATUS_RESET_CONF , " Reseting configuration " )
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee bad configuration of device, doing a factory reset")
2019-08-31 20:23:32 +01:00
ZI_ON_ERROR_GOTO ( ZIGBEE_LABEL_ABORT )
2019-09-15 10:10:59 +01:00
ZI_SEND ( ZBS_FACTRES ) // factory reset
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_RESET ) // reset device
2019-08-31 20:23:32 +01:00
ZI_WAIT_RECV ( 5000 , ZBR_RESET )
2019-09-15 10:10:59 +01:00
ZI_SEND ( ZBS_W_PAN ) // write PAN ID
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_W_EXTPAN ) // write EXT PAN ID
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_W_CHANN ) // write CHANNEL
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_W_LOGTYP ) // write Logical Type = coordinator
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_W_PFGK ) // write PRECFGKEY
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_W_PFGKEN ) // write PRECFGKEY Enable
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
ZI_SEND ( ZBS_WNV_SECMODE ) // write Security Mode
ZI_WAIT_RECV ( 1000 , ZBR_WNV_OK )
ZI_SEND ( ZBS_W_ZDODCB ) // write Z_ZDO Direct CB
ZI_WAIT_RECV ( 1000 , ZBR_W_OK )
2019-08-31 20:23:32 +01:00
// Now mark the device as ready, writing 0x55 in memory slot 0x0F00
2019-09-15 10:10:59 +01:00
ZI_SEND ( ZBS_WNV_INITZNPHC ) // Init NV ZNP Has Configured
ZI_WAIT_RECV ( 1000 , ZBR_WNV_INIT_OK )
ZI_SEND ( ZBS_WNV_ZNPHC ) // Write NV ZNP Has Configured
ZI_WAIT_RECV ( 1000 , ZBR_WNV_OK )
2019-08-31 20:23:32 +01:00
2019-09-15 10:10:59 +01:00
//ZI_LOG(LOG_LEVEL_INFO, "ZIG: zigbee device reconfigured")
ZI_GOTO ( ZIGBEE_LABEL_START )
2019-08-31 20:23:32 +01:00
2019-09-15 10:10:59 +01:00
ZI_LABEL ( ZIGBEE_LABEL_UNSUPPORTED_VERSION )
ZI_MQTT_STATUS ( ZIGBEE_STATUS_UNSUPPORTED_VERSION , " Only ZNP 1.2 is currently supported " )
ZI_GOTO ( ZIGBEE_LABEL_ABORT )
ZI_LABEL ( ZIGBEE_LABEL_ABORT ) // Label 99: abort
ZI_MQTT_STATUS ( ZIGBEE_STATUS_ABORT , " Abort " )
2019-08-31 20:23:32 +01:00
ZI_LOG ( LOG_LEVEL_ERROR , " ZIG: Abort " )
ZI_STOP ( ZIGBEE_LABEL_ABORT )
} ;
2019-09-15 10:10:59 +01:00
int32_t Z_ReceiveDeviceInfo ( int32_t res , class SBuffer & buf ) {
// Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991
// IEEE Adr (8 bytes) = 0x00124B001D156362
// Short Addr (2 bytes) = 0x0000
// Device Type (1 byte) = 0x07 (coord?)
// Device State (1 byte) = 0x09 (coordinator started)
// NumAssocDevices (1 byte) = 0x02
// List of devices: 0x8683, 0x9199
Z_IEEEAddress long_adr = buf . get64 ( 3 ) ;
Z_ShortAddress short_adr = buf . get16 ( 11 ) ;
uint8_t device_type = buf . get8 ( 13 ) ;
uint8_t device_state = buf . get8 ( 14 ) ;
uint8_t device_associated = buf . get8 ( 15 ) ;
char hex [ 20 ] ;
Uint64toHex ( long_adr , hex , 64 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATUS " \" :{ "
" \" code \" :%d, \" IEEEAddr \" : \" %s \" , \" ShortAddr \" : \" 0x%04X \" "
" , \" DeviceType \" :%d, \" DeviceState \" :%d "
" , \" NumAssocDevices \" :%d " ) ,
ZIGBEE_STATUS_DEVICE_INFO , hex , short_adr , device_type , device_state ,
device_associated ) ;
if ( device_associated > 0 ) {
uint idx = 16 ;
ResponseAppend_P ( PSTR ( " , \" AssocDevicesList \" :[ " ) ) ;
for ( uint32_t i = 0 ; i < device_associated ; i + + ) {
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" 0x%04X \" " ) , buf . get16 ( idx ) ) ;
idx + = 2 ;
}
ResponseAppend_P ( PSTR ( " ] " ) ) ;
}
ResponseJsonEnd ( ) ; // append '}'
ResponseJsonEnd ( ) ; // append '}'
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATUS ) ) ;
XdrvRulesProcess ( ) ;
return res ;
}
2019-08-31 20:23:32 +01:00
2019-09-15 10:10:59 +01:00
int32_t Z_ReceiveCheckVersion ( int32_t res , class SBuffer & buf ) {
2019-08-31 20:23:32 +01:00
// check that the version is supported
// typical version for ZNP 1.2
2019-09-15 10:10:59 +01:00
// 61020200-02.06.03.D9143401.0200000000
// TranportRev = 02
// Product = 00
// MajorRel = 2
// MinorRel = 6
// MaintRel = 3
// Revision = 20190425 d (0x013414D9)
uint8_t major_rel = buf . get8 ( 4 ) ;
uint8_t minor_rel = buf . get8 ( 5 ) ;
uint8_t maint_rel = buf . get8 ( 6 ) ;
uint32_t revision = buf . get32 ( 7 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATUS " \" :{ "
" \" code \" :%d, \" MajorRel \" :%d, \" MinorRel \" :%d "
" , \" MaintRel \" :%d, \" Revision \" :%d}} " ) ,
ZIGBEE_STATUS_DEVICE_VERSION , major_rel , minor_rel ,
maint_rel , revision ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATUS ) ) ;
XdrvRulesProcess ( ) ;
if ( ( 0x02 = = major_rel ) & & ( 0x06 = = minor_rel ) ) {
2019-08-31 20:23:32 +01:00
return 0 ; // version 2.6.x is ok
} else {
2019-09-15 10:10:59 +01:00
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
2019-08-31 20:23:32 +01:00
}
}
int32_t Z_Recv_Default ( int32_t res , class SBuffer & buf ) {
// Default message handler for new messages
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: Z_Recv_Default " ) ) ;
if ( zigbee . init_phase ) {
// if still during initialization phase, ignore any unexpected message
return - 1 ; // ignore message
} else {
if ( ( pgm_read_byte ( & ZBR_AF_INCOMING_MESSAGE [ 0 ] ) = = buf . get8 ( 0 ) ) & &
( pgm_read_byte ( & ZBR_AF_INCOMING_MESSAGE [ 1 ] ) = = buf . get8 ( 1 ) ) ) {
2019-09-15 10:10:59 +01:00
uint16_t groupid = buf . get16 ( 2 ) ;
uint16_t clusterid = buf . get16 ( 4 ) ;
Z_ShortAddress srcaddr = buf . get16 ( 6 ) ;
uint8_t srcendpoint = buf . get8 ( 8 ) ;
uint8_t dstendpoint = buf . get8 ( 9 ) ;
uint8_t wasbroadcast = buf . get8 ( 10 ) ;
uint8_t linkquality = buf . get8 ( 11 ) ;
uint8_t securityuse = buf . get8 ( 12 ) ;
uint32_t timestamp = buf . get32 ( 13 ) ;
uint8_t seqnumber = buf . get8 ( 17 ) ;
ZCLFrame zcl_received = ZCLFrame : : parseRawFrame ( buf , 19 , buf . get8 ( 18 ) , clusterid , groupid ) ;
zcl_received . publishMQTTReceived ( groupid , clusterid , srcaddr ,
srcendpoint , dstendpoint , wasbroadcast ,
linkquality , securityuse , seqnumber ,
timestamp ) ;
char shortaddr [ 8 ] ;
snprintf_P ( shortaddr , sizeof ( shortaddr ) , PSTR ( " 0x%04X " ) , srcaddr ) ;
DynamicJsonBuffer jsonBuffer ;
JsonObject & json_root = jsonBuffer . createObject ( ) ;
JsonObject & json = json_root . createNestedObject ( shortaddr ) ;
if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_REPORT_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) {
zcl_received . parseRawAttributes ( json ) ;
} else if ( zcl_received . isClusterSpecificCommand ( ) ) {
zcl_received . parseClusterSpecificCommand ( json ) ;
}
zcl_received . postProcessAttributes ( json ) ;
String msg ( " " ) ;
msg . reserve ( 100 ) ;
json_root . printTo ( msg ) ;
Response_P ( PSTR ( " %s " ) , msg . c_str ( ) ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCLRECEIVED ) ) ;
XdrvRulesProcess ( ) ;
2019-08-31 20:23:32 +01:00
}
return - 1 ;
}
}
int32_t Z_State_Ready ( uint8_t value ) {
zigbee . init_phase = false ; // initialization phase complete
return 0 ; // continue
}
uint8_t ZigbeeGetInstructionSize ( uint8_t instr ) { // in Zigbee_Instruction lines (words)
if ( instr > = ZGB_INSTR_12_BYTES ) {
return 3 ;
} else if ( instr > = ZGB_INSTR_8_BYTES ) {
return 2 ;
} else {
return 1 ;
}
}
void ZigbeeGotoLabel ( uint8_t label ) {
// look for the label scanning entire code
uint16_t goto_pc = 0xFFFF ; // 0xFFFF means not found
uint8_t cur_instr = 0 ;
uint8_t cur_d8 = 0 ;
uint8_t cur_instr_len = 1 ; // size of current instruction in words
for ( uint32_t i = 0 ; i < sizeof ( zb_prog ) / sizeof ( zb_prog [ 0 ] ) ; i + = cur_instr_len ) {
const Zigbee_Instruction * cur_instr_line = & zb_prog [ i ] ;
cur_instr = pgm_read_byte ( & cur_instr_line - > i . i ) ;
cur_d8 = pgm_read_byte ( & cur_instr_line - > i . d8 ) ;
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZGB GOTO: pc %d instr %d"), i, cur_instr);
if ( ZGB_INSTR_LABEL = = cur_instr ) {
//AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ZIG: found label %d at pc %d"), cur_d8, i);
if ( label = = cur_d8 ) {
// label found, goto to this pc
zigbee . pc = i ;
zigbee . state_machine = true ;
zigbee . state_waiting = false ;
return ;
}
}
// get instruction length
cur_instr_len = ZigbeeGetInstructionSize ( cur_instr ) ;
}
// no label found, abort
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " ZIG: Goto label not found, label=%d pc=%d " ) , label , zigbee . pc ) ;
if ( ZIGBEE_LABEL_ABORT ! = label ) {
// if not already looking for ZIGBEE_LABEL_ABORT, goto ZIGBEE_LABEL_ABORT
ZigbeeGotoLabel ( ZIGBEE_LABEL_ABORT ) ;
} else {
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " ZIG: Label Abort (%d) not present, aborting Zigbee " ) , ZIGBEE_LABEL_ABORT ) ;
zigbee . state_machine = false ;
zigbee . active = false ;
}
}
void ZigbeeStateMachine_Run ( void ) {
uint8_t cur_instr = 0 ;
uint8_t cur_d8 = 0 ;
uint16_t cur_d16 = 0 ;
const void * cur_ptr1 = nullptr ;
const void * cur_ptr2 = nullptr ;
uint32_t now = millis ( ) ;
if ( zigbee . state_waiting ) { // state machine is waiting for external event or timeout
// checking if timeout expired
if ( ( zigbee . next_timeout ) & & ( now > zigbee . next_timeout ) ) { // if next_timeout == 0 then wait forever
//AddLog_P2(LOG_LEVEL_INFO, PSTR("ZIG: timeout occured pc=%d"), zigbee.pc);
if ( ! zigbee . state_no_timeout ) {
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " ZIG: timeout, goto label %d " ) , zigbee . on_timeout_goto ) ;
ZigbeeGotoLabel ( zigbee . on_timeout_goto ) ;
} else {
zigbee . state_waiting = false ; // simply stop waiting
}
}
}
while ( ( zigbee . state_machine ) & & ( ! zigbee . state_waiting ) ) {
// reinit receive filters and functions (they only work for a single instruction)
zigbee . recv_filter = nullptr ;
zigbee . recv_func = nullptr ;
zigbee . recv_until = false ;
zigbee . state_no_timeout = false ; // reset the no_timeout for next instruction
if ( zigbee . pc > ( sizeof ( zb_prog ) / sizeof ( zb_prog [ 0 ] ) ) ) {
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " ZIG: Invalid pc: %d, aborting " ) , zigbee . pc ) ;
zigbee . pc = - 1 ;
}
if ( zigbee . pc < 0 ) {
zigbee . state_machine = false ;
return ;
}
// load current instruction details
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZIG: Executing instruction pc=%d " ) , zigbee . pc ) ;
const Zigbee_Instruction * cur_instr_line = & zb_prog [ zigbee . pc ] ;
cur_instr = pgm_read_byte ( & cur_instr_line - > i . i ) ;
cur_d8 = pgm_read_byte ( & cur_instr_line - > i . d8 ) ;
cur_d16 = pgm_read_word ( & cur_instr_line - > i . d16 ) ;
if ( cur_instr > = ZGB_INSTR_8_BYTES ) {
cur_instr_line + + ;
cur_ptr1 = cur_instr_line - > p ;
}
if ( cur_instr > = ZGB_INSTR_12_BYTES ) {
cur_instr_line + + ;
cur_ptr2 = cur_instr_line - > p ;
}
zigbee . pc + = ZigbeeGetInstructionSize ( cur_instr ) ; // move pc to next instruction, before any goto
switch ( cur_instr ) {
case ZGB_INSTR_NOOP :
case ZGB_INSTR_LABEL : // do nothing
break ;
case ZGB_INSTR_GOTO :
ZigbeeGotoLabel ( cur_d8 ) ;
break ;
case ZGB_INSTR_ON_ERROR_GOTO :
zigbee . on_error_goto = cur_d8 ;
break ;
case ZGB_INSTR_ON_TIMEOUT_GOTO :
zigbee . on_timeout_goto = cur_d8 ;
break ;
case ZGB_INSTR_WAIT :
zigbee . next_timeout = now + cur_d16 ;
zigbee . state_waiting = true ;
zigbee . state_no_timeout = true ; // do not generate a timeout error when waiting is done
break ;
case ZGB_INSTR_WAIT_FOREVER :
zigbee . next_timeout = 0 ;
zigbee . state_waiting = true ;
//zigbee.state_no_timeout = true; // do not generate a timeout error when waiting is done
break ;
case ZGB_INSTR_STOP :
zigbee . state_machine = false ;
if ( cur_d8 ) {
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " ZIG: Stopping (%d) " ) , cur_d8 ) ;
}
break ;
case ZGB_INSTR_CALL :
if ( cur_ptr1 ) {
uint32_t res ;
res = ( * ( ( ZB_Func ) cur_ptr1 ) ) ( cur_d8 ) ;
if ( res > 0 ) {
ZigbeeGotoLabel ( res ) ;
continue ; // avoid incrementing PC after goto
} else if ( res = = 0 ) {
// do nothing
} else if ( res = = - 1 ) {
// do nothing
} else {
ZigbeeGotoLabel ( zigbee . on_error_goto ) ;
continue ;
}
}
break ;
case ZGB_INSTR_LOG :
AddLog_P ( cur_d8 , ( char * ) cur_ptr1 ) ;
break ;
2019-09-15 10:10:59 +01:00
case ZGB_INSTR_MQTT_STATUS :
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATUS " \" :{ \" code \" :%d, \" message \" : \" %s \" }} " ) ,
cur_d8 , ( char * ) cur_ptr1 ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATUS ) ) ;
XdrvRulesProcess ( ) ;
break ;
2019-08-31 20:23:32 +01:00
case ZGB_INSTR_SEND :
ZigbeeZNPSend ( ( uint8_t * ) cur_ptr1 , cur_d8 /* len */ ) ;
break ;
case ZGB_INSTR_WAIT_UNTIL :
zigbee . recv_until = true ; // and reuse ZGB_INSTR_WAIT_RECV
case ZGB_INSTR_WAIT_RECV :
zigbee . recv_filter = ( uint8_t * ) cur_ptr1 ;
zigbee . recv_filter_len = cur_d8 ; // len
zigbee . next_timeout = now + cur_d16 ;
zigbee . state_waiting = true ;
break ;
case ZGB_ON_RECV_UNEXPECTED :
zigbee . recv_unexpected = ( ZB_RecvMsgFunc ) cur_ptr1 ;
break ;
case ZGB_INSTR_WAIT_RECV_CALL :
zigbee . recv_filter = ( uint8_t * ) cur_ptr1 ;
zigbee . recv_filter_len = cur_d8 ; // len
zigbee . recv_func = ( ZB_RecvMsgFunc ) cur_ptr2 ;
zigbee . next_timeout = now + cur_d16 ;
zigbee . state_waiting = true ;
break ;
}
}
}
int32_t ZigbeeProcessInput ( class SBuffer & buf ) {
if ( ! zigbee . state_machine ) { return - 1 ; } // if state machine is stopped, send 'ignore' message
// apply the receive filter, acts as 'startsWith()'
bool recv_filter_match = true ;
bool recv_prefix_match = false ; // do the first 2 bytes match the response
if ( ( zigbee . recv_filter ) & & ( zigbee . recv_filter_len > 0 ) ) {
if ( zigbee . recv_filter_len > = 2 ) {
recv_prefix_match = false ;
if ( ( pgm_read_byte ( & zigbee . recv_filter [ 0 ] ) = = buf . get8 ( 0 ) ) & &
( pgm_read_byte ( & zigbee . recv_filter [ 1 ] ) = = buf . get8 ( 1 ) ) ) {
recv_prefix_match = true ;
}
}
for ( uint32_t i = 0 ; i < zigbee . recv_filter_len ; i + + ) {
if ( pgm_read_byte ( & zigbee . recv_filter [ i ] ) ! = buf . get8 ( i ) ) {
recv_filter_match = false ;
break ;
}
}
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: ZigbeeProcessInput: recv_prefix_match = %d, recv_filter_match = %d " ) , recv_prefix_match , recv_filter_match ) ;
}
// if there is a recv_callback, call it now
int32_t res = - 1 ; // default to ok
// res = 0 - proceed to next state
// res > 0 - proceed to the specified state
// res = -1 - silently ignore the message
// res <= -2 - move to error state
// pre-compute the suggested value
if ( ( zigbee . recv_filter ) & & ( zigbee . recv_filter_len > 0 ) ) {
if ( ! recv_prefix_match ) {
res = - 1 ; // ignore
} else { // recv_prefix_match
if ( recv_filter_match ) {
res = 0 ; // ok
} else {
if ( zigbee . recv_until ) {
res = - 1 ; // ignore until full match
} else {
res = - 2 ; // error, because message is expected but wrong value
}
}
}
} else { // we don't have any filter, ignore message by default
res = - 1 ;
}
if ( recv_prefix_match ) {
if ( zigbee . recv_func ) {
res = ( * zigbee . recv_func ) ( res , buf ) ;
}
}
if ( - 1 = = res ) {
// if frame was ignored up to now
if ( zigbee . recv_unexpected ) {
res = ( * zigbee . recv_unexpected ) ( res , buf ) ;
}
}
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: ZigbeeProcessInput: res = %d " ) , res ) ;
// change state accordingly
if ( 0 = = res ) {
// if ok, continue execution
zigbee . state_waiting = false ;
} else if ( res > 0 ) {
ZigbeeGotoLabel ( res ) ; // if >0 then go to specified label
} else if ( - 1 = = res ) {
// -1 means ignore message
// just do nothing
} else {
// any other negative value means error
ZigbeeGotoLabel ( zigbee . on_error_goto ) ;
}
}
void ZigbeeInput ( void )
{
static uint32_t zigbee_polling_window = 0 ;
static uint8_t fcs = ZIGBEE_SOF ;
static uint32_t zigbee_frame_len = 5 ; // minimal zigbee frame lenght, 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 ( ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZigbeeInput byte=%d len=%d " ) , zigbee_in_byte , zigbee_buffer - > len ( ) ) ;
if ( 0 = = zigbee_buffer - > len ( ) ) { // make sure all variables are correctly initialized
zigbee_frame_len = 5 ;
fcs = ZIGBEE_SOF ;
}
if ( ( 0 = = zigbee_buffer - > len ( ) ) & & ( ZIGBEE_SOF ! = zigbee_in_byte ) ) {
// waiting for SOF (Start Of Frame) byte, discard anything else
2019-09-15 10:10:59 +01:00
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " ZigbeeInput discarding byte %02X " ) , zigbee_in_byte ) ;
2019-08-31 20:23:32 +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 ) ) ;
2019-09-15 10:10:59 +01:00
# ifndef Z_USE_SOFTWARE_SERIAL
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( " ZIG: Bytes follor_read_metric = %0d " ) , ZigbeeSerial - > getLoopReadMetric ( ) ) ;
# endif
2019-08-31 20:23:32 +01:00
// buffer received, now check integrity
if ( zigbee_buffer - > len ( ) ! = zigbee_frame_len ) {
// Len is not correct, log and reject frame
AddLog_P2 ( 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 ) ;
} else if ( 0x00 ! = fcs ) {
// FCS is wrong, packet is corrupt, log and reject frame
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( D_JSON_ZIGBEEZNPRECEIVED " : received bad FCS frame %s, %d " ) , hex_char , fcs ) ;
} else {
// frame is correct
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_JSON_ZIGBEEZNPRECEIVED " : received correct frame %s " ) , hex_char ) ;
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 ) ) ;
2019-09-04 17:06:34 +01:00
ResponseTime_P ( PSTR ( " , \" " D_JSON_ZIGBEEZNPRECEIVED " \" : \" %s \" } " ) , hex_char ) ;
2019-08-31 20:23:32 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZNPRECEIVED ) ) ;
XdrvRulesProcess ( ) ;
// now process the message
ZigbeeProcessInput ( znp_buffer ) ;
}
zigbee_buffer - > setLen ( 0 ) ; // empty buffer
}
}
/********************************************************************************************/
void ZigbeeInit ( void )
{
zigbee . active = false ;
if ( ( pin [ GPIO_ZIGBEE_RX ] < 99 ) & & ( pin [ GPIO_ZIGBEE_TX ] < 99 ) ) {
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " Zigbee: GPIOs Rx:%d Tx:%d " ) , pin [ GPIO_ZIGBEE_RX ] , pin [ GPIO_ZIGBEE_TX ] ) ;
2019-09-15 10:10:59 +01:00
# ifdef Z_USE_SOFTWARE_SERIAL
ZigbeeSerial = new SoftwareSerial ( ) ;
ZigbeeSerial - > begin ( 115200 , pin [ GPIO_ZIGBEE_RX ] , pin [ GPIO_ZIGBEE_TX ] , SWSERIAL_8N1 , false , 256 ) ; // ZNP is 115200, RTS/CTS (ignored), 8N1
ZigbeeSerial - > enableIntTx ( false ) ;
zigbee_buffer = new SBuffer ( ZIGBEE_BUFFER_SIZE ) ;
# else
2019-08-31 20:23:32 +01:00
ZigbeeSerial = new TasmotaSerial ( pin [ GPIO_ZIGBEE_RX ] , pin [ GPIO_ZIGBEE_TX ] , 0 , 0 , 256 ) ; // set a receive buffer of 256 bytes
2019-09-15 10:10:59 +01:00
ZigbeeSerial - > begin ( 115200 ) ;
if ( ZigbeeSerial - > hardwareSerial ( ) ) {
ClaimSerial ( ) ;
zigbee_buffer = new PreAllocatedSBuffer ( sizeof ( serial_in_buffer ) , serial_in_buffer ) ;
} else {
zigbee_buffer = new SBuffer ( ZIGBEE_BUFFER_SIZE ) ;
}
# endif
zigbee . active = true ;
zigbee . init_phase = true ; // start the state machine
zigbee . state_machine = true ; // start the state machine
ZigbeeSerial - > flush ( ) ;
2019-08-31 20:23:32 +01:00
}
}
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void CmndZigbeeZNPSend ( void )
{
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 > 0 ) {
char stemp [ 3 ] ;
strlcpy ( stemp , codes , sizeof ( stemp ) ) ;
code = strtol ( stemp , nullptr , 16 ) ;
buf . add8 ( code ) ;
size - = 2 ;
codes + = 2 ;
}
ZigbeeZNPSend ( buf . getBuffer ( ) , buf . len ( ) ) ;
}
ResponseCmndDone ( ) ;
}
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
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_JSON_ZIGBEEZNPSENT " : bad message len %d " ) , len ) ;
return ;
}
uint8_t data_len = len - 2 ; // removing CMD1 and CMD2
if ( ZigbeeSerial ) {
uint8_t fcs = data_len ;
ZigbeeSerial - > write ( ZIGBEE_SOF ) ; // 0xFE
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZNPSend SOF %02X " ) , ZIGBEE_SOF ) ;
ZigbeeSerial - > write ( data_len ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZNPSend LEN %02X " ) , data_len ) ;
for ( uint32_t i = 0 ; i < len ; i + + ) {
uint8_t b = pgm_read_byte ( msg + i ) ;
ZigbeeSerial - > write ( b ) ;
fcs ^ = b ;
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZNPSend byt %02X " ) , b ) ;
}
ZigbeeSerial - > write ( fcs ) ; // finally send fcs checksum byte
AddLog_P2 ( LOG_LEVEL_DEBUG_MORE , PSTR ( " ZNPSend FCS %02X " ) , fcs ) ;
}
// Now send a MQTT message to report the sent message
char hex_char [ ( len * 2 ) + 2 ] ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEEZNPSENT " \" : \" %s \" } " ) ,
ToHex_P ( msg , len , hex_char , sizeof ( hex_char ) ) ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZNPSENT ) ) ;
XdrvRulesProcess ( ) ;
}
2019-09-15 10:10:59 +01:00
void CmndZigbeePermitJoin ( void )
{
uint32_t payload = XdrvMailbox . payload ;
if ( payload < 0 ) { payload = 0 ; }
if ( ( 99 ! = payload ) & & ( payload > 1 ) ) { payload = 1 ; }
if ( 1 = = payload ) {
ZigbeeGotoLabel ( ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 ) ;
} else if ( 99 = = payload ) {
ZigbeeGotoLabel ( ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX ) ;
} else {
ZigbeeGotoLabel ( ZIGBEE_LABEL_PERMIT_JOIN_CLOSE ) ;
}
ResponseCmndDone ( ) ;
}
2019-08-31 20:23:32 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool Xdrv23 ( uint8_t function )
{
bool result = false ;
if ( zigbee . active ) {
switch ( function ) {
case FUNC_LOOP :
if ( ZigbeeSerial ) { ZigbeeInput ( ) ; }
if ( zigbee . state_machine ) {
//ZigbeeStateMachine();
ZigbeeStateMachine_Run ( ) ;
}
break ;
case FUNC_PRE_INIT :
ZigbeeInit ( ) ;
break ;
case FUNC_COMMAND :
result = DecodeCommand ( kZigbeeCommands , ZigbeeCommand ) ;
break ;
}
}
return result ;
}
# endif // USE_ZIGBEE