
Lチカの次は、ラジコン戦車を動かす
前回は、ESP32の動作確認確認も含めて、LEDチカチカさせる実験を行いました。
ESP32を使って電子工作スタート
【Freenove ESP32-S3-WROOM CAMボード】LEDチカチカ(抵抗値の計算など)
【Freenove ESP32-S3-WROOM CAMボード】ブラウザやスマホからLチカのONOFF
実は、数年前に、Rasberry Pi(ARMプロセッサを搭載したシングルボードコンピュータ)を使って既にWebからラジコン戦車を動かしたことがありました。
既にやった事を何で再びやるのかと言えば、Rasberry Piに比較して、ESP32のボードは比較できないくらい安いからです。
つまり、頭脳となるコンピュータ部分が安く購入できるので、何かを組み上げた時も安く組み上げられるという事になります。
今のことろ、最初の目標としては、自動カーテン開閉装置を作成する事なのですが、その前に遊びながらノウハウを身に着けたいと考えています。
モーターを制御するドライバ使用
Rasberry Piの時も使用しましたが、「TA7291P」というパーツを使います。
ネットからデータシートを探してみました。
このパーツを使用すれば、モータ電源を分けて、Vref端子により、モータへの加圧調整ができるのでスピードコントロールなどに使えますね。
モーター部分の配線図とブレットボード組立
配線図は、Rasberry Piの時のがあったので、そのまま使う事にしました。

4番ピンのVrefに10KΩを接続しているのは、今回は微妙なモーター速度の調整はしないからです。
IN1 | IN2 | モーター動作 |
0 | 0 | ストップ |
PWM | 0 | 正転(PWM値に応じて可変) |
0 | PWM | 逆転(PWM値に応じて可変) |
1 | 1 | ブレーキ |
つまり、IN1がプラスになれば、正転になり、IN2がプラスになれば、逆転するという仕組みになります。
モータ速度の調整は次の段階になったらやってみたいと思います。

