メモ:M5StackでBluepad32

トラブル

Arduino IDEで、Bluepad32 ボードパッケージをインストールし、ボード選択で esp32_bluepad32 > M5Stack-Core-ESP32 を選択してスケッチ例の ESP32_Bluepad32 > Controller をビルド&書き込みして動作確認できた。
ところが、スケッチ例 M5Stack > Basics > Display をビルドするとエラーが発生する。

c:\Users\***\Documents\Arduino\libraries\M5Stack\src\utility\Power.cpp:10:10:
 fatal error: esp_bt_main.h: No such file or directory
 #include <esp_bt_main.h>
          ^~~~~~~~~~~~~~~
compilation terminated.

原因

M5StackのボードパッケージとBluepad32とでは使用しているBluetoothプロトコルスタックが異なる。

  • M5Stack : Bluedroid
  • Bluepad32 : btstack

そのためヘッダファイルおよびAPIが異なる。上記のエラーは、POWER::powerOFF() で esp_bluedroid_disable() を呼び出すために esp_bt_main.h をインクルードしているために発生している。

修正

Arduino/libraries/M5Stack/src/utility/Power.cpp を下記のように修正すればビルドは通る。だたし、esp_bluedroid_disable() の代わりになにか処理が必要な気がする。

#if CONFIG_BLUEDROID_ENABLED
    #include <esp_bt_main.h>
#endif
    // stop bt
#if CONFIG_BLUEDROID_ENABLED
    esp_bluedroid_disable();
#endif

動作確認用スケッチ

#include <Bluepad32.h>
#include <M5Stack.h>

ControllerPtr myControllers[BP32_MAX_GAMEPADS];

TFT_eSprite sprite = TFT_eSprite(&M5.Lcd); // スプライト作成

void onConnectedController(ControllerPtr ctl) {
    bool foundEmptySlot = false;
    for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
        if (myControllers[i] == nullptr) {
            Serial.printf("CALLBACK: Controller is connected, index=%d\n", i);
            ControllerProperties properties = ctl->getProperties();
            Serial.printf("Controller model: %s, VID=0x%04x, PID=0x%04x\n", ctl->getModelName().c_str(), properties.vendor_id,
                           properties.product_id);
            myControllers[i] = ctl;
            foundEmptySlot = true;
            break;
        }
    }
    if (!foundEmptySlot) {
        Serial.println("CALLBACK: Controller connected, but could not found empty slot");
    }
}

void onDisconnectedController(ControllerPtr ctl) {
    bool foundController = false;

    for (int i = 0; i < BP32_MAX_GAMEPADS; i++) {
        if (myControllers[i] == ctl) {
            Serial.printf("CALLBACK: Controller disconnected from index=%d\n", i);
            myControllers[i] = nullptr;
            foundController = true;
            break;
        }
    }

    if (!foundController) {
        Serial.println("CALLBACK: Controller disconnected, but not found in myControllers");
    }
}

void processGamepad(ControllerPtr ctl)
{
    sprite.fillScreen(BLACK);
    sprite.setCursor(10, 30);
    sprite.printf("DPad: 0x%02x", ctl->dpad());
    sprite.setCursor(10, 60);
    sprite.printf("Btn: 0x%04x", ctl->buttons());
    sprite.setCursor(10, 90);
    sprite.printf("St-L: %4d, %4d", ctl->axisX(), ctl->axisY());
    sprite.setCursor(10, 120);
    sprite.printf("St-R: %4d, %4d", ctl->axisRX(), ctl->axisRY());
    sprite.setCursor(10, 150);
    sprite.printf("L2:%4d R2:%4d", ctl->brake(), ctl->throttle());
    sprite.pushSprite(0, 0);
}

void processControllers() {
    for (auto myController : myControllers) {
        if (myController && myController->isConnected() && myController->hasData()) {
            if (myController->isGamepad()) {
                processGamepad(myController);
            }else{
                Serial.println("Unsupported controller");
            }
        }
    }
}

void setup() {
    M5.begin();
    M5.Power.begin();
    pinMode(25, OUTPUT);
    digitalWrite(25, LOW); // アンプを無効化(スピーカーOFF)
    
    M5.Lcd.fillScreen(BLACK);
    sprite.setColorDepth(8);   // 色深度を8ビットに設定(メモリ節約)
    sprite.createSprite(320, 240); // スプライトのサイズを画面サイズに設定
    sprite.setTextColor(WHITE, BLACK);
    sprite.setTextSize(3);

    Serial.printf("Firmware: %s\n", BP32.firmwareVersion());
    const uint8_t* addr = BP32.localBdAddress();
    Serial.printf("BD Addr: %2X:%2X:%2X:%2X:%2X:%2X\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);

    BP32.setup(&onConnectedController, &onDisconnectedController);
    BP32.forgetBluetoothKeys();
    BP32.enableVirtualDevice(false);
}

void loop() {
    bool dataUpdated = BP32.update();
    if (dataUpdated) processControllers();

    delay(150);
}

PlatformIO

PlatformIOで Arduino + Bluepad32 なプロジェクトもテンプレートが提供されている。
ただ、そこまでして Bluepad32 を使うかは疑問。当方では動作確認していない。


ライセンス

btstack はGPLと商用ライセンスのデュアルライセンスであることに注意。つまりGPLを選択すればソース公開義務があり、ソース非公開にしたければ商用ライセンスの取得が必要。

参考

M5StickCでBluepad32を使い画面表示をしている例はある。
gijin77.blog.jp