本記事は、Cerevoスタッフが業務や趣味について思うままに書き綴るアドベントカレンダー企画「Cerevo アドベントTechBlog 2017」の第2日目です。
Cerevo アドベントTechBlog 2017
http://tech-blog.cerevo.com/archives/category/adventcalendar/2017/
組み込みアルバイトの池本です。ミニ四駆を触るのは10年ぶりくらいです。当時はちょうど某運動靴が流行り始めた頃で、小学生だった僕は「コーナーで差をつけろ!」とか言いながら片側のタイヤをひたすら薄くしていました。可愛いですね。
概要
みなさんご存知、スマホで操作するミニ四駆改造キット「MKZ4」は、オフロードタイプの「ワイルドミニ四駆」向けに開発されたキットです。
スマホで自由自在に操作できる「IoTミニ四駆」の仕組みを解説(技適済みWi-Fiモジュール「ESP8266」で始めるIoT入門) – Cerevo TechBlog
https://tech-blog.cerevo.com/archives/1115/つくって、学んで、楽しめる。IoT時代の電子工作とプログラムの基礎がわかる、スマホで操作する改造ミニ四駆製作キット「MKZ4」を発売
https://info-blog.cerevo.com/2016/06/30/2428/MKZ4 – Cerevo Maker series
https://maker.cerevo.com/ja/mkz4/
一般的にミニ四駆として知られている「レーサーミニ四駆」(たとえば下記のようなミニ四駆です)はシャーシの大きさやパーツが異なるため、MKZ4で改造することができません。
タミヤ ミニ四駆PROシリーズ No.40 ライキリ (MAシャーシ) 18640
この記事では、MKZ4をレーサーミニ四駆へ移植する開発テストの模様をお伝えします。
なお、移植には回路、ソフト、メカそれぞれの改造が必要ですが、私が担当したのは回路とソフトの部分のみです。回路については主に小型化と電源電圧の低圧化(4.5V→3V)、ソフトについてはWebSocketを使った低遅延化がメインになります。
メカ部分に関してはそのうち誰かがまとめてくれるはずです。
これまでの成果
回路
回路の変更目的は、小型化と低電圧化です。
小型化は当然レーシングタイプのボディーに収めるためですが、まだハード側の制約が決まっていないため、ユニバーサル基板でのテストまで行いました。
低電圧化は、シャーシに乗る単三電池2本のみで駆動させるためです。動作電圧が3.0V ~ 3.6Vと余裕のないESP8266から同2.3V~3.6VのESP32へ変更したことがメインとなります。
回路構成
- ESP32 モジュール: MH-ET LIVE ESP-32 Mini kit
- 1~3.3V to 3.3V DC-DC 昇圧モジュール: DD0606SA
- モータードライバ: DRV8835使用ステッピング&DCモータドライバモジュール
ESP32 モジュール: MH-ET LIVE ESP-32 Mini kit
低電圧化のため、動作電圧が3.0V-3.6VのESP8266から、2.3V-3.6VのESP32に変更しました。いくつかあるESP32モジュールの中でも小さなMH-ET LIVE ESP-32 Mini kitを採用しました。
1~3.3V to 3.3V DC-DC 昇圧回路: DD0606SA
せっかく2.3Vから動作するESP32にしたのになぜ昇圧が必要なのかと思われるかもしれませんが、これはモーター回転時のESP32への電源供給を安定させるためです。
モーターは回り始めに最も電流を必要とするのですが、その際電池電圧が下の波形のように一時的に大きく下がります。これをそのままESP32に供給してしまうと、必要な最低電圧すら確保できないためリセットがかかってしまいます。そのため、昇圧回路を間に挟むことで安定させています。
モータードライバ: DRV8835使用ステッピング&DCモータドライバモジュール
PWM入力のモータードライバです。MKZ4ではI2C入力のモータードライバを使用していたのですが、I2Cと比べてPWMのほうがノイズに強く使いやすいため変更しました。
ノイズ対策
実は上の回路図、そのままだとモーターからのノイズでWiFiが切れたりリセットがかかったりします。コンデンサをたくさん積んだり色々試したのですが、結局モーターの外にノイズを出さないことが一番重要みたいです。下のように、モーターの電極間と電極➖ケース間にコンデンサをはんだ付けします。
ソフトウェア
本章の内容は、主にESP32のソフトウェア開発環境とWebSocketによる遅延低減についてです。
ソースコードは以下で見ることができます。
GitHub – cerevo/MKZ4_typeR: Cerevo techblog MKZ4 typeR
https://github.com/cerevo/MKZ4_typeR
開発環境
※以下の内容は2017年11月以前のものです。ESP32の開発環境自体がまだかなり早いペースでupdateされているので、ここに書かれていることは古い情報になっている可能性があります。
現在ESP32の開発環境として主に2つの選択肢があります。一つはESP-IDF (Espressif IoT Development Framework)で、もう一つはArduino core for the ESP32です。Arduino coreのほうはよくあるArduinoと同じような感覚で開発できますが、ESP-IDFはより低レイヤまで自分でいじれるといった感じです。ただ、基本的にどちらでもできることは変わらない(ESP-IDF環境でArduinoのライブラリを呼び出したり、Arduino環境でFreeRTOSのタスクを作ったりできる)ので、インターフェイスの好みで選べばいいかと思います。
SPIフラッシュへの書き込みツールは現在Arduino IDE用のものしか提供されていないようです。
ESP-IDF
セットアップ方法
ESP-IDF Programming Guide – Get Startedに従ってください。
公式リンク
テスト時のバージョン:
esp-idf:
$ cd ${PATH_TO_ESP-IDF} $ git log commit 3a271a4ae7df8a9049fbbb801feafca5043c31eb (HEAD -> master, origin/master, origin/HEAD) Merge: a1d23051 95d63137 Author: He Yin Ling <heyinling@espressif.com> Date: Sat Oct 14 09:41:03 2017 +0800 Merge branch 'bugfix/freertos_dual_core_hooks' into 'master' bugfix/freertos_dual_core_hooks See merge request !1399
Toolchain: xtensa-esp32-elf-linux64-1.22.0-73-ge28a011-5.2.0.tar.gz
Arduino core for the ESP32
セットアップ方法
espressif/arduino-esp32 – Installation Instructions – Using Arduino IDE に従ってください。
公式リンク
テスト時のバージョン:
Arduino IDE: 1.8.5
Arduino core for the ESP32:
$ cd ~/Arduino/hardware/espressif/esp32 $ git log commit 9fe32304c8c73cf16f9405cca0b925cae3dbf96a (HEAD -> master, origin/master, origin/HEAD) Author: Me No Dev <me-no-dev@users.noreply.github.com> Date: Sat Oct 14 00:08:41 2017 +0300 Update esp-idf_component.md
各種ライブラリ、ツール
SPIFFS uploader (for Arduino IDE)
Arduino IDE上で、SPIフラッシュへのデータの書き込みが行えるようになります。
公式リンク
参考:日本語解説(非公式)
テスト時のバージョン:v0.1
AsyncTCP + ESPAsyncWebServer
Arduino core向けの非同期WebServerライブラリです。httpサーバーなどwebサーバーを非同期で走らせることができます。WebSocketもサポートされています。
espressifの中の人(たぶん)、me-no-devさんが開発しています。
セットアップ方法
Arduino coreのlibrariesフォルダに
- AsyncTCP
- ESPAsyncWebServer
をクローンします。
#!bash cd ${arduino_core_directory}/libraries git clone https://github.com/me-no-dev/AsyncTCP git clone https://github.com/me-no-dev/ESPAsyncWebServer
ここで、
${arduino_core_directory}
はArduino coreをインストールした場所です。
linuxなら
~/Arduino/hardware/espressif/esp32
windowsなら
C:/Users/[YOUR_USER_NAME]/Documents/Arduino/hardware/espressif/esp32
あたりになるかと思います。
公式リンク
テスト時のバージョン:
AsyncTCP:
$ cd ~/Arduino/hardware/espressif/esp32/libraries/AsyncTCP $ git log commit a1a184a953fc62e159730df17d9be77c2a2b2853 (HEAD -> master, origin/master, origin/HEAD) Author: me-no-dev <hristo@espressif.com> Date: Fri Oct 13 10:37:57 2017 +0300 Do not go through API if already on LwIP thread
ESPAsyncWebServer:
$ cd ~/Arduino/hardware/espressif/esp32/libraries/ESPAsyncWebServer $ git log commit 313f3372c61f6f4bff9c157f02a07ba912d3d4d9 (HEAD -> master, origin/master, origin/HEAD) Author: me-no-dev <hristo@espressif.com> Date: Tue Oct 10 22:38:13 2017 +0300 Don't send Content Length for WS 101 Fixes: https://github.com/me-no-dev/ESPAsyncWebServer/issues/255 Thanks @keduro :)
ESP-IDF環境でArduinoライブラリを使う
ESP-IDFにおけるcomponentsのひとつとしてarduino-esp32を取り込むことで使えるようになります。
セットアップ方法
espressif/arduino-esp32 – Installation Instructions – Using as ESP-IDF component
WebSocketによる低遅延化
背景・目的
MKZ4ではhttpのgetリクエストで通信を行っていたため、http自体の遅延が避けられませんでした。それでもそんなに車速が出なかったため良かったのですが、速さが売りのレーシングタイプでは操縦に支障が出る程度の遅延となってしまいます。そこで、双方向通信プロトコルであるWebSocketを使うことでこれを解消します。
準備
以下の環境で行います。
- Arduino core for the ESP32
- AsyncTCP + ESPAsyncWebServer
また、必須ではないですが、
- SPIFFS uploader (for Arduino IDE)
も使えるようにしておくとサーバーにスクリプトを上げるのが楽になります。
ESP32による非同期WebServerとWebSocketの使い方(概要)
詳しい使い方は公式に書いてあるので、ここでは概要のみにとどめます。
ESP32では非同期WebサーバーのAsyncTCPとESPAsyncWebServerがArduino coreの追加ライブラリとして提供されています。これらを使うことでWebServer用のタスクがloop()とは別に走ってくれるようになり、非同期通信が行なえます。
Arduinoコード中で使用するには、まずAsyncWebServerクラスのインスタンスを作成します。これはインスタンスを作る際に指定したポートへの通信を引き受け、リクエストに応じてHandlerを呼び出してくれます。HandlerとしてWebSocket通信を扱うAsyncWebSocketクラスのインスタンスを登録すればWebSocket通信を行うことができるようになります。
例としてAsyncWebSocketのHandlerを登録する部分を抜粋したコードは以下のようになります。(これ単体では動きません。)
#include <ESPAsyncWebServer.h> constexpr int port = 80; AsyncWebServer server(port); //port番のポートをlistenするAsyncWebServerのインスタンス AsyncWebSocket ws("/ws"); // ws://IP:PORT/ws 宛のWebSocket通信を処理するHandler //WebSocketの各種イベント(接続、切断、メッセージフレームの受信など)を実際に処理する関数 void onWebSocketEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { //なにがしかの処理 } //Arduinoのsetup()関数 void setup() { ... ws.onEvent(onWebSocketEvent); //AsyncWebSocket ws にコールバックとしてonWebSocketEventを登録 server.addHandler(&ws); //AsyncWebServer server にHandlerとしてwsを登録 ... }
モーターやサーボの制御は、
//WebSocketの各種イベント(接続、切断、メッセージフレームの受信など)を実際に処理する関数 void onWebSocketEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { //なにがしかの処理 }
の部分に書くことになります。ただし、注意が必要なポイントとして、公式ページにもあるように、コールバックの中でdelayやyieldなどの関数を呼んではいけません。今回はモーターの制御において、回転方向を逆転させる際に一定時間のブレーキをdelayを用いて挟むようにしています。そのため、この関数の中で直接その処理を行うことができないため、モーターとサーボの制御を行うタスクを新しく用意し、コールバックの中ではそのタスクへのQueueに制御値を追加するだけにしています。
また、ユーザー側(操縦者側)ですが、javascriptでWebSocketが利用できるため、MKZ4のWebServerで行っていたのと同様に、AsyncWebServerの適当なアドレスにアクセスするとスクリプトを含めたhtmlファイルが返ってくるようにすればよいです。これはAsyncWebServerのon()メソッドでもできます
char *controller_html = "(html埋め込み文字列)"; void controller_html_handler(AsyncWebServerRequest *request) { request->send(200, "text/html", controller_html); } void setup() { ... server.on("/controller.html", controller_html_handler); // http://ESP32_IP:port/controller.htmlへのGETリクエストに対してcontroller_htmlの内容を返すハンドラを登録 ... }
が、次に説明するSPIFFS uploaderを利用した方法のほうが楽だと思います。
SPIFFS uploaderでサーバーにファイルをアップロード
ESP32は4MiBのSPI Flashを持っており、一部はプログラム領域として使われるのですが、余った部分をデータ記憶域として利用することができます。SPIFFSはこの領域にアクセスするPOSIXライクなファイルシステムを提供してくれるライブラリです。例えば、以下のように使えます。
void setup() { ... SPIFFS.begin(); auto f = SPIFFS.open("hoge.txt", "w"); f.println("hogehoge"); // ファイル"hoge.txt"にhogehogeと書き込まれる ... }
この領域にPCから直接ファイルを書き込めるツールがSPIFFS uploaderです。Adruinoのプロジェクトディレクトリにあるdata/ディレクトリの中身を自動的にuploadしてくれます。ESP32からは、data/をルートとしたパスでアクセスすることができます。
ここでは、Arduinoのプロジェクトディレクトリ以下が次のような構成だとします。
${Arduino_Project}/ main.ino data/ www/ controller.html
この状態でArduino IDEからSPIFFS uploaderで書き込むと、main.inoの中で、
const char *URI_Root = "/uri_root" void setup { ... SPIFFS.begin(); server.serveStatic(URI_Root, SPIFFS, "/www"); ... }
などとすることで、http://ESP_IP:port/uri_root/hogeへのリクエストがSPIFFS内の”/www/hogeファイルへのアクセスとして処理されます。また、htmlだけでなくjsファイルなども配置することができ、htmlファイル内でそれらの相対パスでの指定も可能です。つまり、普通のwebサーバー的なフォルダ構成をdata/以下に作ってしまえばあとはいいかんじにアップロードしてくれるということです。
成果
体感では遅延がかなり減った気がします。それでもかなりスピードが出るので操縦は難しいですが……。
まとめ
いかがでしょうか。今回はまだ試作の段階ですが、要望が大きければ今後製品化の可能性もあるかもしれません。レーサーミニ四駆を魔改造したい! という方はCerevoのTwitterやFacebookアカウントまでぜひご要望くださいませ。