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