Xiao ble scan response discussion

I might have found what caused the problem stated here.

The scanner sketch uses the same code to process both the advertising packets and scan response packets. The MSD filter is universal and applies to all incoming packets. The first thing is to make sure both packets are of a common structure otherwise we should not turn on the filter.

After enabling active scanning on the scanner, the BLE stack will wait for the scan response after seeing the scan request. Referring to the current profile of the peripheral, the transmission of advertising and scan response packets takes place within a very short time. If the scanner does not dump all the buffer contents immediately upon receipt and resumes scanning, the buffer will easily be overwritten by next incoming packet.

If the scanner works properly one shall see the following at the serial output. Look at the timing, the scan response is captured within the next milli second after the advertising packet.

If we slow down the scanner by introducing some meaningless tasks, the scan response will vanish since it is always overwritten by the next advertising packet.

/* Check for Manufacturer Specific Data */
len = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, buffer, sizeof(buffer));
if (len)
{
Serial.printf("%14s ", “MAN SPEC DATA”);
Serial.printBuffer(buffer, len, ‘-’);
Serial.println();
memset(buffer, 0, sizeof(buffer));
}

Serial.println();

for (int i = 0; i <= 3; i++)
{
Serial.println("What the Hell??? ");
}

Serial.println();

// For Softdevice v6: after received a report, scanner will be paused
// We need to call Scanner resume() to continue scanning
Bluefruit.Scanner.resume();
}

The serial output will become like this:
scan_ng

The faster the peripheral is broadcasting, the lesser time we got to process the data.

Finally, to explain why the scanner could sometimes (1~5%) pick up the scan response with my earlier sketch. Well, it did not always capture the response initiated by itself. It sometimes should have captured the response initiated by other BLE devices in the vicinity.

EDIT: the reason of being unable to print out SR packets was due to callback routine being held up too long to process the ADV packet and missed the callback for SR packet which arrived within half a milli second after the previous ADV packet. MS has found the proper solution, please scroll down to see his sketch.

I have investigated the possible payloads,
Advertised data : 13 bytes (including 2 bytes of CID). If 14 bytes are sent, Scan response cannot receive. I do not know why it is less.
ScanResponse data : 29 bytes (including 2 bytes of CID); this is the maximum value since there is a 2-byte header.

edit
If we could increase the interval between transmissions on channels 37, 38, and 39, would that increase the probability of picking up a response?
I’ll put it in a cookie tin to see if avoiding ambient influences will help.

When the advertised data is 12 bytes, 29 bytes of response data is sent. When the advertised data is 13 bytes, no response is sent.
Comparison of the current waveforms of Peripheral. The analysis is difficult.

Is it just a problem when receiving the packets with the Arduino sketch? Will the reception be ok when using the Nordic mobile app?

I suspect the serial created by tinyusb is too slow. In my first example the scan response packet will be missed if I add 4 lines of “What the Hell ???” i.e. around 100 characters.

What if you write the report to SD card instead of writing to serial?

The mobile app can receive 24 bytes in the advertisement and 29 bytes in the response. Considering the header length, I believe this is the maximum data length.

I think the problem is that the print statement is in a callback function. I will try to change it so that the print statement is executed outside of a callback function.

Alright I can repeat your problem. Serial cannot print the SR data if ADV data is too long. However I did not find any problem using mobile app:

If I disable the serial printout of the data I get below:

I need to get back to work now.
One more capture before the boring Monday meeting.

p-len

I kicked the print statement out of the callback function and it works fine.
26 bytes of advertisement data and 29 bytes of response data can be received stably.

Peripheral sketch

#include <Adafruit_SPIFlash.h>
#include <bluefruit.h>

void startAdv(void)
{  
  // Set Flag for discovery mode, optional 
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE);

  // advertising data
  uint8_t adv_data[] = { 0xFF, 0xFF, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
                         0x20, 0x21, 0x22, 0x23, 0x24, 0x25
                       };                          
  Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, adv_data, sizeof(adv_data)/sizeof(adv_data[0]));

  // scan response data
  uint8_t rsp_data[] = { 0xFF, 0xFF, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
                         0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9,
                         0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8 
                       };
  Bluefruit.ScanResponse.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, rsp_data, sizeof(rsp_data)/sizeof(rsp_data[0]));

  Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED);  // for XIAO BLE
//  Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED);   // for mobile tool
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setIntervalMS(100, 100);    // in unit of ms
  Bluefruit.Advertising.setFastTimeout(1);          // number of seconds in fast mode
  Bluefruit.Advertising.start(1);                   // stop advertising after 1 seconds
}


void setup()
{
  // Shut down unused QSPI flash memory
  Adafruit_FlashTransport_QSPI flashTransport;
  flashTransport.begin();
  flashTransport.runCommand(0xB9);
  flashTransport.end();

  // Enable DC-DC converter
  NRF_POWER->DCDCEN = 1;
  
  Bluefruit.begin();
  Bluefruit.autoConnLed(false); // turn off LED to save power
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
}

void loop()
{
  startAdv();
  __WFE();
  __WFI();
  delay(10000);  // advertising period = 10s
  Bluefruit.Advertising.clearData();  // refresh advertising data for each cycle
  Bluefruit.ScanResponse.clearData();  // refresh scan response data for each cycle
}

Central sketch

#include <bluefruit.h>

bool cbFlag = false;
uint8_t advBuffer[32];
uint8_t rspBuffer[32];
uint8_t advLen;
uint8_t rspLen;

