Fix BLE crash: move all NimBLE calls to Core 0

Root cause: Arduino Core 3.x breaks cross-core NimBLE API calls.
Calling BLEMidiServer.controlChange() from loop() on Core 1 while
NimBLE host runs on Core 0 causes immediate crash.

Fix:
- Move ble_midi_transport.begin() to ble_init_task on Core 0
- All BLE MIDI sends use queue_cc() (cross-core safe flag setter)
- ble_midi_transport.update() processes queued sends on Core 0
- Pin NimBLE-Arduino to v1.4.3 (stable LTS for BLE MIDI)
- Keep CONFIG_BT_ENABLED out of sdkconfig.h (causes double-init)
This commit is contained in:
ash
2026-07-01 06:46:15 +00:00
parent 62d1e430d5
commit 762500e227
6 changed files with 43 additions and 15 deletions
+7
View File
@@ -20,6 +20,8 @@ public:
bool is_connected();
void queue_cc(uint8_t channel, uint8_t cc, uint8_t value);
private:
static void on_control_change(uint8_t channel, uint8_t controller, uint8_t value, uint16_t timestamp);
static void on_note_on(uint8_t channel, uint8_t note, uint8_t velocity, uint16_t timestamp);
@@ -30,4 +32,9 @@ private:
std::function<void(const MidiEvent&)> receive_callback;
bool initialized;
bool client_connected;
uint8_t queued_channel;
uint8_t queued_cc;
uint8_t queued_value;
bool queued;
};
+1
View File
@@ -9,6 +9,7 @@ lib_deps =
adafruit/Adafruit TinyUSB [email protected]
fastled/FastLED@^3.9.0
max22/ESP32-BLE-MIDI@^0.3.2
h2zero/NimBLE-Arduino@^1.4.3
build_unflags =
-DARDUINO_USB_MODE=1
-4
View File
@@ -70,10 +70,6 @@ def patch_sdkconfig_bt():
content = f.read()
defines = {
# Do NOT set CONFIG_BT_ENABLED here — forces Arduino framework
# to pre-init BT controller at startup, which conflicts with
# TinyUSB on ESP32-S3 (causes reboot on USB MIDI send).
# NimBLEDevice::init() handles its own init.
"CONFIG_BTDM_CTRL_MODE_BLE_ONLY": 1,
"CONFIG_BT_NIMBLE_MAX_CONNECTIONS": 1,
"CONFIG_BT_NIMBLE_TASK_STACK_SIZE": 6144,
+1 -1
View File
@@ -54,7 +54,7 @@ void AppTask::update() {
pending_cc = false;
delay(1);
usb_midi->send_cc(pending_channel, pending_cc_num, pending_value);
if (ble_midi) ble_midi->send_cc(pending_channel, pending_cc_num, pending_value);
if (ble_midi) ble_midi->queue_cc(pending_channel, pending_cc_num, pending_value);
}
}
+16 -6
View File
@@ -4,7 +4,7 @@
static BleMidiTransport* instance = nullptr;
BleMidiTransport::BleMidiTransport() : initialized(false), client_connected(false) {
BleMidiTransport::BleMidiTransport() : initialized(false), client_connected(false), queued(false) {
instance = this;
}
@@ -70,24 +70,34 @@ bool BleMidiTransport::begin() {
void BleMidiTransport::update() {
client_connected = BLEMidiServer.isConnected();
if (queued) {
queued = false;
BLEMidiServer.controlChange(queued_channel, queued_cc, queued_value);
}
}
void BleMidiTransport::on_midi_receive(std::function<void(const MidiEvent&)> callback) {
receive_callback = callback;
}
void BleMidiTransport::queue_cc(uint8_t channel, uint8_t cc, uint8_t value) {
queued_channel = channel;
queued_cc = cc;
queued_value = value;
queued = true;
}
void BleMidiTransport::send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) {
BLEMidiServer.noteOn(channel, note, velocity);
Serial.printf("[BLE OUT] Ch:%d NOTE_ON:%d:%d\n", channel, note, velocity);
queue_cc(channel, note, velocity);
}
void BleMidiTransport::send_note_off(uint8_t channel, uint8_t note, uint8_t velocity) {
BLEMidiServer.noteOff(channel, note, velocity);
Serial.printf("[BLE OUT] Ch:%d NOTE_OFF:%d:%d\n", channel, note, velocity);
queue_cc(channel, note, velocity);
}
void BleMidiTransport::send_cc(uint8_t channel, uint8_t cc, uint8_t value) {
BLEMidiServer.controlChange(channel, cc, value);
queue_cc(channel, cc, value);
}
bool BleMidiTransport::is_connected() {
+18 -4
View File
@@ -23,6 +23,13 @@ AppTask controller(&led_driver, &switch_driver, &midi_transport, &ble_midi_trans
TaskHandle_t midi_task_handle = NULL;
void ble_init_task(void* parameter) {
Serial.println("[TASK] BLE init task started on core 0");
ble_midi_transport.begin();
Serial.println("[TASK] BLE init complete, deleting task");
vTaskDelete(NULL);
}
void midi_task(void* parameter) {
Serial.println("[TASK] MIDI task started on core 0");
@@ -346,10 +353,6 @@ void setup() {
switch_driver.set_mux(&mux);
switch_driver.begin();
Serial.println("[INIT] Initializing BLE MIDI...");
delay(1000);
ble_midi_transport.begin();
Serial.println("[INIT] Initializing USB MIDI...");
midi_transport.begin();
delay(500);
@@ -357,6 +360,17 @@ void setup() {
Serial.println("[INIT] Registering MIDI callbacks...");
controller.begin();
Serial.println("[INIT] Starting BLE init on core 0...");
xTaskCreatePinnedToCore(
ble_init_task,
"ble_init",
8192,
NULL,
3,
NULL,
0
);
xTaskCreatePinnedToCore(
midi_task,
"midi_task",