開発機(Windows 10)とESP32間のOTAアップデートの試験法

びえびえ's Personal webpage  > IoT >  開発機(Windows 10)とESP32間のOTAアップデートの試験法
0 Comments

そう言えばあんまり書いてなかった・・・

 結構さらっと流していた話題だったのと、1回作っちゃうと再利用したい意欲が強まって(ものぐさ)二度と作らなくなるものなので、一応備忘録として私はこんな風に試験実施しています、という話題を書いておきます。あくまで私の場合、であって、ESP-IDFを開発環境に使っていれば色々と自由度はありそうなので、これにこだわる必要はないと思います。

アップグレード前のESP32側イメージでの処理

 OTAアップデートができます、とは言っても、受け口がなければ成り立たない話なので、最初に書き込むイメージはOTAアップデート対応になるようにしておきます(これは、UARTなどから書き込む初期イメージ)。

ESP32側ソフトウェアの対応

 まず第一に、パーティションテーブルはOTA対応できるように分割しておく必要がありますが、こちらは前回の記事に書きましたね。

 さらに、CMakefile.txtにてプログラム領域の最後尾にHTTPS接続に利用する公開鍵をベタで書き込んでおきます。ここも前回の記事に書いていますので参考にいていただければと思います。

Wifi処理設定のビルド時の書き込み

 実はもう一個先の記事ではさらーっと流したところがありまして。OTAアップデートのイメージが入ったHTTPサーバのURLとWifi接続のSSIDとパスワードを決めておく必要があります。DNSから見えているサーバならサーバ名でURL指定できますし、Wifi接続用のSSIDとパスワードも指定をきれいにできると思いますが、手元でやっているうちはそんなの決められない・・・ということもあるので、私はIPアドレス等を直接指定しています(開発試験中は面倒ですしね)。ただ、そのうちソースコードのどこをいじればHTTPサーバを変えられるか覚えきれなくなって精神衛生上よろしくないので、ソースコードを置いているディレクトリ(mainディレクトリ)のKconfig.projbuildファイルに以下の内容を入れておいて、idf.py menuconfig実施時に本メニューが追加で選択できるようにしています。


    menu "Over The Air(OTA) Support"

        config CPS_OTA_OVER_WIFI_SUPPORT
            bool "Over The Air update via Wifi Support"
            select BOOTLOADER_APP_ROLLBACK_ENABLE
            select ESP_WIFI_SW_COEXIST_ENABLE
            help
                This option should be set to y if the Wifi function is enabled
                and you want to update a firmware over the air.

        config WIFI_MAX_RETRY
            int "The maximum retries to connect Wifi AP."
            default 3
            help
                This option specifies the maximum numbers of trying to connect Wifi AP.

        config FIRMWARE_UPGRADE_URL
            string "URL to upgrade file."
            default "http://192.168.3.10:8080/upgrade"

        config APP_WIFI_SSID
            string "Application Wifi SSID"
            default "App_Wifi"

        config APP_WIFI_PASSWORD
            string "Application Wifi Password"
            default "Wifi_123"

        config OTA_RECV_TIMEOUT
            int "Timeout value to connect HTTPS OTA server(msecs)."
            default 5000

    endmenu

 このようにしておいてから、OTAアップデートを行うタスクでは以下のように初期化します。

#define TAG_OTA "OTA"   // デバッグ用のシリアル出力に表示されるタグ

static esp_netif_t *wifi_init_start(void)
{
    esp_netif_t *netif = NULL;
    char *desc = NULL;
    esp_err_t ret;
    bool isSucceeded = true;

    /* 不揮発性メモリ初期化 */
    ret = nvs_flash_init();
    /* 空きがなかったら消して改めて取り直すしかない */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        nvs_flash_erase();
        ret = nvs_flash_init();
    }

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ret = esp_wifi_init(&cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG_OTA, "esp_wifi_init failed!");
        isSucceeded = false;
        goto wifi_init_end;
    }

    // ステーションモード(クライアント側)としてWifiを立ち上げようとしています
    esp_netif_inherent_config_t esp_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA();
    asprintf(&desc, "%s: %s", XEBEC_OTA, esp_netif_cfg.if_desc);
    esp_netif_cfg.if_desc = desc;
    esp_netif_cfg.route_prio = 128;
    netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_cfg);
    free(desc);
    esp_wifi_set_default_wifi_sta_handlers();

    // この辺は、接続できたとき、接続が切断したとき、IPアドレスが取れたとき、
    // というのイベントハンドラ指定です。大体ありきたりなので足りるので、
    // 別のところで書き方は覚えちゃって大丈夫です。
    // あまり特別なことはしないので・・・
    ret = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &on_wifi_connect, NULL);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }
    ret = esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &on_wifi_disconnect, NULL);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }
    ret = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &on_got_ip, NULL);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }
    ret = esp_wifi_set_storage(WIFI_STORAGE_RAM);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }

    // ここが今回の設定の肝です!SSID, パスワードを書き換えるのを忘れるので、
    // menuconfigできれいにやってしまおうということです。
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = CONFIG_APP_WIFI_SSID,
            .password = CONFIG_APP_WIFI_PASSWORD
        },
    };
    ret = esp_wifi_set_mode(WIFI_MODE_STA);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }
    ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
    if (ret != ESP_OK) {
        isSucceeded = false;
        goto wifi_init_end;
    }
