ESP-WROOM-02でI2SのWiFi送信速度を計る

ESP-WROOM-02でI2SのWiFi送信速度を計る

ひょんなことから、I2SのデータをWiFiで飛ばせないかという要件が出てきました。
そこで、今や電子工作の間でもよく知られるWiFiモジュール「ESP-WROOM-02」を使って、I2Sデータの送信テストをやってみました。

先に結論を言うと、要件では最低限16Mbps(2MByte/Sec)の速度が必要だったんですが、ESP-WROOM-02 ではその速度ではムリ!ということが分かり断念することに…。

しかしレートの低い、例えば音声信号として一般的な 16bit/32Fs/48KSPS くらいの転送量ならできそうなので、I2Sの送信方法などと共に検証結果を残すことにします。

ESP-WROOM-02

ESP-WROOM-02MIPS系のコアにWiFi機能が載っているSoCチップ「ESP8266EX」を搭載した、メーカー公式のモジュール。2015年ごろ発売なので、もう4年くらい経ちますね。

今では、ESP32-WROOM-32 という機能と性能が強化された後継種も出ていますが、小型省電力といったニーズから今だ需要があります。

ESP-WROOMシリーズの特徴は、なんといってもその安さと、開発リソースやドキュメントが充実している点にあります。そのため、ネットにいくらでも情報があるので詳しくは触れませんが、最近の情報を書いておきます。

ESP-WROOM-02 データシート

開発環境

ESP-WROOM-02 には、デフォルトでATコマンドプログラムが書き込まれているので、購入後起動してすぐに簡単なテストができるんですが、ちゃんと使おうと思ったらユーザーが開発したプログラムで上書きして使う必要があります。

書き込みやロギングには、シリアル通信を使うため、専用のライタは不要ですが、USBシリアル(もしくはそれを内蔵するブレークアウト基板)を使うのが一般的。

開発環境は3つ用意されていて、好きな方法を選べます。

Arduino IDE

 ESP-WROOM-02 は Arduino として使えます。

ESP8266 NONOS SDK

 メーカー(Espressif Systems)が用意している独自APIベースのSDK。

ESP8266 RTOS SDK

 こちらもメーカー用意の、フリーRTOSベースのSDK。NONOSと一部互換。

Arduino IDE だとグラフィカルで簡単でとっつきやすいため、ネット上の記事ではほとんどが Arduino になっていますね。

Arduino は簡単に使えるようにするのが目的のシステムなので、お遊びや実験、簡易評価などには良いかもしれませんが、真にデバイスを使いこなすようなことをやろうとすると、それが裏目に出て逆に苦労することもあります。今回は、Arduinoは使いません。

今ではRTOS SDKでもWindows上で簡単に開発できる!

SDKを使った開発はひと手間必要で少々面倒な点もありましたが、最近になって少し変わりました。Arduinoは使いたくない…という方には朗報かも知れません。

公式のリリースでは、2019/01/31 に「ESP8266 RTOS SDK (IDF Style)」というRTOSベースのSDKが出ています。これを使うとIDF(メーカー独自の開発環境)が使えるため、これまでより簡略化された手順で開発することができます。
ESP8266EX Resources | Espressif Systems

Windows上でもコンパイルできるので、従来は推奨されていたVirtualBoxは不要。ターミナルソフトもいらないし、Flash Download Tools も不要! mingw32のコマンド画面から make flash monitor と打てば、ビルドした後フラッシュへの書き込み待機状態となり、書き込みが終わるとそのままシリアルコンソール画面になります。

GUI環境が欲しければEclipseが使えるので、Arduino IDEと同じくらいの開発環境が整います。これらのセットアップ方法は次のページからたどれます。
EESP8266_RTOS_SDK/docs/en/get-started/

この開発環境は「MSYS2」ベースのものとなっていて、上のページからたどれるwindows-setup.rst にある「esp32_win32_msys2_environment_and_toolchain-20180110.zip」を、自分のPCに解凍して置くだけでOK!

・・・のハズなんですが、実はこれには肝心の「ESP8266」のコンパイラが含まれていないので、下の手順でコンパイラを入れて、パスを通してあげればOKです。

ESP8266_RTOS_SDK – GitHub の「Get toolchain」から Windows用のコンパイラ(xtensa-lx106-elf)をDL&解凍して「msys32\opt」ディレクトリに置く。

