1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ESP32 WROOM 32E / M5Stamp Pico でA2DPとBLE-MIDIとHigh Speed Serial MIDI

Posted at

こんなことやらんでしょ…

ほんとこんなことやる人他に居ないと思うけど、Teensy4.1でシンセサイザーを作る際のサブCPUとして最適だと思うESP32。さらにM5Stamp Picoだと小さくてとてもいい。Teensy4.1にはBluetoothもWiFiもないので、それを補ってもらうためのもの。

Raspberry Pi Zero 2の方が安いし一発で済むかもだけど、ベアメタルで使うの結構ハードル高いから。てかZero 2 + ESP32もいいと思う。

ざっくりとした接続方法

  • ESP32はI2Sのマスターにする(本当はスレーブで動かしたかったがどうやらサポートされてない様子)
  • A2DPで受けたオーディオをI2SでTeensy4.1に送り、Teensy4.1の中で他の音とミックスして出力
  • BLE-MIDIで受けたものをTeensy4.1に送る
  • Teensy4.1から送られたMIDIデータをBT-MIDIに流す
  • ESP32側はSerial2、Teensy4.1側はSerial4を使ってる。

これでBLE-MIDI対応のシンセサイザーが作れるし、iPhoneから送ったオーディオも扱える。ESP32の使い方としては非常に勿体無い。

ESP32 <-> Teensy4.1間のMIDI送受信はMIDIライブラリを使いつつ速度を1.5Mbpsに上げている。BLE-MIDIがどんだけガシガシ送ってくるのか来ないのかわかんないけど。実際使ってみると特に問題なく動く。

ソースコード

Arduino IDEを使う。

#define USE_M5ATOM  1

#if USE_M5ATOM
#include <M5Atom.h>
#endif

#include <AudioTools.h>
#include <BluetoothA2DPSink.h>
#include <BLEMIDI_Transport.h>
#include <hardware/BLEMIDI_ESP32.h>

//  For ESP32 onboard LED
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif

//  A2DP
AudioInfo info(44100, 2, 32);
I2SStream out;
NumberFormatConverterStream convert(out);
BluetoothA2DPSink a2dp_sink(convert);
bool isA2dpConnected = false;

//  MIDI
#define TWVMIDI_RXD 36
#define TWVMIDI_TXD 18
BLEMIDI_CREATE_INSTANCE("TWV2000M", btMidi);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, twvMidi);
bool isBleMidiConnected = false;

void updateLed()
{
#if USE_M5ATOM
    M5.dis.drawpix(0, (isBleMidiConnected? 0x0000ff: 0x000000) | (isA2dpConnected? 0xff0000: 0x000000));
#else
    digitalWrite(LED_BUILTIN, isBleMidiConnected? HIGH: LOW);
#endif
}

void setup() {
#if USE_M5ATOM
    M5.begin(true, false, true);
    M5.dis.drawpix(0, 0x000000);
#endif

    Serial.begin(115200);
    //delay(500);
    //AudioToolsLogger.begin(Serial, AudioLogger::Info);
    Serial.println("Setup start");

    // Configure i2s to use 32 bits
    auto cfg = out.defaultConfig();
    cfg.copyFrom(info);
    cfg.pin_bck = 26;
    cfg.pin_ws = 25;
    cfg.pin_data = 22;
    out.begin(cfg);
    convert.begin(16, 32);  // Convert from 16 to 32 bits
    a2dp_sink.set_auto_reconnect(true, 1000);
    a2dp_sink.start("TWV2000M");
 
    a2dp_sink.set_on_connection_state_changed([](esp_a2d_connection_state_t state, void* ctx) {
        Serial.printf("A2DP connection state changed: %d\n", state);
        if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) isA2dpConnected = true;
        else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) isA2dpConnected = false;
        updateLed();
    });

    a2dp_sink.set_on_audio_state_changed([](esp_a2d_audio_state_t state, void* ctx) {
        Serial.printf("A2DP audio state changed: %d\n", state);
    });

    //  Sub MIDI
    Serial2.begin(1500000, SERIAL_8N1, TWVMIDI_RXD, TWVMIDI_TXD);   //  Specify pin number
    twvMidi.begin();    //  I think this makes serial speed to 31250bps
    twvMidi.turnThruOff();  //  Important!!! Disable MIDI THRU
    Serial2.updateBaudRate(1500000);

    twvMidi.setHandleSystemExclusive([](byte* data, unsigned len) {
        Serial.printf("ESP32 Serial-MIDI len=:%d ", len);
        for (int i = 0; i < len; i++)
        {
            Serial.printf("%02x ", data[i]);
        }
        Serial.println();

        btMidi.sendSysEx(len, data, true);
    });

    BLEbtMidi.setHandleConnected([]() {
        isBleMidiConnected = true;
        updateLed();
    });

    BLEbtMidi.setHandleDisconnected([]() {
        isBleMidiConnected = false;
        updateLed();
    });

    btMidi.begin();
    btMidi.setHandleNoteOn([](byte channel, byte note, byte velocity) {
        //Serial.printf ("Note on: ch=%d, note=%d, vel=%d\n", channel, note, velocity);
        twvMidi.sendNoteOn(note, velocity, channel);
    });

    btMidi.setHandleNoteOff([](byte channel, byte note, byte velocity) {
        //Serial.printf ("Note off: ch=%d, note=%d, vel=%d\n", channel, note, velocity);
        twvMidi.sendNoteOff(note, velocity, channel);
    });

    btMidi.setHandleControlChange ([](byte channel, byte number, byte value) {
        //Serial.printf ("CC: ch=%d, number=%d, vzlue=%d\n", channel, number, value);
        twvMidi.sendControlChange(number, value, channel);
    });

    btMidi.setHandleProgramChange ([](byte channel, byte number){
        twvMidi.sendProgramChange(number, channel);
    });

    btMidi.setHandleAfterTouchChannel ([](byte value, byte channel){
        twvMidi.sendAfterTouch(value, channel);
    });

    btMidi.setHandleSystemExclusive([](byte* data, unsigned len) {
        Serial.printf("ESP32 BLE-MIDI len=%d: ", len);
        for (int i = 0; i < len; i++)
        {
            Serial.printf("%02x ", data[i]);
        }
        Serial.println();

        twvMidi.sendSysEx(len, data, true);
    });
}

void loop() {
    btMidi.read();
    twvMidi.read();

/*
    static uint32_t last = 0;
    if (millis() - last > 1000) {
        last = millis();
        Serial.printf("Free Heap: %d\n", ESP.getFreeHeap());
    }
*/
}
1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?