Arduino Nano 33 IoTのBLEとIMUでBLE MIDIコントローラーを作ってみよう

# 概要
[この記事](https://qiita.com/wdice02136/items/25b2ac3789918c398d43)に続き、[Arduino nano 33 IoT](https://akizukidenshi.com/catalog/g/gM-17977/)の**BLE**と**IMU**を使ってMIDIコントローラーを作ってみます。

今回のデモの音源も、iOSアプリの **[KORG Gadget 2](https://www.korg.com/jp/products/software/korg_gadget/)** で、制御するのは、"Phenix"の**Pitch**(Pitch)と**Volume**(CC7)です。

ボードをX軸に倒すとPitchが変わり、Y軸に倒すとVolumeが変わる、というものになります。

KORG Gadget2のシンセの中で、"Phonix"だけPitchBendのレンジ変えられるので、このシンセを選びました。

このボードにはユーザーボタンがないので、音を出すのは、"Phonix"のキーボードを押すか、シーケンスを組むかで音を出してください。

# 開発環境準備
Arduino IDEで下記をインストールしてください。

## ボードマネージャー
- [Arduino SAMD Boards](https://docs.arduino.cc/software/ide-v2/tutorials/ide-v2-board-manager#samd)

## ライブラリ
- [WiFiNINA](https://www.arduino.cc/reference/en/libraries/wifinina/) (今回は使いませんが。)
- [ArduinoBLE](https://www.arduino.cc/reference/en/libraries/arduinoble/)
- [Arduino_LSM6DS3](https://www.arduino.cc/reference/en/libraries/arduino_lsm6ds3/)

昔に比べて、ArduinoのWebページ、かなり分かりやすくなってますね。

# ソース
```cpp:Theramin_AN33IOT.ino
#include <ArduinoBLE.h>
#include <Arduino_LSM6DS3.h>

 // BLE MIDI Service
BLEService midiService("03B80E5A-EDE8-4B33-A751-6CE34EC4C700");

// BLE MIDI OUT Characteristic
BLECharacteristic midiOutChar("7772E5DB-3868-4112-A1A9-F2669D106BF3",
    BLERead | BLENotify, 5);

//MIDI Buff
uint8_t midiPayload[] = {0x80, 0x80, 0xB0, 0x01, 0x64};

//CC Buff
int i;
uint8_t cc[128];

//Pitch Buff
uint16_t pitch;

//IMU Buff
float x, y, z;
uint16_t midiX, midiY;

void setup() {
  // initialize serial communication
  Serial.begin(9600); 

  // initialize the built-in LED pin to indicate when a central is connected  
  pinMode(LED_BUILTIN, OUTPUT);

  //Buff initialize
  for(i = 0; i < 128; i++)
  {
    cc[128] = 64;
  }

  pitch = 8192;

  // begin initialization
  if (!BLE.begin()) {
    Serial.println("starting BLE failed!");
    while (1);
  }

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
    while (1);
  }

  //BLE Initialize
  BLE.setLocalName("AN33IoT");
  BLE.setAdvertisedService(midiService);

  midiService.addCharacteristic(midiOutChar);
  BLE.addService(midiService);

  // start advertising
  Serial.println("Bluetooth device active, waiting for connections...");
  BLE.advertise();
}

void loop() {
  // wait for a BLE central
  BLEDevice central = BLE.central();

  if (central) {
    Serial.print("Connected to central: ");
    Serial.println(central.address());
    digitalWrite(LED_BUILTIN, HIGH);

    while (central.connected()) {
      if (IMU.accelerationAvailable()) {
        IMU.readAcceleration(x, y, z);

        midiX = mapAndLimit((int)(x * 16384.0), -8192, 8191, 0, 16383);
        midiY = mapAndLimit((int)(y * 1000.0), 500, -500, 0, 127);            

        notifyPitch(midiX, 1);
        notifyCC(7, (uint8_t)midiY, 1);            

        delay(1);
      }
    }

    // when the central disconnects, turn off the LED:
    digitalWrite(LED_BUILTIN, LOW);
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
  }
}

//NotifyCC
void notifyCC(uint8_t ccNum, uint8_t midiValue, uint8_t sensitivity)
{
    if(abs(cc[ccNum] - midiValue) > sensitivity)
    {
        cc[ccNum] = midiValue;

        //Send Note
        midiPayload[2] = 0xb0;
        midiPayload[3] = ccNum;
        midiPayload[4] = midiValue;

        Serial.print("CC");
        Serial.print(ccNum);
        Serial.print(":");
        Serial.println(midiValue);

        //Notify MIDI Data        
        midiOutChar.writeValue(midiPayload, 5);
    }
}

//NotifyPitch
void notifyPitch(int value, int sensitivity)
{
  if(abs(pitch - value) > sensitivity)
  {
    //元情報にデータを入れておく
    pitch = value;

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

    Serial.print("Pitch");
    Serial.print(":");
    Serial.println(value);

    //Notify MIDI Data        
    midiOutChar.writeValue(midiPayload, 5);
  }
}

//Map and Limit
uint16_t mapAndLimit(int inputValue, int fromLow, int fromHigh, int toLow, int toHigh)
{
    int tmp, minValue, maxValue;    

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

    tmp = inputValue - fromLow;
    tmp = (int)((double)tmp * (double)(toHigh - toLow) / (double)(fromHigh - fromLow));    
    tmp += toLow;
    tmp = tmp < minValue ? minValue : tmp;
    tmp = tmp > maxValue ? maxValue : tmp;

    return (uint16_t)tmp;        
}
```

# 動作所感
相変わらずPitchはガタガタですが、ESP32のBLEでも綺麗に動いてくれました。

# M5StickCとの違い
## 1. コンパイル、アップロードが速い
M5StickCも大分速度上がってましたが、このボードは、もうワンランク、コンパイル、アップロードが速かったですね。流石、純正ですね。

## 2.アナログ入力が8つある
今、自分が作りたいのは、 **[KORG Gadget 2](https://www.korg.com/jp/products/software/korg_gadget/)** の"Chicago"のTB-303の上の6つのノブに当たるCCを可変抵抗で操作するものです。このボードには、アナログ入力が8端子あるので、簡単に作成できます(できたら、また記事上げます。)。
IMUで新しい音作りが出来るか期待したんですが、303系の音作りはやっぱりノブですね。
Wavetable系は、まだIMUで面白いことできる気もしますが...

# まとめ
ようやく日本の技適の通ったArduino nano 33 IoTを使えば、簡単にBLE MIDIコントローラーを作れることが分かりました。皆さんも、お気に入りのMIDIコントローラを作ってみてはいかがでしょうか?

ノブ6つのコントローラでいいなら、[nanoKEY Studio](https://www.korg.com/jp/products/computergear/nanokey_studio/)買った方が速いんですけどね...
これならWinでも動くし...
ただ、コンパクトなのが欲しいんですよね...