プログラムと電子工作・ラジコンカー(4)スマホで自在に走行
さあ、やっと最後の第4段階です。スマートフォンのジョイスティック風アプリで、ラジコンカーを自在に操作しましょう。
第2段階で、プログラムした走行パターンどおりに、車体が走るようになっていますね。次は、第3段階のジョイスティック風アプリで操作できるように、第2段階のスケッチを改造します。
以前作成した「プログラムと電子工作・Webサーバ」を思い出してください。M5StickC Plus に実装した index.html と PCのブラウザで通信しました。
今度は、第3段階で作成したジョイスティック風アプリの index.html とモーター制御の drive.html を M5StickC Plus に実装します。
index.html ファイルから index_html.h を作成するには、「プログラムと電子工作・開発ツール html2h.py」を使用します。
自分のスマートフォンで、ラジコンカーが自由自在に走るようになった瞬間の達成感が半端ないです。プログラミングと電子工作だけでなく、完成後のプレイも存分に楽しんでください。
目標
スマートフォンの画面を指でなぞったとおりに、ラジコンカーを走行させます。
部品・機材
使用する部品は次のとおりです。
電子部品
M5StickC Plus 1台
第2段階で製作したラジコンカー車体
開発用機材
PC(Windows10 または 11)、開発環境 Arduino-IDE、Python 導入ずみ
USB-A・USB-C ケーブル
スマートフォン
Wi-Fi ルータ
開発手順
index_html.h の作成
PC のテキストエディタもしくはメモ帳で、「プログラムと電子工作・開発ツール html2h.py」の html2h.py を作成する。日本語の文字コードは UTF-8で保存してください。
html2h.py を使用して 第3段階で作成した index.html から index_html.h を作成する。
Windows のコマンドプロンプト
> cd <index.html を保存しているフォルダ>
> python3 html2h.py index.html > index_html.h
コマンドプロンプトを開き、「cd△」を入力後、index.html を保存しているフォルダのアイコンをエクスプローラーからコマンドプロンプトへドラッグ&ドロップすると、1行目が簡単に入力できます。(△:半角スペース)
ラジコンカーの準備
全体回路図にしたがって M5StickC Plus(電源OFF)をラジコンカー車体(電源OFF)に接続する。
PC と M5StickC Plus を USBケーブルで接続する。
スケッチ radiconcar.ino、index_html.h、drive.cpp、drive.h を robotcar フォルダ内に作成する。
Arduino-IDE でスケッチ radiconcar.ino を開く。
検証・コンパイルする。
M5StickC Plus に書き込む。
USBケーブルを M5StickC Plus から抜く。
ラジコンカー車体の電源を ONして、M5StickC Plus のディスプレイに表示される IPアドレスをメモする。
スマートフォンの準備と動作テスト
スマートフォンを M5StickC Plus と同じ Wi-Fiルータに接続する。
スマートフォンのブラウザ(Chrome、Safariなど)を開き、検索欄に M5StickC Plus の IPアドレスを入力する。例えば、「192.168.1.23」
スマートフォンのジョイスティック風アプリが開いたら、「プログラムと電子工作・ラジコンカー(3)ジョイスティック風アプリ」の図1 のように操作する。
回路図
M5StickC Plus2には対応していません。Plus2は電源回路が、無印、Plusとは大きく異なります。6Vを入力すると故障するおそれがあります。
ラジコンカーの車体
スケッチ
Webサーバ
「プログラムと電子工作・Webサーバ」で作成したスケッチと同じ仕組みです。
第2段階で作成した robotcar.ino のスケッチと異なる点は、次のとおりです。
setup():①WiーFiアクセスポイントに接続する機能、②Webサーバページを登録する機能を追加します。定数 ssid、password はお手持ちの Wi-Fiルータの設定値に書き換えてください。
// それぞれのWi-Fi環境を設定する。
const char* ssid = "xxxxxxxx"; /*SSID*/
const char* password = "xxxxxxxx"; /*PASSWORD*/
WebServer server(80); /*WebServerオブジェクト*/
...
void setup() {
...
// WiーFiアクセスポイントに接続する。
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { /*Wi-Fi AP接続待ち*/
delay(500);
M5.Lcd.print(".");
}
...
// Webサーバの IPアドレスを表示する。
M5.Lcd.setCursor(0, LHEIGHT*1); /*2行目*/
M5.Lcd.printf("%s\n", WiFi.localIP().toString().c_str());
...
// Webサーバページを登録する。
server.on("/", func_index_html); /*index.html*/
server.on("/index.html", func_index_html); /*index.html*/
server.on("/drive.html", func_drive_html); /*drive.html*/
server.onNotFound(func_not_found); /*not found*/
server.begin(); /*web server start*/
}
関数 func_index_html() は、ジョイスティック風アプリのソースコードをスマートフォンに送信するだけです。変数 index_html は index_html.h に定義されています。
void func_index_html()
{
server.send(200, "text/html", index_html);
}
関数 func_drive_html() は、スマートフォンから受信した HTTPリクエストから GETパラメータ「forward」と「rotate」の値を取り出し、drive() へ渡してモーターを制御します。またスマートフォンへ HTTPレスポンスを返信します。
void func_drive_html()
{
if (HTTP_GET == server.method()) {
// --- GETメソッド
// 対応する送信 javascript は次のようになる。
// fetch('/drive.html?forward=1.0&rotate=0.5', {method: "get"});
String forward = server.arg("forward");
String rotate = server.arg("rotate");
// 前後の空白を取り除く。
forward.trim();
rotate.trim();
float f = forward.toFloat();
float r = rotate.toFloat();
Serial.printf("get forward: %6.2f, rotate: %6.2f\n", f, r);
// クライアントに返信する。
server.send(200, "text/plain", "OK");
drive(f, r);
}
}
loop():Webサーバへの HTTPリクエストを処理するコードのみになります。
void loop() {
// 自動的に繰り返し実行する処理をここに書く。
server.handleClient();
delay(1);
}
スケッチ全体
drive.cpp、drive.h は、第2段階で作成したものをそのまま使用します。radiconcar.ino と同じフォルダにコピーしてください。
定数 ssid、password はお手持ちの Wi-Fiルータの設定値に書き換えてください。
radiconcar.ino
#include <WebServer.h>
#include "drive.h"
#include "index_html.h"
#define LHEIGHT 25 /*1行の高さ(px)M5StickCPlus:25、M5StickC:15*/
// それぞれのWi-Fi環境を設定する。
const char* ssid = "xxxxxxxx"; /*SSID*/
const char* password = "xxxxxxxx"; /*PASSWORD*/
WebServer server(80); /*WebServerオブジェクト*/
//------------------------------------------------------------------------------
void setup() {
// 電源ON時に 1回だけ実行する処理をここに書く。
M5.begin(); /*M5を初期化する*/
M5.Lcd.setTextSize(2); /*文字サイズは小さめ*/
M5.Lcd.setRotation(3); /*上スイッチが左になる向き*/
Serial.begin(115200); /*デバッグ用のシリアル通信を初期化する*/
// 初期化
begin_drive();
M5.Lcd.fillScreen(BLACK); /*背景を黒にする*/
M5.Lcd.setCursor(0, LHEIGHT*0); /*1行目*/
M5.Lcd.print("radiconcar");
// WiーFiアクセスポイントに接続する。
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { /*Wi-Fi AP接続待ち*/
delay(500);
M5.Lcd.print(".");
}
M5.Lcd.fillScreen(BLACK); /*背景を黒にする*/
M5.Lcd.setCursor(0, LHEIGHT*0); /*1行目*/
M5.Lcd.print("WiFi OK");
// Webサーバの IPアドレスを表示する。
M5.Lcd.setCursor(0, LHEIGHT*1); /*2行目*/
M5.Lcd.printf("%s\n", WiFi.localIP().toString().c_str());
Serial.print("WiFi connected\r\nIP address: ");
Serial.println(WiFi.localIP());
// Webサーバページを登録する。
server.on("/", func_index_html); /*index.html*/
server.on("/index.html", func_index_html); /*index.html*/
server.on("/drive.html", func_drive_html); /*drive.html*/
server.onNotFound(func_not_found); /*not found*/
server.begin(); /*web server start*/
// 最後はモーターを停止する。
stop();
}
//------------------------------------------------------------------------------
void loop() {
// 自動的に繰り返し実行する処理をここに書く。
server.handleClient();
delay(1);
}
//------------------------------------------------------------------------------
// index.html
void func_index_html()
{
server.send(200, "text/html", index_html);
}
//------------------------------------------------------------------------------
// drive.html
void func_drive_html()
{
if (HTTP_GET == server.method()) {
// --- GETメソッド
// 対応する送信 javascript は次のようになる。
// fetch('/drive.html?forward=1.0&rotate=0.5', {method: "get"});
String forward = server.arg("forward");
String rotate = server.arg("rotate");
// 前後の空白を取り除く。
forward.trim();
rotate.trim();
float f = forward.toFloat();
float r = rotate.toFloat();
Serial.printf("get forward: %6.2f, rotate: %6.2f\n", f, r);
// クライアントに返信する。
server.send(200, "text/plain", "OK");
drive(f, r);
}
}
//------------------------------------------------------------------------------
// URIが見つからなかったとき
void func_not_found()
{
server.send(404, "text/plain", "File not found.\n\n");
}
「プログラムと電子工作・Webサーバ」の webserver.ino と第2段階「プログラムと電子工作・ラジコンカー(2)プログラムどおりに走行」の robotcar.ino、 drive.cpp、drive.h を一つに統合したものが radiconcar.ino になります。
全体の仕組みを単純な仕組みに分解して、それぞれの単純な仕組みを実現するスケッチを作成し、動作テストし、うまく行ったもの同士を最後に組み合わせて大きな仕組みを実現する手法は、回り道に見えても堅実なやり方です。
結果
スマートフォンのブラウザ(Chromeや Safari)の入力欄に、M5StickC Plus の画面に表示される IPアドレスを入力します。M5StickC Plus の Webサーバからジョイスティック風アプリを取得します。
スマートフォン画面のなるべく下の方をタップします。タップしたまま、ゆっくり指を上に滑らせます。モーターの停止状態から動き始めるまでは、前に大きく指を滑らせて大きく加速し、動き出してからは少し指を戻して減速するようにします。指を離すと停止します。
左右旋回はほんの少しだけ指を左右にずらします。慣れてくると、減速しつつ旋回し、再度加速するといったスムーズな走行ができるようになります。
ラジコンカーを走らせる様子の動画です。音が出ます。
練習問題
2台のスマートフォンから同時に同じロボットカーにアクセスしたらどうなりますか?
このラジコンカーは後退できません。後退できるようにするにはどうしたらいいと思いますか?
参考
String()
Webサーバ
Webサーバのライブラリについては、こちらの Webサイトを参考にしました。
Webサーバのサンプルスケッチは、下記のメニューにあります。
Arduino-IDE メニュー→ファイル→スケッチ例→WebServer→HelloServer
ライセンス
このページのソースコードは、複製・改変・配布が自由です。営利目的にも使用してかまいませんが、何ら責任を負いません。
謝辞
福武教育文化振興財団から 2023年度助成をいただき製作しました。