ESP-NOWを使って、モーターカーを無線操作してみましょう。
今回はKUMITATE-C3を2台使用します。
1台をリモコン側、もう1台をモーターカー側として使用し、リモコン側の4つのボタンを押すと、
- 前進
- 後退
- 左旋回
- 右旋回
できるようにします。
通信にはESP-NOWを使うため、Wi-Fiルーターは必要ありません。
ESP32同士が直接通信してモーターカーを操作します。
今回作るもの
システム全体は次のような構成です。
【リモコン側】
KUMITATE-C3
├─ ボタン1:前進
├─ ボタン2:後退
├─ ボタン3:左旋回
└─ ボタン4:右旋回
│
│ ESP-NOW
▼
【モーターカー側】
KUMITATE-C3
│
KUMITATE-MOTOR
│
├─ 左DCモーター
└─ 右DCモーター
リモコン側ではボタンの状態を読み取り、押されたボタンに対応した操作命令をESP-NOWで送信します。
モーターカー側では受信した命令に応じて左右のDCモーターを制御します。
これまで学習してきた、
ボタン入力
ESP-NOWによる無線通信
DCモーターの正転・逆転
左右2個のモーター制御
を組み合わせた作例です。
ESP-NOWの基本はこちら
この記事ではESP-NOWの初期設定やMACアドレスなどについて詳しく説明しません。
ESP-NOWそのものを初めて使う場合は、先にこちらの記事を確認してください。
ESP-NOWとは?ESP32同士で無線通信する方法を初心者向けに解説

