System_ON_Sleep of XIAO BLE

I am also a hobbyist like you. I learn something new daily through reading.

If the scanner always performs scanning and does not sleep, the chance to miss the advertising packet is really small unless there is heavy interference in the 2.4GHz band. If a peripheral advertises sufficiently long its packets should reach the scanner. A trial is needed to determine the advertising interval (hence power consumption) in the actual environment so that one can select an appropriate battery for the peripheral.

@msfujino
regarding the peripheral number, you may define it as DEC instead of HEX. This number will never exceed 1 byte. To retrieve the value at the central, just read the appropriate byte instead of working on 2 bytes.

The reason for reserving two bytes for Peripheral number is that I wanted to put the identification number in the upper one byte in case the experimental CID = 0xFFFF duplicates with other experimental devices.
I am currently experimenting with using scan response to increase the amount of data that can be transferred. I am reading the bluefruit library very hard but am struggling. Do you have any tips on how to do this?

@msfujino
What if we do it in another way:

  1. advertise data set 1 for 1s
  2. clear data
  3. advertise data set 2 for another 1s
  4. delay 10s (sleep)

Theoretically, scan response may be a more power saving approach since it broadcasts only on that particular channel the request is received. Advertising however always broadcasts on 3 channels.
Yet to my earlier experiment, the central capture rate of scan response was only 5% (peripheral 100% duty) but the capture rate of advertising was at least 5/30 or 17% (peripheral 10% duty).
I do not have a shielded environment to isolate from other devices in the vicinity. So I am unable to tell what has caused the bad scan response performance. Whether it is due to too many irrelevant devices making scan requests or the library itself I can hardly tell.

hobbya,
I think the idea of sending the data set twice would work if we implement the code. I am currently working on a way to send the data in a scan response for comparison.
The following directory has an example of sending local names in a scan response. Peripheral puts the local name in the scan response packet, but Central is not able to retrieve it properly. I am in the process of trying to figure out if the cause is wrong code or if the functionality is not implemented. I still need more time.

\Arduino15\packages\Seeeduino\hardware\nrf52\1.1.1\libraries\Bluefruit52Lib\examples\Projects\rssi_proximity

Previously I used the central_scan_advanced example for the scan response. It would not work until I disabled the scanner filter. I did not investigate further due to the code complexity.

Still have not figured out the reason. Attached is my sketch.
The advertising and scan response packets could be properly received by the Nordic mobile app. However the central only picked up one scan response over 3 minutes with the MSD filter disabled. It would not pick up any scan response if the filter was enabled.
NRF52840_Scan_Response.zip (3.9 KB)

Hi hobbya, I will try your sketch from now on.

Here are my findings for today.
To make it easier to distinguish, two data data1 and data2 were defined and sent on the advertise and scan response. When checked by the mobile app, 55 bytes were received, including 2 bytes of CID. However, XIAO Central could only receive data1 on the advertise. The conclusion is that there is a problem with Central.

  uint8_t data1[] = { 0xFF, 0xFF,                                                 // CID 2
                      0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, // data 24 
                      0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 
                      0x21, 0x22, 0x23, 0x24};
  uint8_t data2[] = { 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xB0, // data 29 
                      0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xC0, 
                      0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9};
                      
  
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED);

  Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, data1, sizeof(data1));
  Bluefruit.Advertising.addTxPower();   
  Bluefruit.ScanResponse.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, data2, sizeof(data2)); 

  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setIntervalMS(100, 100);  // fast mode 100mS, slow mode 100mS
  Bluefruit.Advertising.setFastTimeout(1);        // fast mode 1 sec
  Bluefruit.Advertising.start(1);                 // stop advertising after 1 sec, fast 1 sec slow 0 sec

So I read ble_gap.h and found the area of concern. I will continue to investigate a little further.
l\Arduino15\packages\Seeeduino\hardware\nrf52\1.1.1\cores\nRF5\nordic\softdevice\s140_nrf52_7.3.0_API\include\ble_gap.h:709 and 1278

/**@brief Advertising report type. */
typedef struct
{
  uint16_t connectable   : 1; /**< Connectable advertising event type. */
  uint16_t scannable     : 1; /**< Scannable advertising event type. */
  uint16_t directed      : 1; /**< Directed advertising event type. */
  uint16_t scan_response : 1; /**< Received a scan response. */
  uint16_t extended_pdu  : 1; /**< Received an extended advertising set. */
  uint16_t status        : 2; /**< Data status. See @ref BLE_GAP_ADV_DATA_STATUS. */
  uint16_t reserved      : 9; /**< Reserved for future use. */
} ble_gap_adv_report_type_t;



