ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ESP32 IDF HTTP OTA
    DEVICE 2023. 11. 9. 14:02

    OTA 펌웨어 업데이트 기능 구현을 위한 과정을 정리한다.

     

    1.파티션 정의

    파티션에 대한 예시테이블은 IDF폴더 안에 파티션 관련 예시파일이 몇 개 있었다.

    4M 기준으로  수정은 다음과 같이 하였으며, 이를 컴파일할 폴더에 복사해놨다.

     

     

    nvs(2)는 NVS(Non-Volatile Storage) API 용입니다 .
    NVS는 장치별 PHY 교정 데이터(초기화 데이터와 다름)를 저장하는 데 사용됩니다.
    프로젝트에 최소 0x3000바이트의 NVS 파티션을 포함하는 것이 좋습니다.

     

    phy(1)은 PHY 초기화 데이터를 저장하기 위한 것입니다. 

    이를 통해 PHY를 펌웨어가 아닌 장치별로 구성할 수 있습니다.
    기본 구성에서는 phy 파티션이 사용되지 않으며 PHY 초기화 데이터가 앱 자체에 컴파일됩니다. 

    따라서 이 파티션을 파티션 테이블에서 제거하여 공간을 절약할 수 있습니다.


    factory(0x00)은 기본 앱 파티션입니다. 

    부트로더는 data/ota 유형의 파티션이 없는 한 팩토리 앱을 실행합니다. 

    이 경우 이 파티션을 읽어 부팅할 OTA 이미지를 결정합니다.

     

     "otadata"  OTA 업데이트용 데이터를 보관하는 새로운  슬롯도 있습니다.

    부트로더는 실행할 앱을 파악하기 위해 이 데이터를 참조합니다.

    “ota data”가 비어 있으면 팩토리 앱이 실행됩니다

     

    ota_0(0x10) … ota_15(0x1F)는 OTA 앱 슬롯입니다.


    nvs_keys(4)는 NVS 키 파티션용입니다. 

    NVS 암호화 기능이 활성화된 경우 NVS 암호화 키를 저장하는 데 사용됩니다 .
    이 파티션의 크기는 4096바이트(최소 파티션 크기)여야 합니다.

     

    파티션에 대한 정보는 아래의 링크 내용을 보면 나온다.

    https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html

     

    Partition Tables - ESP32 - — ESP-IDF Programming Guide latest documentation

    Note Using C file I/O API to open a file (fopen`) in any write mode (w, w+, a, a+, r+) will fail and return NULL. Using open with any other flag than O_RDONLY will fail and return -1 while errno global variable will be set to EROFS. This is also true for a

    docs.espressif.com

    OTA관련 파티션에 대해 정리하면,

    1) otadata는 최초 데이터가 없을 경우 factory 내용을 부팅한다.

    2) 이 후, 업데이트시마다 otadata의 내용을 참조하여 ota_0이나 ota_1로 부팅한다.

    3) factory는 업데이트 대상이 아니다. 

    4) 용량이 부족할 경우 factory를 삭제하고 ota_0과 ota_1로 확장하여 사용하면 된다.

     

    2. 서버 소스

    node.js로 작성하는 소스의 일부는 다음과 같다.

    http서버의 /uploads/app.bin이라고 요청하면 해당 폴더의 app.bin파일이 다운받아지는 구조다.

    브라우저에서 직접 주소를 치고 app.bin파일이 다운받아지는지 확인하면 끝이다.

    //펌웨어 다운로드
    app.get('/uploads/*', function (req, res) {
      var uri = url.parse(req.url, true);
      var pathname = uri.pathname;
      var fileurl = pathname.replace('/uploads/', '');
      const file = `${__dirname}/uploads/` + fileurl;
    
      console.log('firwmware download : ', uri.pathname);
    
      res.download(file);
    });

     

    2. 예시 소스 참조

    IDF의 example>system>ota>simple_ota_example을 참조한다.

    단 여기에는 HTTPS로 업데이트 하게 되어있다(기본이 HTTPS다)

    따라서 아래와 같이 기존 내용을 수정한다.

    주석을 친 부분이 기존 소스에서 주로 수정한 부분들이다.

    void simple_ota_example_task(void *pvParameter)
    {
        char url_temp[100];
        sprintf(url_temp, "http://%s:%s/%s", otaserver, otaport, "uploads/app.bin");
    
        ESP_LOGI(OTA_TAG, "Starting OTA example task");
    // #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
    //     esp_netif_t *netif = get_example_netif_from_desc(bind_interface_name);
    //     if (netif == NULL) {
    //         ESP_LOGE(OTA_TAG, "Can't find netif from interface description");
    //         abort();
    //     }
    //     struct ifreq ifr;
    //     esp_netif_get_netif_impl_name(netif, ifr.ifr_name);
    //     ESP_LOGI(OTA_TAG, "Bind interface name is %s", ifr.ifr_name);
    // #endif
        esp_http_client_config_t config = {
            .url = url_temp,
    // #ifdef CONFIG_EXAMPLE_USE_CERT_BUNDLE
    //         .crt_bundle_attach = esp_crt_bundle_attach,
    // #else
    //         .cert_pem = (char *)server_cert_pem_start,
    // #endif /* CONFIG_EXAMPLE_USE_CERT_BUNDLE */
            .event_handler = _http_event_handler,
            .keep_alive_enable = true,
    // #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
    //         .if_name = &ifr,
    // #endif
        };
    
    // #ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
    //     config.skip_cert_common_name_check = true;
    // #endif
    
        esp_https_ota_config_t ota_config = {
            .http_config = &config,
        };
        ESP_LOGI(OTA_TAG, "Attempting to download update from %s", config.url);
        esp_err_t ret = esp_https_ota(&ota_config);
        if (ret == ESP_OK) {
            ESP_LOGI(OTA_TAG, "OTA Succeed, Rebooting...");
            esp_restart();
        } else {
            ESP_LOGE(OTA_TAG, "Firmware upgrade failed");
        }
        while (1) {
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
    }

     

    그러나 다음과 같은 에러 메시지가 나온다.

    ESP32에서는 기본이 HTTPS OTA 다운로드이기 때문에 인증서 사용을 안한다고 나오는 에러이다.

     

     이를 위한 테스트용옵션으로 변경해야 한다.

    idf.py menuconfig > Component config > ESP HTTPS OTA에서 아래 내용을 체크해준다.

     

     

    이후 실행을 하면 한참동안 로그가 발생되지 않다가 아래와 같이 한번에 나오며

    아래와 같이 V1.01에서 V1.0으로 된 것을 확인할 수 있다.

     

    프로그램 상 OTA TASK는 로드가 걸리는 부분이므로 프로그램에 주의를 하여 와치독에 걸리지 않도록 주의해야 한다.

    댓글

Designed by Tistory.