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?

M5StickCでBLE MIDIコントローラーを作ってみよう@2023

Last updated at Posted at 2023-11-04

概要

2019年にM5StickCでMIDIコントローラーを作ってみようという記事を書き、色々、機器を作って遊んでいこうと思っていた矢先、Micro:bitがV2になり、BLEの利用が制限されると同時に、BLE MIDIの熱が冷めてしまいました。

が、今年になって、熱が再燃したので、再度、挑戦してみます。

デモ

M5StickC x KORG Gadget Phoenix
(YouTube)

前と同じです。

  • ボタンを押すとNote On、離すとNote Off
    (たまにチャタリングでかNote Offが送信できないのはご愛嬌)
  • 縦に傾けると音程変更(Pitch Bend)
  • 横に動かすと音量変更(CC: 7)

開発環境構築

最近は、VSCodeでの開発も盛んですが、使いやすさから、前回と同じくArduino IDEで開発していきます。ArudinoIDEも、メジャーバージョンアップも行われ、こちら統合Platformとして順当に進化してますね。

PC(というかMac)も変わったので改めて環境構築しました。

公式を参考に、M5StickC開発環境を整えていきます。

3年前と大きく変わったことは、BLEライブラリを別途入れる必要が無くなったことです。Arudino本家からESP32搭載のArduino「Arduino nano 33 IoT」が出たためでしょう。かなり開発のハードルが低くなりました。

構成

  • M5StickC
  • KORG Gadget2 for iPad(iPhoneでも可)

#ソース

Theramin.ino
#include <M5StickC.h>
#include <BLEDevice.h>
#include <BLEServer.h>

//Note
const int NOTE_NUM = 60;

//BLE関連
//デバイス名
const char *DEVICE_NAME = "m5sC";

//BLE MIDIのサービスとキャラクタラスティック
const char *SERVICE_UUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
const char *CHAR_UUID = "7772E5DB-3868-4112-A1A9-F2669D106BF3";

//BLEデータ
BLEServer *pServer;
BLECharacteristic *pCharacteristic;
bool isConnected = false;

//MIDIデータ関連
//ccデータバッファ
int cc[128];
int pitch;

//IMUバッファ
float x, y, z;
int intX, intY;

//Buttonバッファ
bool pressed[2];

//Display関連
int cnt;
int circleSize;
int dspX, dspY;

//BLE MIDIデータ
unsigned char buff[] = {0x80, 0x80, 0xB0, 0x01, 0x64};

//BLE関連関数
// サーバーのコールバック関数
class cbServer: public BLEServerCallbacks {
  void onConnect(BLEServer *pServer) {
    isConnected = true;
    M5.Lcd.fillScreen(BLACK); 
      M5.Lcd.drawCentreString("Connected", 40, 80, 1);
  };

  void onDisconnect(BLEServer *pServer) {
    isConnected = false;
    M5.Lcd.fillScreen(BLACK); 
    M5.Lcd.drawCentreString(DEVICE_NAME, 40, 80, 1);
  }
};

//マッピングと制限を行う関数
int mapAndLimit(int value, int fromLow, int fromHigh, int toLow, int toHigh) {
  int tmp, maxValue, minValue;

  if(toLow < toHigh)
  {
    minValue = toLow;
    maxValue = toHigh;
  }
  else
  {
    minValue = toHigh;
    maxValue = toLow;
  }

  tmp = map(value, fromLow , fromHigh, toLow, toHigh);
  tmp = tmp > maxValue ? maxValue : tmp;
  tmp = tmp < minValue ? minValue : tmp;
  return tmp;
}

//MIDI関連関数
//BLE Noteを送信
void notifyNote(int note, int velocity)
{    
  //noteをvelcityの強さで送信
  buff[2] = 0x90;
  buff[3] = note;      
  buff[4] = velocity;

  //MIDIデータをNotify
  pCharacteristic->setValue(buff, 5);
  pCharacteristic->notify();
}

//BLE MIDIのCCデータを送信
void notifyCC(int ccNum, int value, int sensitivity)
{
  if(abs(cc[ccNum] - value) > sensitivity)
  {
    //元情報にデータを入れておく
    cc[ccNum] = value;

    //valueをCC(ccNum)のデータにする
    buff[2] = 0xb0;
    buff[3] = ccNum;      
    buff[4] = value;

    //MIDIデータをNotify
    pCharacteristic->setValue(buff, 5);  
    pCharacteristic->notify();    
  }
}

