Wi-Fi通信を利用したラジコンカーの作り方を詳しく紹介します。
車体は市販の部品を組み合わせ流だけで、クローラータイプで作成します。
プログラムについてはサンプルプログラムを準備してますので、コピペで書き込むだけです。
操作にはスマホやパソコン等のブラウザを使用して動作確認までできるので、是非チャレンジしてみましょう♪
Bluetooth通信を利用したラジコンカーの作り方も以下のリンクで詳しく紹介しています。
1.作成するラジコンカーについて
・ラジコンカー本体
・駆動回路(モータードライバMX1508、マイコンボード)
・モーター(タミヤ製ダブルギヤボックス)
・電源部(モバイルバッテリー)
・操作画面の紹介(スマホ、ブラウザ)
2.簡易編
・サンプルプログラム(コピペ)
・プログラムの詳細
・動作確認
3.応用編(可変速)
・サンプルプログラム(コピペ)
・プログラムの詳細
・動作確認
4.まとめ
1.作成するラジコンカーについて
今回作るラジコンカーの部品構成について詳しく紹介します。
できるだけ市販部品の組み合わせで完成できるように部品選定しました。
モーターの配線だけはどうしても半田付けが必要になるので頑張ってチャレンジしてみましょうw
・ラジコンカー本体
ラジコンカー本体はタミヤの「ダブルギヤボックス」と「ユニバーサルプレート」「トラック&ホイールセット」を使用したクローラータイプで作成しています。
駆動回路はDCモーター駆動用ドライバ(MX1508)を使用し、制御用マイコンボードには「ATOMS3」を使用しています。
「ATOMS3」については、以下のリンクで詳しく紹介しています。
電源はモバイルバッテリー(写真では自作のものを使用)の5Vを分岐タイプのUSBケーブルで「ATOMS3」とモーター駆動回路に供給し、モーターは3Vモーターのため「三端子レギュレータ」で3.3Vに降圧して使用しています。
各部品の詳細は以下でそれぞれ詳しく紹介します。
・駆動回路(モータードライバMX1508、マイコンボード)
駆動回路は以下写真のようになります。モータ駆動用ドライバ(MX1508)とマイコンボード「ATOMS3」をミニブレッドボードに実装しています。
回路図は下画像のようになります。
3V電源はモバイルバッテリーからの5Vを三端子レギュレータを使って3.3Vに降圧して使用しています。
この駆動部については、以下のリンクでDCモーターの制御方法と合わせて詳しく紹介しています。
・モーター(タミヤ製ダブルギヤボックス)
モーターにはタミヤ製のダブルギヤボックス等を使用して下写真のように組み立てています。
タミヤ製の部品を組み合わせて作るスタンダードな構成なので、1つ作っておくと色々応用が効きます。
ドライバ基板に接続するコネクタは以下の工具とコネクタを1つ持っておくと色々応用が効くので便利です。圧着工具としては格安ですが、値段は値段なりで圧着してそのまま使えるかというと・・・そのまま使うのは難しいように思います。
圧着後に形を整えて端子を挿入することになるので手間はかかりますw、個人的には値段の安さを考えるとお得なので結構便利に使ってます。ご参考までに^^;
・電源部(モバイルバッテリー)
電源部は小型のモバイルバッテリーがあれば十分です。USBケーブルは100均で売っている下写真の「USB-microB」と「USB-TypeC」の分岐端子が便利です。
以下は今回使用した5V電源で、私がモバイルバッテリー代わりに使っているものです。
ケースは、最近はモノによっては袋になってしまいましたが「M5Stack社」のユニットを購入した時に使われているケースを加工して、9Vの充電電池とブレッドボード用の「5V、3.3V、USB-TypeA付電源基板」を加工して作成したモノです。
ケースの加工と基板の加工が結構手間ですが便利に使っています。
写真の右が加工前の基板で左が加工したものです。(ゴム足も100均で売っているものなので探してみましょう♪)
以下は100均で売ってるので探してみましょう♪
・操作画面の紹介(スマホ、ブラウザ)
操作画面にはスマホやパソコンのブラウザ画面を使用します。
マイコンボードの「ATOMS3」をサーバーとして設定し液晶画面に表示される「SSID(自宅のネットワーク環境)」に接続して「IPアドレス」を入力することで以下の画面が表示されます。
簡易編
「簡易編」では上画像のような画面が表示され、各ボタンを押すことでラジコンカーを操作できます。
ブラウザ画面のプログラムをできるだけシンプルにしていますので、各ボタンを押すたびに画面が更新されます。
速度についてはプログラムの中で「0〜255」で指定します。初期値は「127」にしています。
応用編(可変速)
「応用編」では基本的な操作は「簡易編」と同じですが、画面下のスライドバーで速度を可変できるようにしています。
「簡易編」とは違い各ボタンを押しても画面は更新されません。モーターを可変速するためのデータを送信することもできます。
このため、ブラウザプログラムには「Javascript」を使用しているのでプログラムは少し長くなります。
2.簡易編
簡易編では、以前紹介した以下リンクのDCモーター駆動プログラムにWi-Fi操作用のプログラムを追加したものです。
操作画面のブラウザページはできるだけシンプルに作成しています。
・サンプルプログラム(コピペ)
簡易編のサンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include <M5AtomS3.h> // ATOMS3用ライブラリ
#include <WiFi.h> // WiFi接続用
#include <WebServer.h> // サーバー設定用
#define INT1 8 // モーター1正転信号端子(INT1)
#define INT2 7 // モーター1逆転信号端子(INT2)
#define INT3 6 // モーター2正転信号端子(INT3)
#define INT4 5 // モーター2逆転信号端子(INT4)
#define CH_INT1 0 // PWM出力チャンネル(0,1/ 2,3/ 4,5/ 6,7/ 8,9/ 10,11 /12,13 /14,15で周波数、分解能設定が共通)
#define CH_INT2 1 // ※チャンネル7は液晶バックライトと共用になるため指定禁止
#define CH_INT3 2
#define CH_INT4 3
#define FREQ 500 // PWM出力周波数(200〜1000推奨 ※高いとトルク低下)
#define BIT_NUM 8 // PWM出力bit数(8bit固定)
// サーバー設定、ポート80で接続
WebServer server(80);
// 変数宣言
int speed = 127; // モーター速度指定値格納用
// HTML 文字列を作成する ※R"( ここに書いたものは改行も無視して文字列として扱われる )"
char html[] = R"(
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {display: flex;flex-direction: column;align-items: center;}
h1 {text-align: center;font-size: 16px;margin: 0;color: #555;}
hr {width: 320px;border-color: #aaa;border-width: 1px;}
.button-group {display: flex;justify-content: center;margin: 5px;}
.button-group > button {width: 100px;height: 70px;background-color: #eaeaea;border: none;border-radius: 5px;font-size: 16px;margin: 0 5px;user-select: none;}
</style>
<title>Wi-Fi MOTOR CONTROLLER</title>
</head>
<body>
<h1>Wi-Fi MOTOR CONTROLLER</h1>
<hr>
<div class="button-group">
<button onclick="location.href='/get/for_left'">左前</button>
<button onclick="location.href='/get/forword'">前進</button>
<button onclick="location.href='/get/for_right'">右前</button>
</div>
<div class="button-group">
<button onclick="location.href='/get/left_turn'">左旋回</button>
<button onclick="location.href='/get/stop'">停止</button>
<button onclick="location.href='/get/right_turn'">右旋回</button>
</div>
<div class="button-group">
<button onclick="location.href='/get/rev_left'">左後</button>
<button onclick="location.href='/get/reverse'">後退</button>
<button onclick="location.href='/get/rev_right'">右後</button>
</div>
</body>
</html>
)";
// PWM出力実行関数 **************************************************************
void pwmOutput(int duty1, int duty2, int duty3, int duty4) {
ledcWrite(CH_INT1, duty1); // PWM出力実行(チャンネル, Duty比)
ledcWrite(CH_INT2, duty2);
ledcWrite(CH_INT3, duty3);
ledcWrite(CH_INT4, duty4);
}
// サーバーリクエスト時処理関数 ****************************************************
// ルート(IPアドレス)アクセス時の応答関数
void handleRoot() {
server.send(200, "text/html", html); // レスポンス200を返しhtml送信
}
// エラー(Webページが見つからない)時の応答関数
void handleNotFound() {
server.send(404, "text/plain", "404 Not Found!"); // text送信
}
// GET受信(モーター動作)処理関数 *************************************************
void forLeft() { // 左前進
pwmOutput(speed/2, 0, speed, 0); // PWM出力実行関数呼び出し
server.send(200, "text/html", html); // レスポンス200を返しhtml送信
}
void forword() { // 前進
pwmOutput(speed, 0, speed, 0);
server.send(200, "text/html", html);
}
void forRight() { // 右前進
pwmOutput(speed, 0, speed/2, 0);
server.send(200, "text/html", html);
}
void leftTurn() { // 左旋回
pwmOutput(0, speed, speed, 0);
server.send(200, "text/html", html);
}
void stop() { // 停止
pwmOutput(255, 255, 255, 255); // ブレーキ停止
delay(300);
pwmOutput(0, 0, 0, 0); // フリー停止
server.send(200, "text/html", html);
}
void rightTurn() { // 右旋回
pwmOutput(speed, 0, 0, speed);
server.send(200, "text/html", html);
}
void revLeft() { // 左後退
pwmOutput(0, speed/2, 0, speed);
server.send(200, "text/html", html);
}
void reverse() { // 後退
pwmOutput(0, speed, 0, speed);
server.send(200, "text/html", html);
}
void revRight() { // 右後退
pwmOutput(0, speed, 0, speed/2);
server.send(200, "text/html", html);
}
// 初期設定 ----------------------------------------------------------
void setup() {
M5.begin(true, true, false, false); // AtomS3初期設定(LCD,UART,I2C,LED)
USBSerial.begin(9600); // シリアル通信初期化
delay(1000); // シリアル出力開始待ち
USBSerial.println("ATOM S3 Wi-Fi MOTOR CONTROLLER!!"); // シリアル出力
// PWM出力に使用する端子を出力設定
pinMode(INT1, OUTPUT); // モーター1正転
pinMode(INT2, OUTPUT); // モーター1逆転
pinMode(INT3, OUTPUT); // モーター2正転
pinMode(INT4, OUTPUT); // モーター2逆転
// PWM初期設定
ledcSetup(CH_INT1, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT2, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT3, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT4, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcAttachPin(INT1, CH_INT1); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター1正転)
ledcAttachPin(INT2, CH_INT2); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター1逆転)
ledcAttachPin(INT3, CH_INT3); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター2正転)
ledcAttachPin(INT4, CH_INT4); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター2逆転)
// 液晶表示初期化
M5.Lcd.begin(); // 画面初期化
M5.Lcd.setRotation(2); // 画面向き設定(USB位置基準 0:上/ 1:左/ 2:下/ 3:右)
M5.Lcd.fillScreen(BLACK); // 背景
M5.Lcd.println("Wi-Fi Serch!"); // Wi-Fi接続画面表示
M5.Lcd.println(WiFi.softAPIP()); // SSID画面表示
// Wi-Fiアクセスポイント接続
// const char ssid[] = "ATOMS3-01"; // SSID
// const char pass[] = "11111111"; // パスワード(8文字以上)
// WiFi.softAP(ssid, pass); // ソフトAP開始
// Wi-Fiローカル接続
const char ssid[] = "自宅のWiFi接続先を記入"; //接続先SSID
const char pass[] = "接続先のパスワードを記入"; //接続先パスワード
WiFi.begin(ssid, pass); // Wi-Fi接続開始
while (WiFi.status() != WL_CONNECTED) { // 接続完了するまで繰り返す
delay(500); // 0.5秒待機
M5.lcd.print("."); //「.」画面表示
}
// サーバー設定(クライアントからのリクエストに応じて設定した関数を実行)
server.on("/", handleRoot); // ルート(IPアドレス)アクセス時の応答関数を設定
server.onNotFound(handleNotFound); // Webページが見つからない時の応答関数を設定
server.on("/get/for_left", forLeft); // 左前進ボタンオン受信処理
server.on("/get/forword", forword); // 前進ボタンオン受信処理
server.on("/get/for_right", forRight); // 右前進ボタンオン受信処理
server.on("/get/left_turn", leftTurn); // 左旋回ボタンオン受信処理
server.on("/get/stop", stop); // 停止ボタンオン受信処理
server.on("/get/right_turn", rightTurn); // 右旋回ボタンオン受信処理
server.on("/get/rev_left", revLeft); // 左後退ボタンオン受信処理
server.on("/get/reverse", reverse); // 後退ボタンオン受信処理
server.on("/get/rev_right", revRight); // 右後退ボタンオン受信処理
server.begin(); // Webサーバー開始
}
// メイン -------------------------------------------------------------------
void loop() {
server.handleClient(); // クライアントからのアクセス確認
// 液晶表示
M5.Lcd.setTextColor(WHITE, BLACK); // 文字色
M5.Lcd.setTextFont(2); // フォント
M5.Lcd.drawCentreString("WiFi Motor", M5.lcd.width()/2, 0, 4); // 上中央座標を基準に文字表示(表示内容, x, y, フォント)
M5.Lcd.drawCentreString("Remote Controller", M5.lcd.width()/2, 21, 2); // 上中央座標を基準に文字表示(表示内容, x, y, フォント)
M5.Lcd.drawFastHLine(0, 38, 128, WHITE); // 指定座標から横線
M5.Lcd.setCursor(0, 40); // カーソル座標指定
M5.Lcd.printf("SSID: %s\n", WiFi.SSID()); // SSID表示
// M5.Lcd.printf("SSID: %s\n", WiFi.softAPSSID()); // アクセスポイント時のSSID表示
M5.Lcd.setTextColor(ORANGE, BLACK); // 文字色
M5.Lcd.print("IP : "); // IPアドレス表示
M5.Lcd.println(WiFi.localIP());
// M5.Lcd.println(WiFi.softAPIP()); // アクセスポイント時のIPアドレス表示
M5.Lcd.drawFastHLine(0, 74, 128, WHITE); // 指定座標から横線
M5.Lcd.setCursor(0, 78); // カーソル座標指定
M5.Lcd.setTextColor(CYAN, BLACK); // 文字色
M5.Lcd.printf("Speed:%4d \n", speed); // モーター速度指定値表示
M5.Lcd.printf("%3d, %3d, %3d, %3d\n", ledcRead(CH_INT1), ledcRead(CH_INT2), ledcRead(CH_INT3), ledcRead(CH_INT4)); // 各Duty比取得
delay(100); // 遅延時間
}
・プログラムの詳細
「21行目」でモーターの速度を指定しています。
設定範囲は「0〜255(0〜3.3V)」で初期値は「127(約1.5V駆動相当)」に設定しています。
大きくするとモーターの回転速度が速くなるので必要に応じて調整してください。
「24〜59行目」がスマホやパソコンのブラウザで表示されるラジコン操作画面の「HTMLプログラム」です。
「R”( ここにHTMLプログラムを記入 )” 」ここにコピペで貼り付けるだけで表示できます。
私は「HTML」や「JavaScript」は苦手なので、ほとんど「ChatGPT」に書いてもらってますw
ChatGPTについては、以下のリンクで紹介しています。
「76〜114行目」にはクライアント(ブラウザ)からの「リクエスト」に対する「レスポンス」と一緒に実行するプログラム(PWM出力実行関数呼び出し)を記入しています。
ここで操作画面のボタンに応じてモーターの動作を制御しています。
「160〜168行目」でサーバー(ATOMS3)にアクセスがあった時に実行する関数を指定しています。
「Arduinoコマンド」を使用した「サーバー」の使い方は以下のリンクで詳しく紹介していいます。
「150〜156行目」で「Wi-Fi通信」を開始しています。
ご自宅のネットワーク環境の「SSID」と「パスワード」をプログラム内に設定(150, 151行目)して「ATOMS3」の液晶画面に表示される「IPアドレス(192.169.0.xxx)」をスマホやパソコンのブラウザのアドレスバーに入力するだけで接続されて、操作画面が表示されます。
アクセスポイントへの接続プログラムについては、以下のリンクで詳しく紹介しています。
・動作確認
プログラムを書き込んで実行すると、自宅のWi-Fiネットワークに接続が開始されます。
接続が完了すると以下のような画面が「ATOMS3」の画面に表示されます。
液晶画面の中で「192.168.0.2(お使いの環境によって異なります)」が接続先アドレスです。
これを、スマホやパソコンのブラウザ(ChromeやEdge等なんでも良いです)のアドレスバーに入力すると以下のような画面が表示されます。
あとは、各ボタンを押すとラジコンカーが動作します。
速度についてはプログラムの中で指定(21行目)していますので、必要に応じて書き換えてください。
3.応用編(可変速)
応用編のプログラムも、以前紹介した以下リンクのDCモーター駆動プログラムにWi-Fi操作用のプログラムを追加したものです。
操作画面のブラウザページには「JavaScript」を使用して、より実用的な操作画面にしています。
「JavaScript」を使用すルコとでページの更新もなく、データの送信もできるのでモーターの速度をブラウザ画面のスライドバーで可変速できるようにしています。
・サンプルプログラム(コピペ)
応用編のサンプルプログラムは以下になります。コピペで貼り付けて書き込んで実行してください。
※下コード(黒枠)内の右上角にある小さなアイコンのクリックでコピーできます。
#include <M5AtomS3.h> // ATOMS3用ライブラリ
#include <WiFi.h> // WiFi接続用
#include <WebServer.h> // サーバー設定用
#include <ArduinoJson.h> // JSONデータ処理用
#define INT1 8 // モーター1正転信号端子(INT1)
#define INT2 7 // モーター1逆転信号端子(INT2)
#define INT3 6 // モーター2正転信号端子(INT3)
#define INT4 5 // モーター2逆転信号端子(INT4)
#define CH_INT1 0 // PWM出力チャンネル(0,1/ 2,3/ 4,5/ 6,7/ 8,9/ 10,11 /12,13 /14,15で周波数、分解能設定が共通)
#define CH_INT2 1 // ※チャンネル7は液晶バックライトと共用になるため指定禁止
#define CH_INT3 2
#define CH_INT4 3
#define FREQ 500 // PWM出力周波数(200〜1000推奨 ※高いとトルク低下)
#define BIT_NUM 8 // PWM出力bit数(8bit固定)
// サーバー設定、ポート80で接続
WebServer server(80);
// JSONデータ格納用メモリの確保
StaticJsonDocument<200> json;
// 変数宣言
int speed; // モーター速度指定値格納用
// HTML 文字列を作成する ※R"( ここに書いたものは改行も無視して文字列として扱われる )"
char html[] = R"(
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {display: flex;flex-direction: column;align-items: center;}
h1 {text-align: center;font-size: 16px;margin: 0;color: #555;}
hr {width: 320px;border-color: #aaa;border-width: 1px;}
.button-group {display: flex;justify-content: center;margin: 5px;}
.button-group > button {width: 100px;height: 70px;background-color: #eaeaea;border: none;border-radius: 5px;font-size: 16px;margin: 0 5px;user-select: none;}
label {display: block;margin-top: 5px;color: #555;text-align: center;}
output {display: inline-block;width: 30px;}
input[type="range"] { height: 10px;width: 320px;}
</style>
<title>Wi-Fi MOTOR CONTROLLER</title>
</head>
<body>
<h1>Wi-Fi MOTOR CONTROLLER</h1>
<hr>
<div class="button-group">
<button id="button1">左前</button>
<button id="button2">前進</button>
<button id="button3">右前</button>
</div>
<div class="button-group">
<button id="button4">左旋回</button>
<button id="button5">停止</button>
<button id="button6">右旋回</button>
</div>
<div class="button-group">
<button id="button7">左後</button>
<button id="button8">後退</button>
<button id="button9">右後</button>
</div>
<div>
<label for="motor-slider">SPEED:
<output id="motor-value">127</output>
</label>
</div>
<input type="range" id="motor-slider" min="0" max="255">
<script>
let buttons = {
button1: "/get/for_left",
button2: "/get/forword",
button3: "/get/for_right",
button4: "/get/left_turn",
button5: "/get/stop",
button6: "/get/right_turn",
button7: "/get/rev_left",
button8: "/get/reverse",
button9: "/get/rev_right"
};
Object.entries(buttons).forEach(([buttonId, linkAddress]) => {
let button = document.getElementById(buttonId);
button.addEventListener("click", function(event) {
let linkAddress = buttons[event.target.id];
if (linkAddress) {
fetch(linkAddress)
.then(function(response) {
if (response.ok) {
response.text().then(function(text) {
console.log(text);
});
} else {
console.error("レスポンスがエラーを返しました。ステータスコード: " + response.status);
}
})
.catch(function(error) {
console.error("通信中にエラーが発生しました:", error);
});
}
});
});
const motorSlider = document.getElementById("motor-slider");
const motorValue = document.getElementById("motor-value");
motorSlider.addEventListener("input", () => {
motorValue.innerHTML = motorSlider.value;
});
function postSpeed() {
let speed = motorSlider.value;
fetch('/post/motor_value', {
method: 'POST',
body: JSON.stringify({ motor_speed : speed }),
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => { console.log('Success:', data); })
.catch((error) => { console.error('Error:', error); });
}
motorSlider.addEventListener("mouseup", () => { postSpeed(); });
motorSlider.addEventListener("touchend", () => { postSpeed(); });
window.addEventListener("load", () => { postSpeed(); });
</script>
</body>
</html>
)";
// PWM出力実行関数 **************************************************************
void pwmOutput(int duty1, int duty2, int duty3, int duty4) {
ledcWrite(CH_INT1, duty1); // PWM出力実行(チャンネル, Duty比)
ledcWrite(CH_INT2, duty2);
ledcWrite(CH_INT3, duty3);
ledcWrite(CH_INT4, duty4);
}
// サーバーリクエスト時処理関数 ****************************************************
// ルート(IPアドレス)アクセス時の応答関数
void handleRoot() {
server.send(200, "text/html", html); // レスポンス200を返しhtml送信
}
// エラー(Webページが見つからない)時の応答関数
void handleNotFound() {
server.send(404, "text/plain", "404 Not Found!"); // text送信
}
// GET受信(モーター動作)処理関数 *************************************************
void forLeft() { // 左前進
pwmOutput(speed/2, 0, speed, 0); // PWM出力実行関数呼び出し
server.send(200, "text/plain", "for_left"); // レスポンス200を返し動作状態送信
}
void forword() { // 前進
pwmOutput(speed, 0, speed, 0);
server.send(200, "text/plain", "forword");
}
void forRight() { // 右前進
pwmOutput(speed, 0, speed/2, 0);
server.send(200, "text/plain", "for_right");
}
void leftTurn() { // 左旋回
pwmOutput(0, speed, speed, 0);
server.send(200, "text/plain", "for_left");
}
void stop() { // 停止
pwmOutput(255, 255, 255, 255); // ブレーキ停止
delay(300);
pwmOutput(0, 0, 0, 0); // フリー停止
server.send(200, "text/plain", "stop");
}
void rightTurn() { // 右旋回
pwmOutput(speed, 0, 0, speed);
server.send(200, "text/plain", "right_turn");
}
void revLeft() { // 左後退
pwmOutput(0, speed/2, 0, speed);
server.send(200, "text/plain", "rev_left");
}
void reverse() { // 後退
pwmOutput(0, speed, 0, speed);
server.send(200, "text/plain", "reverse");
}
void revRight() { // 右後退
pwmOutput(0, speed, 0, speed/2);
server.send(200, "text/plain", "rev_right");
}
// POST受信(モーター速度指定値)、速度更新処理関数 *******************************************
void postMotorValue() {
server.send(200, "text/plain", server.arg(0)); // 受信した結果(200:成功)と受信データを文字列で返す
String json_data = server.arg(0); // 受信したJSON形式データを文字列として格納
DeserializationError error = deserializeJson(json, json_data); // JSON形式データに変換(エラーも確認)
// 受信したJSONデータ処理
if (error) { // エラーの場合
USBSerial.print(F("deserializeJson() failed: ")); // エラーメッセージの表示
USBSerial.println(error.f_str());
} else { // 正常な場合
speed = (int)json["motor_speed"]; // キー"motor_speed"の値を取得
}
// モーター速度更新
int change_speed[4] = {0}; // モーター速度更新Duty値格納配列(0で初期化)
USBSerial.printf("%d, %d, %d, %d\n", ledcRead(CH_INT1), ledcRead(CH_INT2), ledcRead(CH_INT3), ledcRead(CH_INT4));
if (ledcRead(CH_INT1)) { change_speed[0] = speed; } // 現在のDuty比を取得して0でなければ速度更新
if (ledcRead(CH_INT2)) { change_speed[1] = speed; }
if (ledcRead(CH_INT3)) { change_speed[2] = speed; }
if (ledcRead(CH_INT4)) { change_speed[3] = speed; }
pwmOutput(change_speed[0], change_speed[1], change_speed[2], change_speed[3]); // PWM出力実行関数呼び出し
}
// 初期設定 ----------------------------------------------------------
void setup() {
M5.begin(true, true, false, false); // AtomS3初期設定(LCD,UART,I2C,LED)
USBSerial.begin(9600); // シリアル通信初期化
delay(1000); // シリアル出力開始待ち
USBSerial.println("ATOM S3 Wi-Fi MOTOR CONTROLLER!!"); // シリアル出力
// PWM出力に使用する端子を出力設定
pinMode(INT1, OUTPUT); // モーター1正転
pinMode(INT2, OUTPUT); // モーター1逆転
pinMode(INT3, OUTPUT); // モーター2正転
pinMode(INT4, OUTPUT); // モーター2逆転
// PWM初期設定
ledcSetup(CH_INT1, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT2, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT3, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcSetup(CH_INT4, FREQ, BIT_NUM); // PWM設定(チャンネル, 周波数, bit数)
ledcAttachPin(INT1, CH_INT1); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター1正転)
ledcAttachPin(INT2, CH_INT2); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター1逆転)
ledcAttachPin(INT3, CH_INT3); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター2正転)
ledcAttachPin(INT4, CH_INT4); // PWMチャンネル割り当て(端子番号, チャンネル)(モーター2逆転)
// 液晶表示初期化
M5.Lcd.begin(); // 画面初期化
M5.Lcd.setRotation(2); // 画面向き設定(USB位置基準 0:上/ 1:左/ 2:下/ 3:右)
M5.Lcd.fillScreen(BLACK); // 背景
M5.Lcd.println("Wi-Fi Serch!"); // Wi-Fi接続画面表示
M5.Lcd.println("Waiting"); // Wi-Fi接続画面表示
// Wi-Fiアクセスポイント接続
// const char ssid[] = "ATOMS3-01"; // SSID
// const char pass[] = "11111111"; // パスワード(8文字以上)
// WiFi.softAP(ssid, pass); // ソフトAP開始
// Wi-Fiローカル接続
const char ssid[] = "自宅のWiFi接続先を記入"; //接続先SSID
const char pass[] = "接続先のパスワードを記入"; //接続先パスワード
WiFi.begin(ssid, pass); // Wi-Fi接続開始
while (WiFi.status() != WL_CONNECTED) { // 接続完了するまで繰り返す
delay(500); // 0.5秒待機
M5.lcd.print("."); //「.」画面表示
}
M5.Lcd.fillScreen(BLACK); // 背景
// サーバー設定(クライアントからのリクエストに応じて設定した関数を実行)
server.on("/", handleRoot); // ルート(IPアドレス)アクセス時の応答関数を設定
server.onNotFound(handleNotFound); // Webページが見つからない時の応答関数を設定
server.on("/get/for_left", forLeft); // 左前進ボタンオン受信処理
server.on("/get/forword", forword); // 前進ボタンオン受信処理
server.on("/get/for_right", forRight); // 右前進ボタンオン受信処理
server.on("/get/left_turn", leftTurn); // 左旋回ボタンオン受信処理
server.on("/get/stop", stop); // 停止ボタンオン受信処理
server.on("/get/right_turn", rightTurn); // 右旋回ボタンオン受信処理
server.on("/get/rev_left", revLeft); // 左後退ボタンオン受信処理
server.on("/get/reverse", reverse); // 後退ボタンオン受信処理
server.on("/get/rev_right", revRight); // 右後退ボタンオン受信処理
server.on("/post/motor_value", postMotorValue); // モーター速度指定値POST受信処理
server.begin(); // Webサーバー開始
}
// メイン -------------------------------------------------------------------
void loop() {
server.handleClient(); // クライアントからのアクセス確認
// 液晶表示
M5.Lcd.setTextColor(WHITE, BLACK); // 文字色
M5.Lcd.setTextFont(2); // フォント
M5.Lcd.drawCentreString("WiFi Motor", M5.lcd.width()/2, 0, 4); // 上中央座標を基準に文字表示(表示内容, x, y, フォント)
M5.Lcd.drawCentreString("Remote Controller", M5.lcd.width()/2, 21, 2); // 上中央座標を基準に文字表示(表示内容, x, y, フォント)
M5.Lcd.drawFastHLine(0, 38, 128, WHITE); // 指定座標から横線
M5.Lcd.setCursor(0, 40); // カーソル座標指定
M5.Lcd.printf("SSID: %s\n", WiFi.SSID()); // SSID表示
// M5.Lcd.printf("SSID: %s\n", WiFi.softAPSSID()); // アクセスポイント時のSSID表示
M5.Lcd.setTextColor(ORANGE, BLACK); // 文字色
M5.Lcd.print("IP : "); // IPアドレス表示
M5.Lcd.println(WiFi.localIP());
// M5.Lcd.println(WiFi.softAPIP()); // アクセスポイント時のIPアドレス表示
M5.Lcd.drawFastHLine(0, 74, 128, WHITE); // 指定座標から横線
M5.Lcd.setCursor(0, 78); // カーソル座標指定
M5.Lcd.setTextColor(CYAN, BLACK); // 文字色
M5.Lcd.printf("Speed:%4d \n", speed); // モーター速度指定値表示
M5.Lcd.printf("%3d, %3d, %3d, %3d \n", ledcRead(CH_INT1), ledcRead(CH_INT2), ledcRead(CH_INT3), ledcRead(CH_INT4)); // 各Duty比取得
delay(100); // 遅延時間
}
・プログラムの詳細
「27〜132行目」がスマホやパソコンのブラウザで表示されるラジコン操作画面の「HTML」と「JavaScript」のプログラムです。
「R”( ここにHTMLプログラムを記入 )” 」ここにコピペで貼り付けるだけで表示できます。
私は「HTML」や「JavaScript」は苦手なので、ほとんど「ChatGPT」に書いてもらってますw
ChatGPTについては、以下のリンクで紹介しています。
「149〜187行目」にはクライアント(ブラウザ)からの「リクエスト」に対する「レスポンス」と一緒に、実行するプログラム(PWM出力実行関数呼び出し)を記入しています。
ここで操作画面のボタンに応じてモーターの動作を制御しています。
「189〜209行目」では操作画面のスライドバーを操作した時にモーターの速度(設定範囲0〜255)が送信されて速度を更新しています。
受信されたデータは「JSON」形式のデータで処理されて、速度指定変数「speed」に設定されて速度が更新されます。
「258〜267行目」でサーバー(ATOMS3)にアクセスがあった時に実行する関数を指定しています。
「Arduinoコマンド」を使用した「サーバー」の使い方は以下のリンクで詳しく紹介していいます。
「245-253行目」で「Wi-Fi通信」を開始しています。
ご自宅のネットワーク環境の「SSID」と「パスワード」をプログラム内に設定(246, 247行目)して「ATOMS3」の液晶画面に表示される「IPアドレス(192.169.0.xxx)」をスマホやパソコンのブラウザのアドレスバーに入力するだけで接続されて、操作画面が表示されます。
アクセスポイントへの接続プログラムについては、以下のリンクで詳しく紹介しています。
・動作確認
プログラムを書き込んで実行すると、自宅のWi-Fiネットワークに接続が開始されます。
接続が完了すると以下のような画面が「ATOMS3」の画面に表示されます。
液晶画面の中で「192.168.0.2(お使いの環境によって異なります)」が接続先アドレスです。
これを、スマホやパソコンのブラウザ(ChromeやEdge等なんでも良いです)のアドレスバーに入力すると以下のような画面が表示されます。
あとは、各ボタンを押すとラジコンカーが動作します。
速度については画面下部のスライドバーを操作することで速度を変更することができます。
4.まとめ
Wi-Fi通信を利用したラジコンカーの作り方を詳しく紹介しました。
自作のラジコンカーを作る時には、タミヤのキットを使用すると手軽に作成できますが、使用されているモーターの電圧は3Vや1.5Vのものが多いです。
一方、モーターを制御するためのマイコンボード(今回は「ATOMS3」使用)は5Vで、USBの5V電源を使用して使う物が多いです。
このため、電源として使用する5Vからモーター駆動用の3V電源を得るために、今回は「3端子レギュレーター」を使用して、モーターを駆動するドライバも電圧範囲が「2〜10V(制御電圧2〜7V)」のMX1508搭載のものを使用しました。
使用したマイコンボード「ATOMS3」は小型ながら液晶画面やWi-Fi通信も搭載しているため、必要部品をミニブレッドボードに実装することでコンパクトな構成で実現でき、モーターを駆動させるものの動力源として色々便利に使えると思います。
遠隔操作する方法としては、クライアント(ブラウザ)からの「リクエスト」に対して「レスポンス」を返すという「サーバーの仕組み」を利用しました。
応答速度についてはネットワーク環境に依存するため、反応が遅れて思い通りに動かないこともよくあり、速度の速いラジコンの操作は難しいかもしれません・・・
しかし、今回の「サーバーの仕組み」を利用すれば稼働監視や遠隔操作を行う「IoT」でのデータ取得やデータ送信の理解にもつながるのため、遊びながらこの仕組みを理解して応用できるようになりましょう。
「IoT」で稼働監視や遠隔操作を行う方法については、以下のリンクで詳しく紹介しています。
コメント