void setup() 
{
  Serial.begin(115200);
  while (!Serial);

  Bluefruit.begin(0, 1);
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values

  /* Set the device name */
  Bluefruit.setName("Bluefruit52");

  /* Set the LED interval for blinky pattern on BLUE LED */
  Bluefruit.setConnLedInterval(250);

  /* Start Central Scanning */
  Bluefruit.Scanner.setRxCallback(scan_callback);
  Bluefruit.Scanner.restartOnDisconnect(true);
  Bluefruit.Scanner.filterMSD(0xFFFF);          // 0xFFFF for Experiment
  Bluefruit.Scanner.setIntervalMS(100,100);
  Bluefruit.Scanner.useActiveScan(true);        // Request scan response data
  Bluefruit.Scanner.start(0);                   // 0 = Don't stop scanning after n seconds

  Serial.println("Scanning ...");
}

void scan_callback(ble_gap_evt_adv_report_t* report)
{
  cbFlag = true;

  if (report->type.scan_response)
  {
    rspLen = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, rspBuffer, sizeof(rspBuffer)); 
  }
  else
  {
    advLen = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, advBuffer, sizeof(advBuffer));
  }

  Bluefruit.Scanner.resume();
}




void loop() 
{
  if(cbFlag)
  {
    Serial.print("++++++++ ADV "); Serial.println(advLen);
    for(int i = 0; i < advLen; i++)
    {
      if(advBuffer[i] < 16) { Serial.print(0); }
      Serial.print(advBuffer[i], HEX); Serial.print(" ");       
    }
    Serial.println();
    memset(advBuffer, 0, sizeof(advBuffer));
    
    Serial.print("-------- RSP "); Serial.println(rspLen);
    for(int i = 0; i < rspLen; i++)
    {
      if(rspBuffer[i] < 16) { Serial.print(0); }
      Serial.print(rspBuffer[i], HEX); Serial.print(" ");       
    }
    Serial.println();
    memset(rspBuffer, 0, sizeof(rspBuffer));
    
    Serial.println("========"); Serial.println();
    
    cbFlag = false;
  }
}

Great job ms ! You have made it !!
I recall that many coders remind not to put too many tasks in the interrupt routines. They always recommend to change a flag and pass on the tasks to another routine. It likely will create a system resource competition affecting other callbacks if the existing callback routine is being held up for too long.

Beautiful :slight_smile:

The advertising data length is 29 bytes, what did you change?
My Central only receives 26 bytes.

I removed the flag for discovery mode since this is optional.

Yes, it was. I had forgotten.
I removed the flag and can send and receive 29 bytes.

Now I can go back to the “System ON Sleep” thread.

Looks like the problem is only mitigated but not completely resolved as I occasionally got empty RSP:

Anyway it is still usable since not all the RSP packets within that advertising period were empty. This however further proves the difficulty to handle a fast-arriving scan response.

The occasional empty RSP issue seems to disappear by commenting out:

//memset(advBuffer, 0, sizeof(advBuffer));
//memset(rspBuffer, 0, sizeof(rspBuffer));

Time competition again …

Double buffer, so that the buffer can be updated in the callback during printing. Changed the interrupt flag clearing position so that interrupts can be flagged even during printing.
So far, it seems that data never goes to zero.

Since there is no handshaking, I think it is necessary to check the data at Central in actual use.

#include <bluefruit.h>

bool cbFlag = false;
uint8_t advBuffer[32];
uint8_t rspBuffer[32];
uint8_t advPrintBuffer[32];
uint8_t rspPrintBuffer[32];
uint8_t advLen;
uint8_t rspLen;

void setup() 
{
  Serial.begin(115200);
  while (!Serial);

  Bluefruit.begin(0, 1);
  Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values

  /* Set the device name */
  Bluefruit.setName("Bluefruit52");

  /* Set the LED interval for blinky pattern on BLUE LED */
  Bluefruit.setConnLedInterval(250);

  /* Start Central Scanning */
  Bluefruit.Scanner.setRxCallback(scan_callback);
  Bluefruit.Scanner.restartOnDisconnect(true);
  Bluefruit.Scanner.filterMSD(0xFFFF);          // 0xFFFF for Experiment
  Bluefruit.Scanner.setIntervalMS(100,100);
  Bluefruit.Scanner.useActiveScan(true);        // Request scan response data
  Bluefruit.Scanner.start(0);                   // 0 = Don't stop scanning after n seconds

  Serial.println("Scanning ...");
}

void scan_callback(ble_gap_evt_adv_report_t* report)
{
  cbFlag = true;

  if (report->type.scan_response)
  {
    rspLen = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, rspBuffer, sizeof(rspBuffer)); 
    memcpy(rspPrintBuffer, rspBuffer, rspLen);
    memset(rspBuffer, 0, rspLen);
  }
  else
  {
    advLen = Bluefruit.Scanner.parseReportByType(report, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, advBuffer, sizeof(advBuffer));
    memcpy(advPrintBuffer, advBuffer, advLen);
    memset(advBuffer, 0, advLen);
  }

  Bluefruit.Scanner.resume();
}

void loop() 
{
  if(cbFlag)
  {        
    cbFlag = false;
    
    Serial.print("++++++++ ADV "); Serial.println(advLen);
    for(int i = 0; i < advLen; i++)
    {
      if(advPrintBuffer[i] < 16) { Serial.print(0); }
      Serial.print(advPrintBuffer[i], HEX); Serial.print(" ");       
    }
    Serial.println();
    
    Serial.print("-------- RSP "); Serial.println(rspLen);
    for(int i = 0; i < rspLen; i++)
    {
      if(rspPrintBuffer[i] < 16) { Serial.print(0); }
      Serial.print(rspPrintBuffer[i], HEX); Serial.print(" ");       
    }
    Serial.println();
    
    Serial.println("========"); Serial.println();
  }
}

The results of the discussion can be found here.
System_ON_Sleep of XIAO BLE - #63 by msfujino