ついでに、最新のRTOS SDKをgitコマンドにて取得します。
cd ~/esp; git clone https://github.com/espressif/ESP8266_RTOS_SDK.git

「msys32\etc\profile.d」ディレクトリに、適当な名前の(例:esp8266.sh)のファイルを作り、次のように記述してパスを設定する(作るだけで良い)

後はmingw32のコマンド画面を再起動すればパスが設定されます。

コンパイラの解凍方法に注意

コンパイラ(xtensa-lx106-elf)は tar.gz 形式で圧縮されていますが、シンボリックリンクが含まれているため、7-ZipやLhacaなどWindowsの解凍ツールでは正しく解凍できず、サイズ0のファイルになったりします。

これを避けるために、msys2 のコマンド画面から tar -zxvf FileName を使って解凍する必要があります。

その前に、msys2 には tar がインストールされていないので、pacman -S vim git wget sed diffutils grep tar unzip にてインストールのこと。「msys2 tar インストール」で調べてください。

※SDKや開発環境は割とよく更新されます。今後、手順や必要なファイルなどが変更になるかも知れませんので注意してください。

I2Sドライバ

ユーザープログラムからI2Sを使うためのドライバやライブラリは、今のところ、公式リリースされている RTOS SDK にも NONOS SDK にも、なぜか入っていません。

Arduino版では、(今はどうかわかりませんが)I2Sのライブラリは用意はされているものの、出力しかサポートされておらず入力ができないという情報を見かけました。

I2Sレジスタを操作して自力でなんとかしようにも、仕様が完全に公開されていません。

一番詳しく載っているのは ESP8266 Technical Reference の9章なんですが、このドキュメントからだけでは完全なプログラミングはできないんです。でも、GitHubや海外のコミュニティーなどで、出処は不明ですがI2Sのヘッダやサンプルコードが落ちていたりして、なんとか試せるみたいな状況ではありました。

しょうがないので、出所不明のヘッダを頼りにガリガリしていたところ、つい最近10日ほど前に、GitHubにある最新の ESP8266_RTOS_SDK にI2Sのドライバが追加されていることに気付きました。

調べてみると、このドライバはDMAを使ったI2SのRead/Writeができるようです。
なんというタイミング!これはラッキー!つことで、早速それを利用してみることに。

なお、メインのヘッダとソースは次の位置にあります。
ESP8266_RTOS_SDK/components/esp8266/include/driver/i2s.h
ESP8266_RTOS_SDK/components/esp8266/driver/i2s.c

I2Sピンの割当て

I2Sで使うピンは次の通りです。

I2Sピンの割当て(I2S入力)
ピンGPIO信号名マスター時スレーブ時
5GPIO13I2SI_BCK出力入力
4GPIO12I2SI_DATA入力入力
3GPIO14I2SI_WS入力入力
I2Sピンの割当て(I2S出力)
ピンGPIO信号名マスター時スレーブ時
6GPIO15I2SO_BCK出力入力
11GPIO3I2SO_DATA出力出力
7GPIO2I2SO_WS出力出力

また、マスターとして使う場合には、ピン10(GPIO4)を Function2(CLK_XTAL)に設定することにより、マスタークロック(MCLK)出力として使えるようです。

I2Sフォーマット

I2Sの信号フォーマットは、LRCLK(WS)から1クロックずれてデータが始まる正規のフォーマット(フィリップスフォーマット)の他に、亜種であるRight-Justifiedが指定できるようです。(Left-Justifiedはできないっぽい)

I2Sタイミングチャート(16bit/32fs の場合)

I2S 16bit/32Fs フォーマット

検証の構成図

I2S出力する例
I2S出力する例

よくありそうなのは、WiFiを使ってMP3などのエンコードされたストリームを受信し、デコード後のデータをI2Sで出力してDACへ流し込むというもので、ネット上でもいくつか実験例を見ることができます。

ちなみに、ESP-WROOM-02 はRAMが少なく十分な量のバッファが確保できないことから、そのままでは満足できるストリーミング再生は厳しいようです。

I2S入力する例
I2S入力する例

デジタルマイクから音声データをI2Sで入力してファイルに保存する、といった使い方をいくつか見たことがあります。

ただ、さすがにコーデックまで載せてWiFi送信するのは厳しい?かも知れません。そもそも ESP-WROOM-02 は、IoTでの利用を想定したもので…なんて言い出すと元も子もありませんが。。

今回の検証構成
今回想定する構成

