Close

Software

A project log for Zicada - DIY Zigbee Multisensor

Open-Source Temperature, Humidity, and Contact Sensing

maxkMax.K 11/16/2025 at 17:400 Comments

The nRF52840 is supported by Platformio, and inexpensive boards like the nice!nano can be programmed directly via their USB-port. I hoped that this would simplify software development, but there are two main issues: First, the nRF52 does not ship with a USB bootloader preinstalled, unlike the Raspberry Pi Pico. When using blank, factory-fresh chips, the only way to program them is through SWD. There’s not even a serial boot option by default.

A second problem is Zigbee. Its protocol stack is proprietary and managed by the Connectivity Standards Alliance. Without paying licensing fees, manufacturers are not allowed to use the protocol or even mention “Zigbee“ in their branding. Vendors provide prebuilt Zigbee stacks, that function as black boxes. Nordic uses the ZBOSS Zigbee protocol stack as a precompiled library, which is only fully usable within the Nordic SDK. As a result, when developing my own Zigbee board, I was tied to Nordic’s SDK and needed an SWD/JTAG programmer load the software.

 There are several options for SWD debuggers, depending on the budget. The cheapest approach is buying a black pill STM32F4 board and flashing it with the Black Magic Probe (BMP) firmware. This turns the STM32 into a SWD debugger that can program the nRF52840. The BMP can then be selected as a debugger in VS Code. To support my board’s 2.2V logic, I added a level shifter to the STM32 board:
While this was a working solution for basic flashing, I found it to be unreliable and inconvenient. I later bought a Segger J-Link mini for around 60€. It’s by far the cheapest fully-fledged JTAG debugger, provided you only use it for non-commercial projects. And although the documentation doesn't state it, the J-Link works perfectly fine with a 2.2V target. I also bought a super-cheap pogo pin clamp that uses the test pads on the back of my PCB, so I don't have to solder on connector to every single board.

Now with a working way to flash the nRF52840, I began looking for a good starting point for my application. I had hoped that there were some official examples by Nordic, that I could base my code on. After all, a Zigbee temperature sensor is a very common type of device. But I had to realize that the latest version of the NRF Connect SDK (3.0.0) does not even include Zigbee support anymore. It requires a separate add-on which I could not get to work. Instead, I am using the slightly older version 2.6.0 with integrated Zigbee support. But even this version only comes with a small set of samples which demonstrate Zigbee communication between three Nordic evaluation boards. As a better starting point, I found Glen Akins Zigbee button project: https://bikerglen.com/blog/building-battery-powered-zigbee-buttons/

This project already implemented many components that I needed for my sensor, most importantly a Zigbee cluster definition.

Zigbee clusters are standardized sets of commands and attributes bundled together for specific device functions. This can be temperature, lighting, power consumption, etc. For my device, I had to combine the following clusters:

These clusters are defined in the Zigbee Cluster Library which ensure that Zigbee devices can communicate across different manufacturers. And it gets a little more complicated: Zigbee uses a client-server model, so for each cluster, you have to know if your device stores the information or the coordinator. What’s the coordinator? There are three types of Zigbee devices:

There can be many routers and end devices in a network but only one coordinator. The coordinator can be a USB adapter running software like Zigbee2MQTT or Zigbee Home Automation (ZHA), which is part of Home Assistant. If all of your clusters are correctly defined and your device conforms to the standards, it will be able to join the network. During this joining procedure, the coordinator interviews the device to find out what clusters it is using and what attributes it can report. In a home automation software like Home Assistant the device will then be displayed correctly with its attributes and controls. 

Debugging cluster definitions and figuring out the correct configuration was tedious. There are few examples on the internet and Nordic’s documentation for the ZBOSS stack is very limited. For example, I had the problem that the On/Off cluster for the door sensor is not directly recognized in Zigbee2MQTT. A better alternative would be the IAS Zone cluster, which is mostly meant for security systems. But this cluster need a specific IAS zone enrollment, separate from the regular joining procedure. After trying for a couple of hours I gave up on this, as there seem to be issues with Nordic’s implementation. The workaround is a custom converter, which tells Zigbee2MQTT that is should treat the on/off cluster as a contact sensor. Fortunately, ZHA supports the On/Off cluster natively without any modifications. 

Another difficult topic is attribute reporting. My initial assumption was that my sensor would just send a temperature value to the coordinator at a fixed interval. But it’s not that simple, since the interval is negotiated with the coordinator. For example, if the device supports intervals from 1 min to 30 min, the coordinator might choose 5 min. The ZBOSS stack then sets a timer to wake the devices every 5 minutes to measure the temperature. In general, the ZBOSS stack manages all of the device’s timers, wakeups, and sleep states. You can configure interrupts for buttons and schedule periodic events within the stack, but ZBOSS manages when the device should wake up. To some degree, this simplifies programming but also makes debugging much trickier if some timer does not behave as expected. For example, when adding an LED indication for the contact sensor, the timer for turning the LED off was not properly scheduled, turning the device on every few minutes. This took me a lot of time to find and fix. 

In comparison, the non-Zigbee software parts of the device, such as reading the HDC2080 were straightforward. Nordic supplies a vast number of libraries as part of Zephyr. And configuring pins can be done visually with a device tree configurator. This also simplifies working with different hardware revisions or evaluation boards. 

Besides Zigbee-related issues, I had to apply some tweaks to improve the power consumption of the device. Initially, I configured the hall sensor pin as an edge interrupt, which should fire for both rising and falling edges (EDGE_BOTH). After some troubleshooting I found out that this kind of interrupts stops the controller from sleeping and draws a couple hundred microamps. The solution was switching to a level-triggered interrupt and inverting the trigger level after each activation.

 To test my software, I have a few of the Zicada sensors running in my home automation network for the past few weeks. The software seems quite stable and I am very happy with the power consumption. During standby, the entire device runs on around 6uW. With temperature and humidity reporting happening every 5 minutes, the average power consumption is less than 17uW. In comparison, Ikea’s Parasoll sensor uses an average of around 22uW in standby alone.

Overall, the projects met my design goals: Inexpensive hardware, good smart home integration, and excellent power efficiency. Getting the software to a stable state was challenging, but I think that I have gotten the Zigbee stack under control for now. Time will tell how it performs long-term.

Discussions