├── ESP32C6_Zigbee_Example.ino ├── LICENSE └── README.md /ESP32C6_Zigbee_Example.ino: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /** 16 | * @brief This example demonstrates simple Zigbee light bulb. 17 | * 18 | * The example demonstrates how to use ESP Zigbee stack to create a end device light bulb. 19 | * The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator. 20 | * 21 | * Proper Zigbee mode must be selected in Tools->Zigbee mode 22 | * and also the correct partition scheme must be selected in Tools->Partition Scheme. 23 | * 24 | * Please check the README.md for instructions and more detailed description. 25 | */ 26 | 27 | #ifndef ZIGBEE_MODE_ED 28 | #error "Zigbee end device mode is not selected in Tools->Zigbee mode" 29 | #endif 30 | 31 | #include "driver/temperature_sensor.h" 32 | temperature_sensor_handle_t tempsensor; 33 | 34 | #include "esp_zigbee_core.h" 35 | #include "esp_zigbee_endpoint.h" 36 | #include "esp_zigbee_cluster.h" 37 | #include "freertos/FreeRTOS.h" 38 | #include "freertos/task.h" 39 | #include "ha/esp_zigbee_ha_standard.h" 40 | #include "driver/gpio.h" // gpio_read_level 41 | 42 | #define LED_PIN RGB_BUILTIN 43 | #define BTN_PIN GPIO_NUM_9 44 | 45 | 46 | bool light_state = 0; 47 | uint8_t light_level = 254; 48 | 49 | /* Default End Device config */ 50 | #define ESP_ZB_ZED_CONFIG() \ 51 | { \ 52 | .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, \ 53 | .install_code_policy = INSTALLCODE_POLICY_ENABLE, \ 54 | .nwk_cfg = { \ 55 | .zed_cfg = { \ 56 | .ed_timeout = ED_AGING_TIMEOUT, \ 57 | .keep_alive = ED_KEEP_ALIVE, \ 58 | }, \ 59 | }, \ 60 | } 61 | 62 | #define ESP_ZB_DEFAULT_RADIO_CONFIG() \ 63 | { \ 64 | .radio_mode = RADIO_MODE_NATIVE, \ 65 | } 66 | 67 | #define ESP_ZB_DEFAULT_HOST_CONFIG() \ 68 | { \ 69 | .host_connection_mode = HOST_CONNECTION_MODE_NONE, \ 70 | } 71 | 72 | /* Zigbee configuration */ 73 | #define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */ 74 | #define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN 75 | #define ED_KEEP_ALIVE 3000 /* 3000 millisecond */ 76 | #define LIGHT_ENDPOINT 1 /* esp light bulb device endpoint, used to process light controlling commands */ 77 | #define MANUFACTURER "DIY" 78 | #define MODELNAME "LedLight" 79 | #define APP_PROFILE_ID 0x0104 /* 0x0104 == Home Automation (HA) */ 80 | #define POWER_SOURCE 3 /* 0x03 == battery */ 81 | #define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */ 82 | 83 | 84 | static void set_zcl_string(char *buffer, const char *value) 85 | { 86 | buffer[0] = (char) strlen(value); 87 | memcpy(buffer + 1, value, buffer[0]); 88 | } 89 | 90 | static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) 91 | { 92 | ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask)); 93 | } 94 | 95 | void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) 96 | { 97 | uint32_t *p_sg_p = signal_struct->p_app_signal; 98 | esp_err_t err_status = signal_struct->esp_err_status; 99 | esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p; 100 | switch (sig_type) { 101 | case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: 102 | log_i("Zigbee stack initialized"); 103 | esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION); 104 | break; 105 | case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START: 106 | case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT: 107 | if (err_status == ESP_OK) { 108 | log_i("Start network steering"); 109 | esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); 110 | } else { 111 | /* commissioning failed */ 112 | log_w("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status)); 113 | } 114 | break; 115 | case ESP_ZB_BDB_SIGNAL_STEERING: 116 | if (err_status == ESP_OK) { 117 | esp_zb_ieee_addr_t extended_pan_id; 118 | esp_zb_get_extended_pan_id(extended_pan_id); 119 | log_i("Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 0x%04hx)", 120 | extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], 121 | extended_pan_id[3], extended_pan_id[2], extended_pan_id[1], extended_pan_id[0], 122 | esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()); 123 | } else { 124 | log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status)); 125 | esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000); 126 | } 127 | break; 128 | default: 129 | log_i("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, 130 | esp_err_to_name(err_status)); 131 | break; 132 | } 133 | } 134 | 135 | static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) 136 | { 137 | esp_err_t ret = ESP_OK; 138 | switch (callback_id) { 139 | case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: 140 | ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *)message); 141 | break; 142 | 143 | case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: 144 | ret = zb_read_attribute_handler((esp_zb_zcl_cmd_read_attr_resp_message_t*) message); 145 | break; 146 | 147 | default: 148 | log_w("Receive Zigbee action(0x%x) callback", callback_id); 149 | break; 150 | } 151 | return ret; 152 | } 153 | 154 | static void esp_zb_task(void *pvParameters) 155 | { 156 | esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG(); 157 | esp_zb_init(&zb_nwk_cfg); 158 | 159 | // list of endpoints of this device/node 160 | esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create(); 161 | 162 | 163 | ///////////////////////////////////////////////////// 164 | // Create endpoint for LED light 165 | ///////////////////////////////////////////////////// 166 | 167 | // clusters of the light endpoint 168 | esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create(); 169 | 170 | // initalize the cluster cfg with defaults for a light 171 | esp_zb_color_dimmable_light_cfg_t light_cfg = ESP_ZB_DEFAULT_COLOR_DIMMABLE_LIGHT_CONFIG(); 172 | 173 | // Basic cluster 174 | // 175 | 176 | // indicate use of battery instead of default value 177 | light_cfg.basic_cfg.power_source = POWER_SOURCE; 178 | esp_zb_attribute_list_t *cluster_basic = esp_zb_basic_cluster_create(&light_cfg.basic_cfg); 179 | 180 | char buffer[16]; 181 | set_zcl_string(buffer, MANUFACTURER); 182 | esp_zb_basic_cluster_add_attr(cluster_basic, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, buffer); 183 | set_zcl_string(buffer, MODELNAME); 184 | esp_zb_basic_cluster_add_attr(cluster_basic, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, buffer); 185 | 186 | esp_zb_cluster_list_add_basic_cluster(cluster_list, cluster_basic, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 187 | 188 | // Identify Cluster 189 | // 190 | esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_identify_cluster_create(&light_cfg.identify_cfg); 191 | esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 192 | 193 | // Groups Cluster 194 | // 195 | esp_zb_attribute_list_t *esp_zb_groups_cluster = esp_zb_groups_cluster_create(&light_cfg.groups_cfg); 196 | esp_zb_cluster_list_add_groups_cluster(cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 197 | 198 | // Scenes Cluster 199 | // 200 | esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&light_cfg.scenes_cfg); 201 | esp_zb_cluster_list_add_groups_cluster(cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 202 | 203 | // On/Off Cluster 204 | // 205 | esp_zb_attribute_list_t *esp_zb_on_off_cluster = esp_zb_on_off_cluster_create(&light_cfg.on_off_cfg); 206 | esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 207 | 208 | // Level Cluster 209 | // 210 | esp_zb_attribute_list_t *esp_zb_level_cluster = esp_zb_level_cluster_create(&light_cfg.level_cfg); 211 | esp_zb_level_cluster_add_attr(esp_zb_level_cluster, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, &light_level); 212 | esp_zb_cluster_list_add_level_cluster(cluster_list, esp_zb_level_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); 213 | 214 | // add clusters to endpoint 215 | esp_zb_ep_list_add_ep(ep_list, cluster_list, LIGHT_ENDPOINT, APP_PROFILE_ID, ESP_ZB_HA_LEVEL_CONTROLLABLE_OUTPUT_DEVICE_ID); 216 | 217 | esp_zb_device_register(ep_list); 218 | esp_zb_core_action_handler_register(zb_action_handler); 219 | esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK); 220 | 221 | //Erase NVRAM before creating connection to new Coordinator 222 | esp_zb_nvram_erase_at_start(true); //Comment out this line to erase NVRAM data if you are conneting to new Coordinator 223 | 224 | ESP_ERROR_CHECK(esp_zb_start(true)); 225 | esp_zb_main_loop_iteration(); 226 | } 227 | 228 | 229 | static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) 230 | { 231 | esp_err_t ret = ESP_OK; 232 | 233 | if(!message){ 234 | log_e("Empty message"); 235 | } 236 | if(message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS){ 237 | log_e("Received message: error status(%d)", message->info.status); 238 | } 239 | 240 | log_i("Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)", message->info.dst_endpoint, message->info.cluster, 241 | message->attribute.id, message->attribute.data.size); 242 | 243 | if (message->info.dst_endpoint != LIGHT_ENDPOINT) { 244 | return ESP_ERR_INVALID_ARG; 245 | } 246 | 247 | switch (message->info.cluster) { 248 | case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY: { 249 | if (message->attribute.id == ESP_ZB_ZCL_ATTR_IDENTIFY_IDENTIFY_TIME_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { 250 | uint16_t identifyTime = * (uint16_t*) message->attribute.data.value; 251 | log_i("Identify for %ds", identifyTime); 252 | } 253 | break; 254 | } 255 | case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF: { 256 | if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { 257 | light_state = message->attribute.data.value ? *(bool *)message->attribute.data.value : light_state; 258 | log_i("Light sets to %s", light_state ? "On" : "Off"); 259 | neopixelWrite(LED_PIN,255*light_state,255*light_state,255*light_state); // Toggle light 260 | } 261 | break; 262 | } 263 | case ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL: { 264 | 265 | break; 266 | } 267 | case ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL: { 268 | if (message->attribute.id == ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { 269 | light_level = message->attribute.data.value ? *(uint8_t *)message->attribute.data.value : 255; 270 | log_i("Level set to %d", light_level); 271 | neopixelWrite(LED_PIN, light_level * light_state, light_level * light_state, light_level * light_state); 272 | } 273 | break; 274 | } 275 | } 276 | 277 | return ret; 278 | } 279 | 280 | 281 | static esp_err_t zb_read_attribute_handler(esp_zb_zcl_cmd_read_attr_resp_message_t *message) 282 | { 283 | log_i("Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)", message->info.dst_endpoint, message->info.cluster, 284 | message->variables->attribute.id, message->variables->attribute.data.size); 285 | 286 | return ESP_OK; 287 | } 288 | 289 | 290 | /********************* Arduino functions **************************/ 291 | void setup() { 292 | // Init Zigbee 293 | esp_zb_platform_config_t config = { 294 | .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(), 295 | .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(), 296 | }; 297 | ESP_ERROR_CHECK(esp_zb_platform_config(&config)); 298 | 299 | // Init RMT and leave light OFF 300 | neopixelWrite(LED_PIN,0,0,0); 301 | 302 | // Start Zigbee task 303 | xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL); 304 | } 305 | 306 | void loop() { 307 | } 308 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 volkerp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32C6_Zigbee_Example 2 | Use Zigbee with ESP32C6 and Arduino 3 | 4 | Turns a ESP32C6 dev board into a dimmable zigbee light. 5 | 6 | ## Arduino setup 7 | - Add ESP32 board support in Arduino File :: Preferences :: Add. boards manager URLs 8 | Add _https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json_ 9 | (notice the _dev_ for development/alpha) 10 | - In Tools :: Board :: Boards Manager 11 | search for 'esp32 by Expressif Systems', install _3.0.0-alphaX_ release 12 | - Select your board from the Tools :: Board menu. E.g. _ESP32C6 Dev Module_ 13 | - Select Tools :: Partition Scheme _Zigbee 4MB with spiffs_ 14 | - Select Tools :: Zigbee Mode _Zigbee ED (end device)_ 15 | - Select Tools :: Core Debug Level _Verbose_ to get log output in the serial monitor 16 | 17 | ## Reset device 18 | For reset/rejoin uncomment the line that reads 19 | ```esp_zb_nvram_erase_at_start(true);``` 20 | 21 | ## zigbee2mqtt external definition 22 | ```yaml 23 | const {identify, light} = require('zigbee-herdsman-converters/lib/modernExtend'); 24 | 25 | const definition = { 26 | zigbeeModel: ['LedLight'], 27 | model: 'LedLight', 28 | vendor: 'DIY', 29 | description: 'ESP32C6', 30 | extend: [identify(), light()], 31 | meta: {}, 32 | }; 33 | 34 | module.exports = definition; 35 | ``` 36 | --------------------------------------------------------------------------------