CAUTION: Select the good library maintained over time

Building and maintaing a Dev setup for this application is quite difficult even with AI in 2025, because there hase been a few depreciated project in the past that have been replaced by new, updates vs harware, and well maintained so far and hopefully in the future.

I started to use exploitable setup of BLE-MIDI on a ESP32 back in 2021 through Phil Schatzmann library mentioned below but depreciated since unless you downgrade to previous versions of ESP espressif (in my case, using espDevKit C4 and esp328266 boards).

As of August 2025 at least,my best approach would be to use:

( Check Update as of Aug 25 with Grok)

Developing an ESP32-based instrument that uses BLE-MIDI (Bluetooth Low Energy for Musical Instrument Digital Interface) is a straightforward and popular project for creating wireless MIDI controllers or simple instruments. The ESP32’s built-in BLE support makes it ideal for low-power, low-latency communication with devices like smartphones, tablets, or computers running MIDI-compatible software (e.g., DAWs like Ableton Live or synth apps). The best approach is to use the Arduino IDE for simplicity, especially if you’re new to embedded development, as it abstracts much of the complexity while allowing quick prototyping. For more advanced or performance-critical applications, you could switch to Espressif’s IDF framework, but Arduino is recommended for most users due to its extensive library ecosystem and ease of use.

This approach focuses on:

  • Hardware: Minimal setup with ESP32 and input sensors.
  • Software: Arduino IDE with BLE-MIDI libraries.
  • Development: Iterative coding, testing, and integration.

Hardware Requirements

  • ESP32 Board: Any ESP32 development board, such as the ESP32-WROOM-32, ESP32 DevKitC, or Wemos Lolin32 Lite (battery-friendly for portable instruments). Ensure it has BLE support (most do).
  • Input Components: Depending on your instrument design:
    • Buttons/switches for notes or triggers (e.g., momentary switches).
    • Potentiometers or sliders for controls like volume, pitch, or modulation.
    • Sensors (e.g., capacitive touch for experimental instruments like plant-based controllers, or accelerometers for motion-sensitive ones).
  • Power: USB for development; add a battery (e.g., LiPo) for portability.
  • Prototyping: Breadboard and jumper wires for initial testing.

Example Wiring for a Basic Instrument (1 button for notes, 1 potentiometer for control):

  • Button: Connect to GPIO 13 and GND (use internal pull-up).
  • Potentiometer: Connect to GPIO 15, 3.3V, and GND.
  • Use 3.3V logic levels—ESP32 is not 5V tolerant.

Software Setup

  1. Install Arduino IDE: Download from arduino.cc if not already installed.
  2. Add ESP32 Support:
    • In Arduino IDE, go to File > Preferences.
    • Add https://dl.espressif.com/dl/package_esp32_index.json to “Additional Boards Manager URLs”.
    • Go to Tools > Board > Boards Manager, search for “esp32”, and install the package by Espressif.
    • INDISPENSABLE: Downgrade Install of esp32 espressif down to v 2.017
  3. Install Libraries:
    • Go to Tools > Manage Libraries.
    • Search and install:
      • “ESP32 BLE MIDI” (by Maxime André) for core BLE-MIDI functionality. This library handles MIDI over BLE as a server or client.
      • Optionally, “Control Surface” (by tttapa) for a higher-level abstraction to map controls to MIDI messages—great for complex instruments. It supports ESP32 BLE and includes examples for potentiometers, buttons, and more.
      • “MIDI Library” (by FortySevenEffects) if not auto-installed as a dependency.
  4. Board Settings: Select your ESP32 board under Tools > Board. Set upload speed to 115200 to avoid errors.

 

STK - Synthesis ToolKit - C++ library alternative to MIDI for Real-Time Sound Synthesis

 

STK ALTERNATIVE:

Synthesis Toolkit for ArduinoESP32:

https://www.pschatzmann.ch/home/2020/09/28/the-synthesis-toolkit-skt-library-for-the-arduino-esp32-midi/

 

 

 

 

It shall use the ESP32 BLE MIDI library made byMaxime ANDRÉ in 2023 and available from the arduino library manager

https://docs.arduino.cc/libraries/esp32-ble-midi/

this tqkes to call the library with:

 

 