この記事では、ESP-NOWで2台のESP32が通信できることを前提としてモーターカーを作っていきます。
用意するもの
今回は主に次のものを使用します。
- KUMITATE-C3 × 2台
- KUMITATE-MOTOR × 1台
- DCモーター × 2個
- モーターカー用の車体・タイヤ
- モーター用電源
KUMITATE-C3の1台をリモコンとして使用します。
もう1台のKUMITATE-C3をKUMITATE-MOTORと組み合わせ、モーターカーへ搭載します。
操作方法を決める
今回はKUMITATE-C3に搭載されている4つのボタンを利用します。
それぞれのボタンに動作を割り当てます。
| ボタン | 動作 |
|---|---|
| ボタン1 | 前進 |
| ボタン2 | 後退 |
| ボタン3 | 左旋回 |
| ボタン4 | 右旋回 |
| 何も押していない | 停止 |
重要なのは、何も押していないときは停止することです。
リモコンから操作している間だけモーターを動かし、ボタンを離したら停止するようにします。
操作命令を数値で表す
ESP-NOWで「前進」「後退」という文字列を送ることもできますが、今回は操作命令を数値として定義します。
たとえば、
enum Command {
STOP = 0,
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
とします。
これで、
0 = 停止
1 = 前進
2 = 後退
3 = 左旋回
4 = 右旋回
という対応になります。
リモコン側では現在のボタン状態からこの値を決定し、ESP-NOWで送信します。
受信側では届いた値を見てモーターの動作を切り替えます。
まず受信側のMACアドレスを確認する
ESP-NOWで特定のKUMITATE-C3へデータを送るため、モーターカー側のMACアドレスを確認します。
モーターカー側のKUMITATE-C3へ次のプログラムを書き込みます。
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
Serial.print("MAC Address: ");
Serial.println(WiFi.macAddress());
}
void loop() {
}
シリアルモニターに表示されたMACアドレスを控えておきます。
このアドレスをリモコン側のプログラムで使用します。
リモコン側の処理
リモコン側では、
ボタンを確認する
↓
操作命令を決める
↓
ESP-NOWで送信する
という処理を繰り返します。
考え方はシンプルです。
たとえば、
ボタン1が押された
↓
FORWARD
↓
ESP-NOW送信
となります。
ボタンを離した場合は、
ボタンなし
↓
STOP
↓
ESP-NOW送信
とします。
リモコン側のプログラム
KUMITATE-C3の4つのボタンに対応するGPIO番号を設定します。
以下のGPIO番号は使用するKUMITATE-C3のボタン割り当てに合わせて変更してください。
#include <WiFi.h>
#include <esp_now.h>
const int BUTTON_FORWARD = 0;
const int BUTTON_BACKWARD = 1;
const int BUTTON_LEFT = 2;
const int BUTTON_RIGHT = 3;
enum Command {
STOP = 0,
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
// モーターカー側KUMITATE-C3の
// MACアドレスへ変更してください
uint8_t receiverAddress[] = {
0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC
};
esp_now_peer_info_t peerInfo = {};
void setup() {
Serial.begin(115200);
pinMode(BUTTON_FORWARD, INPUT_PULLUP);
pinMode(BUTTON_BACKWARD, INPUT_PULLUP);
pinMode(BUTTON_LEFT, INPUT_PULLUP);
pinMode(BUTTON_RIGHT, INPUT_PULLUP);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW初期化失敗");
return;
}
memcpy(peerInfo.peer_addr, receiverAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("送信先登録失敗");
return;
}
}
void loop() {
Command command = STOP;
if (digitalRead(BUTTON_FORWARD) == LOW) {
command = FORWARD;
}
else if (digitalRead(BUTTON_BACKWARD) == LOW) {
command = BACKWARD;
}
else if (digitalRead(BUTTON_LEFT) == LOW) {
command = LEFT;
}
else if (digitalRead(BUTTON_RIGHT) == LOW) {
command = RIGHT;
}
esp_now_send(
receiverAddress,
(uint8_t *)&command,
sizeof(command)
);
delay(50);
}
このプログラムでは50msごとにボタンの状態を確認して操作命令を送っています。
ボタンを押していない場合もSTOPを送信するため、ボタンを離すとモーターカーが停止します。
モーターカー側の処理
受信側では、
ESP-NOWで命令を受信
↓
命令を判定
↓
左右のモーターを制御
します。
たとえばFORWARDを受信したら左右のモーターを前進方向へ回します。
LEFTなら左右のモーターに異なる動作をさせて左へ旋回します。
モーター制御用のGPIO
KUMITATE-MOTORでは左右のモーターを制御するために4本の信号を使用します。
ここでは、
const int B_1A = 7;
const int B_1B = 6;
const int A_1A = 5;
const int A_1B = 4;
とします。
これまでのモーターカーの記事と同じ構成です。
モーターカー側のプログラム
受信した操作命令によってモーターを切り替えます。
#include <WiFi.h>
#include <esp_now.h>
const int B_1A = 7;
const int B_1B = 6;
const int A_1A = 5;
const int A_1B = 4;
enum Command {
STOP = 0,
FORWARD,
BACKWARD,
LEFT,
RIGHT
};
void stopAll() {
digitalWrite(B_1A, LOW);
digitalWrite(B_1B, LOW);
digitalWrite(A_1A, LOW);
digitalWrite(A_1B, LOW);
}
void forward() {
digitalWrite(B_1A, HIGH);
digitalWrite(B_1B, LOW);
digitalWrite(A_1A, HIGH);
digitalWrite(A_1B, LOW);
}
void backward() {
digitalWrite(B_1A, LOW);
digitalWrite(B_1B, HIGH);
digitalWrite(A_1A, LOW);
digitalWrite(A_1B, HIGH);
}
void turnLeft() {
// 右モーター
digitalWrite(B_1A, HIGH);
digitalWrite(B_1B, LOW);
// 左モーター停止
digitalWrite(A_1A, LOW);
digitalWrite(A_1B, LOW);
}
void turnRight() {
// 右モーター停止
digitalWrite(B_1A, LOW);
digitalWrite(B_1B, LOW);
// 左モーター
digitalWrite(A_1A, HIGH);
digitalWrite(A_1B, LOW);
}
void onDataRecv(const esp_now_recv_info_t *info,
const uint8_t *incomingData,
int len) {
Command command;
if (len != sizeof(command)) {
return;
}
memcpy(&command, incomingData, sizeof(command));
switch (command) {
case FORWARD:
forward();
break;
case BACKWARD:
backward();
break;
case LEFT:
turnLeft();
break;
case RIGHT:
turnRight();
break;
default:
stopAll();
break;
}
}
void setup() {
Serial.begin(115200);
pinMode(B_1A, OUTPUT);
pinMode(B_1B, OUTPUT);
pinMode(A_1A, OUTPUT);
pinMode(A_1B, OUTPUT);
stopAll();
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW初期化失敗");
return;
}
esp_now_register_recv_cb(onDataRecv);
Serial.println("モーターカー受信待機中");
}
void loop() {
}
ESP-NOWでデータを受信するとonDataRecv()が呼び出されます。
受信したCommandをswitch文で判定して、
FORWARD → 前進
BACKWARD → 後退
LEFT → 左旋回
RIGHT → 右旋回
STOP → 停止
と処理を切り替えています。
実際に操作してみよう
リモコン側とモーターカー側の両方へプログラムを書き込んだら動作を確認します。
リモコンの前進ボタンを押します。
前進ボタン
↓
KUMITATE-C3
↓
ESP-NOW
↓
KUMITATE-C3
↓
KUMITATE-MOTOR
↓
左右モーター
↓
前進
ボタンを離すとSTOPが送信され、モーターが停止します。
同様に後退・左旋回・右旋回も確認してみましょう。
通信が途切れた場合にも停止させよう
無線でモーターを制御する場合、もうひとつ考えておきたいことがあります。
通信が途切れたらどうするか?
今回の単純なプログラムでは、最後に受信した命令のままモーターが動き続ける可能性があります。
たとえば前進中にリモコンの電源が切れると、新しいSTOPが届きません。
そこで実際に使用する場合は、
一定時間ESP-NOWのデータを受信しなかったら自動停止する
処理を入れておくと安全です。
たとえば最後にデータを受信した時刻を記録します。
unsigned long lastReceiveTime = 0;
データを受信したら更新します。
lastReceiveTime = millis();
そしてloop()で、
if (millis() - lastReceiveTime > 500) {
stopAll();
}
とすれば、500ms以上通信がなければモーターを停止できます。
無線で実際の機械を動かす場合は、このような通信異常時の動作も考えておくことが重要です。
ボタン操作から無線モーターカーまでつながった
今回の作例では、ひとつひとつの処理自体はそれほど複雑ではありません。
リモコン側では、
GPIOでボタンを読む
だけです。
通信部分では、
ESP-NOWで数値を送る
だけです。
モーターカー側では、
受信した数値によってGPIOを切り替える
だけです。
しかし、これらを組み合わせると、
人の操作
↓
入力
↓
無線通信
↓
マイコン
↓
モータードライバ
↓
DCモーター
↓
機械の動作
というひとつのシステムになります。
組み込み開発では、このように複数の機能を組み合わせて目的の動作を作っていきます。
KUMITATE-MOTORで試してみよう
今回のモーターカー側では、KUMITATE-C3とKUMITATE-MOTORを使用できます。
KUMITATE-MOTORでは左右のDCモーターを制御できるため、
- 前進
- 後退
- 左旋回
- 右旋回
- PWMによる速度制御
などへ発展できます。
▶ KUMITATE-MOTORの詳細はこちら
https://kumicla.tatepro.com/asp-products/kumitate-motor/
ESP-NOWと組み合わせれば、単体でモーターを動かすだけでなく、別のKUMITATE-C3から無線操作できるようになります。
次は「傾けて操作するリモコン」にしてみよう
今回は4つのボタンを使ってモーターカーを操作しました。
しかしESP-NOWで送信するデータは、ボタンの状態である必要はありません。
たとえば加速度センサーを使えば、
前へ傾ける → 前進
後ろへ傾ける → 後退
左へ傾ける → 左へ曲がる
右へ傾ける → 右へ曲がる
という操作もできます。
KUMITATE-SENSORには加速度センサーが搭載されているため、
KUMITATE-C3+KUMITATE-SENSORをコントローラー
KUMITATE-C3+KUMITATE-MOTORをモーターカー
として組み合わせることもできます。
ボタンで操作するリモコンから、コントローラーそのものを傾けて操作するモーターカーへ発展させてみましょう。
まとめ
今回はESP-NOWを使って、2台のKUMITATE-C3でモーターカーを無線操作する方法を紹介しました。
構成は、
KUMITATE-C3のボタン
↓
操作命令を作る
↓
ESP-NOWで送信
↓
別のKUMITATE-C3で受信
↓
KUMITATE-MOTORを制御
↓
モーターカーが動く
となります。
今回のポイントは、
- KUMITATE-C3を2台使って送信側と受信側に分ける
- 4つのボタンへ前後左右を割り当てる
- 操作命令をESP-NOWで送信する
- 受信した命令によって左右のモーターを制御する
- ボタンを離したら停止する
- 通信が途切れた場合にも自動停止する仕組みを入れる
というところです。
ESP-NOWで単純な数値を送るだけだった通信が、実際のモーターカーを動かすところまでつながりました。
次はボタンを使わず、加速度センサーでコントローラーの傾きを検出してモーターカーを操作してみましょう。

