1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ESP32で8ポテンショメータをBLEデバイスにする

Last updated at Posted at 2023-02-18

M5Unitの一つとして、8個のポテンショメータを持ったユニットがあります。

今回は、これを8軸のアナログ値をもったGamePadにします。GamePadにすることで、Windows等のPCのデバイスとして扱えるようになりますし、HTMLのJavascriptから扱えるようになります。
また、別の方法として、独自のBLEペリフェラルとして定義して、8軸のアナログ値だけでなく、8ポテンショメータユニットについているRGB LEDの色も変えられるようにしてみます。

今回は、ESP32として、M5Stick-Cを採用しています。ESP32であればどれでも大丈夫です。また、M5Stick-Cについているボタンも返せるようにしています。
ESP32の実装は、PlatformIOで作成しています。

ソースコードもろもろは以下に置いてあります。

poruruba/Angle8_Test

BLE接続のGamePadにする

以下のライブラリを使わせていただきました。

以下の投稿を参考にさせていただきました。わかりやすかったです。
 ESP32-BLE-Gamepad ライブラリの使用方法

Arduino\Angle8_BleGamepad\src\main.c
#include <M5StickC.h>
#include <Wire.h>
#include <BleGamepad.h>
#include "M5_ANGLE8.h"

#define ANGLE_MARGIN  5

BleGamepad bleGamepad("Angle8BleGamepad", "MyHome", 100);
M5_ANGLE8 angle8;

void setup() {
  // put your setup code here, to run once:
  M5.begin(true, true, true);
  Serial.println("setup start");

  Wire.begin(32, 33);

  while (!angle8.begin(ANGLE8_I2C_ADDR)) {
    Serial.println("angle8 Connect Error");
    delay(100);
  }

  BleGamepadConfiguration config;
  config.setButtonCount(3);
  config.setHatSwitchCount(0);
  config.setWhichAxes(true, true, true, true, true, true, true, true);
  config.setAxesMax(0xFFF);
  config.setAxesMin(0);
  bleGamepad.begin(&config);

  Serial.println("setup finished");
}

void loop() {
  // put your main code here, to run repeatedly:
  M5.update();

  static bool buttons[3] = { 0 };
  if( M5.BtnA.isPressed() ){
    if( !buttons[0] ){
      buttons[0] = true;
      bleGamepad.press(1);
      Serial.printf("BtnA Pressed\n");
    }
  }else{
    if( buttons[0] ){
      buttons[0] = false;
      bleGamepad.release(1);
      Serial.printf("BtnA Released\n");
    }
  }
  if( M5.BtnB.isPressed() ){
    if( !buttons[1] ){
      buttons[1] = true;
      bleGamepad.press(2);
      Serial.printf("BtnB Pressed\n");
    }
  }else{
    if( buttons[1] ){
      buttons[1] = false;
      bleGamepad.release(2);
      Serial.printf("BtnB Released\n");
    }
  }
  if( buttons[2] != angle8.getDigitalInput() ){
    buttons[2] = !buttons[2];
    if( buttons[2] ){
      bleGamepad.press(3);
      Serial.printf("Switch On\n");
    }else{
      bleGamepad.release(3);
      Serial.printf("Switch Off\n");
    }
  }


  static uint16_t values[8] = { 0 };
  bool changed = false;
  for( uint8_t i = 0 ; i < 8 ; i++ ){
    uint16_t value = angle8.getAnalogInput(i, _12bit);
    if( abs(value - values[i] ) >= ANGLE_MARGIN ){
      values[i] = value;
      changed = true;
    }
  }
  if( changed ){
    bleGamepad.setAxes(values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]);
  }

  delay(1);
}

独自のBLEペリフェラルにする

BLEのプライマリサービスやキャラクタリスティックを独自に定義します。
GamePadの方では、ESP32からみて出力のみでしたが、独自の方では、出力に加えて、LED色を変えるための入力も備えています。

Arduino\Angle8_BleCustom\src\main.c
#include <M5StickC.h>
#include <Wire.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#include "M5_ANGLE8.h"

M5_ANGLE8 angle8;

#define DISCONNECT_WAIT 3000

#define UUID_SERVICE "08030900-7d3b-4ebf-94e9-18abc4cebede"
#define UUID_WRITE "08030901-7d3b-4ebf-94e9-18abc4cebede"
#define UUID_READ "08030902-7d3b-4ebf-94e9-18abc4cebede"
#define UUID_NOTIFY "08030903-7d3b-4ebf-94e9-18abc4cebede"

#define ANGLE_MARGIN  5

BLECharacteristic *pCharacteristic_write;
BLECharacteristic *pCharacteristic_read;
BLECharacteristic *pCharacteristic_notify;
BLEAdvertising *g_pAdvertising = NULL;

#define UUID_VALUE_SIZE 20
uint8_t value_buffer[UUID_VALUE_SIZE] = { 0 };

bool isConnected = false;

void readValue(void);
void sendBuffer(void);

class MyCallbacks : public BLEServerCallbacks {
  void onConnect(BLEServer* pServer){
    isConnected = true;
    Serial.println("Connected\n");
  }