今回は、16bit/32Fs/0.5MSPS(2ch)のI2Sデータを外部から取り込み、エンコードせずにそのままWiFiで送信できるのか?という検証なので、このような構成を想定しています。

ソケットを使って、LとRの16bit値を単に交互に送信します。

検証の回路図

PICからテストデータをI2Sで送信する回路です。

回路図

ESP-WROOM-02 検証用回路図

ESP-WROOM-02 周辺は、データシートのFigure 5-2. にある最も基本的な回路です。

ネット上では、起動モードに関わるピンのプルアップがいるいらないで色々書かれているようですが、ESP8266EX のデータシートを見ると、多くのピンが内部でプルアップされていることが明記されており、ENピン以外のプルアップは不要です。つまり、ESP-WROOM-02 のデータシートに載っている回路そのままで良いかと。

電源容量は少なくとも250mAを確保する必要があります。当方の実験では、安定化電源の出力設定が230mA未満だと正常に起動しませんでした。データシートには平均電流80mAと書かれていますが、起動時には瞬間的に多くの電力を消費するようです。

ESP-WROOM-02 検証の様子ブレークアウト基板は秋月電子で売っている AE-ESP-WROOM-02-1100MIL、USBシリアルには AE-FT234X を使用しました。

USB内蔵の基板だともっと簡単に済みます。

電子太郎 ESP-WROOM-02開発ボード 2.4 GHz Wi-Fiモジュール MicroUSBESP-WROOM-02モジュール
ESP-WROOM-02本体に加えてUSBシリアルとUSBからの給電のための3.3Vレギュレターを内蔵した基板。USBで接続するだけ。
ESP-WROOM-02ピッチ変換済みモジュール《フル版》ESP-WROOM-02モジュール
スイッチサイエンスのESP-WROOM-02ピッチ変換済みモジュールのフル版。フル版でないとI2Sピンが出ていないので要注意。
HiLetgo® ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + BluetoothデュアルモードESP-WROOM-32モジュール
ESP-WROOM-02の後継機種で同様に使える。160MHz→240MHzにUPしBluetoothが追加された等。USBシリアル内蔵。

I2S受信速度の検証

まずは、WiFi送信抜きで、I2Sの受信パフォーマンスだけを見てみます。

I2Sにはスターとスレーブの割当てがありますが、データの送受信方向とは無関係で、ビットクロック(BCK)を送出する側がマスターになります。
ただ、ESP-WROOM-02 の場合、BCKの生成に制約があるらしく、スピードが要求される場合はBCKを受信する側、つまりスレーブ側でないと高速で動けないようです(後述)

送信側がマスターで ESP-WROOM-02 側がスレーブ

というわけで、この構成が本命です。

まず、テストデータをI2S送信するPIC側のプログラムです。PICには手持ちにあった dsPIC33CK32MP202(100MHz)を使いましたが、SPI/I2Sペリフェラルを持つ同等のPIC24シリーズでも、同じようなコードになると思われます。

テストデータは単に0x000~0x3FFのカウント値ですが、Rchには最上位ビットを立てて、受信側で確認できるようにしています。

次に、I2S受信する ESP-WROOM-02 側のプログラムです。
先に説明したとおり、I2Sドライバが含まれる最新のRTOS SDK上で動作します。

この例では、1024✕8ごとにLchとRchの受信データを表示して確認します。

検証結果

PIC側から16Mbps(16bit/32Fs(0.5MSPS))で送信したデータを、正しく受信していることがログや波形でも確認できました。

16MbpsのI2S信号波形16Mbps(16bit/32Fs(0.5MSPS))のI2S波形
BCK:16.7MHz
DATA

16MbpsのI2S信号波形(拡大)拡大したところ。
BCK:16.7MHz
DATA

反射による乱れが見えていますが、BCKの立ち上がりで確実にDATAをサンプルできる状態であることが分かります。

1MbpsのI2S信号波形1MbpsのI2S波形。
BCK:1MHz
DATA

1Mbps程度なら余裕ですね。

しかし!ドライバのコード内からログ出しするなどして調査した結果、残念なことに受信はできてもそれを処理する余裕がないことが判明。よって、このレートでWiFi送信できる可能性は無いことが、この時点で分かったのでした。。。

ん~、こんなんじゃ、いくらかハイスペックである ESP32-WROOM-32 を使っても、多分ムリだと思います(というくらい余裕がない…)

送信側がスレーブで ESP-WROOM-02 側がマスター

