WiFiモジュールESP32で画像付きサーバーの開発に成功
前回までのブログは、失敗続きで暗かった。この年(もう70才を数年越えた)で、無謀にもみんながやっていないI2Cのクロックストレッチをソフトで実現しようとして、ハードウエアの基礎知識不足を見事にさらけだし、あえなく撤退した。
しかし、今度の記事は胸を張って明るく報告できる。今流行りのWiFiモジュールESP8266の兄貴分ESP-WROOM-32(以下ESP32)で、画像付きのウェブサーバーの開発に挑み、このほど動かすことが出来たのだ。
ESP8266ではフラッシュの部分をファイルシステムにするSPIFFSがArduinoIDEで用意されていたので、これを利用して画像データファイルをホームページに表示することが出来た。ただ、ESP8266では性能的に一杯一杯で、わずか12KB程度のjpegファイルの表示に、ひといき(0.5秒くらいか)時間がかかり実用的とは言えない。
ESP32は、ESP8266に比べると、CPU数が1から2、クロックも1.5倍(160->240Mhz)と性能が一段と強化されている(他にもBlueToothなど機能も豊富になった)。こいつなら少しは実用的な画像を背景にしたホーム画面や、イラストで作ったボタンが使えるかもしれない。
(ここが詳しい)
ところが、ESP32のArduinoIDEの環境では、今のところSPIFFSが動いていないようなのだ。(このブログ参照)しかし、ウエブ情報を集めていくと、製造元(espressif社)の提供する開発環境、ESP-IDFや、他のプロジェクト(Rua-RTOS)で、フラッシュファイルシステムを作ったという話が出てくる。
うまく動いているらしいが、リンク先が海外で、いまひとつ確実なことがわからない。でもここまできたらESP-IDFを導入して画像が出るところまで動かし、ESP8266との差を確かめたくなった。
以下は、ESP-IDFの環境整備から、画像表示に成功するまでのESP32開発記録である。実は終盤になって画像を表示させるのにSPIFFSより遥かに簡単な方法が見つかるという、どんでん返しがあったのだが、詳しくは本文で。
ESP-IDF開発環境の導入から始める(9/2/2017)
ESP-IDFとはArduinoIDEとは別の開発環境である。製造元の正式環境だが、make主体のCUIの世界で、WindowsにLinuxの環境を作るなど、準備に手間がかかり、今まで敬遠してきた。しかし目標である画像の出るサーバー画面の実現のためには避けて通れなくなった。
気分を新たにして、開発環境を準備する。ハードウエアはArduinoの時と同じ秋月で売られている純正ブレークアウト基板DevkitCを使う。こいつは横幅が広いので普通のブレッドボードに差すとジャンパーが出せなくなるが、ここはミニブレッドボード2枚を並べてしのぐ。
ESP-IDFのインストールは紹介するサイトが今や山ほどある(以前は少なかったが)。戸惑うことはない。むしろ沢山ありすぎて何を選べば良いのか迷うほどだ。まあ、余りこだわることはない。こことか、このあたりを参考にインストールを始めた。
最初のmingw(Win上のLinux環境)のインストールファイルが500MB近くもあり、サーバーの線が細くてダウンロードに1時間近くかかったのが問題だったくらいで、インストールそのものは、意外にも順調だった。ここでも「ねむいさん」のブログにお世話になる。彼(彼女?)は何と去年のうちに環境をインストールしテストまでされていた。
mingw上に自分のホームディレクトリを作り、開発環境は出来上がったが、沢山のサイトを拾い読みしていたために、ホームディレクトリの位置がさまざまで、esp-idf(開発環境本体)のディレクトリパスが中々通らない。何とか、ごまかし、ごまかし(mingwなので、サブディレクトリ区切りがバックスラッシュ\と、/が混在してややこしい)、makeが通るようにする。
やっと動き出して、中味のファイルや、サンプルコードを覗く。ここはC++でもなく、純然たるCの世界だった。STM32で少しかじったFreeRTOSがメインのOSのようで、何か久しぶりに旧友に会ったような気分でなつかしい。
ただ、MakeFileのあたりの情報隠蔽がかなり深い。実際のMakefileの中身が全く表に出て来ないので何をやっているのかわからない。ソースがメインプログラムしかないのも不思議である。わからないことだらけだが、とりあえず先に進む。
LチカとHelloWorldは簡単に動いた(9/3/2017)
以前の記事のとおり、ESP32はArduinoの環境で既に動かし、LEDをウェブから制御できるサーバーまで作った。それでもESP-IDFの環境に慣れるため、サンプルソースにLチカ(blink)と、hello worldのソースがあったので、これらを使って練習することにした。
ホームディレクトリに、サンプルソースの一式(AVRで言うプロジェクトのようなものか)をコピーし、make menuconfigで、シリアルラインの定義をしたあと、makeに入る。延々とビルドが始まった。
ふーむ、Lチカくらいで、何か、すべてのリソース(IPV6からBluetooth、SSLまで)をビルドしているようだけど、どう言うことなんだろう。まあ、何十分もかかるわけでもないので待つしかないが、何か無駄のような。
Lチカは、指示通り、make menuconfigでIOピンの位置を修正する。前に使ったLEDをGPIOに設置し、makeに入る。幸いNO Errorのようだ。続いて、make flashでファームに焼く。これもエラーは出ない。すると、めでたくLEDが1秒近い間隔で点滅し始めた。よーし、動いた。
次は、Hello worldである。もうひとつ手順が増える。make flashのあと、make monitorでコンソールにUSBコンソールのCOM3仮想ターミナルを立ち上げる。やたらとメッセージが多いが、無事、コンソールにHello World!の文字が10行づつ繰り返された。これも問題ない。
SPIFFSのgithubを見つける(9/6/2017)
次は、HTTPサーバーだが、その前にこれまで見つけてあるSPIFFSプロジェクトを入れることにした。目星をつけたいくつかのサイトを調べているうち、英文だが、外国人(イタリア?)の英語なので、とても理解しやすいサイトを見つけてある。
以前に見つけたところとは別のサイトだが、esp-idfの環境で、SPIFFSが出来たという報告である。沢山の感謝とお礼のレスポンス付きだ。これは間違いなく動いているようだ。喜び勇んでダウンロードする。
順調に進むかと思われたが、そう簡単に問屋は許さなかった。今度も、ディレクトリパスが難しい。make menuconfigそのものが通らない。いくつかの関門を潜り抜け、menuconfigまで行ったが、今度は本番のmakeでビルドエラーが出る。残念!
何かおかしい。インストールした場所が悪いのか。githubなので、clone先が所定のところにないと、正しく動かないようだ。余り深入りは避け、次の日、出勤した事務所のPCで最初からインストールしてみた。これが不思議、makeが通ったのである。
SPIFFS動作成功(9/8/2017)
帰宅して、もう一度やり直す。やった。makeが通る。make flashで実際にファーム焼き込み。これもうまく行った。原因はやっぱりgitを展開するディレクトリの位置だったようだ。いやあ気難しい。
まあ、あまりこれにこだわっていても仕方がない。先に進もう。それにしても単なるフラッシュだけの操作なのだけど、LチカとHelloWorldと同じように、延々とすべてのライブラリのコンパイルが続く。
testSPIFFS.cのソースコードをあらためて精読する。mainで各種の関数をテストし、それをコンソールに出力している。そんなに複雑ではない。これだけで動くようだ。何はともあれmake monitorでコンソールを動かしてみた。
やった、それらしい出力が次から次に出力される。ディレクトリを増やしたり(mkdir)、ファイル一覧(ls)なども出来る。うむ、うまく動いているようだ。フラッシュファイルにアップロードする方法がまだわからないが、テスト環境には、フラッシュに入れたファイルイメージが残されており、何らかの方法でアップロードは出来るようだ。
ここまで進むと先は見えてきた。ESP-IDFのサンプルソースにはHTTPサーバーのソースがあるので、このSPIFFSを、サーバーに合体させれば動く見通しがついた。どちらを母体にするか迷ったが、構造の複雑なサーバーのソースコードにSPIFFSの要素を組み込んでいく。その前に、HTTPサーバーを動かしておかないといけない。しかし、これが意外と難渋したのである。
やっとホームページが出せた。サンプルソースでは動かない(9/18/2017)
サンプルがあるので、簡単にHTTPサーバーは動くかと思ったが、これがなかなかいうことを聞かない。いくつかあるHTTPサーバーのソースの一つをビルドしたが、ホームページ(index.html)を出すようなところが見当たらない(http_request_server)。
やっていることは、どこからかのソケットを受けてそれをSTDOUT(コンソール)に表示するだけである。これはサーバーではないよね。クライアントがやることだ。少なくともesp-idfのサンプルソースesp-idf/examples/protocols/http_requestと、https_requestにあるソースには、リクエストしかなく、これでは動かない。
困ったときはgoogle先生である。esp-idf http_server などのキーワードで検索を続けると、別のそれらしいソースコードが見つかった。ドイツの電子工作ショップのサイトで、ソースだけで説明記事に戻れないが、コードを読む限りでは、クライアントのリクエストに対してindex.htmlを返す部分がある。もうひとつタスクが必要のようだ(netconn_server)。
半信半疑ながら、このソースに取り替えて、再度ビルドしてみる。よーし、OK! 簡単なindex.htmlが表示された。良かった。でも、なぜ本家のサンプルには、本来のHTTPサーバーの雛形がないのだろう。謎である。次はいよいよhtmlファイルのフラッシュファイル化に進む。
HTTPサーバーの解析に夢中になる(9/16/2017)
フラッシュファイルを入れるため、サーバーのソースを読みふける。おおー、段々全体が見えてきた。嬉しい。いや勉強になる。これまで近づこうとしてなかなか機会のなかったソケットプログラムだ。lwipとか、nghttpとか、新しいプロトコルを知る。
電子工作ではなくて、ウェブプログラミングで遊んでいる。この世界も複雑で奥が深い。HTTPサーバーの教科書が少ない。事務所の帰りにいつもの秋葉原書泉で参考書を探すが、自分が知りたい基礎の部分を解説したものはなかなか見つけられない。
このesp-idfの開発環境にも大分慣れてきたが、それににしても、このフルビルドは何とかならないか。延々と必要もないモジュール群をコンパイルしていく。一旦コンポーネント(ライブラリ)が出来ると、あとは少し早くなるが、それまでは大変だ(まあ、モジュールを選択するのも大変なので、こういうやり方もあるか)。
文字列の連結、文字列のサーチなどArduinoIDFにはあった機能をせっせと開発する。コーディングとしては楽しかったが、これらはすべてCの標準関数(string.h)にあることがわかって、お蔵入りした。完全な無駄足である。
SPIFFSでテキストファイルの送出は成功した(9/22/2017)
画像表示は、読み込みのとき大きなバッファースペースが必要なので、まずは文字ベースでindex.htmlに埋め込む方法をテストする。これが結構難しい。どうも思ったようにhtmlに展開してくれない。
それより問題なのは、nett_conn_serverというFreeRTOSのタスク上でSPIFFSを動かすとstack overflowでプログラムが落ちるのである。menuconfigなどでスタックサイズを広げるが改善されない。これもウェブで調べて解決方法が見つかった。タスクを起こす関数に、スタックサイズを指定するパラメーターがあったのだ。
これを通常の3倍(6144バイト)まで広げてやっとstack overflowは止まった。しかし、それでもindex.htmlに思ったような文字列が出てこない。何回か試行錯誤するうち、重大な誤りをみつけた。
文字列の長さを得るのに、sizeofを使っていたのだが、ポインターを使った文字列では、これがアドレスを収容するエリア(ここでは4バイト)になってしまうことに、だいぶ後になってから気づくというお粗末である。
しかも、関数の引数にすると呼ばれた先では、strlenでも文字数が正しく得られない。それやこれやで苦闘の結果、何とか出せたのだが、<object src=XXXXX />のタグでは、スクロールバーのようなものが出てしまう。目的は画像ファイルなので、道草を食いたくないのだが、テキストファイルの表示だけで四苦八苦である。
何と、もっと良い方法を見つけた。バイナリの埋め込み(9/24/2017)
色々とウェブをさ迷ううち、SPIFFSではない、もっと楽な画像ファイルの表示方法があることを偶然つきとめた。いや情報は浴びるように摂っていれば良いことがあるという言葉どおりの快挙である。
esp-idf esp32 webserverなどのキーワードで、調べているうち、スマホのきれいな画像のボタンでLEDをウェブから制御しているページを見つけた。
ボタンがきれいなイラスト画像(pngファイル)である。へえー、サーバーでどこかのクラウドサーバーにリンクして画像を持ってきているのだろうかと、思っていたら、どうもそうではない。 mainと同列にある、component.mkファイルにCOMPONET_EMBED_FILES:=でファイル名を定義すると、これをbinary dataとしてフラッシュに埋め込んでくれるというのだ。
これはすごい。以前、AVRでやったことのある、フラッシュにバイナリーデータを埋め込むobjcopyと同じ手法である。埋め込んだ後、エントリーポイントを取得すれば、プログラムのなかでこのバイナリーデータを使うことが出来る。
まだSPIFFSではjpegファイルまで進んでいないが、ファイルを読み込まなくても、外部参照だけでデータをとりこめる。しかもバッファーの長さを気にする必要もない(コンスタントデータなのでバッファーの長さは気にしないで良い)。
これは試してみるしかない。早速、サイトの情報を頼りに、見よう見まねで、適当なjpegファイルをとりだし(SPIFFSスタックの中にあった画像)、component.mkファイルに設定し、ビルドしてみた。おーし、何のエラーも出ずビルドは終わった。
次はmake flashである。いつもより少し時間がかかるが無事終了した。jpegファイル40KB分だけ遅くなっているか。さあ、make monitorでサーバーを立ち上げる。
おおー、やった。画像がホームページに出た。しかも早い!一瞬だ。嬉しくて何度もディスプレイの前でガッツポーズをする。ホームページそのものは、画像とテストメッセージ一行の何も意味もないページだが、このあとには無限の可能性が待っている。
SPIFFSでも画像ファイルの送出に成功。そう遅くにもならない(9/25/2017)
勢いに乗って、SPIFFSでも画像が送れるかやってみる。このnetconn_serverというしかけが良く見えないので、バッファーサイズをどれくらいまで広げられるかわからない。これが、画像ファイルの読み込みを遅らせていた理由だが、もうそんなことは言っていられない。適当なサイズ(4KB)でneconn_write()を繰り返し発行することにする。
恐らく、バイナリ埋め込みに比べれば遅くで使い物にならないかもしれないが、まずはやってみる。動かしてみた。おやあ、またstack overflowで止まる。祈る気持ちでスタックサイズを2048の3倍からさらに4倍の、8192まで上げて再度動かしてみる。
よーし、動いた。そんなに遅くならない。esp-idfについているデバッグ用のメッセージにms単位のタイムスタンプがあるので、これで測ってみると、同じ画像で、埋め込みで平均62msが、SPIFFSだと108msだ。ブロックサイズを広げればもう少し早くなるかもしれない。
とにかく、ESP32の画像付きサーバーの開発はこれで一段落した。胸を張ってブログに報告できる。
以下に、サーバーのソース一式をONE DRIVEで公開します。ディレクトリごとzipファイルにしたので、これをesp-idfのホームディレクトリに展開し、mainの直上のディレクトリでビルドすれば、バイナリデータを埋め込んでくれます。ソースには2種類のやりかたがコメントとして残っているので、適当に選んでください。WiFiの設定はmake menuconfigで行います。
ここをクリックしてONEDRIVEに行く
| 固定リンク
| コメント (2)
| トラックバック (0)
最近のコメント