ESP-IDFを使ってみる

OTAアップグレード


ESP-IDFは、Over The Air(OTA)アップグレードを実行する2つの方法を提供します。
- app_update コンポーネントが提供するネイティブ API を使用します。
- esp_https_ota コンポーネントが提供する簡素化された API を使用することで、HTTPS 経由でのアップグレードが可能になります。

それぞれのサンプルがこ ちらに公開されています。
そこで、最も基本的なsimple_ota_exampleの使い方を紹介します。



STEP1 同じセグメント内にHTTPSサーバーが必要です。
STEP5.1 ESP32はWiFi/Ethernet経由でルーターに接続します。
STEP5.2 ESP32はHTTPSサーバから新しいバイナリーイメージを取得します。
STEP5.3 ESP32は新しいバイナリーイメージを有効にして再起動します。



まずは、ローカルなHTTPSサーバーを構築する必要が有ります。
HTTPSサーバーの構築はこちらを 参考にさせていただきました。
この中で分かりにくいのは、証明書署名要求(CSR)の作成の部分です。
証明書署名要求(CSR)の作成時に、幾つか入力する必要が有ります。
必須項目は、Country Name, Organization Name, Common Nameです。
Common Name (CN) は、Webブラウザに入力されたアドレスと一致している必要があります。
WebサーバーがFQDN (Fully Qualified Domain Name) を持っている場合は、それを指定します。
IPアドレスしか持たない場合は、IPアドレスを指定します。

私は以下の様に入力しました。
192.168.10.49はローカルHTTPサーバのIPアドレスです。
# openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JA
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Nagoya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:192.168.10.49
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

