icon

Getting Started with Zigbee on Beetle ESP32-C6 Mini

Introduction

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

Hardware Requirements

- 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

Software Requirements

Arduino IDEESP32-C6 board support package( ESP32 Core V 3.x.x on Arduino IDE)

What is Zigbee and Why It Matters

Understanding Zigbee Technology

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).

Key Benefits of Zigbee

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.

From CSA

From CSA

Why ESP32-C6 is Perfect for Zigbee

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 ZigbeeIts dual-core processor offers plenty of processing power for handling networking tasksLow power modes allow for energy-efficient operationThe compact form factor makes it suitable for integration into small devicesIt also supports Wi-Fi, Bluetooth, and Thread, enabling multi-protocol applications

Setting Up Your Development Environment

Installing the Required Software

Install the latest version of Arduino IDE from arduino.cc

Add ESP32 board support to Arduino IDE:

Open Arduino IDEGo to File > PreferencesIn "Additional Board Manager URLs, " add: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.jsonGo to Tools > Board > Boards ManagerSearch for "esp32" and install the latest version

Configuring the Beetle ESP32-C6

Connect your ESP32-C6 to your computer via USBSelect the correct board from Tools > Board > ESP32 Arduino >Beetle ESP3C6 or ESP32C6 Dev ModuleSelect the correct port from Tools > Port

Building the Zigbee On/Off Light

Circuit Connection

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

Code for the Zigbee Light Device

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

CODE
//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.
CODE
// 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 Switch

In 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

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 Device

This 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.
CODE
// 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 Network

Creating 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 automaticallyOnce connected, the switch will discover the light device

Testing the System

- 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

Understanding the Code

Zigbee Network Formation

The light device is configured to start a Zigbee network as a coordinatorThe switch joins the network as an end deviceBoth devices implement the Home Automation profile, with specific device types (on/off light and on/off switch)

Command Processing

When the button is pressed on the switch device, it sends an on/off commandThe command is directed specifically to the light device's endpointThe light device receives the command and updates its LED state accordinglyBoth devices communicate using standard Zigbee Cluster Library (ZCL) commands

Device Discovery

The switch uses the Zigbee Device Object (ZDO) Match Descriptor Request to find devices with the on/off clusterWhen it discovers the light device, it stores its address for communicationThis enables automatic discovery without hardcoding device addresses

Real-World Applications

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.

Troubleshooting

Common Issues and Solutions

Devices Not Connecting:

Make sure both devices are using the same channelCheck if the ESP32-C6 Zigbee radio is properly initializedVerify the devices are powered and within range of each other

Compilation Errors:

Make sure you have the latest version of ESP32 Core installedVerify ESP32-C6 board is correctly selected

Conclusion

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

Resources

- ESP-Zigbee Documentation - Zigbee Alliance(Communication Standards Alliance) - ESP32-C6 Technical Reference Manual
License
All Rights
Reserved
licensBg
1