方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

【ESP32】OTAアップデート機能を実装して、遠隔でファームウェア更新する方法

この記事は約32分で読めます。

はじめに

ESP32のWiFiモジュールを搭載した、方言を話すおしゃべり猫型ロボット「ミーア」の開発を進めているが、今回は製品をユーザーに提供後に新機能をリリースした際に、開発者がリモートでファームウェアの更新を行えるようにするために、OTAアップデート機能を導入する。

ミーア
おしゃべり猫型ロボット「ミーア」は、100以上の種類の豊かな表情で、悲しみや喜びを共有します。様々な性格(皮肉・おせっかい・ロマンチスト・天然)や方言(大阪弁・博多弁・鹿児島弁・広島弁)も話してくれます。ミーアとの暮らしで、毎日の生活をもっ...

ESP32が提供する2つのOTAアップデート機能

OTA(Over The Air)は、データの送受信を無線通信で行うための技術。OTA機能により、ESP32のファームウェアは無線(Wi-Fiなど)を通じてリモートからアップデートが可能になる。

ESP32のOTA Exampleはこちら。

esp-idf/examples/system/ota at v5.2.1 · espressif/esp-idf
Espressif IoT Development Framework. Official development framework for Espressif SoCs. - espressif/esp-idf

ESP-IDFは、ESP32のOTAアップデートに対応するために2つの異なる方法(app_update, esp_https_otaコンポーネント)を提供している。

app_updateコンポーネント

  • ESP32の基本的なOTAアップデート機能を提供する。
  • ファームウェアを遠隔で更新するための低レベルのAPIを提供しており、開発者が細かく制御できる。
  • 低レベルのAPIとは、「より詳細な操作が必要で、開発者がアップデートプロセスの各ステップを自分で管理し、カスタマイズする必要があること」を意味する。
  • より柔軟なOTAアプリケーションの実装が可能だが、実装が複雑になる可能性がある。

esp_https_otaコンポーネント

  • HTTPSを通じてOTAアップデートを行うための、高レベルのAPIを提供する。
  • 高レベルのAPIとは、「複雑な処理が多く内部で隠蔽されており、開発者はより簡単な手順でOTAアップデートを実装できること」を意味する。
  • HTTPS通信を利用しているため、セキュリティが強化されており、インターネット経由で安全にファームウェアを更新できる。
  • 簡単に設定できるため、迅速にOTA機能を実装できるが、カスタマイズの自由度は低くなる。

今回は、AWS S3内にOTAアップデート用のファームウェアをアップロードして、HTTPSエンドポイントとするのでesp_https_otaコンポーネントを使用する。

esp_https_otaに関する説明と実装方法はこちら。

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_https_ota.html

OTAによるFirmware Updateの流れ

HTTPSサーバーの設定とファームウェアのアップロード

  • 開発者が新しいファームウェアバージョン(firmware.binなどのバイナリファイル)を作成する。
  • この新しいファームウェアファイルをHTTPSサーバーにアップロードする。

ESP32によるファームウェアのダウンロードとアップデート:

  • ESP32は設定されたタイミング(例えば、デバイスの起動時や定期的なチェック時)にHTTPSサーバーに接続する。
  • HTTPSサーバーから新しいfirmware.binファイルを検出し、ダウンロードを開始する。
  • ダウンロードしたファームウェアはESP32のフラッシュメモリ内のota_0またはota_1パーティションに保存される。ESP32は次回の起動時に新しいファームウェアを使用して起動する。

まずは、専用のOTAパーティションテーブルの作成から始める。次にESP-IDFのesp_https_ota APIを使用してファームウェアのダウンロードとアップデートを行うコードを記述する。

OTAパーティションテーブルの作成

ESP32でOTAアップデート用には、少なくとも2つのOTA partition(例: ota_0、ota_1)ga必要。

現状のpartition tableがこちら。

C++
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,      0x09000,   0x5000,
otadata,  data, ota,      0x0e000,   0x2000,
app0,     app,  ota_0,   0x010000, 0x390000,
spiffs,   data, spiffs,  0x400000, 0xC00000

otadata: OTAデータパーティション。OTAアップデートの状態を追跡するのに使用され、どのアプリケーションパーティションがアクティブかを示す。