ESP32 + BLE-MIDI ( version and Guides from KontinuumLAB

Sources:

– Arduino website ref: https://www.arduino.cc/reference/en/libraries/esp32-ble-midi/

// BLE-MIDI bLUETOTH SETUP
// BE CAREFUL WITH TRICKY SETUP DETAILS: READ THIS:

// source and initial code from :
// KontinuumLAB: https://www.youtube.com/watch?v=5peRGrayIJA
//

// LIRARIES:
// MODIFY from Tools/boar/libraries/ search for EP32 => !!! WARNING !!! you need to downgrade to install esp32 v2.0.17 and not above as there are ble-midi libraries issues when using v3.0.0 and above on date of July 2024 at least.
// => this creates a new Boards Menu for ESP32 Boards

// Note: the maker from KontinuumLAB uses the “Nola 32 Lite”
// I use a board ESP32DeviKitC4 and use the modules ESP32 DEV-MODULE or the DoIT ESP DEVKIT V1 

// NOTE: when using a board model esp32 devkitC4 use Tools/board/esp32/ DoIT ESP DEVKIT V1

// IMPORTANT: to avoid later issues, make sure to set de upload speed to 115200 from the Selected Board menu

// add libraries for Bluetooth and MIDI: search for “BLE-MIDI” from Sketch/include libraries/manage libraries

// NOTE: USING SERIAL BAUDRATE 115200 for debugging
// => check for telnet putty to send serial prints to distant server while using the esp BLE wireless (still for dev logs)

// ****************************************************************************************

 

INITIAL CODE EXAMPLE FROM KONTINUUMLAB:

// TEMPLATE BASIC BLE-MIDI EXAMPLE TO CONNECT, recognise the ESP32 as a MIDI Input Controller and send (or receive) MIDI data to a smartphone and computer.
// ANdroid phone: Download Android App e.g. Bluetooth MIDI Connecotr (BLE-MIDI) to BLE connect and identify the boad as a MIDI Input
// Other peripherals: to do

// The customize your loop

// BE CAREFULL WITH TRICKY SETUP DETAILS: READ THIS:

// source:
// KontinuumLAB: https://www.youtube.com/watch?v=5peRGrayIJA
// initial code from :

// LIRARIES:
// add from Tools/boar/libraries/ search for EP32 => !!! WARNING !!! you need to downgrade to install esp32 v2.0.17 and not above as there are ble-midi libraries issues when using v3.0.0 and above
// => thi creates a new Boards Menu for ESP32 Boards

// Note: the maker from KontinuumLAB uses the “Nola 32 Lite”

// when using a board model esp32 devkitC4 use Tools/board/esp32/ DoIT ESP DEVKIT V1

// IMPORTANT: to avoid later issues, make sure to set de upload speed to 115200 from the Selected Board menu

// add libraries for Bluetooth and MIDI: search for “BLE-MIDI” from Sketch/include libraries/manage libraries

// NOTE: USING SERIAL BAUDRATE 115200 for debugging
// => check for telnet putty to send serial prints to distant server while using the esp BLE wireless (still for dev logs)

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

//BLEMIDI_CREATE_DEFAULT_INSTANCE()
BLEMIDI_CREATE_INSTANCE(“AWESOME_INSTRUMENT”, MIDI)

const int btnPin = 36;
const int potPin = 39;
const int potPin2 = 34;

int midiNote;
int btnState;
int lastBtnState;
int sensiVal;

int velocity;

#define LED_BUILTIN 2

void setup() {
pinMode(btnPin, INPUT_PULLUP);

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

BLEMIDI.setHandleConnected(onConnected);
BLEMIDI.setHandleDisconnected(onDisconnected);

MIDI.begin();
Serial.begin(115200);
}

void loop() {

lastBtnState = btnState;
btnState = digitalRead(btnPin);
String msg = “Button State: “;
msg += ” ” + (String) btnState;
Serial.println(msg);

sensiVal = analogRead(btnPin);
msg = “SenSiVal: “;
msg += ” ” + (String) sensiVal;
Serial.println(msg);
Serial.println();

velocity = 127; //map(analogRead(potPin2), 0, 4095, 0, 127);

// if(lastBtnState == 1 && btnState == 0){
midiNote = 57; //map(analogRead(potPin), 0, 4095, 60, 80);
MIDI.sendNoteOn(midiNote, velocity, 1);
// }
delay(1000);
// else if(lastBtnState == 0 && btnState == 1){
MIDI.sendNoteOff(midiNote, 0, 1);
// }

}

void onConnected() {
digitalWrite(LED_BUILTIN, HIGH);
}
void onDisconnected() {
digitalWrite(LED_BUILTIN, LOW);
}

 

What’s next?

Connect to a phone or computer. For this you will need 2 applications:

– 1 App to connect as a BLE-MIDI controller to your device

– 1 App to exploit the MIDI signals you will receive or transmit… and make sounds… or other things.

It is fairly easy and free on Android with the MIDI-BLE Connector App, and then any other MIDI App such as FluidSynth.
It is a little trickier aftwerwards to connect to a windows computer, involving first to connect your BT device as a regulaar BT device with the windows tools, add an application such as MIDIBerry or the Free trial of BLE-MIDI connector, and then to use a custom MIDI Signals router and MIDILoop.

 

 

 

Application Example on Android

Source:

 

Fairly simple

Application Example on Windows

lBLE-MIDI A little trickier as there are 2 ways for windows to manage MIDI (MME and UWP protocols), and BLE-MIDI is detected as a BT device by windows but it does not use the good one to properly route signals as MIDI events.THis takes the custom App and config file with procedure to make it below, as proposed by Franck1119 on GigPerformer website.

 

https://community.gigperformer.com/t/midi-bluetooth-ble-router-for-windows/13072/18

1) Once your BLE-MIDI firmware is uploaded and your board running, connect to your windows computer as a regulaar BT Device.

