Arduino ESP32-S3の使い方

Arduino, ESP32-S3-N8R2を購入しました、N8R2は8MByte Flash、2MByte PSRAMを搭載しています。 N16R8もあります。こちらは16MByte Flash、8MByte PSRAMを搭載しています。 N16R8は価格も高いので、N8R2(800円くらいでした)を選びました。

ESP32-S3は他のArduinoとは違って、2つのスイッチと2つのUSB TypeCのコネクタがついています。

Arduino IDEのボードマネージャで、esp32をインストールします。

Boardとして”ESP32S3 Dev Module"を選びます。 IDEを使ってスケッチをESP32に書き込むにはUSBと書かれたType Cコネクタを使います。 ESP32を接続して電源を入れた後、BOOTボタンを押したまま、RST(Reset)ボタンを押します。 これで、Aeduino IDEからESP32がCOMポートとして見えます。



他にも数々の設定項目が表示されます。私は以下の3箇所を変更しました。 特に、USBからスケッチを書き込むためには、"USB CDC On Boot:"Enabled"に設定します。



LinuxでArduino IDEを使っている時、私の場合、スケッチ書き込み時に、Permission denided 'dev/ttyACM0'  とエラーが表示されました。この場合、以下のコマンドでグループを確認します。この場合、dialoutというグループでした。

> ls -l /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0  6月  2 16:01 /dev/ttyACM0

現在、ユーザーが所属しているグループを確認します。dialoutが含まれていませんでした。

> groups
bear adm cdrom sudo dip plugdev lpadmin sambashare

グループにdialoutを加えます。これで書き込めるようになります。

> sudo usermod -aG dialout $USER

USB MIDIをインストールする

ESP32-S3はTinyUSBというライブラリを使って、USB MIDIに対応しています。 Arduino IDEのESP32-S3で使われるTinyUSB は、通常は別途ライブラリとしてインストールするものではなく、 ESP32 Arduino Core に含まれています。

USB MIDIをインストールします。

次のようなスケッチで確認することができます。

#include "USB.h"
#include "USBMIDI.h"

USBMIDI MIDI;

void setup() {
  USB.begin();
}

void loop() {
  MIDI.noteOn(60, 127, 1);
  delay(500);
  MIDI.noteOff(60, 0, 1);
  delay(500);
}
 

ESP32 BLE MIDIをインストールする

ESP32-S3はBluetoothに対応してみます。ライブラリを見てみるとそのものずばりのESP32 BLE MIDIがありましたのでインストールしました。

インストールするとき以下のダイアログが表示され、関連するモジュールNimBLE-Arduinoがインストールされます。

インストール後、Exampleにある01-Basic-Midi-Deviceをコンパイルしてみます。

以下のようなエラーになりました。

In file included from /Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/BLEMidi.h:4,
                 from /private/var/folders/f0/57_0lcld44v9vkth22lzqq_40000gp/T/.arduinoIDE-unsaved202655-1371-1j7ua4h.7afg/01-Basic-Midi-Device/01-Basic-Midi-Device.ino:2:
/Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/BLEMidiServer.h:18:10: error: 'void BLEMidiServerClass::onConnect(NimBLEServer*)' marked 'override', but does not override
   18 |     void onConnect(BLEServer* pServer) override;
      |          ^~~~~~~~~
/Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/BLEMidiServer.h:19:10: error: 'void BLEMidiServerClass::onDisconnect(NimBLEServer*)' marked 'override', but does not override
   19 |     void onDisconnect(BLEServer* pServer) override;
      |          ^~~~~~~~~~~~
exit status 1

Compilation error: exit status 1

このエラーを調べてみると、NimBLE-Arduinoのバージョンが新しいことが原因とのこと、 現在インストールされていたのは最新のバージョンで2.5.0でした。これを、REMOVEし、1.4.3をインストールします。

この状態でコンパイルすると、コンパイルは通ります。スケッチをESP32-S3へ書き込み動作させます。 Serial Monitorを見ていると以下のようなエラーが出てBLE MIDIのデバイスとして認識できません。

000062  
A6      : 0x00000000  A7      : 0x3fcebc25  A8      : btdm: bss start 0x3fcef180, len 36
btdm: data start 0x3fcef174, data start rom 0x40057350, len 12
MAGIC fadebead VERSION 0001000b
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

このエラーを解消するために、ESP32 Coreのバージョンをダウンします。今日現在インストールされていたのは3.3.8でした。 3.2.0へバージョンダウンします。