これ以外に悩む個所は有りません。
ブラウザーを起動してアドレスバーに[https://IPアドレス]を指定して、以下が表示されればHTTPサーバー構築は完了です。




次にサーバー証明書を使用してESP32からローカルHTTPサーバーにアクセスできるかどうかを確認します。
サーバー証明書は「/etc/ssl/certs/server.crt」に格納されています。
ローカルHTTPサーバーへのアクセスは、こ のサンプルを利用します。
まずは、こ のファイルを変更してhttpsのコードだけを有効にします。
static void http_test_task(void *pvParameters)
{
    //http_rest_with_url();
    //http_rest_with_hostname_path();
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH
    //http_auth_basic();
    //http_auth_basic_redirect();
#endif
#if CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH
    //http_auth_digest_md5();
    //http_auth_digest_sha256();
#endif
    //http_encoded_query();
    //http_relative_redirect();
    //http_absolute_redirect();
    //http_absolute_redirect_manual();
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
    //https_with_url();
#endif
    https_with_hostname_path();
    //http_redirect_to_https();
    //http_download_chunk();
    //http_perform_as_stream_reader();
    //https_async();
    //https_with_invalid_url();
    //http_native_request();
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
    //http_partial_download();
#endif

    ESP_LOGI(TAG, "Finish http example");
#if !CONFIG_IDF_TARGET_LINUX
    vTaskDelete(NULL);
#endif
}

menuconfigでルーターのSSIDとパスワードを指定して、ファームをビルドしてESP32に書き込みます。
以下の様にHTTPSのステータスが200で表示されます。
I (13748) HTTP_CLIENT: HTTPS Status = 200, content_length = 12333
I (13748) HTTP_CLIENT: HTTP_EVENT_DISCONNECTED
I (13748) HTTP_CLIENT: Finish http example

ホスト名をローカルHTTPサーバーに変更します。
static void https_with_hostname_path(void)
{
    esp_http_client_config_t config = {
        //.host = "www.howsmyssl.com",
        .host = "192.168.10.49",
        .path = "/",
        .transport_type = HTTP_TRANSPORT_OVER_SSL,
        .event_handler = _http_event_handler,
        .cert_pem = howsmyssl_com_root_cert_pem_start,
    };

この
サーバー証明書をローカルHTTPサーバーの証明書に置き換えます。
サーバー証明書はテキストファイルなので、エディターを使って直接書き換えました。
再びファームをビルドしてESP32に書き込みます。
以下の様にHTTPSのステータスが200で表示されれば、ローカルHTTPサーバーへのアクセスは成功です。
I (12188) HTTP_CLIENT: HTTPS Status = 200, content_length = 10671
I (12188) HTTP_CLIENT: HTTP_EVENT_DISCONNECTED
I (12198) HTTP_CLIENT: Finish http example



こ ちらのOTAのサンプルをビルドしてESP32に書き込みます。
menuconfigでは、firmware upgrade url endpointをローカルサーバーのIPアドレスに書き換えます。
Enable certificate bundleは無効にします。


ビルドして実行するとhello_world.binが無いのでupgradeが失敗します。
I (11776) simple_ota_example: Starting OTA example task
I (11776) simple_ota_example: Attempting to download update from https://192.168.10.49/hello_world.bin
I (11796) main_task: Returned from app_main()
E (12326) esp_https_ota: File not found(404)
E (12326) esp_https_ota: Failed to establish HTTP connection
E (12326) simple_ota_example: Firmware upgrade failed
I (12496) wifi:<ba-add>idx:0 (ifx:0, f8:b7:97:36:de:52), tid:5, ssn:0, winSize:64
I (12596) wifi:<ba-add>idx:1 (ifx:0, f8:b7:97:36:de:52), tid:0, ssn:15, winSize:64

binディレクトリにあるsimple_ota.binをローカルHTTPサーバにコピーし、/var/www/html/以下に置きます。
$ ls build/*.bin
build/ota_data_initial.bin  build/simple_ota.bin

$ scp build/
simple_ota.bin nop@192.168.10.49:$HOME

$ ssh
nop@192.168.10.49

$ mv
simple_ota.bin hello_world.bin

$ sudo cp hello_world.bin /var/www/html/

$ ls -l /var/www/html/
合計 888
-rw-r--r-- 1 root root 895952  8月  2 16:00 hello_world.bin
-rw-r--r-- 1 root root  10671  8月  2 12:33 index.html


再びOTAのサンプルコードを実行します。
upgradeが成功し、新しいファームを有効にするために再起動します。
I (11757) simple_ota_example: Starting OTA example task
I (11757) simple_ota_example: Attempting to download update from https://192.168.10.49/hello_world.bin
I (11777) main_task: Returned from app_main()
I (12317) esp_https_ota: Starting OTA...
I (12317) esp_https_ota: Writing to <ota_1> partition at offset 0x1d0000
I (12547) wifi:<ba-add>idx:0 (ifx:0, f8:b7:97:36:de:52), tid:5, ssn:0, winSize:64
I (12627) wifi:<ba-add>idx:1 (ifx:0, f8:b7:97:36:de:52), tid:0, ssn:35, winSize:64
I (25127) esp_image: segment 0: paddr=001d0020 vaddr=3f400020 size=1f048h (127048) map
I (25167) esp_image: segment 1: paddr=001ef070 vaddr=3ff80000 size=0001ch (    28)
I (25167) esp_image: segment 2: paddr=001ef094 vaddr=3ffb0000 size=00f84h (  3972)
I (25177) esp_image: segment 3: paddr=001f0020 vaddr=400d0020 size=a0284h (656004) map
I (25397) esp_image: segment 4: paddr=002902ac vaddr=3ffb0f84 size=03008h ( 12296)
I (25407) esp_image: segment 5: paddr=002932bc vaddr=40080000 size=178e8h ( 96488)
I (25447) esp_image: segment 0: paddr=001d0020 vaddr=3f400020 size=1f048h (127048) map
I (25487) esp_image: segment 1: paddr=001ef070 vaddr=3ff80000 size=0001ch (    28)
I (25487) esp_image: segment 2: paddr=001ef094 vaddr=3ffb0000 size=00f84h (  3972)
I (25497) esp_image: segment 3: paddr=001f0020 vaddr=400d0020 size=a0284h (656004) map
I (25717) esp_image: segment 4: paddr=002902ac vaddr=3ffb0f84 size=03008h ( 12296)
I (25727) esp_image: segment 5: paddr=002932bc vaddr=40080000 size=178e8h ( 96488)
I (25827) simple_ota_example: OTA Succeed, Rebooting...
I (25827) wifi:state: run -> init (0x0)
I (25827) wifi:pm stop, total sleep time: 783944 us / 15198242 us

I (25827) wifi:<ba-del>idx:1, tid:0
I (25827) wifi:<ba-del>idx:0, tid:5
I (25837) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1, snd_ch_cfg:0x0
I (25887) wifi:flush txq
I (25887) wifi:stop sw txq
I (25887) wifi:lmac stop hw txq
I (25887) wifi:Deinit lldesc rx mblock:10


ローカルHTTPサーバーにhello_world.binが有る限り、upgradeを繰り返します。
ローカルHTTPサーバーからhello_world.binを削除すると、upgrade失敗で終了します。
実際の運用では、upgradeを定期的に実行し、ファームのバージョンをチェックするなどの対応が必要になります。
こ ちらのサンプルでは、ファームのバージョンチェックを行って、upgradeの実行可否を決めています。

続く...