//BLE MIDIのピッチベンドを送信
void notifyPitch(int value, int sensitivity)
{
  if(abs(pitch - value) > sensitivity)
  {
    //元情報にデータを入れておく
    pitch = value;

    //valueをCC(ccNum)のデータにする
    buff[2] = 0xe0;
    buff[3] = value & 127;      
    buff[4] = value >> 7;

    //MIDIデータをNotify
    pCharacteristic->setValue(buff, 5);  
    pCharacteristic->notify();    
  }
}

void setup(){
  int i;

  // M5Stackの初期化
  M5.begin();
  // MPU6886の初期化
  M5.MPU6886.Init();

  //CCバッファの初期化
  for(i = 0; i < 128; i++)
  {
    cc[i] = 64;
  }

  //Pitchバッファの初期化
  pitch = 8192;

  //Display関連の初期化
  cnt = 0;
  circleSize = 2;
  dspX = 40;
  dspY = 80;

  //Btnの初期化
  for(i = 0; i < 2; i++)
  {
    pressed[i] = false;
  }

  // BLE初期化
  BLEDevice::init(DEVICE_NAME);

  // サーバーの作成
  BLEServer *pServer = BLEDevice::createServer();
  // コールバック関数の設定
  pServer->setCallbacks(new cbServer());

  // サービスの作成
  BLEService *pService = pServer->createService(SERVICE_UUID);

  // キャラクタリスティックの作成
  pCharacteristic = pService->createCharacteristic(
    CHAR_UUID,
    BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
  );

  // サービスの開始
  pService->start();

  //アドバタイジングの開始
  BLEAdvertising *pAdvertising = pServer->getAdvertising();    
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->start();

  //ディスプレイにデバイスネームを表示
  M5.Lcd.fillScreen(BLACK); 
  M5.Lcd.drawCentreString(DEVICE_NAME, 40, 80, 1);
}

void loop() {
  if (isConnected)
  {
    //BtnAでNote On
    M5.update();    
    if(M5.BtnA.read() && !pressed[0])
    {
      pressed[0] = true;
      notifyNote(NOTE_NUM, 100);
    }
    //BtnAを離したらNote Off
    else if(!M5.BtnA.read() && pressed[0])
    {
      pressed[0] = false;
      notifyNote(NOTE_NUM, 0);      
    }

    // 加速度を取得する
    // X、Y、Zの値を得る
    M5.MPU6886.getAccelData(&x, &y, &z);

    x = x * 128.0;
    y = y * 16383.0;

    intX = mapAndLimit((int)x, 64, -64, 0, 127);
    intY = mapAndLimit((int)y, 8192, -8192, 0, 16383);

    //NoteOnの状態のときのみMIDI情報を送る
    if(pressed[0])
    {
      //IMUのデータをCC7, Pitchに送信
      notifyCC(7, intX, 1);
      notifyPitch(intY, 1);
    }

    //100msごとDisplay更新
    if(cnt == 50)
    {
      M5.Lcd.fillScreen(BLACK); 

      if(pressed[0])
      {
        circleSize = 7;
      }
      else
      {
        circleSize = 2;
      }

      dspX = mapAndLimit((int)x, 64, -64, 0, 80);
      dspY = mapAndLimit((int)y, -8192, 8192, 0, 160);
      M5.Lcd.drawCircle(dspX, dspY, circleSize, RED);
    }
    cnt = (cnt+1) % 100;
    
    // 少し休ませる
    delay(1);
  }
}

M4StickCの後継種、M5StickCPlusでもライブラリとヘッダ、ディスプレイ周りをCPlusに合わせれば動くと思います。

3年前からの進捗

BLEライブラリがかなり進化してました。前作ったときは、delayを10ms取らないとまともに動かなかったのですが、今回は、1msでも問題なく動作しました。

また、PCが早くなったのか、公式も入ったことでライブラリの最適化が進んだのか、コンパイル時間も大幅に速くなっていました。

ただ、BLEの限界でしょうね。相変わらず、ピッチベンドのデジタル臭さは抜けませんでした。

まとめ

仕事柄、無線業界のウワサは色々入ってきていて、ESP32がIoT業界で覇権を取った背景に、チップの価格の安さもさることながら、ソフトウェア改善の速さがあると聞いていましたが、今回、身をもって体験することができました。

ようやく電波認証の通ったArudino nanoの無線シリーズの方でも色々遊んでみようと思ってますが、今回の実験で、WLANついているのになぜか安いESP32が乗っている方(Arduino nano 33 IoT)で十分、と判断できそうです。

参考

前貼った、BLE MIDIの規格のリンクが切れていたので、貼り直しておきます。
全体公開になってますね。

MIDI over Bluetooth Low Energy(BLE-MIDI)

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?