wifi_init_end:
    if (isSucceeded == false) {
        if (netif != NULL) {
            esp_netif_destroy(netif);
            netif = NULL;
        }
    }
    return netif;
}

Windows 10側のアップデートサーバ構築

Dockerを用いたnginxコンテナの作成

 構築って言っても、単純にSSL通信可能なHTTPサーバ上に新しいESP32用イメージを置いておくだけなので、nginxで構築するとしても単純にささっと済ませたいところです。私はESP32の開発環境を構築しているWindows 10マシンにDockerをインストールしてあるので、Docker Hubにあるnginxのイメージを少しだけ設定ファイル書き換えの操作をして対処しています。Dockerfileとdocker-compose.ymlファイルは単純にこんな内容にしています。

FROM nginx

EXPOSE 443

COPY nginx/nginx.conf /etc/nginx/nginx.conf
COPY nginx/server.crt /etc/nginx/server.crt
COPY nginx/server.key /etc/nginx/server.key

CMD ["/usr/sbin/nginx", "-g", "daemon off;"

nginxコンテナに関して言えば、フォアグラウンドで動かしていないとdockerコンテナとしては終了してしまう、というtipsがありまして。あえてフォアグラウンドで動作させるために最後にCMDを入れてdaemon offを指定しています。

version: "2"
services:
  xebec_update: 
    image: nginx
    build: ./build/webserver
    volumes: 
      - "./app:/usr/share/nginx/html"
    ports:
      - "443:443"

 とりあえずこれで、ローカルマシンのポート番号443上でSSL通信によるHTTPサーバを立てることが可能です。Dockerfile側はnginxイメージを用いてコンテナを作成する際に必要な設定ファイル等の上書き処理を書いています。

モバイルホットスポットの設定

 私のESP32開発環境はノートパソコン上に構築しておりますが、自由に扱えるアクセスポイントがどこにでもある環境とは限らないことと、外部ネットワークの設定がうざったいため、ノートパソコン自体をソフトウェア的にアクセスポイントにして対処しています。Windows 10では「モバイルホットスポット」の名称でノートパソコンを使ったテザリングが可能ですが、これを今回流用します。

 「設定」→「ネットワークとインターネット」で表示される画面の左ペインにある「モバイルホットスポット」を選択します。

モバイルホットスポットの設定画面
こんなやつです。

 これをオンにすると、いわゆるテザリングができるわけですが(本PCにWifi or Bluetoothで外部スマホやPCを接続すると、本PCのインターネット接続を使ってインターネットに接続できる)、これを本PCそのものへの接続にも使っちゃおう大作戦を敢行します。モバイルホットスポットがPC側で割り当てるIPアドレスが常に決まっているため(192.168.137.1)、ESP32側からはそのIPアドレスに向けてHTTPS接続を行うとDockerで作成したnginxコンテナに接続できる、というカラクリです(まぁDocker持ち出したんだったらubuntuとか普通に立てられるはずで、bind9でも立てて名前引きできるようにしても良いのだけれど、そこまでする元気がないので放置)。ネットワーク名とネットワークパスワードは下の「編集」画面から変更できますが、これをESP32のmenuconfigで決定した、

config APP_WIFI_SSID
    string "Application Wifi SSID"
        default "App_Wifi"

config APP_WIFI_PASSWORD
    string "Application Wifi Password"
        default "Wifi_123"

と合わせておきます。するとESP32からのイメージダウンロードは以後同様の手順で実現ができます。

モバイルホットスポットの盲点

 これ、省電力モードの設定になるのがデフォルトなので、有効にした後一定時間過ぎてしまうとオフになってしまいます。そのタイミングでOTAアップデートをかけに行くとタイムアウトしてイヤなので、常時オンにするような設定にしておくとトラブルがなくて良いでしょうね(電源的には優しくないのだけれど)まぁ客先でやるとか自分の手を離れるときはこの辺りに言及のあるPowerShellスクリプトでも仕込んでおく必要があるかなって思っています。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です