今更もういいかとも思ったんですが・・・
前途した ESP-WROOM-02 には BCKの生成に制約がある?説の話です。

ESP-WROOM-02 をマスター側にして16Mbps受信してみましたが、全然ダメでした。

じゃあ送信ならどうなのかと、送信側に設定してI2Sの出力をオシロで観測すると、正しいデータが出ていません。なぜか、片チャンネルあたり8Bitになってしまいます。

SDKに付属しているI2Cのマスター送信サンプル(examples\peripherals\i2s)では、サンプルレートは36Kになっていますが、それだと正しく出力されることが確認できました。でも48Kにしただけで途端にダメになりました。何よそれって感じです。

ドライバ(i2s.c)のコードを見てみると、I2Sレジスタにクロック分周比を設定する箇所が次の様になっているんですが…

指定されたレートに最も近くなるようにクロックを設定しようとしていますが、これってなんかおかしくないですかね?
レートはともかくフレーム周期が意図した通りにならない場合がありそうです。

また、I2Sレジスタへのクロック分周設定と、実際に出力される周波数の関係が、ここのフォーラム に書かれているんですが・・・

CLK_I2S = 160MHz / I2S_CLKM_DIV_NUM
BCLK = CLK_I2S / I2S_BCK_DIV_NUM
WS = BCLK/ 2 / (16 + I2S_BITS_MOD)
Note that I2S_CLKM_DIV_NUM must be >5 for I2S data

サンプルコードにある36Kという低いレートならばこの通りになるんですが、高いレートをセットすると、この関係が崩れてしまうんですよね。

で、ドライバのソースを直接変更してレジスタへの設定を色々試してみましたが、結局、高レートだと正しい出力を得ることができず、これ以上探求することは諦めました。

ESP-WROOM-02 でI2Sループバック

ESP-WROOM-02 のI2S出力を入力側へ接続して、単体での送受信(ループバック)をやってみました。送信側をマスターとします。

I2Sループバックする回路図

ESP-WROOM-02でI2Sループバックする回路図

DATA信号がUSBシリアルのTxDと競合するため抵抗を挿入しています。実行中はPC側から何か送信するわけではないんですが、フラッシュ書き込みの時のためです。

ソフト的には、普通は送受信処理を別々のタスクで実行することを考えると思いますが、DMAでバッファリングしてもらえるため、単一のタスク内で送信した後に受信するやり方でも、バッファオーバーフローしない限り大丈夫です。

そもそもRTOSでは、Windowsみたいにタイムスライスされるわけじゃないですからね。

なお、こちらでもBCKの制約により高レートではうまくいきませんでした。

WiFi送信速度の検証

16MbpsのレートではI2Sの受信だけで手一杯ということが既に判明していますが、WiFi送信のパフォーマンスについても今後のために検証しておくことにしました。

今回は、ESP-WROOM-02 がサーバーとなり、TCP送信するケースについて検証しています。後にUDPでもやっていますが、受信はやっていません。

送信プログラムは、シンプルにC言語のソケットを使いました。WiFi APへの接続後、クライアントの接続をacceptしたら、ダミーデータを送信します。

受信側はPC。VisualStudioでMFCを使ったコードです。
開始ボタンが押されたら受信スレッドを立ち上げ、ESP-WROOM-02 へ接続、ダミーデータを受信して、パフォーマンス情報をメインウィンドウに表示します。

検証結果

当方の無線LAN環境では、

ESP-WROOM-02 → (IEEE802.11 b/g/n) → WiFI AP → (IEEE802.11 ac) → PC

になるんですが、これで1.6~4Mbps(200~500KByte/Sec)という結果でした。

ん~アップロードが不利なのは分かっていましたが、予想してたより遅いですね。
ESP-WROOM-02 をAPのすぐそばに置いてみましたが、さほど変わりませんでした。
一体、b/g/n のどれでつながっているんでしょうか・・・

UDPで試してみたところ、速度は若干上がりますがロストしてしまうデータが結構ありました。いくらUDPだからってこんなにパケット消失したっけ?てな感じです。

結論

I2Sの16Mbps受信自体は問題なくできましたが、他の処理をする余裕が無いです。

そして、WiFiの上りが結構遅い。WiFiのパフォーマンスを上げるための施策があるのかも知れませんが、いずれにせよ、I2Sで受信してそのままWiFiで送信するという処理を、16Mbpsでこなすのは無理という収穫があったので、今回はこれで終了です。