これでコンパイルが通り、ESP32-S3へインストール、RESETスイッチを押すとBluetooth MIDI デバイスとして認識できます。

 
MacでBluetooth MIDIデバイスを認識

上でインストールしたESP32-S3デバイスをMacで認識させます。Audio MIDI設定を起動します。



ウィンドウメニューから「MIDIスタジオを表示」を選択。ウィンドウ上部のBluetoothマークをクリックします。



装置名に「Basic MIDI Device」が表示されますので、これをクリックし「接続」ボタンを押します。



MIDI Monitorを立ち上げるとノートメッセージを確認することができます。



ESP32 BLE MIDIのAPI

上でインストールしたESP32 BLE MIDIのAPIをExampleを見て羅列してみました。

Device(デバイス用)
接続
BLEMidiServer.begin("Device Name") Setupで使用。MIDI Serverをスタートする。"Device Name"がクライアントから見える。
BLEMidiServer.isConnected() 接続していればTrue
送信
BLEMidiServer.noteOn(uint8_t channel, uint8_t note, uint8_t velocity); Note On Messageを送信
BLEMidiServer.noteOff(uint8_t channel, uint8_t note, uint8_t velocity); Note Off Messageを送信
BLEMidiServer.afterTouchPoly(uint8_t channel, uint8_t note, uint8_t pressure); Poliphonic After Touchを送信
BLEMidiServer.controlChange(uint8_t channel, uint8_t controller, uint8_t value) Control Changeを送信
BLEMidiServer.programChange(uint8_t channel, uint8_t program) Program Changeを送信
BLEMidiServer.afterTouch(uint8_t channel, uint8_t pressure) Channel After Touchを送信
BLEMidiServer.pitchBend(uint8_t channel, uint8_t lsb, uint8_t msb) Pitch Bendを送信
受信
BLEMidiServer.setNoteOnCallback(onNoteOn) void onNoteOn(uint8_t channel, uint8_t note, uint8_t velocity, uint16_t timestamp)
LEMidiServer.setNoteOffCallback(onNoteOff) void onNoteOff(uint8_t channel, uint8_t note, uint8_t velocity, uint16_t timestamp)
BLEMidiServer.setAfterTouchPolyCallback(onAfterTouchPoly) void onAfterTouchPoly(uint8_t channel, uint8_t note, uint8_t pressure, uint16_t timestamp)
BLEMidiServer.setControlChangeCallback(onControlChange) void onControlChange(uint8_t channel, uint8_t controller, uint8_t value, uint16_t timestamp)
BLEMidiServer.setProgramChangeCallback(onProgramChange) void onProgramChange(uint8_t channel, uint8_t program, uint16_t timestamp)
BLEMidiServer.setAfterTouchCallback(onAfterTouch) void onAfterTouch(uint8_t channel, uint8_t pressure, uint16_t timestamp)
BLEMidiServer.setPitchBendCallback(onPitchbend) void onPitchbend(uint8_t channel, uint16_t value, uint16_t timestamp)

(注)pitchBendはコンパイルエラーが出ます

/Users/user/Documents/Arduino/Basic_Midi_Device/Basic_Midi_Device.ino: In function 'void loop()':
/Users/user/Documents/Arduino/Basic_Midi_Device/Basic_Midi_Device.ino:26:30: error: call of overloaded 'pitchBend(int, int, int)' is ambiguous
   26 |       BLEMidiServer.pitchBend(7,123,34);
      |       ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
In file included from /Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/BLEMidiBase.h:6,
                 from /Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/BLEMidiServer.h:4,
                 from /Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/BLEMidi.h:4,
                 from /Users/user/Documents/Arduino/Basic_Midi_Device/Basic_Midi_Device.ino:2:
/Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/Midi.h:16:10: note: candidate: 'void Midi::pitchBend(uint8_t, uint8_t, uint8_t)'
   16 |     void pitchBend(uint8_t channel, uint8_t lsb, uint8_t msb);
      |          ^~~~~~~~~
/Users/user/Documents/Arduino/libraries/ESP32-BLE-MIDI/src/utility/Midi.h:28:10: note: candidate: 'void Midi::pitchBend(uint8_t, float, float)'
   28 |     void pitchBend(uint8_t channel, float semitones, float range = 4);
      |          ^~~~~~~~~
exit status 1

Compilation error: call of overloaded 'pitchBend(int, int, int)' is ambiguous

void Midi::pitchBend(uint8_t, uint8_t, uint8_t) という関数が2つあることが問題です。 これを解決するには、Arduino/libraries/ESP32-BLE-MIDI/src/utility/Midi.hと Arduino/libraries/ESP32-BLE-MIDI/src/utility/Midi.cppに記載の関数名を書き換えます。

