【プログラム】NodeMCU+ESP32でラジコンロボット(ジョイスティック)

NodeMCU ESP2866系 NodeMCU(ESP-12F搭載)

ESP32とNodeMCU(ESP8266)間でESP-NOWという通信方法を使ってラジコンを制御するプログラムを作成します。

ESP-NOWはWi-Fiを介さずとも、低遅延で直接デバイス間通信ができるため、リモコン操作に最適です。

このプロジェクトでは、以下の2つのプログラムが必要です。

  1. 受信機 (NodeMCU + L9110S): モーターを駆動します。
  2. 送信機 (ESP32 + ジョイスティック): 操作信号を送ります。

重要: まず「受信機」のプログラムをNodeMCUに書き込み、シリアルモニタでそのMACアドレスを調べてください。その後、そのMACアドレスを「送信機」のプログラムに書き込む必要があります。


1. 受信機(NodeMCU)のプログラム

こちらを先にNodeMCUに書き込み、シリアルモニタを開いてMACアドレスを確認してください。

必要なライブラリ:

  • ESP8266WiFi.h
  • espnow.h(ESP8266 Coreに標準で含まれています)
#include <ESP8266WiFi.h>
#include <espnow.h>

// --- モータードライバー L9110S のピン設定 ---
// モーター1 (例: 左モーター)
#define M1A D1 // GPIO 5
#define M1B D2 // GPIO 4

// モーター2 (例: 右モーター)
#define M2A D5 // GPIO 14
#define M2B D6 // GPIO 12
// -----------------------------------------

// 送信機から受信するデータ構造体
// (送信機側と一致させる必要があります)
typedef struct struct_message {
  int command; // 0:STOP, 1:FWD, 2:BWD, 3:LEFT, 4:RIGHT
} struct_message;

struct_message myData;

// データ受信時のコールバック関数
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&myData, incomingData, sizeof(myData));
  Serial.print("Command Received: ");
  Serial.println(myData.command);

  // コマンドに応じてモーターを制御
  switch (myData.command) {
    case 1: // 前進
      motorForward();
      break;
    case 2: // 後退
      motorBackward();
      break;
    case 3: // 左旋回
      motorLeft();
      break;
    case 4: // 右旋回
      motorRight();
      break;
    default: // 停止 (0 または 不明なコマンド)
      motorStop();
      break;
  }
}

