본문으로 바로가기

[esp32][zigbee][HA_on_off_light][1] 코드 분석

category ESP32 2024. 7. 1. 12:20

: 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

 

 

반응형