app0 (ota_0): 最初のアプリケーションパーティション。ここに新しいファームウェアが格納される。

ただし、OTAアップデートをサポートするためには、後述するOTAアップデートのプロセスの観点から、少なくとももう一つのアプリケーションパーティション(ota_1など)が必要。

OTAアップデートのプロセス

  1. 交互にアップデート: ESP32はota_0ota_1を使って交互にアップデートする。例えば、現在ota_0を使っているとすると、次のアップデートでは、新しいファームウェアをota_1にインストールする。
  2. 自動切り替え: アップデート後、ESP32はotadataパーティションを見て、「次はota_1から起動しよう」と判断する。そうすることで、新しいファームウェアを使って起動できる。
  3. ロールバック: もしota_1にインストールした新しいファームウェアに問題があった場合(例えば起動しないなど)、ESP32は自動的に前のバージョン(ota_0にあるもの)を使用して起動しようとする。これにより、システムが壊れるリスクを減らすことができる。

というわけで、新たにota_1のpartitionを追加する。サイズはapp0と同じになるようにする。現状のapp0のサイズは3.9MBで指定しているが、それで良いか確認する。

app0のサイズの確認方法(PlatformIO)

PlatformIOの場合は、プロジェクト内の.pioディレクトリにはビルドされたファイルが格納されており、この中のバイナリファイル(.binファイル)のサイズをチェックすることで、アップロードされるファームウェアのサイズを知ることができる。

C++
 ~/dev/clocky/clocky_platformio/.pio/build/upesy_wrover ls -l
drwxr-xr-x  91 ky  staff      2912  3 27 17:03 FrameworkArduino
-rw-r--r--   1 ky  staff     17488  3 27 17:02 bootloader.bin
-rw-r--r--   1 ky  staff   3494720  3 27 17:03 firmware.bin
-rwxr-xr-x@  1 ky  staff  42429048  3 27 17:03 firmware.elf

firmware.bin ファイルがアプリケーションのファームウェアバイナリであり、サイズはは 3494720 バイト、つまり約3.49 MB。将来的に大幅にサイズが増加する可能性は無きにしも非ずだが、現在割り当てられている3.9MB>3.49MBなので、このままapp1も設定して良さそう。

元々、spiffsを12MB割り当てていたが、app1の割り当て分を削減して8MBにする。

app1を加味した、修正後のpartition tableはこちら

C++
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,      0x09000, 0x5000,
otadata,  data, ota,      0x0e000, 0x2000,
app0,     app,  ota_0,    0x010000,0x390000,
app1,     app,  ota_1,    0x400000,0x390000,
spiffs,   data, spiffs,   0x790000,0x794000,

factoryパーティションをどうするか?

esp32には、もう一つfactoryという名前のpartitionが存在する。和訳だと「工場」だが、その名の通りデバイスの初期ファームウェアイメージが格納される。これは、デバイスが工場出荷時に持っているファームウェアであり、初回起動時やリセット時に使用できる。

ただ、factoryパーティションもota_0やota_1と同じサイズの容量を確保するとなると、ますますspiffsに割り当てられる領域が減るのでトレードオフで悩ましいところ。

出荷時に直接ESP32にファームウェアを書き込んで、ota_0ota_1 パーティションのみを使用する場合、ユーザーの操作などによりファイルシステムが破損した際に復旧する方法はいくつかありそうなので、今回はfactory parititionの割り当てはせずに実装を進める。

HTTPS OTAアップデートのコード実装

ESP証明書バンドルを利用して証明書ローテーションに対応!?

下記のesp_https_otaコンポネント実装例では、サーバー検証が必要な場合は、追加のルート証明書(PEM形式)をesp_http_client_config_t構成のcert_pemメンバーに提供する必要があると記載されている。

PEM(Privacy-Enhanced Mail)フォーマット

  • 証明書や秘密鍵などの暗号情報をテキスト形式でエンコードするためのもの。PEMフォーマットのファイルは、”—–BEGIN CERTIFICATE—–“で始まり、”—–END CERTIFICATE—–“で終わる。中身はBase64でエンコードされたテキスト。