Midi.h/Midi.cpp
(修正前)void pitchBend(uint8_t channel, float semitones, float range = 4);
(修正後)void pitchBendTones(uint8_t channel, float semitones, float range = 4);

SP32-BLE-MIDIをいくつか修正しました。修正点は以下の通りです。リポジトリを https://github.com/mikatahara/MH-ESP32-BLE-MIDIにおきました。

  • 引数を3つ取る、void pitchBend();関数が2つあったので1つの名前を変えました。
  • void pitchBendTones(uint8_t channel, float semitones, float range = 4); 関数の中で、PitchBendの最大値を0x3FFFにリミットしました。
  • System Exclusiveを送信する関数、 void sendSysEx(uint8_t *sysex, uint16_t sizeofsysex); を追加しました。


3つのpitchBend送信関数の違い

Pitch Bendは14ビット(0 - 0x3FFFの16384段階)のデータを送信することができます。 このデータをPBと書くことにします。

  • pitchBend(uint8_t channel, uint8_t lsb, uint8_t msb)
       msb = (PB>>7)&0x7F; lsb = PB&0x7F; とこの関数をコールする前に計算しておきます。
  • pitchBend(uint8_t channel, uint16_t value)
       value = PB; 関数の中でmsb, lsbを計算してくれます。
  • pitchBendTones(uint8_t channel, float semitones, float range)
       Pitch Bend Range (ピッチベンドを動かした時、センターから音を上げ下げできる範囲)を±2音とします。 この場合、range = 4 と設定します。この範囲の中で、どれだけピッチベンドを動かすかをsemitonesで表します。 semitonesは正負の値です。semitones > -range/2 && semitones < range/2 の値が入ります。 semitones=1.0であれば1音ピッチが上がり、semitones=-1.0であれば1音ピッチが下がります。 semitones=2.0であれば2音ピッチが上がり、semitones=-2.0であれば2音ピッチが下がります。

semitones=2.0の時、integerValue=0x4000となってしまい、送信されるメッセージが 0x0000となってしまうため、 以下のように一部修正しました。

void Midi::pitchBendTones(uint8_t channel, float semitones, float range)
{
    if(semitones < -range/2 || semitones > range/2)
        return;
    uint16_t integerValue = semitones * 16384 / range + 8192;
    if(integerValue==0x4000) integerValue=0x3FFF;
    pitchBend(channel, integerValue);
}

 
System Exclusiveの送信

System Exclusiveメッセージを送信する関数をBLEMidiServerに追加しました。

/** Send System Exclusive */
void sendSysEx(uint8_t *sysex, uint16_t sizeofsysex);

使い方のサンプルは以下の通りです。

#include <Arduino.h>
#include <BLEMidi.h>

uint8_t midiMessage[] = {
  0xF0,  //sysex
  0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
  0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
  0xF7  //end of sysex
};
void setup() {
    BLEMidiServer.begin("Basic MIDI device");
}
void loop() {
    if (BLEMidiServer.isConnected()) {
        BLEMidiServer.sendSysEx(midiMessage, sizeof(midiMessage));
    }
    delay(1000);
}


 
MIDIマシンコントロール

ESP32 BLE MIDIには、MIDI Machine Control(MMC)のメッセージを送信するためのAPIが 付属しています。(対応している製品があれば…ですが。)

MIDIマシンコントロールは、MIDI仕様のサブセットであり、 マルチトラックレコーダーなどの録音機器を制御するための特定のコマンドを提供します。 MMCメッセージは、標準のMIDIケーブルを介して送信でき、再生、早送り、巻き戻し、停止、一時停止、 録音などの機能をリモートで制御できます。 MIDIマシンコントロールはシステムエクスクルーシブメッセージで送信します。

void mmcPlay(void); void mmcDeferredPlay(void); void mmcPause(void); void mmcStop(void);
void mmcRecordStrobe(void); void mmcRecordPause(void); void mmcRecordExit(void); void mmcEject(void);
vvoid mmcChase(void); void mmcReset(void); void mmcFastForward(void); void mmcRewind(void);

メッセージが送信できませんでしたので、一部ライブラリを修正しました。

Midi.h/Midi.cpp
void Midi::sendMMC(mmc_t command)
(修正前)sendMessage(midiMessage, sizeof(midiMessage));
(修正後)sendSysEx(midiMessage,sizeof(midiMessage));