2) Use Franck’s exe file to lauch his router and note the names of your ports to route.

CMD PROMPT:   (when using noInstall version of Franck)
cd filespath              cd desktop/MidiUWPRouter-NoInstall
MidiUWPRouter.exe

3) Update the config file based on you reported ports names

see example below

 

4) run again the .exe from a cmd prompt adding the path and nome of the configFIle if you are not there:
MidiUWPRouter.exe routerconfig.txt

!!! IMPORTANT: over long periods of time, the ID of the BLE-Device may change and you will have to redo steps 2 and 3 to update your config file

=> this should to the same as before and additionnaly log info about the routes successfully created at the bottom part of the App  and BLE-MIDI

 

NOTE: In my case, th routerfile.txt links bleMIDI to LoopMIDI, and looks like this:

[MIDIIN=LPD8-Device-IN]
#Filter=RegEx:LPD8
Filter=ID:SWD\MMDEVAPI\MIDII_2DDA5ADD.P_0001
Filter=ID:SWD\MMDEVAPI\MIDII_F42324FE.P_0001
Filter=ID:SWD\MMDEVAPI\MIDII_FD1EB35A.P_0001
Filter=ID:SWD\MMDEVAPI\MIDII_BDD3205A.P_0001

[MIDIIN=LPD8-Device2-IN]
Filter=RegEx:^Dummy.*

[MIDIIN=BT-Device-IN]
Filter=RegEx:^.{4}Frank.*

[MIDIIN=Cable-a-Device-In]
Filter=ID:SWD\MMDEVAPI\MIDII_0AB1A388.P_0004

[MIDIOUT=Cable-a-Device-Out]
Filter=ID:SWD\MMDEVAPI\MIDII_0AB1A388.P_0000

[MIDIOUT=Cable-b-Device-Out]
Filter=RealName:MIDI

[ROUTE=rt1]
SOURCE=LPD8-Device-IN
DESTINATION=LPD8-Device-Out

[ROUTE=rt2]
SOURCE=BT-Device-IN
DESTINATION=Cable-b-Device-Out

[ROUTE=rt3]
SOURCE=LPD8-Device-IN
DESTINATION=Cable-a-Device-Out

[ROUTE=rt4]
SOURCE=Cable-a-Device-In
DESTINATION=Cable-b-Device-Out

——————————————————————————————–

 

 

 

Circuit Python Ble Midi On Windows

Source: https://www.electromaker.io/project/view/circuit-python-ble-midi-on-windows

ESP32+BLE-MIDI by Nimble
New nimBLE Library to favor?

There is an alternative to the below and older BLE library to be provided here:
https://www.arduino.cc/reference/en/libraries/nimble-arduino/

1) Does it keep its promises?
2) Is it still compatible with the BLE-MIDI libraries and programs I made so far (as of Sept 24)

Let’s check!

At first and first good point, at least for a few projects, it is said to be compatible with ESP32.

Next, let’s install it from the Arduino Library….