  void onDisconnect(BLEServer* pServer){
    isConnected = false;

    BLE2902* desc = (BLE2902*)pCharacteristic_notify->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
    desc->setNotifications(false);

    Serial.println("Disconnected\n");

    g_pAdvertising->stop();
    delay(DISCONNECT_WAIT);
    g_pAdvertising->start();
  }
};

class MyCharacteristicCallbacks : public BLECharacteristicCallbacks{
  void onWrite(BLECharacteristic* pCharacteristic){
    Serial.print("onWrite");
    uint8_t* value = pCharacteristic->getData();
    std::string str = pCharacteristic->getValue(); 
    int recv_len = str.length();
    Serial.printf("(%d)\n", recv_len);

    if( recv_len >= 5 ){
      uint32_t color = (value[2] << 16) | (value[3] << 8) | value[4];
      angle8.setLEDColor(value[0], color, value[1]);
    }
  }
/*
  void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code){
  }
  void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic){
  };
*/
};

void taskServer(void*) {
  BLEDevice::init("Angle8BleCustom");

  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new MyCallbacks());

  BLEService *pService = pServer->createService(UUID_SERVICE);

  pCharacteristic_write = pService->createCharacteristic( UUID_WRITE, BLECharacteristic::PROPERTY_WRITE );
  pCharacteristic_write->setAccessPermissions(ESP_GATT_PERM_WRITE);
  pCharacteristic_write->setCallbacks(new MyCharacteristicCallbacks());

  pCharacteristic_read = pService->createCharacteristic( UUID_READ, BLECharacteristic::PROPERTY_READ );
  pCharacteristic_read->setAccessPermissions(ESP_GATT_PERM_READ);
  pCharacteristic_read->setValue(value_buffer, sizeof(value_buffer));

  pCharacteristic_notify = pService->createCharacteristic( UUID_NOTIFY, BLECharacteristic::PROPERTY_NOTIFY );
  pCharacteristic_notify->setAccessPermissions(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE);
  pCharacteristic_notify->addDescriptor(new BLE2902());

  pService->start();

  g_pAdvertising = pServer->getAdvertising();
  g_pAdvertising->addServiceUUID(UUID_SERVICE);
  g_pAdvertising->start();

  vTaskDelay(portMAX_DELAY); //delay(portMAX_DELAY);
}

void setup() {
  // put your setup code here, to run once:
  M5.begin(true, true, true);
  Serial.printf("setup start\n");

  Wire.begin(32, 33);

  while (!angle8.begin(ANGLE8_I2C_ADDR)) {
    Serial.println("angle8 Connect Error");
    delay(100);
  }

  xTaskCreate(taskServer, "server", 20000, NULL, 5, NULL);

  Serial.printf("setup finished\n");
}

void loop() {
  // put your main code here, to run repeatedly:
  M5.update();

  readValue();

  bool changed = false;

  static uint8_t buttons[3] = { 0 };

  for( uint8_t i = 0 ; i < 3 ; i++ ){
    if( value_buffer[i] != buttons[i] ){
      buttons[i] = value_buffer[i];
      changed = true;
      if( buttons[i] )
        Serial.printf("Btn(%d) Pressed\n", i);
      else
        Serial.printf("Btn(%d) Released\n", i);
    }    
  }

  static uint8_t values[8] = { 0 };
  for( uint8_t i = 0 ; i < 8 ; i++ ){
    if( abs( value_buffer[3 + i] - values[i]) >= ANGLE_MARGIN ){
      values[i] = value_buffer[3 + i];
      changed = true;
      Serial.printf("Angle(%d) Changed\n", i);
    }
  }

  if( changed ){
    sendBuffer();
  }

  delay(1);
}

void readValue(void){
  value_buffer[0] = M5.BtnA.isPressed() ? 0x01 : 0x00;
  value_buffer[1] = M5.BtnB.isPressed() ? 0x01 : 0x00;
  value_buffer[2] = angle8.getDigitalInput() ? 0x01 : 0x00;
  for( uint8_t i = 0 ; i < 8 ; i++ ){
    value_buffer[3 + i] = (uint8_t)angle8.getAnalogInput(i, _8bit);
  }

  if( isConnected )
    pCharacteristic_read->setValue(value_buffer, 11);
}

void sendBuffer(void){
  Serial.println("SendBuffer");

  if( !isConnected )
    return;

  BLE2902* desc = (BLE2902*)pCharacteristic_notify->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
  if( !desc->getNotifications() )
    return;

  pCharacteristic_notify->setValue(value_buffer, 11);
  pCharacteristic_notify->notify();
}

ブラウザからESP32に接続する

Gamepadとして認識する場合には、ブラウザのJavascriptにGamepad APIがあるので、そのまま扱えます。ただし、Windowsにおいて、事前にBluetoothデバイスとして追加しておく必要があります。

image.png

image.png

image.png

ブラウザのJavascriptにWebBluetooth API があるので、独自のBLEペリフェラルを接続できます。

詳しくは、ソースコードを参照して下さい。

Gamepad用
html\angle8_blegamepad\js\start.js

image.png

Custom用
html\angle8_blecustom\js\start.js

※Custom用として動作させる場合は、WindowsにGamepadとして登録してある場合は削除してください。

image.png

以上

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?