具体例

C++
-----BEGIN CERTIFICATE-----
MIIDBzCCAe+gAwIBAgIJAO1AEqR+xTGEMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xOTA3MTYxNzMzMTlaFw0yOTA3MTMxNzMzMTlaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAKzXvHhMkYXV/y6Zqj3XJc+iwEDwUWxkRfV8uJFv2oLbXgU8g+Q2DypqJRS6
5tH1dz4sYKAJXJ/80zNn5ddpgga2pF3Fz0IMq7Lq8WxS7OJroFx/UqjXHIepcZtA
4IhVdDfV7o5FyT7C2sFdE7Oe4sRr8n6U9X6eplYdDZHz5FY1+H/wxZtefC5u+zb5
...
-----END CERTIFICATE-----
ESP HTTPS OTA - ESP32 - — ESP-IDF Programming Guide latest documentation
C++
esp_err_t do_firmware_upgrade()
{
    esp_http_client_config_t config = {
        .url = CONFIG_FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,
    };
    // HTTPS OTA設定情報をota_configに格納
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };
    // esp_https_ota関数で、FirmwareをOTAアップデート
    esp_err_t ret = esp_https_ota(&ota_config); 
    if (ret == ESP_OK) {
        esp_restart(); // アップデート成功後、デバイスを再起動
    } else {
        return ESP_FAIL;
    }
    return ESP_OK;
}

今回、AWS S3が提供するHTTPSエンドポイントを利用する。AWS S3のHTTPSエンドポイントはAmazonが管理する証明書を使用し、これらの証明書は自動的に管理および更新(ローテーション)される。そのため、ESP32側で特定のルート証明書だけを設定しても、AWS側で証明書が更新されると、その証明書がESP32に設定したルート証明書と一致しなくなる可能性があり、通信が失敗するリスクがある。

なので、ESP-IDFに組み込まれているESP x509証明書バンドルを使う。x509証明書バンドルは、複数のルート証明書を1つのファイルにまとめたものであり、esp_http_client_config_tcrt_bundle_attachメンバを使用することで、このバンドルを使用してサーバーの証明書を検証することができる。

ESP x509証明書バンドルに含まれている証明書リスト(Mozillaが提供しているルート証明書リストをベースにしている)

ESP32の証明書バンドルにAmazon Root CA 1, 2, 3, 4が含まれているので、Amazonの証明書ローテーションが行われても、根本にあるルート証明書は変更されない限り、ESP32は引き続きS3から提供される証明書を信頼し、検証することが可能。証明書ローテーションは、主にエンドポイント証明書や中間CA証明書に適用されることが多く、ルート証明書はそのローテーションプロセスから通常は除外される。

https://ccadb.my.salesforce-sites.com/mozilla/IncludedCACertificateReport
C++
#include "esp_https_ota.h"
#include "esp_http_client.h"

esp_err_t do_firmware_upgrade()
{
    esp_http_client_config_t config = {
        .url = CONFIG_FIRMWARE_UPGRADE_URL,
        .crt_bundle_attach = esp_crt_bundle_attach, // 証明書バンドルをアタッチ
    };

    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };

    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        return ESP_FAIL;
    }
    return ESP_OK;
}

HTTPSサーバーURLを指定

CONFIG_FIRMWARE_UPGRADE_URLに設定するURLは、ESP32がファームウェアのバイナリファイルをダウンロードできる完全なURLでなければならない。

S3にfirmwareというフォルダ(バケット内のプレフィックス)を作成し、その中にfirmware.binファイル(.pio/build/upesy_wrover/firmware.binをexport)を配置する。

そして、platformio.iniファイルにCONFIG_FIRMWARE_UPGRADE_URLを定義する。

C++
[env:myenvironment]
platform = espressif32
board = ...
framework = espidf
; その他の設定
build_flags = 
    -DCONFIG_FIRMWARE_UPGRADE_URL="HTTPSサーバーURL/firmware/firmware.bin"

OTAアップデート関連の関数を専用のモジュール(例えば、ota_update.cota_update.h)に分けて記載

C++
// ota_update.h#pragma once
#include "esp_err.h"