/**@brief Event structure for @ref BLE_GAP_EVT_ADV_REPORT.
 *
 * @note If @ref ble_gap_adv_report_type_t::status is set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA,
 *       not all fields in the advertising report may be available.
 *
 * @note When ble_gap_adv_report_type_t::status is not set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA,
 *       scanning will be paused. To continue scanning, call @ref sd_ble_gap_scan_start.
 */
typedef struct
{
  ble_gap_adv_report_type_t type;                  /**< Advertising report type. See @ref ble_gap_adv_report_type_t. */
  ble_gap_addr_t            peer_addr;             /**< Bluetooth address of the peer device. If the peer_addr is resolved:
                                                        @ref ble_gap_addr_t::addr_id_peer is set to 1 and the address is the
                                                        peer's identity address. */
  ble_gap_addr_t            direct_addr;           /**< Contains the target address of the advertising event if
                                                        @ref ble_gap_adv_report_type_t::directed is set to 1. If the
                                                        SoftDevice was able to resolve the address,
                                                        @ref ble_gap_addr_t::addr_id_peer is set to 1 and the direct_addr
                                                        contains the local identity address. If the target address of the
                                                        advertising event is @ref BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE,
                                                        and the SoftDevice was unable to resolve it, the application may try
                                                        to resolve this address to find out if the advertising event was
                                                        directed to us. */
  uint8_t                   primary_phy;           /**< Indicates the PHY on which the primary advertising packet was received.
                                                        See @ref BLE_GAP_PHYS. */
  uint8_t                   secondary_phy;         /**< Indicates the PHY on which the secondary advertising packet was received.
                                                        See @ref BLE_GAP_PHYS. This field is set to @ref BLE_GAP_PHY_NOT_SET if no packets
                                                        were received on a secondary advertising channel. */
  int8_t                    tx_power;              /**< TX Power reported by the advertiser in the last packet header received.
                                                        This field is set to @ref BLE_GAP_POWER_LEVEL_INVALID if the
                                                        last received packet did not contain the Tx Power field.
                                                        @note TX Power is only included in extended advertising packets. */
  int8_t                    rssi;                  /**< Received Signal Strength Indication in dBm of the last packet received.
                                                        @note ERRATA-153 and ERRATA-225 require the rssi sample to be compensated based on a temperature measurement. */
  uint8_t                   ch_index;              /**< Channel Index on which the last advertising packet is received (0-39). */
  uint8_t                   set_id;                /**< Set ID of the received advertising data. Set ID is not present
                                                        if set to @ref BLE_GAP_ADV_REPORT_SET_ID_NOT_AVAILABLE. */
  uint16_t                  data_id:12;            /**< The advertising data ID of the received advertising data. Data ID
                                                        is not present if @ref ble_gap_evt_adv_report_t::set_id is set to
                                                        @ref BLE_GAP_ADV_REPORT_SET_ID_NOT_AVAILABLE. */
  ble_data_t                data;                  /**< Received advertising or scan response data. If
                                                        @ref ble_gap_adv_report_type_t::status is not set to
                                                        @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA, the data buffer provided
                                                        in @ref sd_ble_gap_scan_start is now released. */
  ble_gap_aux_pointer_t     aux_pointer;           /**< The offset and PHY of the next advertising packet in this extended advertising
                                                        event. @note This field is only set if @ref ble_gap_adv_report_type_t::status
                                                        is set to @ref BLE_GAP_ADV_DATA_STATUS_INCOMPLETE_MORE_DATA. */
} ble_gap_evt_adv_report_t;

Your approach for the peripheral is the same as my sketch. Indeed the central has problems catching the scan response, even though it can recognize the packet as scannable.

/* Display the timestamp and device address */
if (report->type.scan_response)
//if (report->type.scannable)
{
Serial.printf("[SR%10d] Packet received from ", millis());
}

It can print “SR” if I set the type to scannable, which means it recognizes the incoming packet.

The principle of the code is simple, it prints out the report whenever there is a callback. The reports of advertising and scan response shall come up alternatively but as you said the latter does not show up at all.

Some more observations:
Case 1:
Central => my sketch above without MSD filter
Peripheral => adafruit beacon example
Sniffer: peripheral sent out quite a lot of scan responses to central
Central serial: captured a lot less scan responses compared to above

Case 2:
Central => my sketch above with MSD filter
Peripheral => my sketch above but advertising data was reduced to a flag only

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

// Construction of data packet to be advertising
if (counter == 0xFF)
{counter = 0;}
else
{counter = counter + 1;}
user_data[0] = 0x59; // little endian format for first and second entry
user_data[1] = 0x00; // (00-59) means Nordic Semi
user_data[2] = counter; // third entry increments with each advertising cycle
Bluefruit.Advertising.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, user_data, sizeof(user_data)/sizeof(user_data[0]));
Bluefruit.Advertising.clearData();

