: ESP32 Zigbee HA_on_off_light 프로젝트와 HA_on_off_switch 프로젝트를 분석해 보았습니다.
Zigbee 처음이라 틀린부분 댓글로 알려주세요.
▶ 초기화 코드
void app_main(void)
{
esp_zb_platform_config_t config = {
.radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};
esp_zb_platform_config(&config);
xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}
#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_ON_OFF_LIGHT_CONFIG() \
{ \
.basic_cfg = \
{ \
.zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \
.power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \
}, \
.identify_cfg = \
{ \
.identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \
}, \
.groups_cfg = \
{ \
.groups_name_support_id = ESP_ZB_ZCL_GROUPS_NAME_SUPPORT_DEFAULT_VALUE, \
}, \
.scenes_cfg = \
{ \
.scenes_count = ESP_ZB_ZCL_SCENES_SCENE_COUNT_DEFAULT_VALUE, \
.current_scene = ESP_ZB_ZCL_SCENES_CURRENT_SCENE_DEFAULT_VALUE, \
.current_group = ESP_ZB_ZCL_SCENES_CURRENT_GROUP_DEFAULT_VALUE, \
.scene_valid = ESP_ZB_ZCL_SCENES_SCENE_VALID_DEFAULT_VALUE, \
.name_support = ESP_ZB_ZCL_SCENES_NAME_SUPPORT_DEFAULT_VALUE, \
}, \
.on_off_cfg = \
{ \
.on_off = ESP_ZB_ZCL_ON_OFF_ON_OFF_DEFAULT_VALUE, \
}, \
}
static void esp_zb_task(void *pvParameters)
{
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
esp_zb_init(&zb_nwk_cfg);
esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_light_ep = esp_zb_on_off_light_ep_create(HA_ESP_LIGHT_ENDPOINT, &light_cfg);
esp_zb_device_register(esp_zb_on_off_light_ep);
esp_zb_core_action_handler_register(zb_action_handler);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
esp_zb_start(false);
esp_zb_main_loop_iteration();
}
▶ esp_zb_app_signal_handler() 호출 시컨스
: 이함수는 특별히 등록없이도 호출이 됩니다. 헤더파일의 주석 내용 첨부.
/**
* @brief Zigbee stack application signal handler.
* @anchor esp_zb_app_signal_handler
*
* @param[in] signal_s pointer of Zigbee zdo app signal struct @ref esp_zb_app_signal_s.
* @note After esp_zb_start, user shall based on the corresponding signal type refer to esp_zdo_app_signal_type from struct pointer signal_s to do certain actions.
* User could also use refer to esp_zb_bdb_start_top_level_commissioning to change BDB mode.
* @warning This function has to be defined by user in each example.
*/
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_s);
→ 호출되는 case 문 시컨스
1> ESP_ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY
>> Nothing to do.
2> ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
>> 로그 분석
I (468) ESP_ZB_ON_OFF_LIGHT: Zigbee stack initialized
>> switch 장치가 꺼져 있으면 아래처럼 fail됨.
W (2948) ESP_ZB_ON_OFF_LIGHT: Failed to initialize Zigbee stack (status: ESP_FAIL)
>> switch 장치 켜진후에 리셋이 필요함.
3> ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT
if (esp_zb_bdb_is_factory_new()) {
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
Nothing to do.
}
4> ESP_ZB_BDB_SIGNAL_STEERING
>> commissioning 도중에만 나옴.
I (732828) ESP_ZB_ON_OFF_LIGHT: Network steering was not successful (status: ESP_FAIL)
>> switch 장치에서 commission 되면 다음처럼 네트워크 연결 메시지가 출력됩니다.
I (791468) ESP_ZB_ON_OFF_LIGHT: Joined network successfully
(Extended PAN ID: 40:4c:ca:ff:fe:43:b1:d0, PAN ID: 0xba4b, Channel:13, Short Address: 0xefa0)
▶ switch 장치 BOOT 버튼 누를때와 LED 토클되는 부분 관련 코드
→ HA_on_off_light 프로젝트
static void esp_zb_task(void *pvParameters)
{
~~ 중략 ~~
esp_zb_core_action_handler_register(zb_action_handler);
~~ 중략 ~~
}
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message)
{
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);
break;
~~ 중략 ~~
}
~~ 중략 ~~
}
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;
ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, "Empty message");
ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG, "Received message: error status(%d)",
message->info.status);
ESP_LOGI(TAG, "Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)", message->info.dst_endpoint, message->info.cluster,
message->attribute.id, message->attribute.data.size);
if (message->info.dst_endpoint == HA_ESP_LIGHT_ENDPOINT) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) { <-- 요기 cluster ID
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;
ESP_LOGI(TAG, "Light sets to %s", light_state ? "On" : "Off");
light_driver_set_power(light_state);
}
}
}
return ret;
}
▶HA_on_off_switch 프로젝트 > light 장치 바인딩 관련 코드
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
esp_zb_app_signal_type_t sig_type = *p_sg_p;
esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
switch (sig_type) {
~~ 중략 ~~
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);
ESP_LOGI(TAG, "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;
~~ 중략 ~~
}
}
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_SUCCESS) {
ESP_LOGI(TAG, "Found light");
esp_zb_zdo_bind_req_param_t bind_req;
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();
ESP_LOGI(TAG, "Try to bind On/Off");
esp_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light);
}
}
static void bind_cb(esp_zb_zdp_status_t zdo_status, void *user_ctx)
{
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Bound successfully!");
if (user_ctx) {
light_bulb_device_params_t *light = (light_bulb_device_params_t *)user_ctx;
ESP_LOGI(TAG, "The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
free(light);
}
}
}
▶ switch 장치 버튼 눌렀을때 처리 코드
> 아래링크에 좀더 자세히 적음.
void app_main(void)
{
~~ 중략 ~~
switch_driver_init(button_func_pair, PAIR_SIZE(button_func_pair), esp_zb_buttons_handler);
~~ 중략 ~~
}
static switch_func_pair_t button_func_pair[] = {
{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}
};
static void esp_zb_buttons_handler(switch_func_pair_t *button_func_pair)
{
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) {
/* implemented light switch toggle functionality */
esp_zb_zcl_on_off_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.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID;
ESP_EARLY_LOGI(TAG, "Send 'on_off toggle' command");
esp_zb_zcl_on_off_cmd_req(&cmd_req);
}
}
<기타>
▶ erase-flash 사용해 NVRAM 지운후 monitor 로그
더보기
load:0x40875720,len:0x1804
load:0x4086c410,len:0xe58
load:0x4086e610,len:0x2e24
entry 0x4086c41a
I (23) boot: ESP-IDF v5.2.1-dirty 2nd stage bootloader
I (24) boot: compile time Jun 18 2024 19:36:49
I (24) boot: chip revision: v0.0
I (27) boot.esp32c6: SPI Speed : 80MHz
I (32) boot.esp32c6: SPI Mode : DIO
I (36) boot.esp32c6: SPI Flash Size : 2MB
I (41) boot: Enabling RNG early entropy source...
I (47) boot: Partition Table:
I (50) boot: ## Label Usage Type ST Offset Length
I (57) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (65) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (72) boot: 2 factory factory app 00 00 00010000 000e1000
I (80) boot: 3 zb_storage Unknown data 01 81 000f1000 00004000
I (87) boot: 4 zb_fct Unknown data 01 81 000f5000 00000400
I (95) boot: End of partition table
I (99) esp_image: segment 0: paddr=00010020 vaddr=42068020 size=11830h ( 71728) map
I (122) esp_image: segment 1: paddr=00021858 vaddr=40800000 size=067c0h ( 26560) load
I (129) esp_image: segment 2: paddr=00028020 vaddr=42000020 size=66878h (419960) map
I (217) esp_image: segment 3: paddr=0008e8a0 vaddr=408067c0 size=087b8h ( 34744) load
I (226) esp_image: segment 4: paddr=00097060 vaddr=4080ef80 size=01b34h ( 6964) load
I (232) boot: Loaded app from partition at offset 0x10000
I (232) boot: Disabling RNG early entropy source...
I (246) cpu_start: Unicore app
W (255) clk: esp_perip_clk_init() has not been implemented yet
I (261) cpu_start: Pro cpu start user code
I (262) cpu_start: cpu freq: 160000000 Hz
I (262) cpu_start: Application information:
I (264) cpu_start: Project name: light_bulb
I (270) cpu_start: App version: ccac3fb-dirty
I (275) cpu_start: Compile time: Jun 28 2024 12:25:09
I (281) cpu_start: ELF file SHA256: d763a01b5...
I (287) cpu_start: ESP-IDF: v5.2.1-dirty
I (292) cpu_start: Min chip rev: v0.0
I (297) cpu_start: Max chip rev: v0.99
I (302) cpu_start: Chip rev: v0.0
I (306) heap_init: Initializing. RAM available for dynamic allocation:
I (313) heap_init: At 40819840 len 00062DD0 (395 KiB): RAM
I (320) heap_init: At 4087C610 len 00002F54 (11 KiB): RAM
I (326) heap_init: At 50000000 len 00003FE8 (15 KiB): RTCRAM
I (333) spi_flash: detected chip: generic
I (337) spi_flash: flash io: dio
W (341) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (354) sleep: Configure to isolate all GPIO pins in sleep state
I (361) sleep: Enable automatic switching of GPIO sleep configuration
I (368) coexist: coex firmware version: 77cd7f8
I (373) coexist: coexist rom version 5b8dcfa
I (378) main_task: Started on CPU0
I (378) main_task: Calling app_main()
I (388) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (388) phy_init: phy_version 250,e14681b,Jan 24 2024,17:43:11
W (398) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (448) phy: libbtbb version: 939f79c, Jan 24 2024, 17:43:26
I (478) main_task: Returned from app_main()
I (618) ESP_ZB_ON_OFF_LIGHT: ZDO signal: ZDO Config Ready (0x17), status: ESP_FAIL
I (618) ESP_ZB_ON_OFF_LIGHT: Zigbee stack initialized
I (618) ESP_ZB_ON_OFF_LIGHT: Device started up in factory-reset mode
I (628) ESP_ZB_ON_OFF_LIGHT: Start network steering
I (3008) ESP_ZB_ON_OFF_LIGHT: Network steering was not successful (status: ESP_FAIL)
I (6438) ESP_ZB_ON_OFF_LIGHT: Network steering was not successful (status: ESP_FAIL)
~~ 중략 ~~
I (787408) ESP_ZB_ON_OFF_LIGHT: Network steering was not successful (status: ESP_FAIL)
>> switch 장치와 커미션 성공시 로그
I (791468) ESP_ZB_ON_OFF_LIGHT: Joined network successfully (Extended PAN ID: 40:4c:ca:ff:fe:43:b1:d0, PAN ID: 0xba4b, Channel:13, Short Address: 0xefa0)
▶ HA_on_off_switch 분석 참고 링크
https://leevisual.tistory.com/455
반응형
'ESP32' 카테고리의 다른 글
[esp-matter] [generic_switch][2] matter light 장치 연동하기 (0) | 2024.07.05 |
---|---|
[esp32][zigbee][HA_on_off_switch][1] 코드 분석 (0) | 2024.07.01 |
[esp-idf][BLE][hid] multi connection 사용시 수정해야 할 부분 (0) | 2024.04.05 |
[esp-idf][ble_hid_device_demo][7] custom bootloader 추가하기 (0) | 2024.03.18 |
[esp32][esp-idf] app_main() 호출코드 따라가기 (0) | 2024.03.05 |