I'm realizing a BLE MIDI controller for iPad using an Arduino Nano 33 BLE device. The following code is able to:
- make the device discoverable as a BLE MIDI peripheral
- establish a connection with a BLE MIDI central application
The connection is stable only with Android apps. Each iOS app (eg Garageband, AUM, etc.) closes the connection immediately (the led on the arduino board turns on and off in few seconds), but if the device constantly sends MIDI messages (look at the lines of code commented in the loop() function) the connection remains active forever; unfortunately the repeated sending of messages is not the purpose of the controller that I want to realize.
There are probably specific configurations of the BLE service or polling actions to be implemented to comply with the strict iOS standards, but I could not find any working solution or example for the Nano 33 BLE device that does not include sending notes in the loop() function.
#include <ArduinoBLE.h>
byte midiData[] = {0x80, 0x80, 0x00, 0x00, 0x00};
// set up the MIDI service and MIDI message characteristic:
BLEService midiService("03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
BLECharacteristic midiCharacteristic("7772E5DB-3868-4112-A1A9-F2669D106BF3",
BLEWrite | BLEWriteWithoutResponse |
BLENotify | BLERead, sizeof(midiData));
bool midi_connected = false;
void setup() {
// initialize serial communication
Serial.begin(9600);
// initialize built in LED:
pinMode(LED_BUILTIN, OUTPUT);
// Initialize BLE service:
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (true);
}
BLE.setLocalName("MBLE");
BLE.setAdvertisedService(midiService);
BLE.setEventHandler(BLEConnected, onConnected);
BLE.setEventHandler(BLEDisconnected, onDisconnected);
midiCharacteristic.setEventHandler(BLEWritten, onWritten);
midiService.addCharacteristic(midiCharacteristic);
BLE.addService(midiService);
BLE.setConnectable(true);
BLE.setAdvertisingInterval(32);
BLE.setConnectionInterval(32, 64);
BLE.advertise();
}
void loop() {
BLEDevice central = BLE.central();
if (central) {
// midiCommand(0x90, 60, 127);
// delay(250);
// midiCommand(0x80, 60, 0);
// delay(250);
}
}
void onConnected(BLEDevice central) {
digitalWrite(LED_BUILTIN, HIGH);
midi_connected = true;
}
void onDisconnected(BLEDevice central) {
digitalWrite(LED_BUILTIN, LOW);
midi_connected = false;
}
void onWritten(BLEDevice central, BLECharacteristic characteristic) {
auto buffer = characteristic.value();
auto length = characteristic.valueLength();
if (length > 0)
{
// echo on the next midi channel
midiCommand(buffer[2], buffer[3], buffer[4]);
}
}
void midiCommand(byte cmd, byte data1, byte data2) {
midiData[2] = cmd;
midiData[3] = data1;
midiData[4] = data2;
midiCharacteristic.setValue(midiData, sizeof(midiData));
}