Zigbee2Tasmota serves as a gateway
Zigbee2Tasmota is a lightweight Zigbee solution running on an ESP82xx Wi-Fi chip. Hence it is easier to deploy wherever you want in your home. It is largely inspired by Zigbee2mqtt but it's a complete rewrite to make it fit on an ESP82xx with 80kB of RAM and only 1MB of flash memory.
A list of compatible coordinators and Zigbee devices is maintained in the Zigbee Device Compatibility Repository.
Zigbee Introduction
Before using Zigbee to Tasmota, you need to understand a few concepts. Here is a simplified comparison to the Wi-Fi equivalent (sort of).
Zigbee concept | Wi-Fi equivalent |
---|---|
Zigbee coordinator The coordinator is responsible for selecting the channel, PanID, security policy, and stack profile for a network. Zigbee2Tasmota will act as a coordinator. You can have multiple coordinators as long as they have different PanIDs. |
Wi-Fi Access Point |
PanID (Personal Area Network IDentifier) This parameter is unique in a Zigbee network (16-bit integer, 0x0000–0x3FFF). Default: 0x1A63 |
SSID (the Wi-Fi network name) |
ShortAddr Address of the device on the Zigbee network. This address is randomly assigned when the device first connects to the coordinator (16 bits integer, 0x0000–0xFFF7). The coordinator has address 0x0000. You need to track which device has which address or assing a "Friendly Name" to each new discovered device. |
IP address |
GroupAddr Group address of a collection of devices, it allows a single message to address multiple devices at once (16 bits integer, 0x0000–0xFFFF). For example a remote can turn on/off a group of lights. GroupAddr 0x0000 is not assigned. |
Multicast |
Endpoint The endpoint on the coordinator or on the Zigbee device the message is sent from/to. You can see endpoints as logical device providing distinct features (8 bits integer, 1–240). |
IP Port |
IEEEAddr Device hardware address (64 bits). This is unique per device and factory assigned. |
MAC address |
Channel 11-26 Default: 11 (See Zigbee-Wifi coexistence) |
Wi-Fi Channel |
Encryption Key 128-bit encryption key. default: 0x0D0C0A08060402000F0D0B0907050301 |
Wi-Fi password |
Pairing By default the coordinator does not accept new devices unless put in pairing mode. When in pairing mode, it will accept pairing requests from any device within range. Default: pairing disabled |
WPS |
Required Hardware
TI CC253x Zigbee Adapter
Any TI CC2530 or CC2531 chip based module can serve as a coordinator. See list of supported modules with their pinouts and flashing instructions.
These PCB make all the connections required to flash the CC2530 and to run Z2T.
SuperHouse.tv
Jon Oxer created a custom PCB to connect a Wemos D1 Mini and a CC2530 board (with or without CC2591).
H4NC
User h4nc created a custom PCB to connect a NodeMCU and a CC2530 board.
You can also get a complete Z2T module with case, pre-flashed and ready to configure and deploy.
Wi-Fi Adapter
Using an ESP82xx device such as a Wemos D1 Mini or a NodeMCU to flash the CC2530 is a lower cost alternative than using a single purpose CC_DEBUGGER.
In normal operation two free GPIOs are needed for the serial communication with the CC2530.
Configuration
Flash Zigbee Adapter
Zigbee2Tasmota requires a TI CC253x based module flashed with Z-Stack CC2530 firmware file from Koen Kanters.
Due to memory constraints of the CC253x, you can only pair 16 devices to a coordinator (See details).
Flashing options:
- Flashing with CCLoader and ESP8266 (recommended)
- Flashing with CCLib and ESP8266
- Flash with a dedicated CC Debugger and Windows PC
- Flash with RaspberryPi
Flash Tasmota
Once the Zigbee adapter flashing process completes, you can re-use the ESP82xx and flash Tasmota with Zigbee2Tasmota enabled firmware. Otherwise, you can use any ESP82xx device.
To use it you must compile your build. Add the following to user_config_override.h
:
#define USE_ZIGBEE
Optional
Run the ESP at 160MHz instead of 80MHz which ensures higher reliability in serial communication with CC2530.
In platformio_override.ini
uncomment line 51:
board_build.f_cpu = 160000000L
If you find that your Zigbee2Tasmota operation is unstable, you may have an ESP82xx device that cannot operate reliably at the higher frequency. If you are using hardware serial (see below) and you still have unreliability, try compiling for 80MHz (reverse the options above) and flash the ESP82xx device again to see if operating at a lower frequency improves stability. Running at 80MHz will impact software serial communications so hardware serial is highly recommended if running the ESP82xx at 80MHz.
Flash the newly compiled binary usig the normal flashing process.
Connect Zigbee Adapter to Tasmota
If you are using your ESP82xx device to flash the Zigbee adapter as described in tutorials you may want to leave these connections in place in case you ever need to update Zigbee adapter firmware. If not, any of the free GPIOs can be used.
The interface between the ESP82xx Wi-Fi device and the Zigbee adapter uses high speed serial.
Recommended wiring:
ESP Device | Tasmota | CC2530 |
---|---|---|
GPIO13 | Zigbee RX (166) | CC_TXD (A.K.A. P0_3) |
GPIO15 | Zigbee TX (165) | CC_RXD (A.K.A. P0_2) |
3V3 | VCC | |
GND | GND |
Tasmota Settings
In the Configuration -> Configure Module page assign:
- GPIO13 to
Zigbee RX (166)
- GPIO15 to
Zigbee TX (165)
You can quickly configure Tasmota using a custom template instead.
Use this one for the recommended wiring scheme:
{"NAME":"Zigbee","GPIO":[0,0,0,0,0,0,0,0,0,166,0,165,0],"FLAG":0,"BASE":18}
CC2531 USB Wiring
On a CC2531 USB adapter some of the connectors of the CC2531 are connected to extra solder pads. Among those are RX/TX as seen on the image (CC_RXD(P0.3), CC_TXD(P0.1)):
If you want to supply power to the USB stick via 3.3V and GND from your ESP82xx board, you can use the corresponding pins of the debug header. GND is located at pin 1 and 3.3V at pin 9 of the DebugHeader. In addition, on some sticks, it is necessary to solder a bridge between the two small pads to the left of the "DEBUG" label just below the left side of the button, right next to the "D" of "Debug". You can see that one of the solder pads is connected to pin 9 of the debug header.
First Run
When the Tasmota device boots, Zigbee2Tasmota will wait for 15 seconds before initializing the CC2530. This time allows for Wi-Fi and MQTT to connect (hopefully).
First boot:
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":50,"MajorRel":2,"MinorRel":6,"MaintRel":3,"Revision":20190608}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":2,"Message":"Reseting configuration"}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":51,"IEEEAddr":"0x00124B00199DF06F","ShortAddr":"0x0000","DeviceType":7,"DeviceState":9,"NumAssocDevices":0}}
MQT: tele/tasmota/Zigbee_home/RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
ZIG: Zigbee started
ZIG: No zigbee devices data in Flash
Zigbee will automatically boot the CC2530 device, configure the device and wait for Zigbee messages.
Normal boot looks like:
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":50,"MajorRel":2,"MinorRel":6,"MaintRel":3,"Revision":20190608}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":3,"Message":"Configured, starting coordinator"}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":51,"IEEEAddr":"0x00124B00199DF06F","ShortAddr":"0x0000","DeviceType":7,"DeviceState":9,"NumAssocDevices":0}}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":0,"Message":"Started"}}
ZIG: Zigbee started
ZIG: Zigbee devices data in Flash (516 bytes)
Usage
For a list of available command see Zigbee Commands.
Pairing Zigbee Devices
When you first create a Zigbee network, it contains no device except the coordinator. The first step is to add devices to the network, which is called pairing.
By default, and for security reasons, the Zigbee coordinator does not automatically accept new devices. To pair new devices, use ZbPermitJoin 1
. Once Zigbee2Tasmota is in pairing mode, put the Zigbee device into pairing mode. This is usually accomplished by pressing the button on the device for 5 seconds or more. To stop pairing, use ZbPermitJoin 0
.
ZbPermitJoin 1
CMD: ZbPermitJoin 1
MQT: stat/%topic%/RESULT = {"ZbPermitJoin":"Done"}
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":21,"Message":"Enable Pairing mode for 60 seconds"}}
60 seconds later:
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":20,"Message":"Disable Pairing mode"}}
Although this is highly discouraged, you can permanently enable Zigbee pairing, until the next reboot, with ZbPermitJoin 99
.
ZbPermitJoin 99
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":22,"Message":"Enable Pairing mode until next boot"}}
ZbPermitJoin 0
MQT: tele/%topic%/RESULT = {"ZbState":{"Status":20,"Message":"Disable Pairing mode"}}
MQT: tele/%topic%/SENSOR = {"ZbState":{"Status":30,"IEEEAddr":"0x00158D00036B50AE","ShortAddr":"0x8F20","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
Example for Aqara Temperature & Humidity Sensor
To pair this sensor, press and hold the button for 5 seconds. The led will flash several times and you will see logs entries, especially this one:
- Put Zigbee2Tasmota into pairing mode using the
ZigbeePermitJoin
command as described above - Press the Xiaomi Aqara sensor's button for 5 seconds to pair the devices. You will see a message as follows:
MQT: tele/%topic%/SENSOR = {"ZbState":{"Status":30,"IEEEAddr":"0x00158D00036B50AE","ShortAddr":"0x8F20","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
Message with "Status":30
shows some characteristics of the device:
Field name | Value |
---|---|
Status |
30 indicates a device connect or reconnect. This is the opportunity to match IEEEAddress and short address |
IEEEAddr |
Long unique address (64 bits) of the device - factory set |
ShortAddr |
Short address (16 bits) randomly assigned to the device on this Zigbee network |
PowerSource |
true = the device is connected to a power sourcefalse = the device runs on battery |
ReceiveWhenIdle |
true = the device can receive commands when idlefalse = the device is not listening. Commands should be sent when the device reconnects and is idle |
Security |
Security capability (meaning unknown, to be determined) |
Zigbee Friendly Names
Supported since version 8.2!
Instead of a short address like "0x4773"
you can assign a memorable friendly name like "Room_Plug"
.
See ZbName
command to set names.
MQT: tele/%topic%/RESULT = {"ZbReceived":{"0x128F":{"AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":162,"AqaraAccelerometer":[-690,2,138],"AqaraAngles":[-78,0,11],"LinkQuality":158}}}
Setting its friendly name to Vibration_sensor
:
ZigbeeName 0x128F,Vibration_sensor
CMD: ZbName 0x128F,Vibration_sensor
MQT: stat/%topic%/RESULT = {"0x128F":{"Name":"Vibration_sensor"}}
(10 seconds later)
ZIG: Zigbee Devices Data store in Flash (0x402FF800 - 270 bytes)
Now the sensor readings includes the friendly name:
MQT: tele/%topic%/RESULT = {"ZbReceived":{"0x128F":{"Name":"Vibration_sensor","AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":171,"AqaraAccelerometer":[-691,12,130],"AqaraAngles":[-78,1,11],"LinkQuality":153}}}
If you set SetOption83 1
sensor readings will use the friendly name as KSON key, short address is added as Device
:
MQT: tele/%topic%/RESULT = {"ZbReceived":{"Vibration_sensor":{"Device":"0x128F","AqaraVibrationMode":"tilt","AqaraVibrationsOrAngle":171,"AqaraAccelerometer":[-691,8,136],"AqaraAngles":[-78,1,11],"LinkQuality":153}}}
Reading Sensors
Most sensors will spontaneously publish their readings, regularly or once a signifcant change happened: temperature, pressure, humidity, presence, illuminance...
Sensor messages are published via MQTT when they are received from the Zigbee device. Similar to Zigbee2MQTT, Z2T tries to group and debounce sensor values when they are received within a 300ms window.
This sensor monitors humidity, temperature, and air pressure. Its Zigbee model ID is lumi.weather
.
This device publishes sensor values roughly every hour or when a change occurs. You can also force an update pressing the device's button. It sends two kinds of messages, either 3x standard Zigbee messages, or a single proprietary message containing all sensor values.
0x8F20
is the ShortAddress of the sensor, and its name is Kitchen
if you used ZbName 0x8F20,Kithchen
.
MQT: tele/%topic%/SENSOR ={"ZbReceived": {"0x8F20": {"Name": "Kitchen", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
or prefixed by name if you set SetOption83 1
MQT: tele/%topic%/SENSOR ={"ZbReceived": {"Kitchen": {"Device": "0x8F20", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
Topic is device specific, to allow more effective retained messages, if you set SetOption89 1
MQT: tele/%topic%/8F20/SENSOR ={"ZbReceived": {"Kitchen": {"Device": "0x8F20", "Voltage": 2.995, "Battery": 98, "Temperature": 21.01, "Humidity": 53.68, "Pressure": 1004.04, "PressureUnit": "hPa", "Endpoint": 1, "LinkQuality": 88}}
Supported values:
Field name | Value |
---|---|
LinkQuality |
Stength of the Zigbee signal, between 1 and 254 (integer). See this ZigBee and WiFi Coexistence |
Humidity |
Humidity in percentage (float) |
Pressure and PressureUnit |
Atmospheric pressure (float) and unit (string) Currently only hPa (A.K.A. mbar) is supported |
Temperature |
Temperature in Celsius (float) |
Voltage |
Battery voltage (float) |
Battery |
Battery charge in percentage (integer) |
ModelId |
Model name of the Zigbee device (string) Ex: lumi.weather |
ScaledValue and Scale |
Give the raw measure and the scale correction as 10^scale |
And many more... |
If a value is not decoded, it will appear as "<cluster>_<attr>":<value>
where <cluster>
is the Zigbee ZCL Cluster of the attribute (family), <attr>
is the attribute number and <value>
its published value.
Device Information
You can dump the internal information gathered about connected Zigbee devices with the command ZigbeeStatus
.
You can use ZbStatus2
to display all information and endpoints. If probing was successful (at pairing time or using ZbProbe
), Tasmota will automatically find the right endpoint.
Depending on the number of device you have, ZbStatus2
output can exceed tha maximum MQTT message size. You can request the status of each individual device using ZbStatus2 1
, ZbStatus2 2
, ZbStatus2 3
...
ZbStatus1
- List all connected devices
{"ZbStatus1":[{"Device":"0x6B58"},{"Device":"0xE9C3"},{"Device":"0x3D82"}]}
ZbStatus2
- Display detailed information for each device, including long address, model and manufacturer:
{"ZbStatus2":[{"Device":"0x4773","IEEEAddr":"0x7CB03EAA0A0292DD","ModelId":"Plug 01","Manufacturer":"OSRAM","Endpoints":["0x03"]},{"Device":"0x135D","Name":"Temp_sensor","IEEEAddr":"0x00158D00036B50AE","ModelId":"lumi.weather","Manufacturer":"LUMI","Endpoints":["0x01"]}]}
(formatted for readability)
{
"ZbStatus2": [{
"Device": "0x4773",
"IEEEAddr": "0x7CB03EAA0A0292DD",
"ModelId": "Plug 01",
"Manufacturer": "OSRAM",
"Endpoints": ["0x03"]
}, {
"Device": "0x135D",
"Name": "Temp_sensor",
"IEEEAddr": "0x00158D00036B50AE",
"ModelId": "lumi.weather",
"Manufacturer": "LUMI",
"Endpoints": ["0x01"]
}]
}
Understanding Endpoints
The endpoint on the coordinator or on the Zigbee device the message is sent from/to.
Z2T will automatically take the first endpoint in the list; this works most of the time. You normally don't need to specify the endpoint number. In rare case, you can still force a specific endpoint.
Example Endpoints:
Device | Endpoint |
---|---|
OSRAM Plug | 0x03 |
Philips Hue Bulb | 0x0B |
Sending Commands
You can send commands to device or groups of device, for example to turn on/off a light, set its brightness of open/close shutters.
Here is a list of supported commands, see below how to send any command if it's not in the list.
Command | Parameters | Cluster |
---|---|---|
Power | 1|true|"true"|"on" : On0|false|"false"|"off" : Off2|"Toggle" : Toggle |
0x0006 |
Dimmer | 0..254 : Dimmer value255 is normally considered as invalid, and may be converted to 254 |
0x0008 |
DimmerUp | null : no parameter. Increases dimmer by 10% |
0x0008 |
DimmerDown | null : no parameter. Decreases dimmer by 10% |
0x0008 |
DimmerStop | null : no parameter. Stops any running increase of decrease of dimmer. |
0x0008 |
ResetAlarm | <alarmcode>,<clusterid> : (to be documented later) |
0x0009 |
ResetAllAlarms | null : no parameter, (to be documented later) |
0x0009 |
Hue | 0..254 : change Hue value |
0x0300 |
Sat | 0..254 : change Sat value |
0x0300 |
HueSat | 0..254,0..254 : change both Hue and Sat values |
0x0300 |
Color | 0..65534,0..65534 : change the color using [x,y] coordinates |
0x0300 |
CT | 0..65534 : change the white color-temperature in Mireds |
0x0300 |
Shutter | 0..254 : send any Shutter command (prefer the commands below) |
0x0102 |
ShutterOpen | null : no parameter, open shutter |
0x0102 |
ShutterClose | null : no parameter, close shutter |
0x0102 |
ShutterStop | null : no parameter, stop shutter movement |
0x0102 |
ShutterLift | 0..100 : move shutter to a specific position in percent0 %=open, 100 %=closed |
0x0102 |
ShutterTilt | 0..100 : move the shutter to the specific tilt position in percent |
0x0102 |
The format of the command is following:
ZbSend {"Device":"<device>","Send":{"<sendcmd>":<sendparam>}}
where<device>
identifies the target and can be a shortaddr 0x1234
, a longaddr 0x1234567812345678
or a name Kitchen
.
"<sendcmd>":<sendparam>
the command and its parameters
If the device has been correctly paired and its endpoints recorded by Z2T, you shouldn't need to specify a target endpoint. You can use an option "endpoitn":<endpoint>
parameter if Z2T can't find the correct endpoint or if you want to change from the default endpoint. See below Understanding endpoints.
Low-level Commands
There is a special syntax if you want to send arbitrary commands: "Send":"<send_bytes>"
where <send_bytes>
has the following syntax:
"<cluster>_<cmd>/<bytes>"
: send a non-cluster specific command for cluster id <cluster>
, command id <cmd>
and payload <bytes>
.
Example: ZbSend {"Device":"0x1234","Send":"0000_00/0500"}
I.e. send a Read command (0x00) to the general cluster (0x0000) for attribute ManufId (0x0005). Note: all values are little-endian.
Or use '!' instead of '_' to specify cluster-specific commands:
"<cluster>!<cmd>/<bytes>"
: send a cluster specific command for cluster id <cluster>
, command id <cmd>
and payload <bytes>
.
Example: ZbSend {"Device":"0x1234","Send":"0008_04/800A00"}
I.e. send a Dimmer command (0x04) from Level Control cluster (0x0008) with payload being: Dimmer value 0x80, and transition time of 1 second (0x000A = 10 tenths of seconds).
Of course the latter example could be simply: ZbSend {"Device":"0x1234","Send":{"Dimmer":"0x80"}
Examples
OSRAM Plug
ZbSend { "device":"0x4773", "send":{"Power":"On"} }
ZbSend { "device":"0x4773", "send":{"Power":1} }
ZbSend { "device":"0x4773", "send":{"Power":false} }
ZbSend { "device":"0x4773", "send":{"Power":"Toggle"} }
Read the On/Off status: (all three commands below are synonyms)
ZbRead { "device":"0x4773", "endpoint":"0x03", "cluster":"0x0006", "read":"0x0000" }
ZbRead { "device":"0x4773", "endpoint":"0x03", "cluster":"0x0006", "read":["0x0000"] }
ZbRead { "device":"0x4773", "endpoint":3, "cluster":6, "read":0 }
MQT: tele/tasmota/SENSOR = {"ZbReceived":{"0x4773":{"Power":true,"LinkQuality":52}}}
Philips Hue bulb
ZbSend { "device":"0x3D82", "send":{"Power":"Off"} }
ZbSend { "device":"0x3D82", "send":{"Dimmer":128} }
ZbSend { "device":"0x3D82", "send":{"Dimmer":254} }
ZbSend { "device":"0x3D82", "endpoint":"0x0B", "send":{"Dimmer":0} }
Receiving Commands
If you pair device like switches and remotes, you will also see received commands from thos devices.
When a command is received, attributes are published both in their low-level and high-level formats (if known).
Low level format is the following: "<cluster>!<cmd>":"<payload"
Example for IKEA Remote: {"ZbReceived":{"IKEA_remote":{"Device":"0xF72F","0006!02":"","Power":2,"Endpoint":1,"LinkQuality":31}}}
The command received "0006!02":""
is Power Toggle (0x02) from On/Off cluster (0x0006) with no payload. It is also translated as "Power":2
. "Endpoint":1
tells you from which endpoint the command was send. Some Xiaomi multi-switches use different endpoints for each switch.
Light State Tracking
Once Z2T receives a command related to a light (Power, Dimmer, Color, ColorTemp), it sends right after a Read command to get the actual state of the light. This is used for Hue Emulation and Alexa support. The final attributes are read betwenn 200ms and 1000ms later, to allow for the light to achieve its target state.
Example:
16:02:04 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_remote":{"Device":"0xF72F","0006!02":"","Power":2,"Endpoint":1,"Group":100,"LinkQuality":75}}}
16:02:05 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","Power":true,"Endpoint":1,"LinkQuality":80}}}
16:02:06 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_remote":{"Device":"0xF72F","0008!06":"002B0500","DimmerUp":true,"Endpoint":1,"Group":100,"LinkQuality":75}}}
16:02:08 MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","Dimmer":102,"Endpoint":1,"LinkQuality":80}}}
Ikea Remote (E1524/E1810)
"ModelId":"TRADFRI remote control","Manufacturer":"IKEA of Sweden"
- Short press center button -
"0006!02":""
and"Power":2
- Short press dimmer up -
"0008!06":"002B0500"
and"DimmerUp":true
- Short press dimmer down -
"0008!02":"012B05000000"
and"DimmerStep":1
- Short press arrow right -
"0005!07":"00010D00"
and"ArrowClick":0
- Short press arrow left -
"0xF72F","0005!07":"01010D00"
and"ArrowClick":1
- Long press dimmer up -
"0008!05":"0054"
and"DimmerMove":0
- Long press dimmer up release -
"0008!07":""
and"DimmerStop":true
- Long press dimmer down -
"0008!01":"01540000"
and"DimmerMove":1
- Long press dimmer down release -
,"0008!03":"0000"
and"DimmerStop":true
Zigbee Groups
Zigbee has a unique feature call Groups. It allows you to send a single command to a group of devices. For example a remote can control a group of multiple lights.
Zigbee groups are 16 bits arbitrary numbers that you can freely assign. When you send to a group, you don't specify a targer address anymore nor an endpoint.
Groups works in two steps: first you add devices to groups, second you send commands to groups. See the below Zigbee Binding to configure a remote to send commands to a specific group.
Configuring groups for devices requires to send commands. Make sure the device is powered and awake (i.e. wake-up battery powered devices).
List all groups for a device.
ZbSend {"device":"IKEA_Light","Send":{"GetAllGroups":true}}
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<02":"FF00","GetGroupCapacity":255,"GetGroupCount":0,"GetGroup":[],"Endpoint":1,"LinkQuality":80}}}
The following response tells you: "GetGroupCount":1
the light belongs to one group "GetGroup":[100]
and the group number is 100
.
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<02":"FF016400","GetGroupCapacity":255,"GetGroupCount":1,"GetGroup":[100],"Endpoint":1,"LinkQuality":80}}}
Add a group to a device
ZbSend {"device":"IKEA_Light","Send":{"AddGroup":100}}
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<00":"006400","AddGroup":100,"AddGroupStatus":0,"AddGroupStatusMsg":"SUCCESS","Endpoint":1,"LinkQuality":80}}}
Or if the group already exists:
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<00":"8A6400","AddGroup":100,"AddGroupStatus":138,"AddGroupStatusMsg":"DUPLICATE_EXISTS","Endpoint":1,"LinkQuality":80}}}
Remove a group
ZbSend {"device":"IKEA_Light","Send":{"RemoveGroup":100}}
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<03":"006400","RemoveGroup":100,"RemoveGroupStatus":0,"RemoveGroupStatusMsg":"SUCCESS","Endpoint":1,"LinkQuality":80}}}
or if the group does not exist
MQT: tele/%topic%/SENSOR = {"ZbReceived":{"IKEA_Light":{"Device":"0x5ADF","0004<03":"8B6400","RemoveGroup":100,"RemoveGroupStatus":139,"RemoveGroupStatusMsg":"NOT_FOUND","Endpoint":1,"LinkQuality":80}}}
Remove all groups
ZbSend {"device":"IKEA_Light","Send":{"RemoveAllGroups":true}}
MQT: tele/%topic%/SENSOR = {"ZbResponse":{"Device":"0x5ADF","Name":"IKEA_Light","Command":"0004!04","Status":0,"StatusMessage":"SUCCESS","Endpoint":1,"LinkQuality":80}}
Sending commands to a group
Just use the attribute "Group":<group_id>
instead of "Device":<device>
when sending a command.
Example:
- power on all light in group 100:
ZbSend {"group":100,"Send":{"Power":1}}
- set all dimmers in group 100 to 50%:
ZbSend {"group":100,"Send":{"Dimmer":127}}
Zigbee Binding
Binding allows a device to send command to another device in the same Zigbee network, without any additional logic. For example, you can set a remote to control directly a group of lights, without any rules on the coordinator. The coordinator will still receive all commands.
Example of direct binding ZbBind {"Device":"0xC2EF","ToDevice":"0x5ADF","Endpoint":1,"ToEndpoint":1,"Cluster":6}
This command links the device 0xC2EF
that will send all commands for cluster 6
(On/off cluster) frome endpoint 1
to the target device 0x5ADF
on endpoint 1
.
Example of group binding ZbBind {"Device":"0xC2EF","ToGroup":100,"Endpoint":1,"Cluster":6}
This command links the device 0xC2EF
that will send all commands for cluster 6
(On/off clustre) and from endpoint 1
to the group 100
.
Reponse in case of success:
MQT: tele/%topic%/RESULT = {"ZbBind":{"Device":"0xF72F","Name":"IKEA_Remote","Status":0,"StatusMessage":"SUCCESS"}}
Example: IKEA remote and IKEA Light
IKEA remotes only support 1 group, and can be linked to a light only via group numbers (no direct binding).
-
Add the light to group 100
ZbSend {"device":"IKEA_Light","Send":{"AddGroup":100}}
-
Bind the remote to group 100. Note: you need to press a button on the remote right before sending this command to make sure it's not in sleep mode
ZbBind {"Device":"IKEA_Remote","ToGroup":100,"Endpoint":1,"Cluster":6}
Zigbee and Hue Emulation for Alexa
Z2T now supports Hue Emulation for Zigbee lights. It will mimic most of Zigbee gateways, and allows you to control Zigbee lights directly with Alexa, without any MQTT broker nor Alexa skill.
Command ZbLight
configures a Zigbee device to be Alexa controllable. Specify the number of channels the light supports:
0
Simple On/Off light1
White Light with Dimmer2
White Light with Dimmer and Cold/Warm White3
RGB Light4
RGBW Light5
RGBCW Light, RGB and Cold/Warm White
To set the light, use ZbLight <device>,<nb_of_channels
. Ex:
ZbLight 0x1234,2
ZbLight Kitchen_Light,1 (see ZbName)
Once a light is declared, Z2T will monitor any change made to the light via Z2T or via remotes, either from a direct message or via a group message. Z2T will then send a read command to the light, between 200ms and 1000ms later, and memorize the last value.
To read the last known status of a light, use ZbLight <device>
Ex:
ZbLight Kitchen_Light
MQT: stat/%topic%/RESULT = {"ZbLight":{"Kitchen_Light":{"Device":"0x5ADF","Light":2,"Power":0,"Dimmer":130,"Colormode":2,"CT":350}}}
Specific Device Tutorials
OSRAM mini switch and plug
Here is a short tutorial to configuring an ORSRAM switch bound to an OSRAM plug.
1. Pair the OSRAM Plug
Initiate pairing mode:
ZbPermitJoin 1
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbPermitJoin":"Done"}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":21,"Message":"Enable Pairing mode for 60 seconds"}}
Hold the on/off button until your hear a click (+- 10 seconds).
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbState":{"Status":34,"IEEEAddr":"0x7CB03EAA0A0292DD","ShortAddr":"0xF75D","ParentNetwork":"0x0000"}}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x7CB03EAA0A0292DD","ShortAddr":"0xF75D","PowerSource":true,"ReceiveWhenIdle":true,"Security":false}}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x03"]}}
xx:xx:xx MQT: stat/<topic>/SENSOR = {"ZbReceived":{"OSRAM_Plug":{"Device":"0xF75D","Manufacturer":"OSRAM","ModelId":"Plug 01","Endpoint":3,"LinkQuality":44}}}
(Optional) Give the device a name
Giving the device a name makes it easier to configure than using the short address. When naming a device, it is preferable to use the long address IEEEAddr
which remains unchanged if you need to pair again your device.
ZbName 0x7CB03EAA0A0292DD,OSRAM_Plug
xx:xx:xx MQT: stat/<topic>/RESULT = {"0xF75D":{"Name":"OSRAM_Plug"}}
xx:xx:xx ZIG: Zigbee Devices Data store in Flash (0x402FF800 - xxx bytes)
2. Pair the OSRAM Switch mini
Hold the Middle and Arrow Down Buttons for 10 Seconds to Reset the Device.
Initiate pairing mode:
ZbPermitJoin 1
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbPermitJoin":"Done"}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":21,"Message":"Enable Pairing mode for 60 seconds"}}
Hold the Middle and Arrow Up Buttons for 3 Seconds to connect.
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":34,"IEEEAddr":"0x000D6F00109C732A","ShortAddr":"0x080C","ParentNetwork":"0x0000"}}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x000D6F00109C732A","ShortAddr":"0x080C","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x01","0x02","0x03"]}}
xx:xx:xx MQT: tele/<topic>/SENSOR = {"ZbReceived":{"OSRAM_Remote":{"Device":"0x080C","Manufacturer":"OSRAM","ModelId":"Lightify Switch Mini","Endpoint":1,"LinkQuality":46}}}
xx:xx:xx ZIG: Zigbee Devices Data store in Flash (0x402FF800 - xxx bytes)
(Optional) Give the device a name
ZbName 0x000D6F00109C732A,OSRAM_Remote
xx:xx:xx MQT: stat/<topic>/RESULT = {"0x080C":{"Name":"OSRAM_Remote"}}
xx:xx:xx MQT: stat/<topic>/RESULT = {"0xF75D":{"Name":"OSRAM_Plug"}}
xx:xx:xx ZIG: Zigbee Devices Data store in Flash (0x402FF800 - xxx bytes)
3. Configure OSRAM switch bindings
By default the OSRAM switch broadcasts the on/off commands to the entire network. Pressing the Up arrow turns on all devices, and the Down arrows turns off all devices.
This is often not desirable. We will now bind the remote to a group address so you can selectively turn on and off a group of devices.
We will choose group address 101
below, but you can take any number between 1 and 65535. We need to bind cluster 6 (On/Off) for both endpoints 1 and 2.
Press any button on the switch to get it out of sleep mode and immediatly after send the following command:
ZbBind {"Device":"OSRAM_Remote","ToGroup":101,"Endpoint":2,"Cluster":6}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbBind":"Done"}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbBind":{"Device":"0x080C","Name":"OSRAM_Remote","Status":0,"StatusMessage":"SUCCESS"}}
Make sure you see "StatusMessage":"SUCCESS"
Press any button on the switch to get it out of sleep mode and immediatly after send the following command:
ZbBind {"Device":"OSRAM_Remote","ToGroup":101,"Endpoint":1,"Cluster":6}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbBind":"Done"}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbBind":{"Device":"0x080C","Name":"OSRAM_Remote","Status":0,"StatusMessage":"SUCCESS"}}
Make sure you see "StatusMessage":"SUCCESS"
Now the switch will only send commands to the group address 101
and no more to all devices.
You can check the current bindings with the following command (JSON indentation for to ease readability):
ZbBindState OSRAM_Remote
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbBindState":"Done"}
xx:xx:xx MQT: stat/<topic>/RESULT =
{"ZbBindState":
{"Device":"0x080C","Name":"OSRAM_Remote","Status":0,"StatusMessage":"SUCCESS",
"BindingsTotal":2,"Bindings":[
{"Cluster":"0x0006","Endpoint":2,"ToGroup":101},
{"Cluster":"0x0006","Endpoint":1,"ToGroup":101}]}}
You can now see what happens when you press the Up and Down arrow:
xx:xx:xx MQT: stat/<topic>/SENSOR = {"ZbReceived":{"OSRAM_Remote":{"Device":"0x080C","0006!01":"","Power":1,"Endpoint":1,"Group":101,"LinkQuality":13}}}
xx:xx:xx MQT: stat/<topic>/SENSOR = {"ZbReceived":{"OSRAM_Remote":{"Device":"0x080C","0006!00":"","Power":0,"Endpoint":2,"Group":101,"LinkQuality":5}}}
4. Add the Plug to the group address
Now we need to configure the plug to listen to group 101
.
ZbSend {"Device":"OSRAM_Plug","Send":{"AddGroup":101}}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbSend":"Done"}
xx:xx:xx MQT: stat/<topic>/SENSOR =
{"ZbReceived":
{"OSRAM_Plug":
{"Device":"0xF75D","0004<00":"006500","AddGroup":101,
"AddGroupStatus":0,"AddGroupStatusMsg":"SUCCESS",
"Endpoint":3,"LinkQuality":46}}}
Make sure you see "AddGroupStatusMsg":"SUCCESS"
You can also check existing bindings with the following command:
ZbSend {"Device":"OSRAM_Plug","Send":{"GetAllGroups":true}}
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbSend":"Done"}
xx:xx:xx MQT: stat/<topic>/SENSOR =
{"ZbReceived":
{"OSRAM_Plug":
{"Device":"0xF75D","0004<02":"07016500","GetGroupCapacity":7,"GetGroupCount":1,
"GetGroup":[101],"Endpoint":3,"LinkQuality":46}}}
5. (Optional) Control the Plug with Alexa
You can also control your switch with Alexa through Philip Hue Emulation. It will be displayed as an On/Off light (0 channel).
ZbLight OSRAM_Plug,0
Recap of commands
ZbPermitJoin 1
On the Plug: hold the on/off button until your hear a click (+- 10 seconds).
On the Switch: hold the Middle and Arrow Up Buttons for 3 Seconds to connect.
ZbName 0x7CB03EAA0A0292DD,OSRAM_Plug (adapt to your actual IEEE Addresses)
ZbName 0x000D6F00109C732A,OSRAM_Remote (adapt to your actual IEEE Addresses)
ZbSend {"Device":"OSRAM_Plug","Send":{"AddGroup":101}}
ZbLight OSRAM_Plug,0
Press any button on the switch to get it out of sleep mode.
ZbBind {"Device":"OSRAM_Remote","ToGroup":101,"Endpoint":2,"Cluster":6}
ZbBind {"Device":"OSRAM_Remote","ToGroup":101,"Endpoint":1,"Cluster":6}
Philips Hue Motion Sensor
Z2T supports Philips Hue Motion Sensor SML001
and requires some additional configuration. By default the sensor will not report any value unless:
- you configure a Zigbee Binding to your coordinator
- you set an explicit Configuration Reporting policy on the sensor
1. Pair the device
Initiate pairing mode:
ZbPermitJoin 1
xx:xx:xx MQT: stat/<topic>/RESULT = {"ZbPermitJoin":"Done"}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":21,"Message":"Enable Pairing mode for 60 seconds"}}
Press and hold the setup button on the rear of the device for +- 10 seconds (until the green light goes solid) to initiate pairing. Please note that the pairing indicator light is below the main sensor (as oppose to the obvious indicator above the main sensor).
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":34,"IEEEAddr":"0x0017880103295B70","ShortAddr":"0xB6CD","ParentNetwork":"0x0000"}}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x0017880103295B70","ShortAddr":"0xB6CD","PowerSource":false,"ReceiveWhenIdle":false,"Security":false}}
xx:xx:xx MQT: tele/<topic>/RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x02","0x01"]}}
xx:xx:xx MQT: tele/<topic>/SENSOR = {"ZbReceived":{"HueMotion":{"Device":"0xB6CD","Manufacturer":"Philips","ModelId":"SML001","Endpoint":2,"LinkQuality":15}}}
xx:xx:xx ZIG: Zigbee Devices Data store in Flash (0x402FF800 - xxx bytes)
(Optional) Give the device a name
Giving the device a name makes it easier to configure than using the short address. When naming a device, it is preferable to use the long address IEEEAddr
which remains unchanged if you need to pair again your device.
ZbName 0x0017880103295B70,HueMotion
xx:xx:xx MQT: stat/<topic>/RESULT = {"0xB6CD":{"Name":"HueMotion"}}
xx:xx:xx ZIG: Zigbee Devices Data store in Flash (0x402FF800 - xxx bytes)
2. Set the Zigbee bindings
This will tell the device to send attributes readings to the coordinator. Coordinator's address is always 0x0000
.
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0001"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0400"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0402"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0406"}
3. Set Configure Reporting
This configuration tells the motion sensor to report attributes on a regular basis (min once per hour) and whenever a change occurs.
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0001_06/00210020100E30F200"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0400_06/000000210000100E0000"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0402_06/000000290000100E1900"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0406_06/000000180000100E"}
Recap of commands
ZbPermitJoin 1
Press and hold the setup button on the rear of the device for +- 10 seconds (until the green light goes solid).
ZbName 0x0017880103295B70,HueMotion (adapt to your actual IEEE Addresses)
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0001"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0400"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0402"}
ZbBind {"Device":"HueMotion","Endpoint":2,"ToDevice":"0x0000","ToEndpoint":1,"Cluster":"0x0406"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0001_06/00210020100E30F200"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0400_06/000000210000100E0000"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0402_06/000000290000100E1900"}
ZbSend {"Device":"HueMotion","Endpoint":2,"Send":"0406_06/000000180000100E"}
Ikea ON/OFF Switch (E1743)
"ModelId":"TRADFRI on/off switch","Manufacturer":"IKEA of Sweden"
- Short press
O
-0006!00
- Short press
I
-0006!01
- Long press
O
-0008!01
- Long press
I
-0008!05
- Long press release
O
orI
-0008!07
In this example Tradfri switch reports on 0x7596
and is used to control another Tasmota light device:
Rule
on ZbReceived#0x7596#0006!00= do publish cmnd/%topic%/POWER OFF endon
on ZbReceived#0x7596#0006!01= do publish cmnd/%topic%/POWER OFF endon
on ZbReceived#0x7596#0008!01= do publish cmnd/%topic%/Dimmer - endon
on ZbReceived#0x7596#0008!05= do publish cmnd/%topic%/Dimmer + endon
Aqara Water Leak Sensor (SJCGQ11LM)
"ModelId":"lumi.sensor_wleak.aq1","Manufacturer":"LUMI"
In this example sensor reports on 0x099F
and sends an mqtt message to topic stat/leak_sensor/LEAK
:
Rule
on ZbReceived#0x099F#0500!00=010000FF0000 do publish stat/leak_sensor/LEAK ON endon
on ZbReceived#0x099F#0500!00=000000FF0000 do publish stat/leak_sensor/LEAK OFF endon
Unrecognized commands or attributes
If your device pairs successfully with Zigbee2Tasmota but doesn't report on standardized endpoint you will see messages like these:{"ZbReceived":{"0x099F":{"0500!00":"010000FF0000","LinkQuality":70}}}
{"ZbReceived":{"0x7596":{"0006!01":"","LinkQuality":65}}}
In this case you will have to use rules or an external home automation solution to parse the ZbReceived messages. The following section will focus only on rules to utilize the device inside Tasmota ecosystem.
Zigbee2Tasmota Status Codes
You can inspect the log output to determine whether Zigbee2Tasmota started correctly. Zigbee2Tasmota sends several status messages to inform the MQTT host about initialization.
{"ZbState":{"Status":1,"Message":"CC2530 booted","RestartReason":"Watchdog","MajorRel":2,"MinorRel":6}}
Status
contains a numeric code about the status message0
: initialization complete, Zigbee2Tasmota is running normally1
: booting2
: resetting CC2530 configuration3
: starting Zigbee coordinator20
: disabling Permit Join21
: allowing Permit Join for 60 seconds22
: allowing Permit Join until next boot30
: Zigbee device connects or reconnects31
: Received Node Descriptor information for a Zigbee device32
: Received the list of active endpoints for a Zigbee device33
: Received the simple Descriptor with active ZCL clusters for a Zigbee device50
: reporting CC2530 firmware version51
: reporting CC2530 device information and associated devices98
: error, unsupported CC2530 firmware99
: general error, Zigbee2Tasmota was unable to start
Message
(optional) a human-readable message- other fields depending on the message (e.g., Status=
50
or Status=51
)
Zigbee internals
This page is for developers who want to understand how Zigbee2Tasmota (Z2T) works and its implementation details.
CC2530 Serial protocol
The CC2530 is flashed with Texas Instrument ZNP Software version 1.2. The protocol is build on a serial communication between the main cpu and the CC2530.
Z-Stack 1.2 Monitor and Test API{target=_blank}
Serial communication is configured as 8N1, 115200 bauds. We suggest to use GPIO13/15 because they have hardware serial support. Please note that there is only one usable hardware serial, either on GPIO1/3 or GPIO13/15.
To enable hardware serial on GPIO13/15 for Tasmota, set Serial 0
and restart. Otherwise Z2T will use Software serial provided by TasmotaSerial.
Receiving 115200 bauds in software is a timing challenge. Here is the anatomy of a byte transmitted in serial mode. Each bit is 8.7µs, or ~700 CPU cycles at 80MHz (1400 cycles at 160MHz).
It all starts with a LOW "start bit" that generates an interrupt transferred to TasmotaSerial. Then TasmotaSerial enters a tightly controlled loop to read each bit (least sifnificant first). The frame stops with a HIGH stop bit.
What can go wrong? Tasmota may be already handling an interrupt when the start bit arrives, potentially causing a shift by 1 bit and a wrong message.
Here is a 0xFE byte correctly received:
Same frame with a delay in the interrupt handler, and mistakenly read 0xFF:
TasmotaSerial has been improved to allow receiving a train of bytes withtout any disruption.
CC2530 generally sends all the bytes one after the other for a single ZNP message (up to 250 bytes). Instead of giving back control after the first byte, the TasmotaSerial interrupt handler continues to monitor the serial RX line for the next 4 bits and checks whether a new start bit arrived. It avoids any error after the first byte was received.
Still the first byte in the message could have been wrong. Fortunately, the first byte sent by ZNP is always 0xFE (see below). This means that if the interrupt arrives too late, Tasmota will read 0xFF instead of 0xFE. Z2T software does automatic error correction in this case, i.e. if the first byte received is 0xFF, it is automatically assumed to be 0xFE and the rest of the message is read normally.
With these two schemes, software serial for Zigbee proved to be extremely reliable, even at 80MHz. It is highly recommended though to run at 160MHz.
State machine - CC2530 initialization and configuration
After Tasmota boots, it sends the sequence 410001
to force a CC2530 hardware reset. Z2T implements an asynchronous state machine to handle the initialization and configuration of the CC2530. The state machine sends commands and waits for responses (with time-outs) and takes different branches depending on the responses.
Instruction set
The program is encoded as a list of 32 bits instructions, stored in Flash in the zb_prog
array of type Zigbee_Instruction[]
. There is a PC (program counter) that is increased at each tick (i.e. every 50ms).
The state machine has very simple instructions.
Instructions encoded with 4 bytes:
NOOP
: no-operation, do nothing and move to the next instructionLABEL(x)
: no-operation, and defines a Label (8 bits) that can be called by code.GOTO(x)
: moves the PC to the instruction with LABEL(x)ZI_ON_ERROR_GOTO(x)
: if an error occurs, move to labelZI_ON_TIMEOUT_GOTO(x)
: if a timeout occurs, move to lavelWAIT(y)
: wait for milliseconds (unsigned 16 bits). Note the granularity is 50ms and the wait is non-blockingWAIT_FOREVER
: pause the state machine and wait for an external gotoSTOP
: stop completely the state machine, only used after an unrecoverable error
Instructions encoded with 8 bytes:
CALL(f, x)
: call a function, is the address of the function of typeuint32_t f(uint8_t)
. The input parameter is . The response is according to callbacks responses, except-1
(time-out) simply continues the flow.LOG(m)
: log the string . can be in PROGMEM. For debugging only.MQTT_STATE(x, m)
: sends a MQTTZbState
message, with status code and message . can be in PROGMEM.SEND(d)
: send a ZNP sequence to CC2530. is an array of , a macro computes automatically the size of the array. can be in PROGMEM.WAIT_RECV(x, m)
: wait for a specific message to be received with a time-out of (uint16_t). Messages take into account are owly those matching the first 2 bytes. The complete message match is expected or an error is generated. If the message received is longer than , additional bytes are ignoredWAIT_UNTIL(x, m)
: similar toWAIT_RECV
but message that don't match are ignored, until a matching message is received.ON_RECV_UNEXPECTED(f)
: if we received an unexpected (or unsupported) zigbee message, call function
Instructions encoded with 12 bytes:
WAIT_RECV_FUNC(x, m, f)
: similar toWAIT_RECV
and a function is called when the message matches.
All callbacks return int32_t
with the following effect:
> 0
: goto the corresponding label0
: continue-1
: signal a time-out< -1
: trigger an error (goto on_error)
Initialization code for the state machine
At Tasmota start-up, the state-machine fires. The current Z2T pseudo-code does the following:
Init:
- Set-up all the error handling functions
- Wait for 10.5 seconds after boot
- Send a RESET to CC2530
- Wait for CC2530 boot
Check configuration (if something is wrong, go to CONFIGURE):
- Check if the CC2530 was previously configured. It uses the same 1-byte Non-Volatile 0xF00 address and stores 0x55.
- Checks the Z-Stack version
- Checks the internal configuration: PanID, Ext PanID, Channel, PFGK and PFGKEN.
- If all good, emit an MQTT message saying Zigbee is configured
- Goto Start
Configure (only if needed):
- Emit an MQTT message for reconfiguration
- Do a factory reset of CC2530
- Reset the device once again
- Configure the following: PanId, Ext PanId, Channel, set type to Coordinator, PFKEY, PFKEYEN, Security Module
- Create NF 0xF00 location and store 0x55
- Goto Start
Start:
- Wait for CC2530 message saying the coordinator sucessfully started
- Query DeviceInfo
- Query Node Descriptor
- Query Active Endpoints
- Register 2 endpoints with profile 0x0104 (Home Automation) : 0x01 (default), 0x0B (for Xiaomi)
- Query Active Endpoints to verify 0x01 and 0x0B are active
- Close PermitJoin: don't accept any pairing
- Emit an MQTT message to indicate Zigbee started
- Mark Zigbee as initialized, accept incoming messages
- Load device configuration from Flash
- Query any lights declared with
ZbLight
to read their current states - Pause the state machine
Pairing devices
When you open pairing with ZbPermitJoin 1
(60 seconds) or ZbPermitJoin 99
(until next reboot), you allow new devices to join the network.
Example below is for an OSRAM Plug.
When a new devices joins, Z2T receives a TC Device Indication: ZDO_TC_DEV_IND
(45CA) message with the device short (16 bits) address and IEEEAddress (64 bits).
16:39:26 MQT: tele/Zigbee_home/RESULT = {"ZbState":{"Status":30,"IEEEAddr":"0x7CB03EAA0A0292DD","ShortAddr":"0xF75D","PowerSource":true,"ReceiveWhenIdle":true,"Security":false}}
Z2T then queries the device for additional information, like ZbProbe
would do.
First probe for Active Endpoint ZDO_ACTIVE_EP_REQ
16:39:26 MQT: tele/Zigbee_home/RESULT = {"ZbState":{"Status":32,"ActiveEndpoints":["0x03"]}}
Finally query for the following general attributes: Manufacturer Id and Model Id.
16:39:26 ZIG: ZbZCLRawReceived: {"0xF75D":{"0000/0004":"OSRAM","0000/0005":"Plug 01"}}
16:39:26 MQT: tele/tasmota/Zigbee_home/SENSOR = {"ZbReceived":{"0xF75D":{"Manufacturer":"OSRAM","ModelId":"Plug 01","Endpoint":3,"LinkQuality":36}}}