2026.01.14

ESP32-H2でロボットを動かしたい:Gamepad編

こんにちは,S.T.です。

展示会場でロボットやその他の装置を動かす際に問題になるのは,なんといってもWi-Fiです。大抵の場合,輻輳してしまって思うように使えません。一方で,Bluetoothは周波数を切り替えて再送する仕組みがあるため,意外と大丈夫です。実際,国際ロボット展2025ではロボットのコントローラ(Bluetooth接続)には問題が起こりませんでした。

純正のコントローラ以外からロボットに指示を送れるようなデバイスがあれば便利ということで,制作にとりかかってみました。将来的にはブロードキャストなどを用いて複数台の同時制御を目指しますが,今回はシンプルにBluetoothゲームパッドを実装します。

1.なぜ「ESP32-H2」を選ぶのか?

ESP32-H2は,低価格な無線通信可能なマイコンとして,広く使われています。
中でも,ESP32-H2は,Wi-Fiがない代わりにBluetooth 5.3 (LE) や IEEE 802.15.4 (Zigbee/Thread/Matter) に特化しているユニークな機種です。
今回はこのデバイスを「HID(Human Interface Device)」として動作させ、PCやロボットから標準的なゲームパッドとして認識させます。

2.準備するもの・回路構成

必要なパーツ
  1. ESP32-H2 DevkitM-1
  2. タクトスイッチ3つ
  3. 試作用のブレッドボード,ジャンプワイヤ,本番用の基板

ピンアサインは下記のようにしました。

コンポーネント ピン番号 (GPIO) 役割
Button 1 GPIO 9 Aボタン
Button 2 GPIO 10 Bボタン
Button 3 GPIO 11 Xボタン
RGB LED GPIO 8 接続ステータス表示(内蔵LED WS2812)

3.実装:Bluetooth Gamepad化のコード

今回はArduinoIDEを使ってシンプルに実装しました。

#define USE_NIMBLE
#include 
#include 

// ピンとボタンの定義
const int NUM_BUTTONS = 3;
const int buttonPins[NUM_BUTTONS] = {9, 10, 11};      // 使用するGPIOピン
const uint16_t gamepadButtons[NUM_BUTTONS] = {        // 対応するGamepadボタン
  BUTTON_1, // Aボタン相当
  BUTTON_2, // Bボタン相当
  BUTTON_3  // Xボタン相当
};

// 状態管理用
int lastButtonStates[NUM_BUTTONS] = {HIGH, HIGH, HIGH};
bool wasConnected = false;

// デバイス設定
const int rgbLedPin = 8;
Adafruit_NeoPixel pixel(1, rgbLedPin, NEO_GRB + NEO_KHZ800);
BleGamepad bleGamepad("GamepadTest", "Espressif", 100);
BleGamepadConfiguration bleGamepadConfig;

void setup() {
  Serial.begin(115200);
  
  // LED初期化
  pixel.begin();
  pixel.setBrightness(20);
  pixel.setPixelColor(0, pixel.Color(255, 0, 0)); // 赤(未接続)
  pixel.show();

  // ボタンピンの初期化(内蔵プルアップを使用)
  for (int i = 0; i < NUM_BUTTONS; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
  }

  // 自動レポートを無効化(ボタン変化時のみ送信し、トラフィックを抑える)
  bleGamepadConfig.setAutoReport(false);
  bleGamepad.begin(&bleGamepadConfig);
}

void loop() {
  bool isConnected = bleGamepad.isConnected();

  // 接続ステータスLED制御(接続済みなら青、未接続なら赤)
  if (isConnected != wasConnected) {
    if (isConnected) {
      pixel.setPixelColor(0, pixel.Color(0, 0, 255)); // 青
      Serial.println("Connected!");
    } else {
      pixel.setPixelColor(0, pixel.Color(255, 0, 0)); // 赤
      Serial.println("Disconnected!");
    }
    pixel.show();
    wasConnected = isConnected;
  }

  if (isConnected) {
    bool sendNeeded = false;

    for (int i = 0; i < NUM_BUTTONS; i++) {
      int currentState = digitalRead(buttonPins[i]);

      if (currentState != lastButtonStates[i]) {
        if (currentState == LOW) { // 物理スイッチが押された(プルアップなのでLOW)
          bleGamepad.press(gamepadButtons[i]);
          Serial.printf("Button %d pressed\n", i + 1);
        } else {
          bleGamepad.release(gamepadButtons[i]);
          Serial.printf("Button %d released\n", i + 1);
        }
        lastButtonStates[i] = currentState;
        sendNeeded = true;
      }
    }

    // 変化があった場合のみHIDレポートを送信
    if (sendNeeded) {
      bleGamepad.sendReport();
    }
  }

  delay(10); // チャタリング対策
}

ArduinoIDEでボードにESP32-H2を指定して,ビルド・フラッシュすると,動き始めます。
PCなどからデバイス「GamepadTest」が見えたら,通常のGamepadのようにペアリングします。ボード上のLEDが青になれば準備完了です。

あとは,ロボット側にGampeadの入力に応じて動作するプログラムを仕込めば,完了です。

4.まとめ

ESP32-H2を使って,Bluetooth Gamepadを作成しました。
これだけでは非常にシンプルな実装で,使い勝手もよくないので,今後はBluetoothのさまざまな規格を活用して,実用的なものに仕上げていきたいと思います。

次世代システム研究室では,グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。アプリケーション開発者の方,次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら,ぜひ募集職種一覧からご応募をお願いします。

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事