Platform: Particle MSOM, Device OS 6.3.5
API: BleLocalDevice::scanWithFilter(const BleScanFilter&, const BleOnScanResultStdFunction&) and the function-pointer overloads of the same.
There's a problem with scanWithFilter when used with a callback: Callbacks are silently dropped — the scan runs for its full timeout, returns successfully, but the user-supplied callback is never invoked, even though adverts matching the (empty) filter are demonstrably being received by the HAL.
The vector-returning overload Vector<BleScanResult> scanWithFilter(const BleScanFilter&) works correctly under the same conditions.
Example
BleScanFilter scanFilter;
scanFilter.allowDuplicates(true); // critical — see below
BLE.setScanTimeout(500); // 5 s
// (A) callback overload — broken: callback never fires
BLE.scanWithFilter(scanFilter, [&](const BleScanResult& result) {
Log.info("got advert from %s", result.address().toString().c_str());
});
// (B) vector overload — works: all adverts present in returned vector
Vector<BleScanResult> results = BLE.scanWithFilter(scanFilter);
Both forms use the same BleScanDelegator machinery in wiring/src/spark_wiring_ble.cpp. The only difference along the failing path is that start(const BleOnScanResultStdFunction& callback) assigns scanResultCallbackRef_ = callback; before calling hal_ble_gap_start_scan(...). Adverts received by the HAL during the scan reach onScanResultCallback, but scanResultCallbackRef_(result) is either not invoked or invokes an invalid functor.
A workload that reliably triggers (A): scanning for 5 s with 2 BLE peers in range that each advertise on a ~1 Hz cadence. The vector overload returns ~20–30 results; the callback overload returns 0 callback invocations.