簡単なONOFFだけで動かす実験
CopilotのAIの力を借りて、WEB上でタンクの車輪を動かす実験をしてみました。
プログラムの方は数回の修正を重ねて簡単に出来上がりました。
const char* ssid = "SSID";
const char* password = "password";
const IPAddress ip(192, 168, 1, 60);
const IPAddress gateway(192, 168, 1, 1);
const IPAddress subnet(255, 255, 255, 0);
const IPAddress dns1(192, 168, 1, 1);
// 4-5番、1-2番ピンを使ってモーターを動かす
if (!WiFi.config(ip, gateway, subnet, dns1)) {
Serial.println("Failed to configure!");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.println("Connecting to WiFi...");
Serial.println("Connected to WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
digitalWrite(mot_04, LOW);
digitalWrite(mot_05, LOW);
digitalWrite(mot_01, LOW);
digitalWrite(mot_02, LOW);
server.on("/", HTTP_GET, handleRoot);
for (int i = 0; i < 4; i++) {
server.on(("/toggle_45_" + String(i)).c_str(), HTTP_GET, [i]() { toggle45(i); });
server.on(("/toggle_12_" + String(i)).c_str(), HTTP_GET, [i]() { toggle12(i); });
Serial.println("Web server started!");
Serial.println("Handling root page...");
String html = "<!DOCTYPE html><html>";
html += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
html += "<style>html { font-family: Helvetica; text-align: center;} .button { padding: 10px 20px; margin: 5px; }</style>";
html += "<h1>Motor Control</h1>";
html += "<h2>Current Patterns</h2>";
html += "<p>4 and 5: " + getRealTimeState(mot_04, mot_05) + "</p>";
html += "<p>1 and 2: " + getRealTimeState(mot_01, mot_02) + "</p>";
html += "<h2>4 and 5 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_45_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
html += "<h2>1 and 2 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_12_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
html += "</body></html>";
server.send(200, "text/html", html);
void toggle45(int pattern) {
Serial.print("Toggling 4 and 5 pins to pattern: ");
digitalWrite(mot_04, pattern & 0x01); // 最下位ビット
digitalWrite(mot_05, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
void toggle12(int pattern) {
Serial.print("Toggling 1 and 2 pins to pattern: ");
digitalWrite(mot_01, pattern & 0x01); // 最下位ビット
digitalWrite(mot_02, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
String getRealTimeState(int pin1, int pin2) {
String state1 = digitalRead(pin1) == HIGH ? "HIGH" : "LOW";
String state2 = digitalRead(pin2) == HIGH ? "HIGH" : "LOW";
return state1 + ", " + state2;
#include <WiFi.h>
#include <WebServer.h>
// WiFiの接続情報
const char* ssid = "SSID";
const char* password = "password";
const IPAddress ip(192, 168, 1, 60);
const IPAddress gateway(192, 168, 1, 1);
const IPAddress subnet(255, 255, 255, 0);
const IPAddress dns1(192, 168, 1, 1);
// 4-5番、1-2番ピンを使ってモーターを動かす
const int mot_04 = 4;
const int mot_05 = 5;
const int mot_01 = 1;
const int mot_02 = 2;
WebServer server(80);
void setup() {
Serial.begin(115200);
// WiFi設定
if (!WiFi.config(ip, gateway, subnet, dns1)) {
Serial.println("Failed to configure!");
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// モーターピン設定
pinMode(mot_04, OUTPUT);
pinMode(mot_05, OUTPUT);
pinMode(mot_01, OUTPUT);
pinMode(mot_02, OUTPUT);
// 初期値
digitalWrite(mot_04, LOW);
digitalWrite(mot_05, LOW);
digitalWrite(mot_01, LOW);
digitalWrite(mot_02, LOW);
// Webサーバールート設定
server.on("/", HTTP_GET, handleRoot);
for (int i = 0; i < 4; i++) {
server.on(("/toggle_45_" + String(i)).c_str(), HTTP_GET, [i]() { toggle45(i); });
server.on(("/toggle_12_" + String(i)).c_str(), HTTP_GET, [i]() { toggle12(i); });
}
server.begin();
Serial.println("Web server started!");
}
void loop() {
server.handleClient();
}
void handleRoot() {
Serial.println("Handling root page...");
String html = "<!DOCTYPE html><html>";
html += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
html += "<style>html { font-family: Helvetica; text-align: center;} .button { padding: 10px 20px; margin: 5px; }</style>";
html += "</head><body>";
html += "<h1>Motor Control</h1>";
// 現在のパターンを表示
html += "<h2>Current Patterns</h2>";
html += "<p>4 and 5: " + getRealTimeState(mot_04, mot_05) + "</p>";
html += "<p>1 and 2: " + getRealTimeState(mot_01, mot_02) + "</p>";
// 4番、5番ピンの切り替えボタン
html += "<h2>4 and 5 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_45_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
}
// 1番、2番ピンの切り替えボタン
html += "<h2>1 and 2 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_12_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
}
html += "</body></html>";
server.send(200, "text/html", html);
}
void toggle45(int pattern) {
Serial.print("Toggling 4 and 5 pins to pattern: ");
Serial.println(pattern);
digitalWrite(mot_04, pattern & 0x01); // 最下位ビット
digitalWrite(mot_05, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
server.send(303);
}
void toggle12(int pattern) {
Serial.print("Toggling 1 and 2 pins to pattern: ");
Serial.println(pattern);
digitalWrite(mot_01, pattern & 0x01); // 最下位ビット
digitalWrite(mot_02, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
server.send(303);
}
String getRealTimeState(int pin1, int pin2) {
String state1 = digitalRead(pin1) == HIGH ? "HIGH" : "LOW";
String state2 = digitalRead(pin2) == HIGH ? "HIGH" : "LOW";
return state1 + ", " + state2;
}
#include <WiFi.h>
#include <WebServer.h>
// WiFiの接続情報
const char* ssid = "SSID";
const char* password = "password";
const IPAddress ip(192, 168, 1, 60);
const IPAddress gateway(192, 168, 1, 1);
const IPAddress subnet(255, 255, 255, 0);
const IPAddress dns1(192, 168, 1, 1);
// 4-5番、1-2番ピンを使ってモーターを動かす
const int mot_04 = 4;
const int mot_05 = 5;
const int mot_01 = 1;
const int mot_02 = 2;
WebServer server(80);
void setup() {
Serial.begin(115200);
// WiFi設定
if (!WiFi.config(ip, gateway, subnet, dns1)) {
Serial.println("Failed to configure!");
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// モーターピン設定
pinMode(mot_04, OUTPUT);
pinMode(mot_05, OUTPUT);
pinMode(mot_01, OUTPUT);
pinMode(mot_02, OUTPUT);
// 初期値
digitalWrite(mot_04, LOW);
digitalWrite(mot_05, LOW);
digitalWrite(mot_01, LOW);
digitalWrite(mot_02, LOW);
// Webサーバールート設定
server.on("/", HTTP_GET, handleRoot);
for (int i = 0; i < 4; i++) {
server.on(("/toggle_45_" + String(i)).c_str(), HTTP_GET, [i]() { toggle45(i); });
server.on(("/toggle_12_" + String(i)).c_str(), HTTP_GET, [i]() { toggle12(i); });
}
server.begin();
Serial.println("Web server started!");
}
void loop() {
server.handleClient();
}
void handleRoot() {
Serial.println("Handling root page...");
String html = "<!DOCTYPE html><html>";
html += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
html += "<style>html { font-family: Helvetica; text-align: center;} .button { padding: 10px 20px; margin: 5px; }</style>";
html += "</head><body>";
html += "<h1>Motor Control</h1>";
// 現在のパターンを表示
html += "<h2>Current Patterns</h2>";
html += "<p>4 and 5: " + getRealTimeState(mot_04, mot_05) + "</p>";
html += "<p>1 and 2: " + getRealTimeState(mot_01, mot_02) + "</p>";
// 4番、5番ピンの切り替えボタン
html += "<h2>4 and 5 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_45_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
}
// 1番、2番ピンの切り替えボタン
html += "<h2>1 and 2 Control</h2>";
for (int i = 0; i < 4; i++) {
html += "<a href='/toggle_12_" + String(i) + "'><button class='button'>Pattern " + String(i) + "</button></a>";
}
html += "</body></html>";
server.send(200, "text/html", html);
}
void toggle45(int pattern) {
Serial.print("Toggling 4 and 5 pins to pattern: ");
Serial.println(pattern);
digitalWrite(mot_04, pattern & 0x01); // 最下位ビット
digitalWrite(mot_05, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
server.send(303);
}
void toggle12(int pattern) {
Serial.print("Toggling 1 and 2 pins to pattern: ");
Serial.println(pattern);
digitalWrite(mot_01, pattern & 0x01); // 最下位ビット
digitalWrite(mot_02, (pattern & 0x02) >> 1); // 2番目のビット
server.sendHeader("Location", "/");
server.send(303);
}
String getRealTimeState(int pin1, int pin2) {
String state1 = digitalRead(pin1) == HIGH ? "HIGH" : "LOW";
String state2 = digitalRead(pin2) == HIGH ? "HIGH" : "LOW";
return state1 + ", " + state2;
}
ブレッドボードを小さいサイズ2枚にしてマイコン部分とモーター部分を分けました。

最初なかなか動かなかったのですが、自分がブレッドボードの使い方を間違っていました。
ブレッドボードの真ん中を境に、a-eとf-jまでの2系統だったようです。自分は、a-jまで1系統だと思い込んでしまって配線ができてなかったようです。
実際に動かして、WEBから固定IP経由でアクセスするとちゃんと動くようになりました。

ESP32の端子、1、2番ピンと、4、5番ピンを使ってモーターの制御をしてみました。
戦車のモーター位置の関係で、4、5番ピンの位置を逆にしないと、車体が回転してしまいました。
これで、WEBから戦車を動かす事に成功しましたが、次回は、もっとシンプルに、前進、後進、右回転、左回転などのボタンを使って視覚的に戦車を制御してみたいと思います。