Arduino開発環境に戻って、M5StickC Plusのアプリケーションを開発して行きたいと思います。前回まで、UIFlow開発環境で遊んできたのだけど、やっぱり「おもちゃ」の域を脱せないと判ったので。……実は、もう少し遊んでみたいこともあるので、しばらく時間を置いたら、時々UIFlowに戻ってみたいと思っています。
ともあれ、今回はArduino開発環境に戻って、C言語で開発です。
M5StickC Plusを使い始めて最初に気になったのが、「LEDが明るすぎる」ことでした。
※M5StickC Plus回路図から引用
公式HPからM5StickC Plusの回路図を確認したところ(上図)、電流制限抵抗が入っていません。これでは電流が流れ過ぎるのでは?と思いつつも、ESP32の駆動能力調整機能で対処していると理解しました。
では、I/Oポートの駆動能力は、どうやって設定するのでしょうか?
M5StickC Plus+Arduinoでは、専用のM5StickC Plusライブラリを使って、楽してプログラミングする、と言う方針を立てました。M5StickC Plusライブラリには、搭載されているTFTディスプレイST7789や電源管理チップAXP192、RTCチップBM8563等の制御用クラスが用意されています。
しかし、ESP32のI/Oポートの駆動能力を設定できそうなクラスやメソッドは見つかりません。
それはそうですよね。
「M5StickC Plusライブラリ」なので、M5StickC Plusに搭載された周辺機器の制御に関するクラスは揃っていても、ESP32全般の制御は他のESP32ボード等と共通ですから、別ライブラリでサポートされていると考えるべきでしょう。
Arduino IDEから、「ファイルメニュー ⇒ スケッチ例 ⇒ ESP32」等、ESP32用のサンプルコードを漁っていたところ、ESP32の開発企業であるespressif社が提供していると思われるESP-IDF(IoT Development Framework)ライブラリ関数を用いたサンプルを多数見かけました。
ESP-IDFリファレンスを検索(別ウインドウが開きます)
※espressif社のサイトで、「API Reference」等と言うタイトルのページが見つかるはずです。
このESP-IDFライブラリが使えそうです。
実はこのESP-IDFライブラリですが、Arduino IDEでM5StickC Plus用のボードを導入した時に、インストールされた開発環境の中に含まれています。
M5StickC PlusボードをArduino IDEに導入した段階で、既にESP-IDFライブラリ関数のプロトタイプなどが宣言されたヘッダファイルはインクルードされているみたいです。つまり、デフォルトでESP-IDFライブラリ関数は自由に使用可能な状態となっています。追加のインクルードファイルなどは必要ありません(一部を除く)。
ESP-IDFライブラリのAPI Referenceページで調べてみたところ、I/Oポートの駆動能力を設定する関数は、以下の様に宣言されていました。
プロトタイプ:
esp_err_t gpio_set_drive_capability (
gpio_num_t gpio_num,
gpio_drive_cap_t strength);
M5StickC PlusのオンボードLEDはGPIO10に繋がっていますので、駆動能力を設定するためには、以下の様に記述すれば良いことになります。
設定例)gpio_set_drive_capability (GPIO_NUM_10, GPIO_DRIVE_CAP_0);
ESP-IDFライブラリのAPI Referenceページでは、上関数の近くにgpio_get_drive_capability()関数も見つかります。この関数を使って、デフォルトの設定値を確認しておきましょう。
確認用プログラム:
void setup (void)
{
M5.begin ();
gpio_drive_cap_t fun_drv;
gpio_get_drive_capability (GPIO_NUM_10, &fun_drv);
Serial.print ("fun_drv=");
Serial.println (fun_drv);
}
実行結果: fun_drv=2
ESP-IDFライブラリのAPI Referenceページ等で、駆動能力値は0〜3の4段階だと判るので、2だと、上から2番目の駆動能力です。ESP32-PICO-D4データシートによると、最大の駆動能力(=3)ではIol=28mA(TYP)と、流せる電流としては大きいです。
※これでTYP(Typical)条件ですから、MAX(maximum)条件では、もっと大きな電流を流せます。
ESP32のI/Oポートには同じ特性の出力ドライバが4組あって、駆動能力設定値0〜3に対して、ドライバの数1〜4個が対応していると仮定してみましょう。すると、ドライバ1個当たりの電流駆動能力は、28÷4=7mA(TYP)となり、駆動能力2(=ドライバ×3個)でも、21mA(TYP)流せることになります。
やっぱり、ちょっと流し過ぎ?十分電流を流すことのできるLEDを使っているのでしょうか?
人様の設計に文句言うほど偉くはないので受け入れて、「設計上問題ない」と考えることにします。とは言っても、更に一段上の駆動能力値である、3に設定するのは憚られます。
赤色だし、懐中電灯🔦代わりに使えるほど明るくもありません。駆動能力0でも光っていることは十分認知できます。LEDを光らせる場合、バッテリの持ち時間にも影響するので、本HPでは、初期設定値である2よりも駆動能力を落として使いたいと思います。
ここの所は各自、ご判断ください。
前節のESP-IDFには、ESP32のI/Oレジスタに直接アクセスするためのマクロも備わっています。
I/Oレジスタにアクセスするには、ESP_REG()マクロを使います。オンボードLEDが接続されたGPIO10の駆動能力を設定するレジスタ名は、IO_MUX_GPIO10_REGなので、以下の様にするとアクセスできます。
書き込み: ESP_REG (IO_MUX_GPIO10_REG)=<データ>
読み出し: <変数名>=ESP_REG (IO_MUX_GPIO10_REG)
レジスタ名は、ESP32のデータシートに記載があります。
ESP32データシートを検索する(別ウインドウが開きます)
開発元企業espressif社のサポートページが見つかるはずです。以下にIO_MUX_GPIO10_REGについての記述を引用します。
※ESP32データシートから引用
「x (赤文字)」の部分には、GPIO0〜GPIO39の文字が入るので、GPIO10なら、IO_MUX_GPIO10_REGとなります。駆動能力を変更するフィールドは第11,10ビットのFUN_DRVフィールドですが、このレジスタには他にも設定ビットが含まれているので、他の設定ビットを破壊しないように操作しなくてはなりません。
その処理をやってくれるのが、前節のESP-IDFライブラリ関数、というわけです。
上図より、ESP32リセット解除後の初期値がFUN_DRV=0x2であることが読み取れるので、前節で読み出した初期値2は、ESP32のリセット解除後の初期値と同じだったと解ります。
ESP-IDFには、上で示したレジスタの駆動能力FUN_DRVフィールド(2ビット)を操作するためだけに、専用の関数が用意されていたくらいですから、何らかの制御を実行したいと思った時に必要な関数は、ほぼ全て揃っていると考えても良いでしょう。
万が一、それでもESP-IDFライブラリ関数がサポートしていないESP32の機能が有った場合には、I/Oレジスタを直接叩くことも可能だと判りました。これで安心(1^−^)。
本HPで取り上げるのは、何度目でしょうね?使い古したアプリケーションだけど、LEDの駆動能力を変えて光り方(明るさ)を確認するには良いモチーフなのです。
よって、このページでは、1/fゆらぎ乱数列やらLEDキャンドルやらについては言及しません。以下の記事などを参照してください。
「#4-6 LEDキャンドル」
「#11-6 LED暖炉」
「#14-6 暗くなったら蝋燭を灯して」
「#15-10 汎用16ビットタイマユニット」
※オリジナル16ビットCPUコアMoscoviumのデモとして、LEDキャンドルを作成。
「#19-10 何回目?LEDキャンドル」
ESP-IDFライブラリ関数を使って駆動能力を設定し、GPIO10にPWMを出力します。PWMのデューティを1/fゆらぎ乱数列で瞬かせれば終了です。が、それだけでは芸が無いので、TFTディスプレイをロウソクに見立て、バッテリ残量をロウソクの長さで表現してみたいと思います。
UIFlowでは、3.7〜4.1[V]範囲が0〜100[%]範囲にマッピングされていました。M5StickC Plusに搭載されたリチウムポリマ(LiPO)バッテリの定格電圧は3.7Vなので、何と、定格電圧まで低下すると0%を表示していたことになります。このため、こちらの動画の様に、バッテリ残量表示が0%になってから30分近く動作し続けていました。
定格電圧3.7Vで残量0%表示するのは、さすがにコンサバ過ぎはしませんか?
という訳で、より実用的なバッテリ残量の計算基準について考察します。
バッテリ残量計算:
以上の考察結果を、以下表にまとめます。
バッテリ 電圧[V] |
バッテリ 残量[%] |
備考 |
4.2 | 100 | 仕様上の充電上限電圧 |
4.1 | 100 | 充電上限電圧からマージンを取った100%表示電圧 |
3.7 | 50 | LiPOバッテリの定格電圧 |
3.4 | 13 | M5.Axp.GetWarningLeve()メソッドが警告を返す電圧 |
3.3 | 0 | シャットダウン電圧からマージンを取った0%表示電圧 |
2.9 | 0 | 電圧低下によって自動的にシャットダウンする下限電圧 |
水の氷点(個体となる温度)と沸点(気体となる温度)の間を0℃〜100℃にマッピングする様に、3.3V〜4.1Vの間を0%〜100%にマッピングします。
上表に従うと、M5.Axp.GetWarningLeve()メソッドの警告電圧レベルは0%表示電圧値3.3Vより少し高い3.4Vなので、残量が減ってくると、警告が返る → 残量0%になる → 自動的にシャットダウンする、と言う順番となります。良さそうです。
※別解: 仕様上の充電上限電圧である4.2Vを100%とする、警告が返る電圧値3.4Vを0%とする、等の考え方でも、つじつまは合います。UIFlowと表示を合わせる、のも一つの解でしょう。上の考え方は、本ページでの考察による一解です。
完成したプログラムを以下にリンクしておきます。
完成プログラム:m5cp_candle.ino
そう言えば、ESP32ボードを使った時(当時)には、PWMを出力するanalogWrite()関数がエラーとなって使えなかった記憶があったのですが、M5StickC Plusでは、普通に使えました。
※ただし、一度analogWrite()関数を使ってPWM出力した端子は、digitalWrite()関数を使っても、普通のI/Oポートには戻りませんでした(執筆時点のバージョン)。UNOなどでは、PWM出力が停止してI/Oポートに戻るのですが……。
コンパイルしてM5StickC Plusに書き込むと、シリアルモニタから以下のコマンドを受け付けています。
シリアルモニタコマンド仕様:
すべてデバッグ用のコマンドです。プログラム自体はシリアルモニタ無しでも自立します。起動後、LEDキャンドルモードに遷移しているので、すぐにLEDがロウソクの様に瞬き始めます。
前節のプログラムを書き込んで、シリアルモニタで実験してみましょう。以下、実験中の様子です。
実験サンプル:【 】内はシリアルモニタからの送信データ
【led,3】
OK: led off3
LEDキャンドルモードを終了して、LEDを消灯しました。続けます。
【led,0】
OK: led on0
【led,1】
OK: led on1
【led,2】
OK: led on2
オンボードLEDに接続されているGPIO10端子の駆動能力を0〜2に変化してみました。明るさが変わることをご確認ください(上図)。LEDの明るさ → LEDに流れる電流の大きさ、なので、駆動能力を変化させたことで、LEDに流れる電流量を制御したことになります。
普通、LEDの明るさはPWMを使って制御します。PWMは、一定周期でON/OFFを繰り返すことで、平均電圧、従って平均電流値を制御する手法です。駆動能力による電流制御は、直接、流れる電流の瞬間値を制御する点で根本的に異なります。
ただ、ESP32の場合だと、4段階にしか駆動能力を制御できないので、もっと滑らかに明るさを制御するためには、やっぱりPWMを使います。
駆動能力の制御が力を発揮するのは、例えば以下の様なケースです。
【cdl,1】
OK: candle on
LEDキャンドルモードを有効にしました。【led】コマンドで設定した駆動能力のまま、PWMでLEDを瞬かせています。LEDキャンドルとして瞬かせている様子については、他の機体で作ったLEDキャンドルと同じなので、例えば以下の動画でご確認ください。
※「#14-6 暗くなったら蝋燭を灯して」にて、ATtiny2313と外付けLEDで作成した時の動画です。170穴ミニブレッドボード上に電源(3Vボタン電池)まで搭載したワンボードソリューションでした。
今回は、低層ライブラリ(ESP-IDF)を使うことで、M5StickC Plusライブラリがサポートしない、ESP32固有ユニットの制御を可能にすることがテーマでした。一歩一歩、着実に前進中です。