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 연동 영상

 

 

반응형