如何在 Arduino NANO 33 BLE (nRF52) 和 iPad 之间保持活跃的 MIDI BLE 连接

How to keep an active MIDI BLE connection between an Arduino NANO 33 BLE (nRF52) and an iPad

我正在使用 Arduino Nano 33 BLE 设备为 iPad 实现 BLE MIDI 控制器。以下代码能够:

只有 Android 个应用程序连接稳定。 每个 iOS 应用程序(例如 Garageband、AUM 等)立即关闭连接(arduino 板上的 LED 会在几秒钟内打开和关闭),但如果设备不断发送 MIDI 消息(查看 loop() 函数中注释的代码行)连接永远保持活动状态;不幸的是,重复发送消息并不是我想要实现的控制器的目的。

可能 BLE 服务的特定配置轮询操作要实施以符合严格的iOS标准,但我找不到 Nano 33 BLE 设备的任何工作解决方案或示例,其中不包括在 loop() 函数中发送注释.

#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));
}

我(最终)通过查看 Apple 提供的 MIDI BLE 规范 找到了一个解决方案

The accessory shall request a connection interval of 15 ms or less. Apple recommends starting with a request for a connection interval of 11.25 ms and going to 15 ms if the connection request is rejected by the Apple product. Intervals higher than 15 ms are unsuitable for live playback situations.

以后

Apple devices that support Bluetooth Low Energy MIDI will attempt to read the MIDI I/O characteristic after establishing a connection with the accessory. [...] The accessory shall respond to the initial MIDI I/O characteristic read with a packet that has no payload.

所以我在setup()函数中更改了连接间隔

BLE.setConnectionInterval(9, 12);

并在连接事件处理函数中包含几行代码

void onConnected(BLEDevice central) {
  digitalWrite(LED_BUILTIN, HIGH);
  midi_connected = true;
  midiCharacteristic.setValue(0);
}

就是这样!