esp_err_t do_firmware_upgrade();
C++
#include "ota_update.h"
#include "esp_https_ota.h"
#include "esp_http_client.h"

esp_err_t do_firmware_upgrade() {
    esp_http_client_config_t config = {
        .url = CONFIG_FIRMWARE_UPGRADE_URL,
        .crt_bundle_attach = esp_crt_bundle_attach,
    };

    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };

    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        return ESP_FAIL;
    }
    return ESP_OK;
}

下記エラー出た。

esp_crt_bundle_attach を使用するには、ESP-IDF menuconfig ツールで CONFIG_ESP_TLS_CERT_BUNDLE オプションを有効にする必要がある。しかし、今回はESP-IDFではなくPlatformIOでESP32のコードを記載しており、PlatformIOは、コード内で esp_crt_bundle_attach を直接呼び出す方法を提供していない。

Amazon S3のRoot証明書をブラウザから表示。root CA1で有効期限が2038年であることがわかったので、提供期間としては十分であろうと判断し、今回は証明書バンドルではなくルート証明書を埋め込みに切り替えることにした。

PlatformIOでESP32にesp_https_ota機能を実装

PlatformIOとESP-IDFの違い

回り回って基礎に立ちかえることになったが、PlatformIOとESP-IDFの違いをまとめておく。

  • ESP-IDFはESP32向けの専用開発フレームワークであり、PlatformIOはさまざまなプラットフォームやフレームワークに対応する統合開発環境。
  • ESP-IDFには専用のビルドシステムがあり、プロジェクトの設定ファイル(sdkconfig)を介してさまざまな構成オプションをカスタマイズできる。
  • 一方で、PlatformIOは、platformio.ini ファイルを使用してプロジェクトの設定を行う。一方で、ESP-IDFのビルドシステムはCMakeに依存している。CMakeLists.txtで行うような、特定のソースファイルに対する詳細な設定や、特定のファイルをバイナリに埋め込むような操作は、platform.iniだけでは実現が難しい。

先ほどのESP-IDFで提供されているesp_https_ota機能をPlatformIOで実装しようとしたが、exampleが動かなかったというスレッドを発見。

https://community.platformio.org/t/esp32-ota-default-examples-dont-work-in-platformio/20866

そして、esp_https_ota機能のPlatformIO compatibleのGitリポジトリを発見。

https://github.com/maxgerhardt/pio-espidf-simple-ota-example

こちらを参考に必要部分を取り込んでいこうと思ったが、今回のota update機能の設定に関してはCMakeLists.txtを作成しなくてもできそうなので、下記のように実装した。

OTA Update機能コードの実際

C++
// platform.ini
[env:myenv]
build_flags = -DFIRMWARE_UPGRADE_URL="https://example.com/firmware/firmware.bin
C++
// ota_update.h
#pragma once

void do_firmware_upgrade();
C++
// ota_update.cpp
#include "FS.h"
#include "LittleFS.h"
#include "WiFi.h"
#include "esp_https_ota.h"
#include "nvs_flash.h"
#include "WiFi.h"
#include "esp_tls.h"
#include "esp_err.h"

