Closed kgkask closed 4 months ago
@kgkask ,
Could you please refer to this comment https://github.com/espressif/esp-zigbee-sdk/issues/202#issuecomment-2005690377 for your question? The example demonstrates a bidirectional communication case.
Additionally, it would be helpful to attach a patch or a zip file of your project to facilitate our review.
@xieqinan Thanks for your quick response, I actually I have used the same code but in Arduino IDE. It allows sending the string from one side not both so based on my understanding I have to add a client cluster with the server cluster to achieve bidirectional communication (and vice versa).
sorry for the code. here is .rar file:
@xieqinan
I have looked closely to the code, I can receive the response to the write command but when I configure the End device to send write command "esp_zb_zcl_write_attr_cmd_req(&write_req);
" like the coordinator it fails
@xieqinan I find out how to solve it, thanks.
I actually my mistake was that I used only esp_zb_zcl_write_attr_cmd_req(&write_req);
, while I actually need to make use of esp_zb_zcl_custom_cluster_cmd_req_t
and esp_zb_zcl_custom_cluster_cmd_resp_t
which is already there in the code.
Thanks again
@kgkask ,
Do you have any other questions that need to be discussed here? If the bidirectional communication
issue has been resolved, please consider closing it.
Question
How to make the Endpoint write attribute to the coordinator to add a bidirectional communication ability?
Additional context.
I am using the code in #244, I wanted to edit it to make both ESP32H2 send string messages. Form the documentation I found that I have to set an extra cluster for each sides to add this feature, I added the clusters in both sides and I expected to work just fine I have updated the codes to handle the inquiries for each side, however when the end point send the write commands the coordinator does react at all NOT SURE WHAT'S HAPPENING?!
I have tried multiple examples and similar opened issues but I couldn't figure out the source of the issue.
Coordinator node : `#ifndef ZIGBEE_MODE_ZCZR
error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
endif
include "esp_zigbee_core.h"
include "freertos/FreeRTOS.h"
include "freertos/task.h"
include "ha/esp_zigbee_ha_standard.h"
/ Switch configuration /
define GPIO_INPUT_IO_TOGGLE_SWITCH GPIO_NUM_9
define PAIR_SIZE(TYPE_STR_PAIR) (sizeof(TYPE_STR_PAIR) / sizeof(TYPE_STR_PAIR[0]))
define CUSTOM_CLUSTER_ID 0xFC00
define CUSTOM_CLUSTER_ID1 0xFC01
define CUSTOM_STRING_MAX_SIZE 127
define LED_PIN RGB_BUILTIN
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, } switch_func_t;
typedef enum { SWITCH_IDLE, SWITCH_PRESS_ARMED, SWITCH_PRESS_DETECTED, SWITCH_PRESSED, SWITCH_RELEASE_DETECTED, } switch_state_t;
typedef struct { uint8_t pin; switch_func_t func; } switch_func_pair_t;
static switch_func_pair_t button_func_pair[] = { {GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL} };
typedef struct light_bulb_device_params_s { esp_zb_ieee_addr_t ieee_addr; uint8_t endpoint; uint16_t short_addr; } light_bulb_device_params_t;
/ Default Coordinator config /
define ESP_ZB_ZC_CONFIG() \
{ \ .esp_zb_role = ESP_ZB_DEVICE_TYPE_COORDINATOR, \ .install_code_policy = INSTALLCODE_POLICY_ENABLE, \ .nwk_cfg = { \ .zczr_cfg = \ { \ .max_children = MAX_CHILDREN, \ }, \ } \ }
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, }
/ Zigbee configuration /
define MAX_CHILDREN 10 / the max amount of connected devices /
define INSTALLCODE_POLICY_ENABLE false / enable the install code policy for security /
define HA_ONOFF_SWITCH_ENDPOINT 1 / esp light switch device endpoint /
define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK / Zigbee primary channel mask use in the example /
//#define ESP_ZB_PRIMARY_CHANNEL_MASK (1l << 18) / Zigbee primary channel mask use in the example /
/***** Zigbee functions **/ bool ZBM_Set_Zigbee_Attr_String(void attr_data, uint16_t attr_len, char s) { bool res = false;
if(NULL == attr_data || 0 == attr_len || NULL == s) { return res; }
((uint8_t*)attr_data)[0] = (uint8_t)attr_len;
if(0 < attr_len) { memcpy(&((uint8_t*)attr_data)[1], s, attr_len); }
res = true; return res;
}
// done converting to Arduino code static void esp_zb_buttons_handler(switch_func_pair_t *button_func_pair) {
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) { char msg= "This message has a length of 230: COMPUTER, LAMP, FOCUS, HOTMIGA, KEYBOARD, GUITAR, BOTTLE, GLASS, CELL PHONE, DESK, WATER, BATHROOM, FIRE, TRUCK, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN "; char value; value = (char )malloc(sizeof(uint8_t) (strlen(msg) + 1)); memset( value,0,sizeof(uint8_t) * (strlen(msg) + 1)); ZBM_Set_Zigbee_Attr_String(value,strlen(msg),msg); // ID changing the ID esp_zb_zcl_attribute_t attrs = {2, {ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, sizeof(value), value}};
// define the command first then assign values to its subfields values. esp_zb_zcl_write_attr_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = HA_ONOFF_SWITCH_ENDPOINT; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.clusterID = CUSTOM_CLUSTER_ID; cmd_req.attr_number = 1; cmd_req.attr_field = &attrs; esp_zb_zcl_write_attr_cmd_req(&cmd_req); log_i("Send 'write large data(%d)' command", sizeof(value)); } }
// converted to Arduino static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) { ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask)); }
// converted to Arduino from the original code static void bind_cb(esp_zb_zdp_status_t zdo_status, void user_ctx) { if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_i("Bound successfully!"); if (user_ctx) { light_bulb_device_params_t light = (light_bulb_device_params_t *)user_ctx; log_i("The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint); free(light); } neopixelWrite(LED_PIN,0,255,0); //Green LED to indecate connection RGB } }
// converted from the original code static void user_find_cb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
if (zdo_status == ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND) { / Bound the light device to its own bind table. / log_i("ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND");
}
if (zdo_status == ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND) { / Bound the light device to its own bind table. / log_i("ESP_ZB_ZDP_STATUS_DEVICE_NOT_FOUND");
} if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { / Bound the light device to its own bind table. / log_i("Found light"); esp_zb_zdo_bind_req_param_t bind_req; // allocate memory with required size light_bulb_device_params_t light = (light_bulb_device_params_t )malloc(sizeof(light_bulb_device_params_t)); light->endpoint = endpoint; light->short_addr = addr; esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr); esp_zb_get_long_address(bind_req.src_address); bind_req.src_endp = HA_ONOFF_SWITCH_ENDPOINT; bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t)); bind_req.dst_endp = endpoint; bind_req.req_dst_addr = esp_zb_get_short_address(); log_i("Try to bind On/Off"); log_i("Found light, binding..."); esp_zb_zdo_device_bind_req(&bind_req, bind_cb, NULL); bind_req.cluster_id = CUSTOM_CLUSTER_ID; esp_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light); }
}
// done converting to IDF code to arduino void esp_zb_app_signal_handler(esp_zb_app_signal_t signal_struct) { uint32_t p_sg_p = signal_struct->p_app_signal; esp_err_t err_status = signal_struct->esp_err_status; esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)p_sg_p; esp_zb_zdo_signal_device_annce_params_t dev_annce_params = NULL; // keep it for trubleshotting it might be needed this part is from the original code //esp_zb_app_signal_type_t sig_type = *p_sg_p;
switch (sig_type) { case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: log_i("Zigbee stack initialized"); 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: if (err_status == ESP_OK) { if (esp_zb_bdb_is_factory_new()) { log_i("Start network formation"); esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION); } else { log_i("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status)); } break; case ESP_ZB_BDB_SIGNAL_FORMATION: if (err_status == ESP_OK) { esp_zb_ieee_addr_t extended_pan_id; esp_zb_get_extended_pan_id(extended_pan_id); log_i( "Formed network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 0x%04hx)", extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3], extended_pan_id[2], extended_pan_id[1], extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address() ); neopixelWrite(LED_PIN, 0, 255, 0); // Green --> esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); } else { log_i("Restart network formation (status: %s)", esp_err_to_name(err_status)); esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_FORMATION, 1000); } break; case ESP_ZB_BDB_SIGNAL_STEERING: if (err_status == ESP_OK) { log_i("Network steering started"); neopixelWrite(LED_PIN, 255, 255, 255); // white --> } break; case ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE: dev_annce_params = (esp_zb_zdo_signal_device_annce_params_t )esp_zb_app_signal_get_params(p_sg_p); log_i("New device commissioned or rejoined (short: 0x%04hx)", dev_annce_params->device_short_addr); esp_zb_zdo_match_desc_req_param_t cmd_req; cmd_req.dst_nwk_addr = dev_annce_params->device_short_addr; cmd_req.addr_of_interest = dev_annce_params->device_short_addr; esp_zb_zdo_find_on_off_light(&cmd_req, user_find_cb, NULL); break; case ESP_ZB_NLME_STATUS_INDICATION: printf("%s, status: 0x%x\n", esp_zb_zdo_signal_to_string(sig_type), (uint8_t *)esp_zb_app_signal_get_params(p_sg_p)); neopixelWrite(LED_PIN, 255, 0, 0); // white --> break; default: log_i("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break; } } }
// handle case from zb_action_handler: static esp_err_t zb_write_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message) { neopixelWrite(LED_PIN, 0, 0, 255); // Blue --> if (!message) { log_e("Empty message"); } if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) { log_e( "Received message: error status(%d)", message->info.status); } log_i("Write attribute successfully From ED: 0x%d", message->info.src_endpoint); vTaskDelay(80 / portTICK_PERIOD_MS); neopixelWrite(LED_PIN, 0, 255, 0); // green --> Rx'ed Ack return ESP_OK; }
static esp_err_t zb_custom_resp_handler(const esp_zb_zcl_custom_cluster_command_message_t *message){ if (!message) { log_e("Empty message"); } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); } // some logs are missing if needed check the original code log_i("Received %s message: endpoint(%d), cluster(0x%x), data size(%d)",message->info.dst_endpoint, message->info.cluster, message->data.size); log_i("response %s: request %s ", message->info.dst_endpoint, message->info.cluster);
log_i("Receive(%d) response: ", message->data.size); for (int i = 0; i < message->data.size; i++) { log_i("%02x, ", ((uint8_t )message->data.value + i)); } log_i("\n");
return ESP_OK; }
static esp_err_t zb_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message) { if (!message) { log_e("Empty message"); } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); } log_i( "Received %s message: endpoint(%d), cluster(0x%x)", message->info.command.direction); log_i("response %s: request %s ", message->info.dst_endpoint, message->info.cluster);
esp_zb_zcl_read_attr_resp_variable_t vars = message->variables; //Serial.println("size: %d\n", message->variables->attribute.data.size); log_i("size: %d\n", message->variables->attribute.data.size); while (vars) { for (int i = 0; i < vars->attribute.data.size; i++) { //Serial.println("0x%x ", (uint8_t )(vars->attribute.data.value + i)); log_i("0x%x ", (uint8_t *)(vars->attribute.data.value + i));
} return ESP_OK; }
// addition on the original code: static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void message) { esp_err_t ret = ESP_OK; switch (callback_id) { case ESP_ZB_CORE_CMD_WRITE_ATTR_RESP_CB_ID: zb_write_resp_handler((esp_zb_zcl_cmd_write_attr_resp_message_t )message); break; case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t )message); break; case ESP_ZB_CORE_CMD_CUSTOM_CLUSTER_RESP_CB_ID: ret = zb_custom_resp_handler((esp_zb_zcl_custom_cluster_command_message_t )message); break; default: log_w("Receive Zigbee action(0x%x) callback", callback_id); break; } return ret; }
static void esp_zb_task(void *pvParameters) {
/ initialize Zigbee stack / esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG(); esp_zb_init(&zb_nwk_cfg);
// esp_zb_ep_list_t ep_list = esp_zb_ep_list_create(); esp_zb_cluster_list_t cluster_list = esp_zb_zcl_cluster_list_create();
// cluster to turn ON/OFF esp_zb_on_off_cluster_cfg_t on_off_cfg; on_off_cfg.on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE; esp_zb_attribute_list_t *esp_zb_on_off_cluster = esp_zb_on_off_cluster_create(&on_off_cfg); esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// add custom attribute esp_zb_attribute_list_t *custom_attr = esp_zb_zcl_attr_list_create(CUSTOM_CLUSTER_ID); uint8_t custom_string[CUSTOM_STRING_MAX_SIZE] = "_hello_world"; custom_string[0] = CUSTOM_STRING_MAX_SIZE - 1;
esp_zb_custom_cluster_add_custom_attr(custom_attr, 0x02, ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, custom_string); esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// ####################### add Rx cluster to Rx custom attribute ###################################### // cluster to turn ON/OFF esp_zb_on_off_cluster_cfg_t on_off_cfg1; on_off_cfg.on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE; esp_zb_attribute_list_t *esp_zb_on_off_cluster1 = esp_zb_on_off_cluster_create(&on_off_cfg1);// SERVER esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_attribute_list_t *custom_attr1 = esp_zb_zcl_attr_list_create(CUSTOM_CLUSTER_ID1); uint8_t custom_string1[CUSTOM_STRING_MAX_SIZE] = "_hello_world"; custom_string[0] = CUSTOM_STRING_MAX_SIZE - 1;
esp_zb_custom_cluster_add_custom_attr(custom_attr1, 0x02, ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, custom_string1); esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr1, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
// device discription struc esp_zb_endpoint_config_t endpoint_config = { .endpoint = HA_ONOFF_SWITCH_ENDPOINT, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID, .app_device_version = 0, };
esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config); esp_zb_device_register(ep_list); esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK); esp_zb_core_action_handler_register(zb_action_handler); //Erase NVRAM before creating connection to new Coordinator | keeps binding table esp_zb_nvram_erase_at_start(true); //Comment out this line to erase NVRAM data if you are conneting to new Coordinator ESP_ERROR_CHECK(esp_zb_start(false)); esp_zb_main_loop_iteration(); }
/***** GPIO functions **/ static QueueHandle_t gpio_evt_queue = NULL;
static void IRAM_ATTR gpio_isr_handler(void arg) { xQueueSendFromISR(gpio_evt_queue, (switch_func_pair_t )arg, NULL); }
static void switch_gpios_intr_enabled(bool enabled) { for (int i = 0; i < PAIR_SIZE(button_func_pair); ++i) { if (enabled) { enableInterrupt((button_func_pair[i]).pin); } else { disableInterrupt((button_func_pair[i]).pin); } } } /***** Arduino functions **/ // DONE conversion void setup() { // might be needed for logging to print the Rx'ed msg //Serial.begin(115200);
// Init Zigbee 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));
// Init button switch for (int i = 0; i < PAIR_SIZE(button_func_pair); i++) { pinMode(button_func_pair[i].pin, INPUT_PULLUP); / create a queue to handle gpio event from isr / gpio_evt_queue = xQueueCreate(10, sizeof(switch_func_pair_t)); if (gpio_evt_queue == 0) { log_e("Queue was not created and must not be used"); while (1); } attachInterruptArg(button_func_pair[i].pin, gpio_isr_handler, (void *)(button_func_pair + i), FALLING); } // LED start off neopixelWrite(LED_PIN, 0, 0, 0); // white --> // Start Zigbee task xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL); }
void loop() { // Handle button switch in loop() uint8_t pin = 0; switch_func_pair_t button_func_pair; static switch_state_t switch_state = SWITCH_IDLE; bool evt_flag = false;
/ check if there is any queue received, if yes read out the button_func_pair / if (xQueueReceive(gpio_evt_queue, &button_func_pair, portMAX_DELAY)) { pin = button_func_pair.pin; switch_gpios_intr_enabled(false); evt_flag = true; } while (evt_flag) { bool value = digitalRead(pin); switch (switch_state) { case SWITCH_IDLE: switch_state = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE; break; case SWITCH_PRESS_DETECTED: switch_state = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED; break; case SWITCH_RELEASE_DETECTED: switch_state = SWITCH_IDLE; / callback to button_handler / (*esp_zb_buttons_handler)(&button_func_pair); break; default: break; } if (switch_state == SWITCH_IDLE) { switch_gpios_intr_enabled(true); evt_flag = false; break; } vTaskDelay(10 / portTICK_PERIOD_MS); } }`
End device code: `#ifndef ZIGBEE_MODE_ED
error "Zigbee end device mode is not selected in Tools->Zigbee mode"
endif
include "esp_zigbee_core.h"
include "freertos/FreeRTOS.h"
include "freertos/task.h"
include "ha/esp_zigbee_ha_standard.h"
define LED_PIN RGB_BUILTIN
define ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
/ Switch configuration /
define GPIO_INPUT_IO_TOGGLE_SWITCH GPIO_NUM_9
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, } switch_func_t;
typedef enum { SWITCH_IDLE, SWITCH_PRESS_ARMED, SWITCH_PRESS_DETECTED, SWITCH_PRESSED, SWITCH_RELEASE_DETECTED, } switch_state_t;
typedef struct { uint8_t pin; switch_func_t func; } switch_func_pair_t;
static switch_func_pair_t button_func_pair[] = {{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}};
/ Default Coordinator config / //RADIO_MODE_NATIVE, main differnce between IDF and Arduino IDE starting of the command.
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 = ED_AGING_TIMEOUT, \ .keep_alive = ED_KEEP_ALIVE, \ }, \ }, \ }
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, \ }
/ Zigbee configuration /
define INSTALLCODE_POLICY_ENABLE false / enable the install code policy for security /
define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN
define ED_KEEP_ALIVE 8000 / 3000 millisecond /
define HA_ESP_LIGHT_ENDPOINT 10 / esp light bulb device endpoint, used to process light controlling commands /
define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK //(1 << 18) / Zigbee primary channel mask use in the example /
/* Attribute values in ZCL string format
define MANUFACTURER_NAME \
"\x0B" \ "ESPRESSIF"
define MODEL_IDENTIFIER "\x09" CONFIG_IDF_TARGET
define CUSTOM_CLUSTER_ID 0xFC00
define CUSTOM_CLUSTER_ID1 0xFC01
define CUSTOM_STRING_MAX_SIZE 127
// bool ZBM_Set_Zigbee_Attr_String(void attr_data, uint16_t attr_len, char s) { bool res = false;
if(NULL == attr_data || 0 == attr_len || NULL == s) { return res; }
((uint8_t*)attr_data)[0] = (uint8_t)attr_len;
if(0 < attr_len) { memcpy(&((uint8_t*)attr_data)[1], s, attr_len); }
res = true; return res;
}
// done converting to Arduino code static void esp_zb_buttons_handler(switch_func_pair_t *button_func_pair) {
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) { char msg= " This message has a length of 230: COMPUTER, LAMP, FOCUS, HOTMIGA, KEYBOARD, GUITAR, BOTTLE, GLASS, CELL PHONE, DESK, WATER, BATHROOM, FIRE, TRUCK, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN "; char value; value = (char )malloc(sizeof(uint8_t) (strlen(msg) + 1)); memset( value,0,sizeof(uint8_t) * (strlen(msg) + 1)); ZBM_Set_Zigbee_Attr_String(value,strlen(msg),msg); // ID
esp_zb_zcl_attribute_t attrs = {1, {ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, sizeof(value), value}};
// define the command first then assign values to its subfields values. esp_zb_zcl_write_attr_cmd_t cmd_req; cmd_req.zcl_basic_cmd.src_endpoint = HA_ESP_LIGHT_ENDPOINT; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.clusterID = CUSTOM_CLUSTER_ID1; cmd_req.attr_number = 1; cmd_req.attr_field = &attrs;
} }
// DONE conversion to Arduino void esp_zb_app_signal_handler(esp_zb_app_signal_t signal_struct) { uint32_t p_sg_p = signal_struct->p_app_signal; esp_err_t err_status = signal_struct->esp_err_status; esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p;
switch (sig_type) { case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: log_i("Zigbee stack initialized"); 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: if (err_status == ESP_OK) { log_i("Start network steering"); esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); neopixelWrite(LED_PIN, 255, 255, 255); // white -->
case ESP_ZB_BDB_SIGNAL_STEERING: if (err_status == ESP_OK) { esp_zb_ieee_addr_t extended_pan_id; esp_zb_get_extended_pan_id(extended_pan_id); log_i("Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d)", extended_pan_id[0], extended_pan_id[1], extended_pan_id[2], extended_pan_id[3], extended_pan_id[4], extended_pan_id[5], extended_pan_id[6], extended_pan_id[7], esp_zb_get_pan_id(), esp_zb_get_current_channel()); neopixelWrite(LED_PIN, 0, 255, 0); // Green -->
default: log_i( "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break; } }
/***** Define functions **/ static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) { ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask)); }
// DONE conversion static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) { esp_err_t ret = ESP_OK; bool light_state = 0;
if (!message) { log_e("Empty message"); } if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); } neopixelWrite(LED_PIN, 0, 0, 0); // red --> if (message->info.dst_endpoint == HA_ESP_LIGHT_ENDPOINT) { if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) { if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { light_state = message->attribute.data.value ? (bool )message->attribute.data.value : light_state; log_i("Light sets to %s", light_state ? "On" : "Off"); neopixelWrite(LED_PIN, 0, 255, 0); } } else if (message->info.cluster == CUSTOM_CLUSTER_ID) { // iterate over every single byte /for (int i = 0; i < message->attribute.data.size; i++) { printf("0x%2x", (uint8_t )(message->attribute.data.value + i)); }/ //printf("\n"); log_i("%s",(char )message->attribute.data.value); //printf("\n"); //log_e("\n %s",(char )message->attribute.data.value); } } vTaskDelay(80 / portTICK_PERIOD_MS); neopixelWrite(LED_PIN, 0, 255, 0); // green --> return ret; }
static esp_err_t zb_custom_req_handler(const esp_zb_zcl_custom_cluster_command_message_t *message){ if (!message) { log_e("Empty message"); return ESP_FAIL; // Return immediately to avoid further processing } else if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { log_e("Received message: error status(%d)", message->info.status); return ESP_ERR_INVALID_ARG; // Return immediately to avoid further processing } // Declare the struct and initialize with default values esp_zb_zcl_custom_cluster_cmd_resp_t req = {};
// Set individual fields // Arduino does not accept decleration with assignment of value at the same time req.zcl_basic_cmd.dst_addr_u.addr_short = message->info.src_address.u.short_addr; req.zcl_basic_cmd.src_endpoint = message->info.dst_endpoint; req.zcl_basic_cmd.dst_endpoint = message->info.src_endpoint; req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; req.profile_id = ESP_ZB_AF_HA_PROFILE_ID; req.cluster_id = CUSTOM_CLUSTER_ID; req.custom_cmd_id = message->info.command.id; //.direction = message->info.command.direction; //.data = {0, 0, 0};
// #msg
//Receive(6) request: 01, 00, 66, 55, 66, 55, printf("Receive(%d) request: ", message->data.size); for (int i = 0; i < message->data.size; i++) { //printf("%02x, ", ((uint8_t )message->data.value + i)); printf("%s",(char )message->data.value); } printf("\n"); //printf("%s",(char )message->attribute.data.value); if (message->info.command.id == 0x01) { esp_zb_zcl_set_attribute_val(HA_ESP_LIGHT_ENDPOINT, CUSTOM_CLUSTER_ID, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, 0x03, message->data.value, false); req.data.type = ESP_ZB_ZCL_ATTR_TYPE_32BIT_ARRAY; req.data.value = message->data.value; esp_zb_zcl_custom_cluster_cmd_resp(&req); }
return ESP_OK; }
// DONE CONVERSION to Arduino static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void message) { esp_err_t ret = ESP_OK; switch (callback_id) { case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t )message); printf("#1"); break; case ESP_ZB_CORE_CMD_CUSTOM_CLUSTER_REQ_CB_ID: ret = zb_custom_req_handler((esp_zb_zcl_custom_cluster_command_message_t *)message); printf("#2"); break; default: log_w("Receive Zigbee action(0x%x) callback", callback_id); break; } return ret; }
// DONE conversion to Arduino signed int esp_zb_zcl_cluster_check_value_handler(uint16_t attr_id, uint8_t endpoint, uint8_t *value) { printf("check value endpoint:%d, attr: %d\n", endpoint, attr_id); return 0; }
// DONE conversion to Arduino void esp_zb_zcl_cluster_write_attr_handler(uint8_t endpoint, uint16_t attr_id, uint8_t *new_value, uint16_t manuf_code) { printf("write attr endpoint:%d, attr: %d\n", endpoint, attr_id); }
// DONE conversion static void esp_zb_task(void *pvParameters) {
/ initialize Zigbee stack / esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG(); esp_zb_init(&zb_nwk_cfg);
// esp_zb_ep_list_t ep_list = esp_zb_ep_list_create(); esp_zb_cluster_list_t cluster_list = esp_zb_zcl_cluster_list_create();
// cluster to turn ON/OFF esp_zb_on_off_cluster_cfg_t on_off_cfg; on_off_cfg.on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE; esp_zb_attribute_list_t *esp_zb_on_off_cluster = esp_zb_on_off_cluster_create(&on_off_cfg);// SERVER esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
// add custom attribute esp_zb_attribute_list_t *custom_attr = esp_zb_zcl_attr_list_create(CUSTOM_CLUSTER_ID); uint8_t custom_string[CUSTOM_STRING_MAX_SIZE] = "_ESPRESSIF"; custom_string[0] = CUSTOM_STRING_MAX_SIZE - 1;
// esp_zb_custom_cluster_add_custom_attr(custom_attr, 0x02, ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, custom_string); esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
// ####################### add Tx cluster to Rx custom attribute ###################################### // cluster to turn ON/OFF esp_zb_on_off_cluster_cfg_t on_off_cfg1; on_off_cfg.on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE; esp_zb_attribute_list_t *esp_zb_on_off_cluster1 = esp_zb_on_off_cluster_create(&on_off_cfg1); esp_zb_cluster_list_add_on_off_cluster(cluster_list, esp_zb_on_off_cluster1, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
esp_zb_attribute_list_t *custom_attr1 = esp_zb_zcl_attr_list_create(CUSTOM_CLUSTER_ID1); uint8_t custom_string1[CUSTOM_STRING_MAX_SIZE] = "_ESPRESSIF"; custom_string[0] = CUSTOM_STRING_MAX_SIZE - 1;
// esp_zb_custom_cluster_add_custom_attr(custom_attr1, 0x02, ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING, ESP_ZB_ZCL_ATTR_ACCESS_READ_WRITE, custom_string1); esp_zb_cluster_list_add_custom_cluster(cluster_list, custom_attr1, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
// device discription struc esp_zb_endpoint_config_t endpoint_config = { .endpoint = HA_ESP_LIGHT_ENDPOINT, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID, .app_device_version = 0, };
// esp_zb_ep_list_add_ep(ep_list, cluster_list, endpoint_config); esp_zb_device_register(ep_list);
/esp_zb_zcl_custom_cluster_handlers_t obj = {.cluster_id = CUSTOM_CLUSTER_ID, .cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, .check_value_cb = esp_zb_zcl_cluster_check_value_handler, .write_attr_cb = esp_zb_zcl_cluster_write_attr_handler}; esp_zb_zcl_custom_cluster_handlers_update(obj);/ //** Added by me //Erase NVRAM before creating connection to new Coordinator | keeps binding table esp_zb_nvram_erase_at_start(true); //Comment out this line to erase NVRAM data if you are conneting to new Coordinator //**** esp_zb_core_action_handler_register(zb_action_handler); esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK); ESP_ERROR_CHECK(esp_zb_start(false)); esp_zb_main_loop_iteration(); }
/***** GPIO functions **/ static QueueHandle_t gpio_evt_queue = NULL;
static void IRAM_ATTR gpio_isr_handler(void arg) { xQueueSendFromISR(gpio_evt_queue, (switch_func_pair_t )arg, NULL); }
static void switch_gpios_intr_enabled(bool enabled) { for (int i = 0; i < PAIR_SIZE(button_func_pair); ++i) { if (enabled) { enableInterrupt((button_func_pair[i]).pin); } else { disableInterrupt((button_func_pair[i]).pin); } } } /***** Arduino functions **/ // DONE converting // DONE conversion void setup() { // might be needed for logging to print the Rx'ed msg //Serial.begin(115200);
// Init Zigbee 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));
// Init button switch for (int i = 0; i < PAIR_SIZE(button_func_pair); i++) { pinMode(button_func_pair[i].pin, INPUT_PULLUP); / create a queue to handle gpio event from isr / gpio_evt_queue = xQueueCreate(10, sizeof(switch_func_pair_t)); if (gpio_evt_queue == 0) { log_e("Queue was not created and must not be used"); while (1); } attachInterruptArg(button_func_pair[i].pin, gpio_isr_handler, (void *)(button_func_pair + i), FALLING); } // LED start off neopixelWrite(LED_PIN, 0, 0, 0); // white --> // Start Zigbee task xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL); }
void loop() { // Handle button switch in loop() uint8_t pin = 0; switch_func_pair_t button_func_pair; static switch_state_t switch_state = SWITCH_IDLE; bool evt_flag = false;
/ check if there is any queue received, if yes read out the button_func_pair / if (xQueueReceive(gpio_evt_queue, &button_func_pair, portMAX_DELAY)) { pin = button_func_pair.pin; switch_gpios_intr_enabled(false); evt_flag = true; } while (evt_flag) { bool value = digitalRead(pin); switch (switch_state) { case SWITCH_IDLE: switch_state = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE; break; case SWITCH_PRESS_DETECTED: switch_state = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED; break; case SWITCH_RELEASE_DETECTED: switch_state = SWITCH_IDLE; / callback to button_handler / (*esp_zb_buttons_handler)(&button_func_pair); break; default: break; } if (switch_state == SWITCH_IDLE) { switch_gpios_intr_enabled(true); evt_flag = false; break; } vTaskDelay(10 / portTICK_PERIOD_MS); } }`