About conflicts and ESP32 blocking and non-blocking web server and sync libraries

MANY CONFLICTING ISSUES MAY OCCUR WHEN SETTING UP LIBRARIES FOR A GIVEN CONFIGURATION, AND ALSO TO HAVE IT MAINTAINED AND STABLE OVER TIME.

Using different boards setup from the same ArduinoIDE can also be an issue. Then installing multiple versions of arduino, integrating libraries in your folders, or else using the online IDE may be a safe practice for long-lasting developments.

Another question raises concerning threading management of e.g; esp32 BLE signals vs WIFI IO signals, introducing the concept of blocking and non-blocking web server management.

 Setup and Code using webserver.h and blocking solution

Use the Standard Web Server (blocking solution)

 

Here is a full code example that integrates the web server into your sketch. This code will allow you to control a single variable (masterVelocity) from a web page.

C++

 

#include <WiFi.h>
#include <WebServer.h>
#include <BLEMIDI_Transport.h>
#include <hardware/BLEMIDI_ESP32_NimBLE.h>

// Replace with your network credentials
const char* ssid = “YOUR_WIFI_SSID”;
const char* password = “YOUR_WIFI_PASSWORD”;

// Create a WebServer object on port 80
WebServer server(80);

// This macro creates the BLE MIDI instance and a MIDI interface
BLEMIDI_CREATE_INSTANCE(“PLK-MIDI-BLE”, MIDI)

// GLOBAL VARIABLES
const int nbcapteurs = 6;
const int BufferSize = 10;
byte SenSi_ID[nbcapteurs] = { 36, 39, 34, 35, 32, 33 };
byte sensorVal[nbcapteurs][BufferSize];
byte triggerSensorVal[nbcapteurs] = { 30, 30, 30, 30, 30, 30 };
byte sensorState[nbcapteurs] = { 0, 0, 0, 0, 0, 0 };
byte MIDI_Note[nbcapteurs] = { 46, 40, 49, 44, 36, 31 };
const int channel = 10;
int masterVelocity = 100; // Master velocity variable from the web interface

// HTML code for the web page
const char index_html[] PROGMEM = R”rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>MIDI Controller</title>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<style>
body { font-family: Arial; text-align: center; margin:auto; padding-top: 20px;}
.slider { width: 300px; }
input[type=text] { width: 100px; }
</style>
</head>
<body>
<h1>MIDI Pads Control</h1>
<h3>Master Velocity</h3>
<input type=”range” class=”slider” min=”0″ max=”127″ id=”velocitySlider” oninput=”updateVelocity(this.value)”>
<p>Velocity: <span id=”velocityValue”>100</span></p>
<script>
function updateVelocity(val) {
document.getElementById(‘velocityValue’).innerHTML = val;
var xhr = new XMLHttpRequest();
xhr.open(‘GET’, ‘/velocity?value=’ + val, true);
xhr.send();
}
</script>
</body>
</html>
)rawliteral”;

void handleRoot() {
server.send_P(200, “text/html”, index_html);
}

void handleVelocity() {
if (server.hasArg(“value”)) {
masterVelocity = server.arg(“value”).toInt();
}
server.send(200, “text/plain”, “OK”);
}

void setup() {
Serial.begin(115200);

// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println(“Connecting to WiFi…”);
}
Serial.println(WiFi.localIP());

// Set up web server routes
server.on(“/”, handleRoot);
server.on(“/velocity”, handleVelocity);

server.begin();

// Your existing setup code
for (int i = 0; i < nbcapteurs; i++) {
for (int j = 0; j < BufferSize; j++) {
sensorVal[i][j] = 0;
}
}
MIDI.begin();
for (int i = 0; i < nbcapteurs; i++) {
pinMode(SenSi_ID[i], INPUT);
}
}

void loop() {
server.handleClient();
MIDI.read();
manage_Signal_Data_Aquisition_And_Storage();
manage_Signals_Mapping();
manage_Serial_Out_Display();
}

void manage_Signal_Data_Aquisition_And_Storage() {
for (int i = 0; i < nbcapteurs; i++) {
for (int j = BufferSize – 1; j > 0; j–) {
sensorVal[i][j] = sensorVal[i][j – 1];
}
sensorVal[i][0] = analogRead(SenSi_ID[i]) / 16;
}
}

