NimBLEDevice::deinit(true) deletes the NimBLEServer, and ~NimBLEServer()
runs:
if (m_deleteCallbacks && m_pServerCallbacks != &defaultCallbacks)
delete m_pServerCallbacks;
SerialBLEInterface::begin() registers itself with pServer->setCallbacks(this)
and that overload's deleteCallbacks parameter defaults to true. Our
SerialBLEInterface is the by-value member _ble, sitting after _usb/_tcp/_ws
inside MultiTransportCompanionInterface, and main.cpp placement-news that
whole object into one heap_caps_malloc block. So the delete above calls
operator delete on a pointer into the middle of a live allocation:
"CORRUPT HEAP: Bad head at 0x..." and a panic reboot.
origin/main only ever reached deinit(true) from prepareForHttpOta, and
disableBle carried an explicit "we deliberately do NOT NimBLEDevice::deinit()
here" note. This branch removed that note and added deinit(true) to
disableBle() and suspendBleForWifiReconnect(), which puts the crash on the
everyday Bluetooth-off toggle, on every Wi-Fi credential apply, and on the
10 s link-loss retry -- so an AP going down while Bluetooth is on panics the
device.
Detach the callbacks first at all three sites. setCallbacks(nullptr) installs
NimBLE's static defaultCallbacks sentinel, which the destructor explicitly
skips. NimBLECharacteristic never deletes its callbacks, so the rx/tx
setCallbacks(this) registrations need no equivalent.
Also drop an unreachable return in wifiEnableGate(): the Pager branch returns
unconditionally, so the shared LowMemory tail below it was dead on that board.
Signed-off-by: Pixel Perfect <me@pixp.cc>