I'm using the ESP32-C6 Zigbee stack to implement a Tuya-based multi-gang wall switch.
The 1-, 2-, and 3-gang versions work correctly and are identified properly.
However, the 4-gang version is detected as a single-gang device by common Zigbee coordinators (Smart Gateway molel : JMWZG1).
Its functionality is also incorrect, as only one endpoint/cluster appears to be exposed.
It seems the stack is not advertising or handling the correct endpoints/clusters for the 4-gang variant.
Additionally, the device model reported by the stack appears as Chinese characters in the app instead of the correct model name.

#include "esp_check.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"#include "esp_zigbee_core.h"
#include "esp_zigbee_type.h"
#include "esp_zigbee_cluster.h"
#include "esp_zigbee_endpoint.h"// ZCL
#include "zcl/esp_zigbee_zcl_basic.h"
#include "zcl/esp_zigbee_zcl_identify.h"
#include "zcl/esp_zigbee_zcl_on_off.h"#include <string.h>
static QueueHandle_t button_queue = NULL;
#define TAG "ZB_SWITCH"
/* Zigbee configuration /
#define INSTALLCODE_POLICY_ENABLE false
#define ESP_ZB_PRIMARY_CHANNEL_MASK (1l << 15)
#define ALL_ZIGBEE_CHANNELS
((1 << 11) | (1 << 12) | (1 << 13) | (1 << 14) |
(1 << 15) | (1 << 16) | (1 << 17) | (1 << 18) |
(1 << 19) | (1 << 20) | (1 << 21) | (1 << 22) |
(1 << 23) | (1 << 24) | (1 << 25) | (1 << 26))
/ Basic manufacturer information */
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF"
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET/* ---- ZED Configuration ---- */
#define ESP_ZB_ZED_CONFIG()
{
.esp_zb_role = ESP_ZB_DEVICE_TYPE_ED,
.install_code_policy = INSTALLCODE_POLICY_ENABLE,
.nwk_cfg.zed_cfg = {
.ed_timeout = 10,
.keep_alive = 300000 },
}#define ESP_ZB_DEFAULT_RADIO_CONFIG()
{
.radio_mode = ZB_RADIO_MODE_NATIVE,
}#define ESP_ZB_DEFAULT_HOST_CONFIG()
{
.host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE,
}// *********** relay ***********
#define RELAY1_GPIO GPIO_NUM_2
#define RELAY2_GPIO GPIO_NUM_15
#define RELAY3_GPIO GPIO_NUM_11
#define RELAY4_GPIO GPIO_NUM_8#define BUTTON1_GPIO GPIO_NUM_3
#define BUTTON2_GPIO GPIO_NUM_9
#define BUTTON3_GPIO GPIO_NUM_10
#define BUTTON4_GPIO GPIO_NUM_12
// *********** Endpoint **************
#define EP_SWITCH1 1
#define EP_SWITCH2 2
#define EP_SWITCH3 3
#define EP_SWITCH4 4static bool relay_state[4] = {false, false, false, false};
static volatile bool button_flag[4] = {false, false, false, false};static void send_onoff_report(uint8_t ep, bool state)
{
uint8_t val = state ? 1 : 0;// update Attribute
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(
ep,
ESP_ZB_ZCL_CLUSTER_ID_ON_OFF,
ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,
ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID,
&val,
false
);
esp_zb_lock_release();// Report
esp_zb_zcl_report_attr_cmd_t report = {0};
report.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
report.attributeID = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID;
report.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
report.zcl_basic_cmd.src_endpoint = ep;esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_report_attr_cmd_req(&report);
esp_zb_lock_release();ESP_LOGI(TAG, "EP%d → Report %s", ep, state ? "ON" : "OFF");
}
static void IRAM_ATTR button_isr(void *arg) {
uint32_t gpio_num = (uint32_t) arg;
switch ((gpio_num))
{
case BUTTON1_GPIO:
button_flag[0] = true;
break;
case BUTTON2_GPIO:
button_flag[1] = true;
break;
case BUTTON3_GPIO:
button_flag[2] = true;
break;
case BUTTON4_GPIO:
//ESP_LOGI(TAG, "EP interrupt");
button_flag[3] = true;
break;
default:
break;
}}
static void button_poll_timer(void *arg)
{
for (int i = 0; i < 4; i++) {
if (button_flag) {
button_flag = false;
Code: Select all
int btn_gpio = (i == 0) ? BUTTON1_GPIO : (i == 1) ? BUTTON2_GPIO : (i == 2) ? BUTTON3_GPIO : BUTTON4_GPIO; if (gpio_get_level(btn_gpio) == 0) { relay_state[i] = !relay_state[i]; int relay_gpio = (i == 0) ? RELAY1_GPIO : (i == 1) ? RELAY2_GPIO : (i == 2) ? RELAY3_GPIO : RELAY4_GPIO; gpio_set_level(relay_gpio, relay_state[i]); ESP_LOGI(TAG, "Physical key (io%d)%d → %s",relay_gpio, i + 1, relay_state[i] ? "ON" : "OFF"); send_onoff_report(i + 1, relay_state[i]); // endpoint = i+1 } }}
}
// *******************************************************************************
// ZCL Attribute Handlers
// *******************************************************************************static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *msg)
{
ESP_RETURN_ON_FALSE(msg, ESP_FAIL, TAG, "NULL message");if (msg->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF &&
msg->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID)
{
uint8_t ep = msg->info.dst_endpoint;
bool new_state = *(bool *)msg->attribute.data.value;if (ep >= 1 && ep <= 4) {
relay_state[ep - 1] = new_state;Code: Select all
int relay_gpio = (ep == 1) ? RELAY1_GPIO : (ep == 2) ? RELAY2_GPIO : (ep == 3) ? RELAY3_GPIO : RELAY4_GPIO; gpio_set_level(relay_gpio, new_state);}
ESP_LOGI(TAG, "ZCL → EP%d = %s", ep, new_state ? "ON" : "OFF");
}
return ESP_OK;
}
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t cb_id, const void *msg)
{
switch (cb_id)
{
case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:
return zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *)msg);case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID:
{
const esp_zb_zcl_cmd_default_resp_message_t *resp =
(const esp_zb_zcl_cmd_default_resp_message_t *)msg;Code: Select all
ESP_LOGI(TAG, "Default response: cl=0x%04X ep=%d" , resp->info.cluster, resp->info.dst_endpoint ); return ESP_OK;}
default:
ESP_LOGW(TAG, "Unhandled Zigbee action: %d", cb_id);
return ESP_OK;
}}
/************************************************Code: Select all
BDB Callback (Restart Steering) ************************************************/ static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) { esp_zb_bdb_start_top_level_commissioning(mode_mask); } // ******************************************************************************* // ZDO / Network Signal Handler // *******************************************************************************void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
uint32_t sig = *(uint32_t *)signal_struct->p_app_signal;
esp_err_t status = signal_struct->esp_err_status;switch (sig) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "Initialize Zigbee stack (Tuya compatible)");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:Code: Select all
if (status == ESP_OK) { ESP_LOGI(TAG, "Factory new: %s", esp_zb_bdb_is_factory_new() ? "YES" : "NO"); if (esp_zb_bdb_is_factory_new()) { ESP_LOGI(TAG, "Starting network steering (join to Tuya)"); esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); } else { ESP_LOGI(TAG, "Rejoined network."); } } else { ESP_LOGW(TAG, "Failed init Zigbee stack: %s", esp_err_to_name(status)); } break;case ESP_ZB_BDB_SIGNAL_STEERING:
if (status == ESP_OK) {
esp_zb_ieee_addr_t panid;
esp_zb_get_extended_pan_id(panid);
ESP_LOGI(TAG,
"Joined Tuya Gateway\n"
"PAN ID: 0x%04hx\n"
"Channel: %d\n"
"ShortAddr: 0x%04hx",
esp_zb_get_pan_id(),
esp_zb_get_current_channel(),
esp_zb_get_short_address());Code: Select all
} else { ESP_LOGW(TAG, "Steering failed retrying..."); esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 2000); } break;default:
ESP_LOGI(TAG, "ZDO signal: %s", esp_zb_zdo_signal_to_string(sig));
break;
}}
// *******************************************************************************
// Zigbee Task
// *******************************************************************************
static void zigbee_task(void *arg)
{
gpio_set_direction(RELAY1_GPIO, GPIO_MODE_OUTPUT);
gpio_set_direction(RELAY2_GPIO, GPIO_MODE_OUTPUT);
gpio_set_direction(RELAY3_GPIO, GPIO_MODE_OUTPUT);
gpio_set_direction(RELAY4_GPIO, GPIO_MODE_OUTPUT);gpio_set_level(RELAY1_GPIO, 0);
gpio_set_level(RELAY2_GPIO, 0);
gpio_set_level(RELAY3_GPIO, 0);
gpio_set_level(RELAY4_GPIO, 0);// Zigbee End Device Config
esp_zb_cfg_t zb_cfg = ESP_ZB_ZED_CONFIG(); // END DEVICE
esp_zb_init(&zb_cfg);
esp_zb_set_primary_network_channel_set(ALL_ZIGBEE_CHANNELS);// common attrs for Basic cluster
uint8_t zcl_ver = 3;
uint8_t pwr_src = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE;// ------------------ BUILD CLUSTERS FOR ENDPOINT 1 ------------------
esp_zb_attribute_list_t *basic1 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_STACK_VERSION_ID, &(uint8_t){2});
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, "ESP32");
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, "ZB-RELAY-1");
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_ZCL_VERSION_ID, &zcl_ver);
esp_zb_basic_cluster_add_attr(basic1, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, &pwr_src);esp_zb_attribute_list_t *identify1 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY);
esp_zb_identify_cluster_add_attr(identify1, ESP_ZB_ZCL_ATTR_IDENTIFY_IDENTIFY_TIME_ID, &(uint16_t){0});esp_zb_attribute_list_t *groups1 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS);
esp_zb_groups_cluster_add_attr(groups1, ESP_ZB_ZCL_ATTR_GROUPS_NAME_SUPPORT_ID, &(uint8_t){0});esp_zb_attribute_list_t *scenes1 = esp_zb_scenes_cluster_create(NULL);
esp_zb_on_off_cluster_cfg_t onoff_cfg1 = { .on_off = false };
esp_zb_attribute_list_t *onoff1 = esp_zb_on_off_cluster_create(&onoff_cfg1);esp_zb_cluster_list_t cluster_list1 = esp_zb_zcl_cluster_list_create();
esp_zb_cluster_list_add_basic_cluster(cluster_list1, basic1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list1, identify1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_groups_cluster(cluster_list1, groups1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_scenes_cluster(cluster_list1, scenes1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_on_off_cluster(cluster_list1, onoff1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
/ client clusters required by some hubs (Tuya) */
// esp_zb_cluster_list_add_identify_cluster(cluster_list1, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_groups_cluster(cluster_list1, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_scenes_cluster(cluster_list1, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_SCENES), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_on_off_cluster(cluster_list1, esp_zb_on_off_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);// ------------------ BUILD CLUSTERS FOR ENDPOINT 2 ------------------
esp_zb_attribute_list_t *basic2 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_STACK_VERSION_ID, &(uint8_t){2});
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, "ESP32");
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, "ZB-RELAY-2");
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_ZCL_VERSION_ID, &zcl_ver);
esp_zb_basic_cluster_add_attr(basic2, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, &pwr_src);esp_zb_attribute_list_t *identify2 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY);
esp_zb_identify_cluster_add_attr(identify2, ESP_ZB_ZCL_ATTR_IDENTIFY_IDENTIFY_TIME_ID, &(uint16_t){0});esp_zb_attribute_list_t *groups2 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS);
esp_zb_groups_cluster_add_attr(groups2, ESP_ZB_ZCL_ATTR_GROUPS_NAME_SUPPORT_ID, &(uint8_t){0});esp_zb_attribute_list_t *scenes2 = esp_zb_scenes_cluster_create(NULL);
esp_zb_on_off_cluster_cfg_t onoff_cfg2 = { .on_off = false };
esp_zb_attribute_list_t *onoff2 = esp_zb_on_off_cluster_create(&onoff_cfg2);esp_zb_cluster_list_t cluster_list2 = esp_zb_zcl_cluster_list_create();
esp_zb_cluster_list_add_basic_cluster(cluster_list2, basic2, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list2, identify2, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_groups_cluster(cluster_list2, groups2, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_scenes_cluster(cluster_list2, scenes2, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_on_off_cluster(cluster_list2, onoff2, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
/ client clusters */
// esp_zb_cluster_list_add_identify_cluster(cluster_list2, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_groups_cluster(cluster_list2, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_scenes_cluster(cluster_list2, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_SCENES), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_on_off_cluster(cluster_list2, esp_zb_on_off_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);// ------------------ BUILD CLUSTERS FOR ENDPOINT 3 ------------------
esp_zb_attribute_list_t *basic3 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_STACK_VERSION_ID, &(uint8_t){2});
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, "ESP32");
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, "ZB-RELAY-3");
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_ZCL_VERSION_ID, &zcl_ver);
esp_zb_basic_cluster_add_attr(basic3, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, &pwr_src);esp_zb_attribute_list_t *identify3 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY);
esp_zb_identify_cluster_add_attr(identify3, ESP_ZB_ZCL_ATTR_IDENTIFY_IDENTIFY_TIME_ID, &(uint16_t){0});esp_zb_attribute_list_t *groups3 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS);
esp_zb_groups_cluster_add_attr(groups3, ESP_ZB_ZCL_ATTR_GROUPS_NAME_SUPPORT_ID, &(uint8_t){0});esp_zb_attribute_list_t *scenes3 = esp_zb_scenes_cluster_create(NULL);
esp_zb_on_off_cluster_cfg_t onoff_cfg3 = { .on_off = false };
esp_zb_attribute_list_t *onoff3 = esp_zb_on_off_cluster_create(&onoff_cfg3);esp_zb_cluster_list_t cluster_list3 = esp_zb_zcl_cluster_list_create();
esp_zb_cluster_list_add_basic_cluster(cluster_list3, basic3, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list3, identify3, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_groups_cluster(cluster_list3, groups3, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_scenes_cluster(cluster_list3, scenes3, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_on_off_cluster(cluster_list3, onoff3, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
/ client clusters */
// esp_zb_cluster_list_add_identify_cluster(cluster_list3, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_groups_cluster(cluster_list3, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_scenes_cluster(cluster_list3, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_SCENES), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_on_off_cluster(cluster_list3, esp_zb_on_off_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);// ------------------ BUILD CLUSTERS FOR ENDPOINT 4 ------------------
esp_zb_attribute_list_t *basic4 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_STACK_VERSION_ID, &(uint8_t){2});
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID, &(uint8_t){1});
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, "ESP32");
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, "ZB-RELAY-4");
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_ZCL_VERSION_ID, &zcl_ver);
esp_zb_basic_cluster_add_attr(basic4, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, &pwr_src);esp_zb_attribute_list_t *identify4 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY);
esp_zb_identify_cluster_add_attr(identify4, ESP_ZB_ZCL_ATTR_IDENTIFY_IDENTIFY_TIME_ID, &(uint16_t){0});esp_zb_attribute_list_t *groups4 = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS);
esp_zb_groups_cluster_add_attr(groups4, ESP_ZB_ZCL_ATTR_GROUPS_NAME_SUPPORT_ID, &(uint8_t){0});esp_zb_attribute_list_t *scenes4 = esp_zb_scenes_cluster_create(NULL);
esp_zb_on_off_cluster_cfg_t onoff_cfg4 = { .on_off = false };
esp_zb_attribute_list_t *onoff4 = esp_zb_on_off_cluster_create(&onoff_cfg4);esp_zb_cluster_list_t cluster_list4 = esp_zb_zcl_cluster_list_create();
esp_zb_cluster_list_add_basic_cluster(cluster_list4, basic4, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(cluster_list4, identify4, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_groups_cluster(cluster_list4, groups4, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_scenes_cluster(cluster_list4, scenes4, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_on_off_cluster(cluster_list4, onoff4, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
/ client clusters */
// esp_zb_cluster_list_add_identify_cluster(cluster_list4, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_groups_cluster(cluster_list4, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_GROUPS), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_scenes_cluster(cluster_list4, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_SCENES), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// esp_zb_cluster_list_add_on_off_cluster(cluster_list4, esp_zb_on_off_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);// ------------------- Endpoint list ----------------------
esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();esp_zb_endpoint_config_t ep1_cfg = {
.endpoint = EP_SWITCH1,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
.app_device_version = 1
};
esp_zb_ep_list_add_ep(ep_list, cluster_list1, ep1_cfg);esp_zb_endpoint_config_t ep2_cfg = {
.endpoint = EP_SWITCH2,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
.app_device_version = 1
};
esp_zb_ep_list_add_ep(ep_list, cluster_list2, ep2_cfg);esp_zb_endpoint_config_t ep3_cfg = {
.endpoint = EP_SWITCH3,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
.app_device_version = 1
};
esp_zb_ep_list_add_ep(ep_list, cluster_list3, ep3_cfg);esp_zb_endpoint_config_t ep4_cfg = {
.endpoint = EP_SWITCH4,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
.app_device_version = 1
};
esp_zb_ep_list_add_ep(ep_list, cluster_list4, ep4_cfg);esp_zb_device_register(ep_list);
// register handler و start
esp_zb_core_action_handler_register(zb_action_handler);ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_stack_main_loop();}
// *******************************************************************************
// app_main
// *******************************************************************************void app_main(void)
{
esp_log_level_set("*", ESP_LOG_DEBUG);
ESP_ERROR_CHECK(nvs_flash_init());
button_queue = xQueueCreate(4, sizeof(uint8_t));esp_zb_platform_config_t config = {
.radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};ESP_ERROR_CHECK(esp_zb_platform_config(&config));
gpio_config_t io_conf1 = {
.pin_bit_mask = 1ULL << BUTTON1_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&io_conf1);gpio_config_t io_conf2 = {
.pin_bit_mask = 1ULL << BUTTON2_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&io_conf2);
gpio_config_t io_conf3 = {
.pin_bit_mask = 1ULL << BUTTON3_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&io_conf3);
gpio_config_t io_conf4 = {
.pin_bit_mask = 1ULL << BUTTON4_GPIO,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE
};
gpio_config(&io_conf4)
;
gpio_install_isr_service(0);
gpio_isr_handler_add(BUTTON1_GPIO, button_isr, (void)BUTTON1_GPIO);
gpio_isr_handler_add(BUTTON2_GPIO, button_isr, (void)BUTTON2_GPIO);
gpio_isr_handler_add(BUTTON3_GPIO, button_isr, (void)BUTTON3_GPIO);
gpio_isr_handler_add(BUTTON4_GPIO, button_isr, (void)BUTTON4_GPIO);esp_timer_handle_t btn_timer;
const esp_timer_create_args_t btn_args = {
.callback = &button_poll_timer,
.name = "button_timer"
};
esp_timer_create(&btn_args, &btn_timer);
esp_timer_start_periodic(btn_timer, 20 * 1000);xTaskCreate(zigbee_task, "zigbee_task", 4096, NULL, 5, NULL);
}