void manage_Signals_Mapping() {
for (int i = 0; i < nbcapteurs; i++) {
if (sensorState[i] == 0) {
if (sensorVal[i][0] > triggerSensorVal[i]) {
int dyn_Vel = map(masterVelocity, 0, 127, 90, 127); // Use masterVelocity
MIDI.sendNoteOn(MIDI_Note[i], dyn_Vel, channel);
sensorState[i] = 1;
}
} else if (sensorState[i] == 1) {
if (sensorVal[i][0] < triggerSensorVal[i]) {
MIDI.sendNoteOff(MIDI_Note[i], 0, channel);
sensorState[i] = 0;
}
}
}
}

void manage_Serial_Out_Display() {
String SerialDevMsg = (String)sensorVal[0][0];
for (int i = 1; i < nbcapteurs; i++) {
SerialDevMsg += “,” + (String)sensorVal[i][0];
}
Serial.println(SerialDevMsg);
}

 

 

 

 

 

_______________________________________________________________________________________________________

 

Blocking vs. Non-Blocking

 

  • WebServer.h (Blocking): This library waits for a client to connect and a request to finish before it can move on to other tasks. In your loop() function, the line server.handleClient(); is where the sketch will pause if a client is trying to access the web page. This can cause a noticeable delay in your MIDI processing, as the code won’t read sensor data or send MIDI messages until the web transaction is complete.

  • ESPAsyncWebServer (Non-Blocking): This is an asynchronous library. It handles web requests in the background, allowing your main loop() to continue running without interruption. This is much better for real-time applications like a MIDI controller where timing is critical. Your sketch can process sensor inputs and send MIDI notes simultaneously while a web page is being served.

     

The reason we switched to the “less good” solution is that ESPAsyncWebServer, while technically superior, was causing a conflict with your BLE-MIDI and NimBLE-Arduino libraries. The standard WebServer.h library is a more stable option because it uses a different approach that doesn’t conflict with your other libraries, allowing you to have a working web interface even if it’s not the most performant solution.

For your project, the WebServer.h solution should be perfectly acceptable, as a web request to change a variable is not a continuous, high-frequency event. The brief pause while the ESP32 handles the web request will likely be imperceptible.

 

 

ok but this will be limiting over time. Are there other ways with non-blocking solution and no conflicts between libraries? Could the async conflict with webserver?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

That’s an insightful question. Yes, you’re right; a blocking solution like WebServer.h can be a limitation for a real-time application.

 

 

 

 

 

 

The core issue is a conflict at a very low level in the ESP32’s software. The BLE-MIDI library and the ESPAsyncWebServer library both need to control and manage the ESP32’s underlying network functions. This leads to them “stepping on each other’s toes” and causing conflicts.

However, there is a way to get a non-blocking, asynchronous solution that avoids these conflicts. The key is to use a different BLE-MIDI library that is known to be compatible with the async web server libraries.

 

 

 

 

 

 

The ESP32-BLE-MIDI library by fbiego is a great alternative. It’s designed to work specifically with the ESP32 and is known to be more compatible with other networking libraries.

 

 

 

 

 

 

 

 

A New Approach for non-blocking setup

 

 

 

 

 

 

 

To get a non-blocking solution without conflicts, you will need to:

 

 

 

 

 

 

  1. Remove the current libraries: Uninstall the BLE-MIDI and NimBLE-Arduino libraries.

  2. Install the new library: In the Library Manager, search for and install ESP32-BLE-MIDI. This library is often more stable and less prone to conflicts with other ESP32-specific libraries.

  3. Re-introduce the async libraries: Reinstall ESPAsyncWebServer and AsyncTCP.

  4. Rewrite your code: You will have to rewrite your sketch to use the ESP32-BLE-MIDI library’s syntax, which is different from the lathoub library.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Could async libraries conflict with WebServer.h?

 

 

 

 

 

 

 

No, the async libraries themselves will not conflict with WebServer.h. You can have both installed without issue because they are just different libraries. The problem is that the async library’s implementation conflicts with the way your current BLE-MIDI library works. Switching to the fbiego library should solve this and give you the non-blocking performance you’re looking for.

 

 

 

 

 

 

 

 

 

 

Photo de profil

 

 

 

 

 

 

Gemini peut se tromper. Veuillez donc vérifier ses réponses

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

Compte Google
Rodolphe Koehly
rodolphe.koehly@gmail.com