/*****************************************************************************************
* * --- IMPORTANT ---
* * This file contains the code for BOTH the Coordinator and the End Device.
* You will need to use #ifdef directives or create two separate projects.
* In menuconfig, define either `ZIGBEE_COORDINATOR` or `ZIGBEE_END_DEVICE`.
* *****************************************************************************************/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"
// A descriptive tag for logging
#if defined(ZIGBEE_COORDINATOR)
static const char *TAG = "ESP_ZB_COORDINATOR";
#else
static const char *TAG = "ESP_ZB_END_DEVICE";
#endif
// --- Custom Cluster Definitions ---
// Use a manufacturer-specific cluster ID (in the range 0xFC00 - 0xFFFF)
#define ESP_ZB_CUSTOM_CLUSTER_ID 0xFC00
// Define a custom attribute ID within our custom cluster
#define ESP_ZB_CUSTOM_ATTR_ID 0x0001
/*****************************************************************************************
* COORDINATOR SPECIFIC CODE
*****************************************************************************************/
#if defined(ZIGBEE_COORDINATOR)
static void esp_zb_task(void *pvParameters) {
esp_zb_app_signal_await();
while (true) {
esp_zb_app_signal_message_t *message = esp_zb_app_signal_get();
esp_zb_zcl_command_t *zcl_command = (esp_zb_zcl_command_t *)message->data;
if (message->signal == ESP_ZB_APP_SIGNAL_ZCL_ACTION) {
// Check if the command is for our custom cluster
if (zcl_command->cluster_id == ESP_ZB_CUSTOM_CLUSTER_ID) {
ESP_LOGI(TAG, "Received command from custom cluster");
// Check if it's a "Report Attributes" command
if (zcl_command->command_id == ESP_ZB_ZCL_CMD_REPORT_ATTRIB_ID) {
esp_zb_zcl_report_attr_cmd_t *report_cmd = (esp_zb_zcl_report_attr_cmd_t *)zcl_command->data;
// Check if the attribute ID matches our custom attribute
if (report_cmd->attr_id == ESP_ZB_CUSTOM_ATTR_ID) {
uint8_t received_data = *(uint8_t *)report_cmd->data_value;
ESP_LOGI(TAG, "Received custom data: %d", received_data);
// --- You can now act on this received_data value ---
}
}
}
}
esp_zb_app_signal_message_release(message);
}
}
void app_main(void) {
esp_zb_cfg_t zb_cfg = ESP_ZB_DEFAULT_CONFIG();
esp_zb_init(&zb_cfg);
// 1. Create an endpoint list
esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();
// 2. Create a cluster list for the endpoint
esp_zb_cluster_list_t *cluster_list = esp_zb_cluster_list_create();
// 3. Define and create the custom cluster
esp_zb_cluster_config_t custom_cluster_cfg = {
.cluster_id = ESP_ZB_CUSTOM_CLUSTER_ID,
.role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, // Coordinator acts as a server
};
esp_zb_cluster_t *custom_cluster = esp_zb_cluster_create(&custom_cluster_cfg);
// 4. Define and create the custom attribute
uint8_t initial_value = 0;
esp_zb_attribute_config_t custom_attr_cfg = {
.attr_id = ESP_ZB_CUSTOM_ATTR_ID,
.attr_type = ESP_ZB_ZCL_ATTR_TYPE_U8,
.attr_access = ESP_ZB_ZCL_ATTR_ACCESS_READ_ONLY, // Coordinator only reads
.data_p = &initial_value,
};
esp_zb_attribute_create(custom_cluster, &custom_attr_cfg);
// 5. Add the custom cluster to the cluster list
esp_zb_cluster_list_add_cluster(cluster_list, custom_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
// 6. Create endpoint configuration
esp_zb_endpoint_config_t endpoint_config = {
.endpoint = 1,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_SIMPLE_DEVICE_ID, // A generic device type
.app_device_version = 0,
};
// 7. Add the endpoint with its clusters
esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config);
esp_zb_device_register(ep_list);
// Set role to Coordinator
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
ESP_ERROR_CHECK(esp_zb_start(true));
esp_zb_main_loop_iteration_req();
xTaskCreate(esp_zb_task, "esp_zb_task", 4096, NULL, 5, NULL);
}
#endif // ZIGBEE_COORDINATOR
/*****************************************************************************************
* END DEVICE SPECIFIC CODE
*****************************************************************************************/
#if defined(ZIGBEE_END_DEVICE)
// Function to send the custom data. You would call this on a button press or sensor reading.
void send_custom_data_to_coordinator(uint8_t data_to_send) {
esp_zb_zcl_status_t state = esp_zb_zcl_set_attribute_val(
1, // Endpoint ID where the cluster is registered
ESP_ZB_CUSTOM_CLUSTER_ID, // Our custom cluster ID
ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE, // The End Device is the client
ESP_ZB_CUSTOM_ATTR_ID, // Our custom attribute ID
&data_to_send, // Pointer to the data
false // This is not part of a transaction
);
if (state == ESP_ZB_ZCL_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Successfully set attribute value: %d. Reporting will be triggered.", data_to_send);
} else {
ESP_LOGE(TAG, "Failed to set attribute value, error: 0x%x", state);
}
}
// This task simulates a sensor sending data every 10 seconds
static void sensor_simulation_task(void *pvParameters) {
uint8_t sensor_value = 0;
while (true) {
send_custom_data_to_coordinator(sensor_value++);
vTaskDelay(10000 / portTICK_PERIOD_MS); // Wait for 10 seconds
}
}
static void bdb_start_top_level_commissioning_cb(uint8_t status) {
ESP_ERROR_CHECK(status);
if (status == ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START) {
ESP_LOGI(TAG, "Device is on the network, starting sensor simulation task.");
xTaskCreate(sensor_simulation_task, "sensor_sim_task", 4096, NULL, 5, NULL);
}
}
void app_main(void) {
esp_zb_cfg_t zb_cfg = ESP_ZB_DEFAULT_CONFIG();
esp_zb_init(&zb_cfg);
// 1. Create an endpoint list
esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();
// 2. Create a cluster list for the endpoint
esp_zb_cluster_list_t *cluster_list = esp_zb_cluster_list_create();
// 3. Define and create the custom cluster
esp_zb_cluster_config_t custom_cluster_cfg = {
.cluster_id = ESP_ZB_CUSTOM_CLUSTER_ID,
.role = ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE, // End Device acts as a client
};
esp_zb_cluster_t *custom_cluster = esp_zb_cluster_create(&custom_cluster_cfg);
// 4. Define and create the custom attribute
uint8_t initial_value = 0;
esp_zb_attribute_config_t custom_attr_cfg = {
.attr_id = ESP_ZB_CUSTOM_ATTR_ID,
.attr_type = ESP_ZB_ZCL_ATTR_TYPE_U8,
.attr_access = ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, // Client can read/write
.data_p = &initial_value,
};
esp_zb_attribute_create(custom_cluster, &custom_attr_cfg);
// 5. Add the custom cluster to the cluster list
esp_zb_cluster_list_add_cluster(cluster_list, custom_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// 6. Create endpoint configuration
esp_zb_endpoint_config_t endpoint_config = {
.endpoint = 1,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_SIMPLE_DEVICE_ID,
.app_device_version = 0,
};
// 7. Add the endpoint with its clusters
esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config);
esp_zb_device_register(ep_list);
// Set role to End Device and start commissioning
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_main_loop_iteration_req();
esp_zb_bdb_start_top_level_commissioning(bdb_start_top_level_commissioning_cb);
}
#endif // ZIGBEE_END_DEVICE