ESP32
[esp32][zigbee][HA_on_off_switch][1] 코드 분석
하니_즐거운하루
2024. 7. 1. 12:20
: ESP32C6을 이용해 zigbee light/switch 예제를 분석해 보았습니다.
▶ 프로젝트위치
▶ Zigbee 관련 초기화 코드 정리
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_NATIVE, \
}
#define ESP_ZB_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
}
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_DEFAULT_ON_OFF_SWITCH_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, \
}, \
}
#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, \
}, \
}
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_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_switch_ep = esp_zb_on_off_switch_ep_create(HA_ONOFF_SWITCH_ENDPOINT, &switch_cfg);
esp_zb_device_register(esp_zb_on_off_switch_ep);
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() 부팅후 호출 시컨스
1> ESP_ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY > Nothing to do.
2> ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP > Zigbee 스택 초기화
--> esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
3> ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT
--> if (esp_zb_bdb_is_factory_new()) {
// factory 상태라면 네트워크 포메이션 시작
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
}else{
// Nothing to do.
}
4> ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS > 네트워크의 open/close 상태 체킹.
W (776) ESP_ZB_ON_OFF_SWITCH: Network(0x4e1c) closed, devices joining not allowed.
5> ESP_ZB_BDB_SIGNAL_FORMATION > commissioning 모드 일 경우
--> esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
I (776) ESP_ZB_ON_OFF_SWITCH: Formed network successfully
(Extended PAN ID: 40:4c:ca:ff:fe:43:b1:d0, PAN ID: 0x4e1c, Channel:13, Short Address: 0x0000)
6> ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS > 네트워크 상태 체킹
I (1366) ESP_ZB_ON_OFF_SWITCH: Network(0x4e1c) is open for 180 seconds
--> 3분동안 네트워크가 Open 상태라는 로그 출력
7> ESP_ZB_BDB_SIGNAL_STEERING > 네트워크 스티어링 시작됨.
I (1366) ESP_ZB_ON_OFF_SWITCH: Network steering started
8> ESP_ZB_NWK_SIGNAL_DEVICE_ASSOCIATED > Nothing to do.
9> ESP_ZB_ZDO_SIGNAL_DEVICE_UPDATE > Nothing to do.
10> ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE
--> esp_zb_zdo_find_on_off_light(&cmd_req, user_find_cb, NULL);
--> user_find_cb(...);
--> esp_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light);
--> bind_cb(...);
I (29026) ESP_ZB_ON_OFF_SWITCH: Found light
I (29026) ESP_ZB_ON_OFF_SWITCH: Try to bind On/Off
I (29036) ESP_ZB_ON_OFF_SWITCH: Bound successfully!
I (29036) ESP_ZB_ON_OFF_SWITCH: The light originating from address(0xd889) on endpoint(10)
11> ESP_ZB_ZDO_SIGNAL_DEVICE_AUTHORIZED > Nothing to do.
I (29106) ESP_ZB_ON_OFF_SWITCH: Network(0x4e1c) is open for 180 seconds
I (35906) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
I (37976) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
W (211606) ESP_ZB_ON_OFF_SWITCH: Network(0x4e1c) closed, devices joining not allowed.
▶ esp_zb_app_signal_handler () > ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE 중 호출 함수
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);
}
}
}
▶ button 누를경우 처리 코드 분석
> 버튼 누를경우 호출되는 함수
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);
}
}
> ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE > esp_zb_zdo_device_bind_req() 호출시 넣어준 cluster_id 를 light 장치에서 비교함.
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_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_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light);
}
}
>> light 프로젝트 안 호출 코드
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);
break;
~~ 중략 ~~
}
return ret;
}
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->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;
ESP_LOGI(TAG, "Light sets to %s", light_state ? "On" : "Off");
light_driver_set_power(light_state);
}
}
}
return ret;
}
< 기타>
▶ erase-flash 후 monitor 로그
> 커미션닝전에 리셋 버튼 누르면 오작동 하고 다시 erase-flash 부터 진행해야함.
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:56:19
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=42090020 size=115e8h ( 71144) map
I (122) esp_image: segment 1: paddr=00021610 vaddr=40800000 size=06a08h ( 27144) load
I (129) esp_image: segment 2: paddr=00028020 vaddr=42000020 size=882c0h (557760) map
I (245) esp_image: segment 3: paddr=000b02e8 vaddr=40806a08 size=07b90h ( 31632) load
I (253) esp_image: segment 4: paddr=000b7e80 vaddr=4080e5a0 size=01c4ch ( 7244) load
I (259) boot: Loaded app from partition at offset 0x10000
I (260) boot: Disabling RNG early entropy source...
I (274) cpu_start: Unicore app
W (283) clk: esp_perip_clk_init() has not been implemented yet
I (290) cpu_start: Pro cpu start user code
I (290) cpu_start: cpu freq: 160000000 Hz
I (290) cpu_start: Application information:
I (293) cpu_start: Project name: light_switch
I (298) cpu_start: App version: 1
I (302) cpu_start: Compile time: Jun 18 2024 19:56:14
I (309) cpu_start: ELF file SHA256: 59c133dba...
I (314) cpu_start: ESP-IDF: v5.2.1-dirty
I (319) cpu_start: Min chip rev: v0.0
I (324) cpu_start: Max chip rev: v0.99
I (329) cpu_start: Chip rev: v0.0
I (334) heap_init: Initializing. RAM available for dynamic allocation:
I (341) heap_init: At 40819DF0 len 00062820 (394 KiB): RAM
I (347) heap_init: At 4087C610 len 00002F54 (11 KiB): RAM
I (353) heap_init: At 50000000 len 00003FE8 (15 KiB): RTCRAM
I (360) spi_flash: detected chip: generic
I (364) spi_flash: flash io: dio
W (368) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k).
Using the size in the binary image header.
I (381) sleep: Configure to isolate all GPIO pins in sleep state
I (388) sleep: Enable automatic switching of GPIO sleep configuration
I (395) coexist: coex firmware version: 77cd7f8
I (400) coexist: coexist rom version 5b8dcfa
I (406) main_task: Started on CPU0
I (406) main_task: Calling app_main()
I (416) gpio: GPIO[9]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (416) phy_init: phy_version 250,e14681b,Jan 24 2024,17:43:11
W (426) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (476) phy: libbtbb version: 939f79c, Jan 24 2024, 17:43:26
I (506) main_task: Returned from app_main()
I (606) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Config Ready (0x17), status: ESP_FAIL
I (606) ESP_ZB_ON_OFF_SWITCH: Zigbee stack initialized
I (606) ESP_ZB_ON_OFF_SWITCH: Device started up in factory-reset mode
I (616) ESP_ZB_ON_OFF_SWITCH: Start network formation
W (776) ESP_ZB_ON_OFF_SWITCH: Network(0xb1cf) closed, devices joining not allowed.
I (776) ESP_ZB_ON_OFF_SWITCH: Formed network successfully
(Extended PAN ID: 40:4c:ca:ff:fe:43:b1:d0, PAN ID: 0xb1cf, Channel:13, Short Address: 0x0000)
I (1366) ESP_ZB_ON_OFF_SWITCH: Network(0xb1cf) is open for 180 seconds
I (1366) ESP_ZB_ON_OFF_SWITCH: Network steering started
--> 이상태에서 switch 장치리셋을 하면 Newtwork steering 을 시작안하니 주의하세요.
>> light 장치도 network steering 에 넣으면 아래처럼 바인딩이 이루어 집니다.
I (28466) ESP_ZB_ON_OFF_SWITCH: ZDO signal: NWK Device Associated (0x12), status: ESP_OK
I (28986) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Device Update (0x30), status: ESP_OK
I (29006) ESP_ZB_ON_OFF_SWITCH: New device commissioned or rejoined (short: 0xd889)
I (29026) ESP_ZB_ON_OFF_SWITCH: Found light
I (29026) ESP_ZB_ON_OFF_SWITCH: Try to bind On/Off
I (29036) ESP_ZB_ON_OFF_SWITCH: Bound successfully!
I (29036) ESP_ZB_ON_OFF_SWITCH: The light originating from address(0xd889) on endpoint(10)
I (29086) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Device Authorized (0x2f), status: ESP_OK
I (29106) ESP_ZB_ON_OFF_SWITCH: Network(0x4e1c) is open for 180 seconds
I (35906) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
▶ light / switch 연동 영상
반응형