Arm Mbed OS support forum

BLE Scan fails in mbed 6.9.0 (Arduino Nano 33 BLE)

Hi all,

I’ve been working working with the BLE stack on mbed os and the Arduino Nano 33 BLE for a bit now. Previously, on version 6.2 (or 6.3?) I had a working scan implemented.

Since upgrading to 6.8 (though now I’m on 6.9) – I’ve had an issue where I get the following error:

++ MbedOS Error Info ++
Error Status: 0x80FF0144 Code: 324 Module: 255
Error Message: Assertion failed: _scan_state == ScanState::pending_scan
Location: 0x4DAA1
File: ./mbed-os/connectivity/FEATURE_BLE/source/generic/GapImpl.cpp+1296
Error Value: 0x0
Current Thread: ble event processing Id: 0x20001A28 Entry: 0x4EBDD StackSize: 0x1000 StackMem: 0x20018F78 SP: 0x20019DA4 

You can see it’s a failed assertion related to the current _scan_state. I did some digging and got mbed trace enabled on my build/board and noticed that I don’t get any trace info on set scan state, which I believe should be logging via trace:

void Gap::set_scan_state(ScanState state)
{
    _scan_state = state;

    if (state == ScanState::idle) {
        tr_info("Scan state: idle");
    } else if (state == ScanState::pending_scan) {
        tr_info("Scan state: pending_scan");
    } else if (state == ScanState::pending_stop_scan) {
        tr_info("Scan state: pending_stop_scan");
    } else if (state == ScanState::scan) {
        tr_info("Scan state: scan");
    }
}

What is interesting in my tracing output is I never see any state info traced:

e[2Ke[39m[INFO][BLGP]: Set scan parameters - own_address_type=RANDOM, scanning_filter_policy=NO_FILTER, phys=1, phy_1m_configuration:[interval=50ms,window=37ms,active_scanning=true]e[0m
e[2Ke[39m[INFO][BLGP]: Start scan - duration=0ms, filtering=DISABLE, period=0mse[0m
e[2Ke[39m[INFO][BLGP]: Initiate scane[0m
e[2Ke[39m[INFO][BLDM]: Set random address - address=4f:db:01:3c:7e:dfe[0m
e[2Ke[39m[INFO][BLDM]: Extended scan enable - enable=true, filter_duplicates=DISABLE, duration=0, period=0e[0m
e[2Ke[39m[INFO][BLGP]: Scan successfully startede[0m

Does anyone have any insight into why my _scan_state has an unexpected value? Any insights would be very helpful.

After some further digging, I’ve found it’s failing at this call in PalGapImpl’s function extended_scan_enable

        DmScanStart(
            scanning_phys.value(),
            DM_DISC_MODE_NONE,
            extended_scan_type,
            filter_duplicates.value(),
            duration_ms > 0xFFFF ? 0xFFFF : duration_ms,
            period
        );

This function is here:

void DmScanStart(uint8_t scanPhys, uint8_t mode, const uint8_t *pScanType, bool_t filterDup,
                 uint16_t duration, uint16_t period)
{
  uint8_t i;              /* scanPhy bit position */
  uint8_t idx;            /* param array index */
  dmScanApiStart_t *pMsg;


  if ((pMsg = WsfMsgAlloc(sizeof(dmScanApiStart_t))) != NULL)
  {
    pMsg->hdr.event = DM_SCAN_MSG_API_START;
    pMsg->scanPhys = scanPhys;

    for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
    {
      if (scanPhys & (1 << i))
      {
        /* scan type for this PHY */
        pMsg->scanType[idx] = pScanType[idx];
        idx++;
      }
    }

    pMsg->mode = mode;
    pMsg->duration = duration;
    pMsg->period = period;
    pMsg->filterDup = filterDup;
    WsfMsgSend(dmCb.handlerId, pMsg);
  }
}

Does anyone have any advice on debugging this issue further? The next output I get is the assert failing as mentioned before (for clarity, this assert is in Gap::on_scan_started)

After even more investigation I found this is a bug in mbed os. In reality there is a race-condition on the assert mentioned, so if the thread processing BLE events has a higher priority than the thread starting the scan, the assert will be false because the initial thread has not finished setting the _scan_state at that point.

I plan on making a minimal reproduction and posting to GitHub to figure out a proper solution, but if anyone else stumbles upon this, adjusting thread priority may be a helpful workaround.

Final update here for anyone having similar issues – the BLE API is not threadsafe, and all calls to the BLE api need to happen in a single thread – so always aim to put those calls into some event_queue when needing to access from multiple places.