こんにちは、エンジニアの@k-tkrです。 Espressif社のマイコンESP8266で純正SDKを使ってHTTPS通信をしてみました。 Arduinoを使ったHTTP通信についてはたくさんの記事がみつかるのですが、純正SDK を使った HTTPS 通信についてはあまり記事が見つからなかったのでここで書いてみようと思います。また、HTTPS通信を使ったOTA(Over the Air)でのプログラム変更も試してみました。
ESP8266は中国Espressif社のマイコンでWiFiモジュールを内蔵しています。 Espressif社のマイコンといえばM5Stackにも使われているESP32が有名ですが、ESP8266はESP32の廉価版であり、ESP32に入っている機能を絞り低価格で使えるマイコンです。 低価格とはいえ、中にWiFiモジュールが組み込まれているので他のマイコンと接続してWiFi専用モジュールとして使用したり単体でWiFi機能付きのマイコンとして使用したりできます。 ESP8266にFLASHメモリ(最近のものは2MB)などを積んだESP-WROOM-02が使いやすいので今回はこれを使用しました。
ESP8266はArduinoを含めいろいろな環境下で開発できるのですが、今回は純正SDKを用いてC言語での開発を行いました。 純正SDKを用いた開発ではGet-startedにあるように元々はLinux上でCLIによりビルドやデバッグを行います。 開発を楽に進めるためにSIM版スマートマットの開発でも使ったVisual Studioのプラグイン(VisualGDB)を用いて開発を行いました。ビルドの依存関係やSDKの取得などの環境構築をほぼ自動で行ってくれるので、ビルドボタンを押すだけでビルドが完了し、開発が格段に速くなりました。何度かトラブルで純正SDKのビルドシステムを追う羽目になったこともありましたが…
EspressifのサイトではUART経由でプログラム書き込みをしprintfでコンソールに情報を表示させてデバッグを行うという方法が紹介されていました。しかしこの方法だとコードを任意の場所で一時停止して変数を確認することができないのでなかなか大変です。またUARTを別の目的で使用している場合はプログラム書き込みの度にUARTを切り替えるのも煩わしいです。 ESP8266はIO12~IO15ピンをJTAGの入出力として使用できるので、OpenOCDとJTAGデバッガを使うことでJTAGデバッグが可能となり、ブレークポイントを張り変数を確認するなどということが簡単にできるようになります(ただしIO12~IO15はSPI通信と共用なのでSPI通信を使う場合はJTAGデバッグはできないです)。JTAG経由でのプログラム書き込みやデバッグもVisualGDB経由でボタン一つで行うことができます。ただ、開発プログラムで一部外部メモリからSPI通信を使ってデータを読み込む必要があり、その際はUART経由でのプログラム書き込みとprintfデバッグを使いローテクの開発を行わざるを得ませんでした。STM32などに比べるとピン数が少ないので仕方ないことではあるのですが、JTAG専用(もしくはSWD)のピンが欲しいところですね。
ESP8266の製造元であるEspressifがRTOS SDKというESP8266用の純正SDKを公開しています。これはRTOSやESP8266のペリフェラルやTCP/IPスタック、WiFi機能へのアクセスを含むライブラリです。 このRTOS SDKですが、ESP8266 RTOS githubのRoadmapを読むと分かるようにv2系とv3系で大きく異なっていて、正直全く別物と言って良いものです。今回は今後の主流になるであろうv3系で開発をしました。v3系はESP32のSDKとも共通部分が多く情報も得やすいです。 ただし2021年現在でもv2系の記事やドキュメント類が散見され、どちらのバージョンを使っているのか注意が必要でした。
ESP8266の開発に関連する情報は十分であるとはいい難いのですが、同じEspressifのマイコンESP32に関する情報はESP8266と比べて桁近いに多いです。そして、一部違いがあるとはいえESP32の情報に書かれてあるかなりの部分がESP8266にも流用可能です。 ESP8266に関する情報が見当たらないときもESP-IDF Programming Guideを含むESP32関連のサイトから情報を見つけられることが多くありました。
本題のHTTPS通信です。 ESP8266 RTOS SDKの中にはesp_tlsライブラリやopenocdのインタフェースと整合的なライブラリなどいくつかのライブラリと実装例があるのですが、内部ではmbedtlsライブラリ(もしくはwolfssl)が呼ばれているので効率と細かい制御のしやすさを考えmbedtlsを直接使用しました。 ESP8266のmbedtlsの使い方はEspressif mbedtls exampleにあるのですが、トランスポート層のソケットライブラリと同じような感覚で使用します。まずTLS関連の初期化を行ったあと、
mbedtls_net_connect(&server_fd, WEB_SERVER,
WEB_PORT, MBEDTLS_NET_PROTO_TCP);
でサーバと接続し、
mbedtls_ssl_handshake(&ssl);
でTLS handshakeをします。その後
mbedtls_ssl_read(&ssl, (unsigned char *)buf, len);
mbedtls_ssl_write(&ssl,(const unsigned char *)REQUEST + written_bytes,
strlen(REQUEST) - written_bytes);
のように直接TCP read/writeに当たる関数を呼び出すのでhttp(s)に限らずTCP+TLSやUDP+(D)TLSのような使い方にも簡単に対応できます。今回の通信方法切り替えに際してhttps以外の通信方法も試したのですが、コードの変更を最小限に抑えたままで通信方法を変更することができ、使い勝手が良かったです。
TLSライブラリは恐ろしくメモリ(特にヒープ)を消費するのでメモリのやり繰りにとても苦労しました。ESP8266のRAMはDRAM, IRAM合わせて112KBあり、大域変数やRAMに置かれるコードなどを引いてもマイコンを立ち上げたときに90KB程度、WiFiとtcpipスタック立ち上げ後も60KB程度はあったヒープサイズが、TLS handshake後にいつの間にか10KB未満になっていることもあり、どれだけメモリ消費するんだよという感じでした。必要ないタスクを殺したり余計な変数を減らして極力メモリが占有されないようにしたり、ここにかなりの時間を費やしました。
HTTPS通信を用いて、ネットワーク経由(Over the Air: OTA)でのプログラム変更も試してみました。Espressif社が出しているOTAの作例はHTTP通信であり、READMEには「実際に使うときはHTTPSのような安全なプロトコルを使ってね」といったようなことが書かれています。 RTOS SDKのv3系にはOTAを簡単かつ安全に行える仕組みがあり、
esp_ota_handle_t update_handle = 0;
const partition_t * update_partition = esp_ota_get_next_update_partition(NULL);
esp_ota_begin(update_partition, fw_size, &update_handle);
// ...
// HTTPS通信でbufにプログラムのバイナリをコピーするコードが入る.
//...
esp_ota_write(update_handle, (const void *)buf, buff_len);
esp_ota_end(update_handle);
esp_ota_set_boot_partition(update_partition);
esp_restart();
のようにOTAが行えます。実行中のプログラムとは別のパーティションを取得して、プログラムを書き込み、起動パーティションを書き込んだものに更新して再起動すれば、新しいプログラムが新しいパーティションから実行されます。ちなみに外部メモリに保存してある工場出荷時のコードに戻すときもこのOTAの仕組みを利用しています。 v2系ではパーティションごとのアドレスマッピングを指定したバイナリをそれぞれ作成する必要があったのですがv3系では一つのアプリケーションバイナリを作成するだけで大丈夫です。
ESP8266は低価格の割に通信関連の機能が充実していてまだまだいろいろなことができそうだなという印象でした。今後はESP32を使った実験にも挑戦したいと考えています。