// Construction of scan response
scan_rsp_data[0] = 0x59; // little endian format for first and second entry
scan_rsp_data[1] = 0x00; // (00-59) means Nordic Semi
scan_rsp_data[2] = 0xD1;
scan_rsp_data[3] = 0xD2;
scan_rsp_data[4] = 0xD3;
scan_rsp_data[5] = 0xD4;
Bluefruit.ScanResponse.addData(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, scan_rsp_data, sizeof(scan_rsp_data)/sizeof(scan_rsp_data[0]));

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

Central serial: captured a lot of scan responses

I am wondering if central is not fast enough to catch and decode both the advertising and scan response packets in a short time. I will try to adjust the 2 payload sizes or to increase the serial buffer next time. I have spent too much time on this and my wife is not happy.

Next, I plan to experiment with enclosing Peripheral and Central in an empty cookie can, isolated from the surrounding BLE.
“Scan Response” gets a bit away from the purpose of “System ON Sleep” in this thread, so it might be better to start a new thread on “Scan Response”. I am also thinking of posting this on the following github with issues.
GitHub - adafruit/Adafruit_nRF52_Arduino: Adafruit code for the Nordic nRF52 BLE SoC on Arduino

Have a nice weekend with your wife.

Yes we should post somewhere for this topic.
Wife is still alseep. Try this, seems to work if we do something basic.

NRF52840_MSD.zip (3.1 KB)

I just checked. It is working fine.
I will study what is different from the previous sketches.
I will also continue to experiment with how far the data length can be increased and what the current consumption will be.
For the benefit of other users, I would appreciate if you could post something about ScanResponse.

Lucky ! Wife has ladies’ gathering and I got some more spare time. Let’s move discussion to here.

This is a continuation of my experiments to reduce the current consumption of Peripheral by using SystemONSleep in a project designed as a data logger that periodically sends small amounts of data to Central.
Previously, in post #31, I reported that the current was reduced to 20uA by putting 8 bytes of data on the Advertising without a connection.
In a subsequent study,
The length of transfer data can be increased to 59 bytes by using Scan Response,
I found that the current can be reduced to 8.5uA (8 bytes data length, 1m distance, and -70dBm RSSI) by optimizing the transmit power, mode and interval of Advertising.

The experimental conditions were

BSP : Seeed nRF52 Boards 1.1.1
Board : Seeed nRF52 Boards / Seeed XIAO nRF52840 Sense
System ON Sleep : Using RTOS delay()
Task : Send 10 ~ 58 data every 10 seconds
Measurement : 3.00V applied from pppk2 to 3V3 pin of XIAO

Current reduction measure

Disable the blue LED used for BLE
Set on-chip regulator to DCDC mode
Set on-board Flash to OFF
Allow Scan only when necessary

Optimize for the application and environment

user data length 8 bytes
Tx Power 0 dBm(RSSI -70dBm)
Advertising Interval 200mS

Throughout the discussion, hobbya has given me tips and advice. I appreciate it.
Xiao ble scan response discussion’‘Xiao ble scan response discussion

The sketches used in the experiment can be found here.
nRF52_XIAO_AdvRes.zip (5.2 KB)

Thanks MS again for a neat and concise sharing, much appreciated.

At the moment the central is spending a lot of time in scanning since it does not know when the data is coming. BLE 5 actually has a new feature: periodic advertising which allows central to sleep by synchronizing the peripheral and central. Unfortunately, the existing Softdevice used by Seeed or Adafruit does not support such feature.

Originally MS has applied RTC interrupt to the peripheral. Perhaps the same idea can be applied to the central to save power. The central can be set to scan continuously in the beginning. When the scan callback is invoked it stops scanning, starts the timer and goes to sleep. Central wakes up e.g. 9s later and resumes scanning.

1 Like

hobbya,
I thought that RTC was more accurate and easier to use than delay() and started using it without thinking much about the current during sleep mode. Later experiments showed that RTCs have a monthly difference of more than 1 minute, and furthermore, using FreeRTOS delay() reduces the current, making the use of RTC meaningless.
In my application Central is always connected to the power supply as it writes data to the SD and displays information. I did not think much about the current in Central. Reducing Central current may be an interesting topic. I am now working on the following topic and will think about it in this topic.
XIAO_BLE wakes up from System_ON_Sleep and Writes weather data to on-board Flash

1 Like

fujino san,
You have spent a lot of time and shared a lot of working examples. It is a blessing to the community if you continue to explore new ideas and post your new findings. Thank you!

1 Like

Here, Here,
You two guys are a Technical Blessing to SEEED studios FOR SURE. If I were the MAN at that company, Your gift basket’s would have already arrived. :grinning:
Great examples of what a community of folks can focus on a common problem and sharing the solutions to it. This has helped me personally by shortening my development time $$ and increased my level of knowledge on the over-all subjects. I stand on the shoulders of Giants.
Thanks Guys :slight_smile:
GL :wink: PJ