diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 192ea9100..20c00952e 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -4,6 +4,7 @@ * Add debug compile features using defines DEBUG_TASMOTA_CORE, DEBUG_TASMOTA_DRIVER and DEBUG_TASMOTA_SENSOR. * See DEBUG_CORE_LOG example in sonoff.ino and DEBUG_DRIVER_LOG example in xdrv_09_timers.ino * Add support for Solax X1 inverter by Pablo Zerón + * Add ZigBee support phase 1 - low level MQTT ZNP messages for CC2530 devices * * 6.6.0.3 20190725 * Change filename of configuration backup from using FriendlyName1 to Hostname solving diacritic issues (#2422) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 7f02ba830..15f536471 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -439,6 +439,9 @@ #define D_CMND_LATITUDE "Latitude" #define D_CMND_LONGITUDE "Longitude" +// Commands xdrv_23_zigbee.ino +#define D_CMND_ZIGBEEZNPSEND "ZigbeeZNPSend" + #define D_JSON_ZIGBEEZNPRECEIVED "ZigbeeZNPReceived" /********************************************************************************************/ #define D_ASTERISK_PWD "****" diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index dbf2ab408..7b1b7a918 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index b1b4ab633..e52c016ae 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 3a8947be7..8e0aa2fb9 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index d8c59320b..dd78f92f2 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index efccbdc89..24e8b51d2 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/es-ES.h b/sonoff/language/es-ES.h index bca6b4573..e0246624b 100644 --- a/sonoff/language/es-ES.h +++ b/sonoff/language/es-ES.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index ed198694c..6e1879e12 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index e4c4db427..f021da461 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 4ee7aceb3..396688de4 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 572d81afb..f9d22f797 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h index cfe29d7b9..009a293c3 100644 --- a/sonoff/language/ko-KO.h +++ b/sonoff/language/ko-KO.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index b2e4f77c6..15b0cc1e8 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Zoemer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 6d69b120b..ac03cf87d 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index ed33302ba..51ead6a24 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index c9639b6b0..50183aaa5 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 3089f748f..7236b0204 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index 8fd0d7380..dab219890 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index e3a50b112..0a2f99d5e 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index 6542f7bdb..f2471f81c 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index b708af0a8..016ece4a9 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Зуммер" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index cf90d0505..3669262b2 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 8ce0553c4..e628fe77a 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -587,6 +587,8 @@ #define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" #define D_SENSOR_BUZZER "Buzzer" #define D_SENSOR_OLED_RESET "OLED Reset" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" #define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" #define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 8ae2b6e32..6468f8091 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -468,6 +468,10 @@ #define IR_RCV_BUFFER_SIZE 100 // Max number of packets allowed in capture buffer (default 100 (*2 bytes ram)) #define IR_RCV_TIMEOUT 15 // Number of milli-Seconds of no-more-data before we consider a message ended (default 15) #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6, max 255) + +// -- Zigbee interface ------------------------------ +//#define USE_ZIGBEE // Enable serial communication with Zigbee CC2530 flashed with ZNP + // ------------------------------------------------ #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index f41451781..8da188db4 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -126,6 +126,7 @@ const uint32_t MIN_BACKLOG_DELAY = 2; // Minimal backlog delay in 0.1 seco const uint32_t SOFT_BAUDRATE = 9600; // Default software serial baudrate const uint32_t APP_BAUDRATE = 115200; // Default serial baudrate const uint32_t SERIAL_POLLING = 100; // Serial receive polling in ms +const uint32_t ZIGBEE_POLLING = 100; // Serial receive polling in ms const uint8_t MAX_STATUS = 11; // Max number of status lines const uint32_t DRIVER_BOOT_DELAY = 1; // Number of milliseconds to retard driver cycles during boot-up time to reduce overall CPU load whilst Wifi is connecting diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 78bbe98d9..061174034 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -189,6 +189,8 @@ enum UserSelectablePins { GPIO_OLED_RESET, // OLED Display Reset GPIO_SOLAXX1_TX, // Solax Inverter tx pin GPIO_SOLAXX1_RX, // Solax Inverter rx pin + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality @@ -259,6 +261,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "i|" D_SENSOR_OLED_RESET "|" D_SENSOR_SOLAXX1_TX "|" D_SENSOR_SOLAXX1_RX "|" + D_SENSOR_ZIGBEE_TXD "|" D_SENSOR_ZIGBEE_RXD "|" ; // User selectable ADC0 functionality @@ -584,6 +587,10 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_SBR_TX, // Serial Bridge Serial interface GPIO_SBR_RX, // Serial Bridge Serial interface #endif +#ifdef USE_ZIGBEE + GPIO_ZIGBEE_TX, // Zigbee Serial interface + GPIO_ZIGBEE_RX, // Zigbee Serial interface +#endif #ifdef USE_MHZ19 GPIO_MHZ_TXD, // MH-Z19 Serial interface GPIO_MHZ_RXD, // MH-Z19 Serial interface diff --git a/sonoff/xdrv_23_zigbee.ino b/sonoff/xdrv_23_zigbee.ino new file mode 100644 index 000000000..23a9038b3 --- /dev/null +++ b/sonoff/xdrv_23_zigbee.ino @@ -0,0 +1,427 @@ +/* + xdrv_23_zigbee.ino - zigbee serial 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 . +*/ + +#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; + +// State machine states +enum class ZnpStates { + S_START = 0, + S_READY +}; + +// ZNP Constants taken from https://github.com/Frans-Willem/AqaraHub/blob/master/src/znp/znp.h +enum class ZnpCommandType { POLL = 0, SREQ = 2, AREQ = 4, SRSP = 6 }; + +enum class ZnpSubsystem { + RPC_Error = 0, + SYS = 1, + MAC = 2, + NWK = 3, + AF = 4, + ZDO = 5, + SAPI = 6, + UTIL = 7, + DEBUG = 8, + APP = 9 +}; + +enum class ZnpStatus : uint8_t { + Success = 0x00, + Failure = 0x01, + InvalidParameter = 0x02, + MemError = 0x03, + BufferFull = 0x11 +}; + +typedef uint64_t IEEEAddress; +typedef uint16_t ShortAddress; + +enum class AddrMode : uint8_t { + NotPresent = 0, + Group = 1, + ShortAddress = 2, + IEEEAddress = 3, + Broadcast = 0xFF +}; + +// Commands in the SYS subsystem +enum class SysCommand : uint8_t { + RESET = 0x00, + PING = 0x01, + VERSION = 0x02, + SET_EXTADDR = 0x03, + GET_EXTADDR = 0x04, + RAM_READ = 0x05, + RAM_WRITE = 0x06, + OSAL_NV_ITEM_INIT = 0x07, + OSAL_NV_READ = 0x08, + OSAL_NV_WRITE = 0x09, + OSAL_START_TIMER = 0x0A, + OSAL_STOP_TIMER = 0x0B, + RANDOM = 0x0C, + ADC_READ = 0x0D, + GPIO = 0x0E, + STACK_TUNE = 0x0F, + SET_TIME = 0x10, + GET_TIME = 0x11, + OSAL_NV_DELETE = 0x12, + OSAL_NV_LENGTH = 0x13, + TEST_RF = 0x40, + TEST_LOOPBACK = 0x41, + RESET_IND = 0x80, + OSAL_TIMER_EXPIRED = 0x81, +}; + +// Commands in the AF subsystem +enum class AfCommand : uint8_t { + REGISTER = 0x00, + DATA_REQUEST = 0x01, + DATA_REQUEST_EXT = 0x02, + DATA_REQUEST_SRC_RTG = 0x03, + INTER_PAN_CTL = 0x10, + DATA_STORE = 0x11, + DATA_RETRIEVE = 0x12, + APSF_CONFIG_SET = 0x13, + DATA_CONFIRM = 0x80, + REFLECT_ERROR = 0x83, + INCOMING_MSG = 0x81, + INCOMING_MSG_EXT = 0x82 +}; + +// Commands in the ZDO subsystem +enum class ZdoCommand : uint8_t { + NWK_ADDR_REQ = 0x00, + IEEE_ADDR_REQ = 0x01, + NODE_DESC_REQ = 0x02, + POWER_DESC_REQ = 0x03, + SIMPLE_DESC_REQ = 0x04, + ACTIVE_EP_REQ = 0x05, + MATCH_DESC_REQ = 0x06, + COMPLEX_DESC_REQ = 0x07, + USER_DESC_REQ = 0x08, + DEVICE_ANNCE = 0x0A, + USER_DESC_SET = 0x0B, + SERVER_DISC_REQ = 0x0C, + END_DEVICE_BIND_REQ = 0x20, + BIND_REQ = 0x21, + UNBIND_REQ = 0x22, + SET_LINK_KEY = 0x23, + REMOVE_LINK_KEY = 0x24, + GET_LINK_KEY = 0x25, + MGMT_NWK_DISC_REQ = 0x30, + MGMT_LQI_REQ = 0x31, + MGMT_RTQ_REQ = 0x32, + MGMT_BIND_REQ = 0x33, + MGMT_LEAVE_REQ = 0x34, + MGMT_DIRECT_JOIN_REQ = 0x35, + MGMT_PERMIT_JOIN_REQ = 0x36, + MGMT_NWK_UPDATE_REQ = 0x37, + MSG_CB_REGISTER = 0x3E, + MGS_CB_REMOVE = 0x3F, + STARTUP_FROM_APP = 0x40, + AUTO_FIND_DESTINATION = 0x41, + EXT_REMOVE_GROUP = 0x47, + EXT_REMOVE_ALL_GROUP = 0x48, + EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, + EXT_FIND_GROUP = 0x4A, + EXT_ADD_GROUP = 0x4B, + EXT_COUNT_ALL_GROUPS = 0x4C, + NWK_ADDR_RSP = 0x80, + IEEE_ADDR_RSP = 0x81, + NODE_DESC_RSP = 0x82, + POWER_DESC_RSP = 0x83, + SIMPLE_DESC_RSP = 0x84, + ACTIVE_EP_RSP = 0x85, + MATCH_DESC_RSP = 0x86, + COMPLEX_DESC_RSP = 0x87, + USER_DESC_RSP = 0x88, + USER_DESC_CONF = 0x89, + SERVER_DISC_RSP = 0x8A, + END_DEVICE_BIND_RSP = 0xA0, + BIND_RSP = 0xA1, + UNBIND_RSP = 0xA2, + MGMT_NWK_DISC_RSP = 0xB0, + MGMT_LQI_RSP = 0xB1, + MGMT_RTG_RSP = 0xB2, + MGMT_BIND_RSP = 0xB3, + MGMT_LEAVE_RSP = 0xB4, + MGMT_DIRECT_JOIN_RSP = 0xB5, + MGMT_PERMIT_JOIN_RSP = 0xB6, + STATE_CHANGE_IND = 0xC0, + END_DEVICE_ANNCE_IND = 0xC1, + MATCH_DESC_RSP_SENT = 0xC2, + STATUS_ERROR_RSP = 0xC3, + SRC_RTG_IND = 0xC4, + LEAVE_IND = 0xC9, + TC_DEV_IND = 0xCA, + PERMIT_JOIN_IND = 0xCB, + MSG_CB_INCOMING = 0xFF +}; + +// Commands in the SAPI subsystem +enum class SapiCommand : uint8_t { + START_REQUEST = 0x00, + BIND_DEVICE = 0x01, + ALLOW_BIND = 0x02, + SEND_DATA_REQUEST = 0x03, + READ_CONFIGURATION = 0x04, + WRITE_CONFIGURATION = 0x05, + GET_DEVICE_INFO = 0x06, + FIND_DEVICE_REQUEST = 0x07, + PERMIT_JOINING_REQUEST = 0x08, + SYSTEM_RESET = 0x09, + START_CONFIRM = 0x80, + BIND_CONFIRM = 0x81, + ALLOW_BIND_CONFIRM = 0x82, + SEND_DATA_CONFIRM = 0x83, + FIND_DEVICE_CONFIRM = 0x85, + RECEIVE_DATA_INDICATION = 0x87, +}; + +// Commands in the UTIL subsystem +enum class UtilCommand : uint8_t { + GET_DEVICE_INFO = 0x00, + GET_NV_INFO = 0x01, + SET_PANID = 0x02, + SET_CHANNELS = 0x03, + SET_SECLEVEL = 0x04, + SET_PRECFGKEY = 0x05, + CALLBACK_SUB_CMD = 0x06, + KEY_EVENT = 0x07, + TIME_ALIVE = 0x09, + LED_CONTROL = 0x0A, + TEST_LOOPBACK = 0x10, + DATA_REQ = 0x11, + SRC_MATCH_ENABLE = 0x20, + SRC_MATCH_ADD_ENTRY = 0x21, + SRC_MATCH_DEL_ENTRY = 0x22, + SRC_MATCH_CHECK_SRC_ADDR = 0x23, + SRC_MATCH_ACK_ALL_PENDING = 0x24, + SRC_MATCH_CHECK_ALL_PENDING = 0x25, + ADDRMGR_EXT_ADDR_LOOKUP = 0x40, + ADDRMGR_NWK_ADDR_LOOKUP = 0x41, + APSME_LINK_KEY_DATA_GET = 0x44, + APSME_LINK_KEY_NV_ID_GET = 0x45, + ASSOC_COUNT = 0x48, + ASSOC_FIND_DEVICE = 0x49, + ASSOC_GET_WITH_ADDRESS = 0x4A, + APSME_REQUEST_KEY_CMD = 0x4B, + ZCL_KEY_EST_INIT_EST = 0x80, + ZCL_KEY_EST_SIGN = 0x81, + UTIL_SYNC_REQ = 0xE0, + ZCL_KEY_ESTABLISH_IND = 0xE1 +}; + +enum class Capability : uint16_t { + SYS = 0x0001, + MAC = 0x0002, + NWK = 0x0004, + AF = 0x0008, + ZDO = 0x0010, + SAPI = 0x0020, + UTIL = 0x0040, + DEBUG = 0x0080, + APP = 0x0100, + ZOAD = 0x1000 +}; + +enum class ConfigurationOption : uint8_t { + STARTUP_OPTION = 0x03, + POLL_RATE = 0x24, + QUEUED_POLL_RATE = 0x25, + RESPONSE_POLL_RATE = 0x26, + POLL_FAILURE_RETRIES = 0x29, + INDIRECT_MSG_TIMEOUT = 0x2B, + ROUTE_EXPIRY_TIME = 0x2C, + EXTENDED_PAN_ID = 0x2D, + BCAST_RETRIES = 0x2E, + PASSIVE_ACK_TIMEOUT = 0x2F, + BCAST_DELIVERY_TIME = 0x30, + APS_FRAME_RETRIES = 0x43, + APS_ACK_WAIT_DURATION = 0x44, + BINDING_TIME = 0x46, + PRECFGKEY = 0x62, + PRECFGKEYS_ENABLE = 0x63, + SECURITY_MODE = 0x64, + USERDESC = 0x81, + PANID = 0x83, + CHANLIST = 0x84, + LOGICAL_TYPE = 0x87, + ZDO_DIRECT_CB = 0x8F +}; + +const char kZigbeeCommands[] PROGMEM = D_CMND_ZIGBEEZNPSEND; + +void (* const ZigbeeCommand[])(void) PROGMEM = { &CmndZigbeeZNPSend }; + +#include + +TasmotaSerial *ZigbeeSerial = nullptr; + +unsigned long zigbee_polling_window = 0; +uint8_t *zigbee_buffer = nullptr; +uint32_t zigbee_in_byte_counter = 0; +uint32_t zigbee_frame_len = 256; +bool zigbee_active = true; +bool zigbee_raw = false; + +// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c +static void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { + unsigned char * pin = in; + static const char * hex = "0123456789ABCDEF"; + char * pout = out; + for(; pin < in+insz; pout +=3, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + pout[2] = ':'; + if (pout + 3 - out > outsz){ + /* Better to truncate output string than overflow buffer */ + /* it would be still better to either return a status */ + /* or ensure the target buffer is large enough and it never happen */ + break; + } + } + pout[-1] = 0; +} + + +void ZigbeeInput(void) +{ + // 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(); + + if ((0 == zigbee_in_byte_counter) && (ZIGBEE_SOF != zigbee_in_byte)) { + // waiting for SOF (Start Of Frame) byte, discard anything else + continue; // discard + } + + if (zigbee_in_byte_counter < zigbee_frame_len) { + zigbee_buffer[zigbee_in_byte_counter++] = zigbee_in_byte; + zigbee_polling_window = millis(); // Wait for more data + } else { + zigbee_polling_window = 0; // Publish now + break; + } + + // recalculate frame length + if (02 == zigbee_in_byte_counter) { + // We just received the Lenght byte + uint8_t len_byte = zigbee_buffer[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_in_byte_counter && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"")); + for (uint32_t i = 0; i < zigbee_in_byte_counter; i++) { + ResponseAppend_P(PSTR("%02X"), zigbee_buffer[i]); + } + ResponseAppend_P(PSTR("\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZNPRECEIVED)); + XdrvRulesProcess(); + zigbee_in_byte_counter = 0; + zigbee_frame_len = 254; + } +} + +/********************************************************************************************/ + +void ZigbeeInit(void) +{ + zigbee_active = false; + if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { + ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); + if (ZigbeeSerial->begin(115200)) { // ZNP is 115200, RTS/CTS (ignored), 8N1 + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + zigbee_buffer = (uint8_t*) serial_in_buffer; // Use idle serial buffer to save RAM + } else { + zigbee_buffer = (uint8_t*) malloc(ZIGBEE_BUFFER_SIZE); + } + zigbee_active = true; + ZigbeeSerial->flush(); + } + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndZigbeeZNPSend(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + while (size > 0) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + ZigbeeSerial->write(code); + size -= 2; + codes += 2; + } + } + ResponseCmndDone(); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv23(uint8_t function) +{ + bool result = false; + + if (zigbee_active) { + switch (function) { + case FUNC_LOOP: + if (ZigbeeSerial) { ZigbeeInput(); } + break; + case FUNC_PRE_INIT: + ZigbeeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kZigbeeCommands, ZigbeeCommand); + break; + } + } + return result; +} + +#endif // USE_ZIGBEE