#ifndef FIRMWARE_UPGRADE_URL
#error "FIRMWARE_UPGRADE_URL is not defined"
#endif
#define CERT_FILE "/root.ca.pem"

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer;
    static int output_len;
    static int total_data_len = 0;
    switch (evt->event_id) {
        case HTTP_EVENT_ERROR:
            Serial.println("HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            Serial.println("HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            Serial.println("HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            Serial.printf("HTTP_EVENT_ON_HEADER, key=%s, value=%sn", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            Serial.printf("HTTP_EVENT_ON_DATA, len=%dn", evt->data_len);
            total_data_len += evt->data_len;
            break;
        case HTTP_EVENT_ON_FINISH:
            Serial.println("HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:{
            Serial.println("HTTP_EVENT_DISCONNECTED");
            break;
        }
        default:
            break;
    }
    return ESP_OK;
}

// ESP32 spiffs内に保存されているルート証明書を呼び出す
String readRootCA() {
    File file = LittleFS.open(CERT_FILE, "r");
    if (!file) {
        Serial.println("Failed to open certificate file");
        return String();
    }
    String cert = file.readString();
    file.close();
    return cert;
}

esp_err_t print_esp_error(esp_err_t err) {
    Serial.printf("esp_error: %sn", esp_err_to_name(err));
    return err;
}

// TODO:nvsからwifi接続情報を呼び出す方法に切り替え検討
void connectWiFi() {
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print("Connecting to Wi-Fi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.print(".");
    }
    Serial.println("Connected to Wi-Fi");
}

void do_firmware_upgrade() {
    Serial.println("OTA firmware upgrade start");
    // Wi-Fi接続の確認
    if (WiFi.status() != WL_CONNECTED) {
        Serial.println("Wi-Fi not connected, attempting to connect...");
        connectWiFi();
        if (WiFi.status() != WL_CONNECTED) {
            Serial.println("Failed to connect to Wi-Fi");
            return;
        }
    }

    if (!LittleFS.begin()) {
        Serial.println("Failed to mount file system");
        return;
    }

    // ルートCA証明書を読み込む
    String cert = readRootCA();
    if (cert.length() == 0) {
        return;
    }
    
    // TODO:nvs初期化の必要性があるか?wifi接続などの永続化データ
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        esp_err_t erase_err = nvs_flash_erase();
        if (erase_err != ESP_OK) {
            Serial.println("Failed to erase NVS partition");
            return;
        }
        err = nvs_flash_init();
    }
    if (err != ESP_OK) {
        Serial.println("Failed to initialize NVS");
        return;
    }

    Serial.println("Starting OTA task");
    esp_http_client_config_t config = {
        .url = FIRMWARE_UPGRADE_URL,
        .cert_pem = cert.c_str(),
        .event_handler = _http_event_handler,
        .keep_alive_enable = true,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);
    // 念の為に通信タイムアウトを10分に設定。
    esp_err_t ret = esp_http_client_set_timeout_ms(client, 600000);
    // otaアップデート実施
    esp_err_t result = esp_https_ota(&config);
    if (result == ESP_OK) {
        esp_restart();
    } else {
        Serial.println("Firmware upgrade failed");
        print_esp_error(result);
    }
}
C++
// main.cpp
void loop() {
  static bool isAppMainExecuted = false;
  if (!isAppMainExecuted) {
    do_firmware_upgrade();
    isAppMainExecuted = true;
  }

結果出力したら、データダウンロードはできるようになったが、途中でなぜかDisconnectedになってしまった。

C++
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=289
HTTP_EVENT_ON_DATA, len=52
HTTP_EVENT_DISCONNECTED
ets Jul 29 2019 12:21:46

HTTP_EVENT_DISCONNECTEDのデバッグ

HTTP_EVENT_DISCONNECTEDになった時の状態をデバッグするために、下記のように追記した。

  • 受診した総データ量をログ出力。これがfirmware.binと同じサイズであれば、データは全てダウンロードできたことがわかる
  • esp_tls_get_and_clear_last_error関数を使って、TLS(レイヤーで発生した最後のエラーを取得。エラーがあった場合(err != 0)、エラーコードと、それに関連するmbedTLSライブラリのエラーコードをログに出力する。これにより、接続の切断が正常な挙動によるものか、何らかのエラーによるものかを判断できる。
  • returnでESP_OKが返却された時は、”Firmware upgrade succeeded”と成功のメッセージを追加。
C++
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    static char *output_buffer;
    static int output_len;
    static int total_data_len = 0;
    switch (evt->event_id) {
		    case HTTP_EVENT_DISCONNECTED:{
            Serial.println("HTTP_EVENT_DISCONNECTED");
            Serial.printf( "Total data received before disconnect: %d bytesn", total_data_len);
            int mbedtls_err = 0;
            esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
            if (err != 0) {
                Serial.printf("Last esp error code: 0x%xn", err);
                Serial.printf( "Last mbedtls failure: 0x%xn", mbedtls_err);
            }
            if (output_buffer != NULL) {
                free(output_buffer);
                output_buffer = NULL;
            }
            output_len = 0;
            break;
        }
    }
    return ESP_OK;
}

void do_firmware_upgrade(){
		if (result == ESP_OK) {
        Serial.println("Firmware upgrade succeeded");
        esp_restart();
    } else {
        Serial.println("Firmware upgrade failed");
        print_esp_error(result);
    }
}

上記デバッグコードを添えて実行したところ、下記のように表示された。

HTTP_EVENT_DISCONNECTEDイベントは、HTTP接続が切断されたことを示すが、これ自体がエラーを意味するわけではない。本来であればHTTP_EVENT_ON_FINISHを表示して欲しいところだが、データは全て受信できていて、TLSレベルのエラーもなさそうなので、一旦これで進めるとする。

C++
HTTP_EVENT_ON_DATA, len=21
HTTP_EVENT_DISCONNECTED
Total data received before disconnect: 3494720 bytes
HTTP_EVENT_DISCONNECTED
Total data received before disconnect: 3494720 bytes
Firmware upgrade succeeded
ets Jul 29 2019 12:21:46

TRANSPORT_BASE: esp_tls_conn_read error, errno=No more processesエラーへの対応

コード実行していると、半分くらいの確率で上記TLS接続エラーがダウンロード途中で発生して、ダウンロードがそのまま止まってダウンロードが中断することがあった。

中断したのち、またダウンロードが再開する場合もあれば、ここでストップしてダウンロード再開しない場合もあった。

ESP32などのマイクロコントローラでTLS接続を読み取ろうとした際にエラーが発生したことを示している。同じようなissueに遭遇している人を発見した。

https://github.com/espressif/esp-idf/issues/11352#issuecomment-1551274007

上記エラーに関する解決策として、いくつかのLWIP(Lightweight IP)関連の設定を調整することを提案している。

ip_internal_networkのデフォルトの設定は下記に記載されている。

https://github.com/espressif/esp-idf/blob/v4.4.3/examples/mesh/ip_internal_network/sdkconfig.defaults

C++
// ip_internal_network/sdkconfig.defaults
CONFIG_LWIP_L2_TO_L3_COPY=y
CONFIG_LWIP_IP_FORWARD=y
CONFIG_LWIP_IPV4_NAPT=y
CONFIG_LWIP_TCP_MSS=624
CONFIG_LWIP_TCP_OVERSIZE_MSS=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y

CONFIG_LWIP_TCP_MSS=624

  • TCPの最大セグメントサイズ(MSS)。MSSが小さすぎると、送信するパケット数が多くなり、処理負荷が増大する。
  • 一方で、大きすぎる値はネットワークのMTU(最大転送単位)を超える可能性がある。パケットの断片化や再組み立ての問題を引き起こす可能性があり、NATに追加の処理負荷(断片化に伴う、グローバルIP↔︎ローカルIPへのパケット転送の追加処理)がかかるので、むやみに大きい値にすることはできない。

このissueをあげた方は、最終的にCONFIG_LWIP_TCP_MSS=1400に設定してTLSエラーを解消していたので、同様の設定をplatform.iniに追加してみる。

C++
// platform.ini
[env:myenv]
platform = espressif32
board = esp32dev
framework = espidf
build_flags =
    -D CONFIG_LWIP_TCP_MSS=1400

これで行けるかと思ったら、ビルド時に下記warningが出現。

C++
User
In file included from /Users/ky/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/esp_additions/freertos/FreeRTOSConfig.h:10,
                 from /Users/ky/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/include/freertos/include/freertos/FreeRTOS.h:57,
                 from /Users/ky/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:1:
/Users/ky/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include/sdkconfig.h:460: warning: "CONFIG_LWIP_TCP_MSS" redefined
 #define CONFIG_LWIP_TCP_MSS 1436

すでに導入しているframework-arduinoespressif32パッケージのsdkconfig.hで

C++
#define CONFIG_LWIP_TCP_MSS 1436

とさらに大きなMSSが設定されていた。

一旦保留で。その後、ビルドしたがTLSエラーは出現せず。

次は、新しいFirmwareを開発者がアップロードした時に、ユーザーに対してアプリ通知行う部分を実装する。

OTAアップデートを活用して、ハードウェアトリガーでユーザー手動でファームウェアを初期化できるように対応したのがこちら。

コメント

タイトルとURLをコピーしました