Zigbee is one of the most popular wireless communication protocols in the smart home ecosystem, known for its reliability, low power consumption, and robust mesh networking capabilities. In this tutorial, we'll explore how to leverage Zigbee on the powerful and compact DFRobot Beetle ESP32-C6 microcontroller to create a simple yet functional smart lighting system with an on/off switch and light.
Whether you're a home automation enthusiast or a developer looking to expand your IoT skill set, this project will provide you with a solid foundation for building Zigbee-based applications.
What You'll Learn- What Zigbee is and why it's important for IoT applications
- How to set up the Beetle ESP32-C6 for Zigbee development
- Implementing a Zigbee on/off light device
- Creating a Zigbee on/off switch controller
- Testing the communication between devices
- Practical applications and next steps
- 2× Beetle ESP32-C6 development boards
- USB cables for programming
- LED (or relay module for controlling a real light)
- Push button(Or the Built in Boot Button)
- Breadboard and jumper wires
- Optional: External power supply
- Arduino IDE
- ESP32-C6 board support package( ESP32 Core V 3.x.x on Arduino IDE)
Zigbee is a low-power, wireless mesh networking protocol based on the IEEE 802.15.4 standard. It operates in the 2.4 GHz band (globally available) and is specifically designed for low-data-rate, short-range communication in personal area networks (PANs).
- Mesh Networking: Zigbee devices can relay messages to each other, extending the range of your network without requiring additional infrastructure.
- Low Power Consumption: Zigbee is designed to be extremely power efficient, making it ideal for battery-operated devices that need to run for months or years without battery replacement.
- Reliability and Security: Zigbee includes robust security features like AES-128 encryption and network authentication, ensuring your smart home devices communicate securely.
- Interoperability: Devices from different manufacturers can communicate with each other when they adhere to the Zigbee standard.
- Scalability: A single Zigbee network can support over 65, 000 devices, making it suitable for both simple home setups and large-scale deployments.
The Beetle ESP32-C6 is an excellent platform for Zigbee development because:
- It features built-in support for 802.15.4 radio, which is the foundation of Zigbee
- Its dual-core processor offers plenty of processing power for handling networking tasks
- Low power modes allow for energy-efficient operation
- The compact form factor makes it suitable for integration into small devices
- It also supports Wi-Fi, Bluetooth, and Thread, enabling multi-protocol applications
- Install the latest version of Arduino IDE from arduino.cc
Add ESP32 board support to Arduino IDE:
- Open Arduino IDE
Go to File > Preferences
- In "Additional Board Manager URLs, " add:
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Go to Tools > Board > Boards Manager
- Search for "esp32" and install the latest version
- Connect your ESP32-C6 to your computer via USB
- Select the correct board from Tools > Board > ESP32 Arduino >Beetle ESP3C6 or ESP32C6 Dev Module
- Select the correct port from Tools > Port
For the light device, we'll use a simple LED circuit:
- Connect an LED to GPIO pin 4 of the ESP32-C6 (use a current-limiting resistor of 220Ω)
- Connect the negative leg of the LED (cathode) to GND
ESP32-C6 GPIO4 ---> 220Ω Resistor ---> LED ---> GND
This code can be found navigating to Go to File > Examples > Zigbee > Zigbee_On_Off_Light
We will be using GPIO 4 make sure the led pin is defined as 4, as shown below, and the button to turn it on or off is BOOT_PIN(Built in boot pin) you can use an external button but you have to define it here
//modify these lines to use differrent LED or Button
uint8_t led = 4;
uint8_t button = BOOT_PIN;
Before Uploading the code, ensure the correct board is selected and the following settings are selected
- Select the Coordinator Zigbee mode: Tools > Zigbee mode: Zigbee ED (end device)
- Select Partition Scheme for Zigbee: Tools > Partition Scheme: Zigbee 4MB with spiffs.
// Based on code by Espressif Systems and Jan Procházka
// Licensed under Apache License 2.0
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
uint8_t led = 4;
uint8_t button = BOOT_PIN;
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
/********************* RGB LED functions **************************/
void setLED(bool value) {
digitalWrite(led, value);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Set callback function for light change
zbLight.onLightChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
}
delay(100);
}
Building the Zigbee On/Off SwitchIn this case we will use the Built in boot button to act as our switch, you can also use an external push button but ensure you add that in the code
/* Zigbee switch configuration */
#define SWITCH_ENDPOINT_NUMBER 5
#define GPIO_INPUT_IO_TOGGLE_SWITCH BOOT_PIN //Modify this line to use an external button
Code for the Zigbee Switch DeviceThis code can be found navigating to Go to File > Examples > Zigbee > Zigbee_On_Off_Switch
Before Uploading the code, ensure the correct board is selected and the following settings are selected
- Select the Coordinator Zigbee mode: Tools > Zigbee mode: Zigbee ZCZR (coordinator/router).
- Select Partition Scheme for Zigbee: Tools > Partition Scheme: Zigbee 4MB with spiffs.
// Based on code by Espressif Systems and Jan Procházka
// Licensed under Apache License 2.0
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee switch configuration */
#define SWITCH_ENDPOINT_NUMBER 5
#define GPIO_INPUT_IO_TOGGLE_SWITCH BOOT_PIN
#define PAIR_SIZE(TYPE_STR_PAIR) (sizeof(TYPE_STR_PAIR) / sizeof(TYPE_STR_PAIR[0]))
typedef enum {
SWITCH_ON_CONTROL,
SWITCH_OFF_CONTROL,
SWITCH_ONOFF_TOGGLE_CONTROL,
SWITCH_LEVEL_UP_CONTROL,
SWITCH_LEVEL_DOWN_CONTROL,
SWITCH_LEVEL_CYCLE_CONTROL,
SWITCH_COLOR_CONTROL,
} SwitchFunction;
typedef struct {
uint8_t pin;
SwitchFunction func;
} SwitchData;
typedef enum {
SWITCH_IDLE,
SWITCH_PRESS_ARMED,
SWITCH_PRESS_DETECTED,
SWITCH_PRESSED,
SWITCH_RELEASE_DETECTED,
} SwitchState;
static SwitchData buttonFunctionPair[] = {{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}};
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
/********************* Zigbee functions **************************/
static void onZbButton(SwitchData *button_func_pair) {
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) {
// Send toggle command to the light
Serial.println("Toggling light");
zbSwitch.lightToggle();
}
}
/********************* GPIO functions **************************/
static QueueHandle_t gpio_evt_queue = NULL;
static void IRAM_ATTR onGpioInterrupt(void *arg) {
xQueueSendFromISR(gpio_evt_queue, (SwitchData *)arg, NULL);
}
static void enableGpioInterrupt(bool enabled) {
for (int i = 0; i < PAIR_SIZE(buttonFunctionPair); ++i) {
if (enabled) {
enableInterrupt((buttonFunctionPair[i]).pin);
} else {
disableInterrupt((buttonFunctionPair[i]).pin);
}
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
//Optional: set Zigbee device name and model
zbSwitch.setManufacturerAndModel("Espressif", "ZigbeeSwitch");
//Optional to allow multiple light to bind to the switch
zbSwitch.allowMultipleBinding(true);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbSwitch);
//Open network for 180 seconds after boot
Zigbee.setRebootOpenNetwork(180);
// Init button switch
for (int i = 0; i < PAIR_SIZE(buttonFunctionPair); i++) {
pinMode(buttonFunctionPair[i].pin, INPUT_PULLUP);
/* create a queue to handle gpio event from isr */
gpio_evt_queue = xQueueCreate(10, sizeof(SwitchData));
if (gpio_evt_queue == 0) {
Serial.println("Queue creating failed, rebooting...");
ESP.restart();
}
attachInterruptArg(buttonFunctionPair[i].pin, onGpioInterrupt, (void *)(buttonFunctionPair + i), FALLING);
}
// When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR mode
if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Waiting for Light to bound to the switch");
//Wait for switch to bound to a light:
while (!zbSwitch.bound()) {
Serial.printf(".");
delay(500);
}
// Optional: List all bound devices and read manufacturer and model name
std::list<zb_device_params_t *> boundLights = zbSwitch.getBoundDevices();
for (const auto &device : boundLights) {
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
Serial.printf(
"IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4],
device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
);
char *manufacturer = zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr);
char *model = zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr);
if (manufacturer != nullptr) {
Serial.printf("Light manufacturer: %s\r\n", manufacturer);
}
if (model != nullptr) {
Serial.printf("Light model: %s\r\n", model);
}
}
Serial.println();
}
void loop() {
// Handle button switch in loop()
uint8_t pin = 0;
SwitchData buttonSwitch;
static SwitchState buttonState = SWITCH_IDLE;
bool eventFlag = false;
/* check if there is any queue received, if yes read out the buttonSwitch */
if (xQueueReceive(gpio_evt_queue, &buttonSwitch, portMAX_DELAY)) {
pin = buttonSwitch.pin;
enableGpioInterrupt(false);
eventFlag = true;
}
while (eventFlag) {
bool value = digitalRead(pin);
switch (buttonState) {
case SWITCH_IDLE: buttonState = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE; break;
case SWITCH_PRESS_DETECTED: buttonState = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED; break;
case SWITCH_RELEASE_DETECTED:
buttonState = SWITCH_IDLE;
/* callback to button_handler */
(*onZbButton)(&buttonSwitch);
break;
default: break;
}
if (buttonState == SWITCH_IDLE) {
enableGpioInterrupt(true);
eventFlag = false;
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
// print the bound lights every 10 seconds
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10000) {
lastPrint = millis();
zbSwitch.printBoundDevices(Serial);
}
}
Setting Up the NetworkCreating a Zigbee Network- First, upload the light device code to one ESP32-C6
- Then, upload the switch device code to the second ESP32-C6
- Power both devices on
When powered up:
- The light device will create a Zigbee network as the coordinator
- The switch device will scan for available networks and join automatically
- Once connected, the switch will discover the light device
- Press the button connected to the switch device
- The LED connected to the light device should toggle on/off
- The serial monitor will show debug information about the commands being sent and received
- The light device is configured to start a Zigbee network as a coordinator
- The switch joins the network as an end device
- Both devices implement the Home Automation profile, with specific device types (on/off light and on/off switch)
- When the button is pressed on the switch device, it sends an on/off command
- The command is directed specifically to the light device's endpoint
- The light device receives the command and updates its LED state accordingly
- Both devices communicate using standard Zigbee Cluster Library (ZCL) commands
- The switch uses the Zigbee Device Object (ZDO) Match Descriptor Request to find devices with the on/off cluster
- When it discovers the light device, it stores its address for communication
- This enables automatic discovery without hardcoding device addresses
This simple project demonstrates the foundation of Zigbee device interaction, which can be expanded to:
- Smart Home Integration: Connect your devices to popular platforms like Home Assistant, Samsung SmartThings, or Amazon Alexa via a Zigbee gateway.
- Multi-Device Networks: Add more lights and switches to create a complete home lighting system.
- Sensor Integration: Incorporate motion sensors, light sensors, or door/window sensors to trigger lighting automatically.
- Energy Monitoring: Add power monitoring to track energy usage of connected lights.
- Scene Control: Implement multi-button controllers for different lighting scenes.
Devices Not Connecting:
- Make sure both devices are using the same channel
- Check if the ESP32-C6 Zigbee radio is properly initialized
- Verify the devices are powered and within range of each other
Compilation Errors:
- Make sure you have the latest version of ESP32 Core installed
- Verify ESP32-C6 board is correctly selected
In this tutorial, we've explored how to implement a basic Zigbee network using the Beetle ESP32-C6, creating a functional smart lighting system with an on/off switch and light. This project demonstrates the fundamental concepts of Zigbee communication, including network formation, device discovery, and command processing.
The ESP32-C6 is an excellent platform for Zigbee development, offering built-in 802.15.4 radio support and powerful processing capabilities in a compact form factor. By understanding these basics, you can expand your project to include more complex functionality and integrate with existing smart home ecosystems.
Next Steps- Add more devices to your Zigbee network
- Implement additional clusters like level control for dimming lights
- Connect your devices to a Zigbee gateway for smart home integration
- Explore power-saving techniques for battery-operated devices
- Add sensors for automated lighting control
- ESP-Zigbee Documentation
- Zigbee Alliance(Communication Standards Alliance)
- ESP32-C6 Technical Reference Manual
Comments
Please log in or sign up to comment.