void setup() {
  Serial.begin(115200);
  
  // モーターピンを初期化
  pinMode(M1A, OUTPUT);
  pinMode(M1B, OUTPUT);
  pinMode(M2A, OUTPUT);
  pinMode(M2B, OUTPUT);
  motorStop(); // まず停止

  // ESP-NOWのセットアップ
  WiFi.mode(WIFI_STA);
  
  // ★★★ このMACアドレスを送信機コードにコピーしてください ★★★
  Serial.print("MAC Address: ");
  Serial.println(WiFi.macAddress());
  // ★★★★★★★★★★★★★★★★★★★★★★★★★★★

  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {
  // コールバック関数がすべて処理するので、loopは空でOK
}

// --- モーター制御関数 ---

void motorStop() {
  digitalWrite(M1A, LOW);
  digitalWrite(M1B, LOW);
  digitalWrite(M2A, LOW);
  digitalWrite(M2B, LOW);
}

void motorForward() {
  // 両方のモーターを前へ
  digitalWrite(M1A, HIGH);
  digitalWrite(M1B, LOW);
  digitalWrite(M2A, HIGH);
  digitalWrite(M2B, LOW);
}

void motorBackward() {
  // 両方のモーターを後ろへ
  digitalWrite(M1A, LOW);
  digitalWrite(M1B, HIGH);
  digitalWrite(M2A, LOW);
  digitalWrite(M2B, HIGH);
}

void motorLeft() {
  // 左モーターを後退、右モーターを前進(その場旋回)
  digitalWrite(M1A, LOW);
  digitalWrite(M1B, HIGH);
  digitalWrite(M2A, HIGH);
  digitalWrite(M2B, LOW);
}

void motorRight() {
  // 左モーターを前進、右モーターを後退(その場旋回)
  digitalWrite(M1A, HIGH);
  digitalWrite(M1B, LOW);
  digitalWrite(M2A, LOW);
  digitalWrite(M2B, HIGH);
}

2. 送信機(ESP32)のプログラム

必要なライブラリ:

  • WiFi.h
  • esp_now.h(ESP32 Coreに標準で含まれています)
#include <esp_now.h>
#include <WiFi.h>

// --- ジョイスティックのピン設定 ---
#define JOY_X_PIN 34  // ジョイスティックX軸 (ADC1_CH6)
#define JOY_Y_PIN 35  // ジョイスティックY軸 (ADC1_CH7)
// ※ESP32のADC2 (GPIO 4, 0, 2, 15, 13, 12, 14, 27, 25, 26) はWi-Fi使用時に
// 使えないことがあるため、ADC1 (GPIO 32-39) の使用を推奨します。
// ---------------------------------

// ★★★ ここに受信機(NodeMCU)のMACアドレスを入力 ★★★
uint8_t broadcastAddress[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
// ★★★★★★★★★★★★★★★★★★★★★★★★★★★

// 送信するデータ構造体
typedef struct struct_message {
  int command; // 0:STOP, 1:FWD, 2:BWD, 3:LEFT, 4:RIGHT
} struct_message;

struct_message myData;
esp_now_peer_info_t peerInfo;

// データ送信時のコールバック関数(デバッグ用)
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("Send Status: ");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail");
}

void setup() {
  Serial.begin(115200);

  // ジョイスティックピンを入力に設定
  pinMode(JOY_X_PIN, INPUT);
  pinMode(JOY_Y_PIN, INPUT);
  
  // ESP-NOWのセットアップ
  WiFi.mode(WIFI_STA);
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  esp_now_register_send_cb(OnDataSent);

  // ピア(受信機)の登録
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

int lastCommand = -1; // 最後に送信したコマンドを記憶

void loop() {
  // ジョイスティックの値を読み取る (ESP32のADCは12bit: 0-4095)
  int joyX = analogRead(JOY_X_PIN);
  int joyY = analogRead(JOY_Y_PIN);
  
  int currentCommand = 0; // デフォルトは停止(0)

  // 閾値(しきいち)を設定 (スティックの遊びを無視するため)
  // 中央値は約2048
  int thresholdHigh = 3000;
  int thresholdLow = 1000;

  // Y軸(前後)の判定
  if (joyY > thresholdHigh) {
    currentCommand = 1; // 前進
  } else if (joyY < thresholdLow) {
    currentCommand = 2; // 後退
  } 
  // X軸(左右)の判定
  // ※前後が優先される
  else if (joyX < thresholdLow) {
    currentCommand = 3; // 左旋回
  } else if (joyX > thresholdHigh) {
    currentCommand = 4; // 右旋回
  }

  // コマンドが変更された場合のみ送信 (通信の負荷軽減)
  if (currentCommand != lastCommand) {
    myData.command = currentCommand;
    
    // データ送信
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    
    if (result == ESP_OK) {
      Serial.print("Command Sent: ");
      Serial.println(currentCommand);
    } else {
      Serial.println("Error sending the data");
    }
    
    lastCommand = currentCommand;
  }
  
  delay(50); // 50ミリ秒待機
}

3. 配線図(ピン接続例)

🔌 送信機側 (ESP32 と ジョイスティック)

  • ジョイスティック VCC -> ESP32 3V3 (または 5V)
  • ジョイスティック GND -> ESP32 GND
  • ジョイスティック VRx -> ESP32 GPIO 34
  • ジョイスティック VRy -> ESP32 GPIO 35
  • (SWピンは今回使用しません)

🔌 受信機側 (NodeMCU と L9110S)

  • L9110S VCC -> NodeMCU VIN (または 外部のモーター用電源 5V-12V)
  • L9110S GND -> NodeMCU GND (および 外部電源のGND)
  • L9110S B-IA (Motor B In 1) -> NodeMCU D1 (GPIO 5)
  • L9110S B-IB (Motor B In 2) -> NodeMCU D2 (GPIO 4)
  • L9110S A-IA (Motor A In 1) -> NodeMCU D5 (GPIO 14)
  • L9110S A-IB (Motor A In 2) -> NodeMCU D6 (GPIO 12)
  • L9110S Motor A Output -> モーター1 (例: 左モーター)
  • L9110S Motor B Output -> モーター2 (例: 右モーター)

注意: モーターの消費電力が大きい場合、NodeMCUのVINピンから電源を取ると不安定になることがあります。その場合は、モータードライバー(L9110S)とモーターには別途、強力な外部電源(バッテリーなど)を接続し、GNDだけをNodeMCUと共通にしてください。


4. 実行手順

  1. 上記の「受信機プログラム」をArduino IDEで開き、ボードを「NodeMCU 1.0 (ESP-12E Module)」に設定します。
  2. NodeMCUをPCに接続し、プログラムを書き込みます。
  3. Arduino IDEのシリアルモニタを開き(ボーレート: 115200)、表示される MAC Address: XX:XX:XX:XX:XX:XX をメモ帳などにコピーします。
  4. 上記の「送信機プログラム」をArduino IDEで開き、ボードを「ESP32 Dev Module」などに設定します。
  5. プログラム内の uint8_t broadcastAddress[] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};XX の部分を、先ほどコピーしたMACアドレスに書き換えます。
    • 例: C8:2B:96:12:34:56 なら 0xC8, 0x2B, 0x96, 0x12, 0x34, 0x56 と書きます。
  6. ESP32をPCに接続し、プログラムを書き込みます。
  7. 両方の電源を入れれば、ESP32のジョイスティックでNodeMCUに接続されたモーターが制御できるはずです。

この構成で不明な点や、速度制御(PWM)を追加したいなど、さらなるご要望があればお気軽にご質問ください。

コメント