XIAO BLE Sense in deep sleep mode

You must use version 1.0 (Adafruit) of the firmware in the Arduino IDE. Looks like your board variant file is not what the demo program is looking for.

Makes sure you have a softdevice installed (should be shipped that way)
Use this on top of the code before setup():

#include <Arduino.h>
#include <bluefruit.h>

Use this in the loop()
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
__WFE();
__WFI();
sd_app_evt_wait() ; //SOFT DEVICE “SYSTEM ON” POWER SAVE

and when ready, shut it down with this:

NRF_POWER->SYSTEMOFF = 1;

or this:

sd_power_system_off();

2 Likes

@fe7565 Thank you so much for your reply. I will give it a try and report back with results.
Thanks.

UPDATE: YES, it works now. Thanks again fe7565! In the deep sleep mode, it draws about 20uA.

1 Like

Hi, were you able to use ArduinoBLE library with the Seeed nRF52 mbed-enabled Boards version 1.0.0? I am having trouble running the library now as it shows I have other missing libraries in the include section of the ArduinoBLE library

C:\Users\XXXX\Documents\Arduino\libraries\ArduinoBLE\src\utility\HCICordioTransport.cpp:23:10: fatal error: mbed.h: No such file or directory
23 | #include <mbed.h>
| ^~~~~~~~
compilation terminated.
exit status 1
Error compiling for board Seeed XIAO nRF52840 Sense.

Unless things changed you cannot use version 1.0 with Mbed programs. Version 1.0 works only with Adafruit.

1 Like

hey, you mind sharing an example of your code with deep sleep working? i have been completely unsuccessful in getting it to work myself

Here is my code that’s what fe7565 explained in reply #68.

#include <Arduino.h>
#include <bluefruit.h>

void setup()
{
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
__WFE();
__WFI();
sd_app_evt_wait() ; //SOFT DEVICE “SYSTEM ON” POWER SAVE

NRF_POWER->SYSTEMOFF = 1;
}

void loop()
{
}

Important thing is NOT to select ‘Seeed nRF52 mbed-enabled Board’, but to select ‘Seeed nRF52 Board’.

2 Likes

@hichiaty & @turing-complete-labs, how did y’all manage to get down to 3-5uA ?? With sd_power_system_off(), it only reaches around 20uA for me (and some other folks here in this thread). I am using regular (not Sense) Xiao BLE.

I followed these steps:

  1. Flashed this bootloader
    BLE_52840_Core/update-Seeed_XIAO_nRF52840_bootloader-0.6.1_nosd.uf2 at main · 0hotpotman0/BLE_52840_Core · GitHub

  2. Installed this library
    image

  3. Used this (minimal) code

void setup()
{
  suspendLoop();
  sd_power_system_off();
}

void loop()
{

}
  1. Measured current using GND and 3V3 pins

What could I possibly be missing ??

Note that adding the following to setup did not improve the sleep current (since I am using sd_power_system_off() anyway)

  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
  __WFE();
  __WFI();
1 Like

We have now discarded the original XIAO BLE on-board package (as you experienced, it was very confusing). Some time ago, we updated the new XIAO BLE on-board package. You guys can update the on-board package according to our wiki.

We divided into two XIAO BLE on-board packages, their application scope is different, we have marked which on-board package should be used in front of each case, you can then refer to the content of XIAO BLE low-power mode using the new on-board package to retry again.

How do you wake back up from sleep? I tried using nrf_gpio_cfg_sense_input() to no avail.

#include <Arduino.h>
#include <Wire.h>

#define wake_button 10

void setup()
{
  Serial.begin(115200);
  while(!Serial);
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
  __WFE();
  __WFI();
  sd_app_evt_wait() ; //SOFT DEVICE “SYSTEM ON” POWER SAVE

  pinMode(wake_button, INPUT);
  attachInterrupt(digitalPinToInterrupt(wake_button), ISR_WAKE, RISING);
}

void loop()
{
  delay(10000);
  nrf_gpio_cfg_sense_input(wake_button, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  Serial.println("SLeeping");
  delay(100);
  NRF_POWER->SYSTEMOFF = 1;
}

void ISR_WAKE(){
  
}
1 Like

After messing around with the board for most of today I can share how to get the power down to a sensible level. I am currently at ~2uA in deep sleep, 3-4uA in light running (e.g. suspendLoop or delay).

Everyone who was stuck at ~20uA, the problem is there is a QSPI flash chip (P25Q16H) that remains powered when powering down the NRF and does not go into deep sleep itself. This flash chip sits there and draws ~18uA all the time. To put it into deep sleep you need to send the command 0xB9 to it.

#include <Adafruit_FlashTransport.h>

Adafruit_FlashTransport_QSPI flashTransport;

//start bluefruit (this also starts the soft device if you don't start the sd then none of your sd_* calls will do anything
Bluefruit.begin();
//before you put the NRF to sleep put the flash to sleep
flashTransport.begin();
flashTransport.runCommand(0xB9);
flashTransport.end();

//map a pin for wakeup 
nrf_gpio_cfg_sense_input(g_ADigitalPinMap[D3], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
//shutdown
sd_power_system_off();

This will leave the flash in deepsleep next time you wake up the NRF, the command from the data sheet to turn it back on is 0xAB but I haven’t played with it yet. I used the adafruit sdfat fork to send in the qspi commands as it is much simpler than interfacing with the qspi hardware directly.

lib_deps =

    adafruit/Adafruit SPIFlash@^4.0.0

    adafruit/SdFat - Adafruit Fork@^2.2.1

Deep sleep with flash also in deep sleep:

6 Likes

NRF running with flash on standby at 20uA, then switching flash to deep sleep (note NRF is still running code)

2 Likes

Excellent! Thank you. I was ok with 20uA for my project, but I am excited that maybe I am able to use the QSPI for persistent memory. I need to change a value of a variable and make that changed variable available after a hard reboot. I think I found a complicated work-around, but using the QSPI would be much better. Any suggestions for the Arduino IDE? I do not use Python.

Amazing power optimisation there! Kudos for figuring that out in the absence of documentation.
Keen to understand what’s required.to reactivate everything.
Game changer!

I don’t use python either, not sure what you mean, my example code is in C? Anyway I used platformio but if you insist on arduino ide then just install the adafruit sdfat fork library as I suggested.

A further deep sleep discovery. The NRF gpio pin config registers persist through deep sleep, this is so you can setup pin states the are retained whilst deep sleeping.

When you use a GPIO via the arduino core, e.g. pinMode(x, INPUT), the pin state is changed to input direction AND the pin is connected to the NRF GPO input buffer. This connection to the input buffer seems to draw ~6uA per pin. The default state of an NRF pin is DISCONNECTED but you cannot use the arduino pinMode() function to put the pin back into this state having previously used it as an input.

The solution is to do it yourself, you can either set the pin state back to default using:

nrf_gpio_cfg_default(g_ADigitalPinMap[ARDUINO_PIN_NUMBER]);

or I’ve written this convenience function that explicitly sets the correct pin config:

void disconnectPin(uint32_t ulPin) {
      if (ulPin >= PINS_COUNT) {
        return;
    }

    ulPin = g_ADigitalPinMap[ulPin];

    NRF_GPIO_Type * port = nrf_gpio_pin_port_decode(&ulPin);

    port->PIN_CNF[ulPin] = 
          ((uint32_t)GPIO_PIN_CNF_DIR_Input        << GPIO_PIN_CNF_DIR_Pos)
        | ((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)
        | ((uint32_t)GPIO_PIN_CNF_PULL_Disabled    << GPIO_PIN_CNF_PULL_Pos)
        | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1       << GPIO_PIN_CNF_DRIVE_Pos)
        | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled   << GPIO_PIN_CNF_SENSE_Pos);
}

I haven’t done any testing of pins put into OUTPUT state but these will continue to draw whatever power they were drawing before deep sleep, I discovered the above issue/solution when attempting to disable pins (by setting them to INPUT) when not in use.

So my shutdown code now looks like this:

void sleepFlash() {
    flashTransport.begin();
    flashTransport.runCommand(0xB9);
    flashTransport.end();
}

void disconnectPin(uint32_t ulPin) {
      if (ulPin >= PINS_COUNT) {
        return;
    }

    ulPin = g_ADigitalPinMap[ulPin];

    NRF_GPIO_Type * port = nrf_gpio_pin_port_decode(&ulPin);

    port->PIN_CNF[ulPin] = 
          ((uint32_t)GPIO_PIN_CNF_DIR_Input        << GPIO_PIN_CNF_DIR_Pos)
        | ((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)
        | ((uint32_t)GPIO_PIN_CNF_PULL_Disabled    << GPIO_PIN_CNF_PULL_Pos)
        | ((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1       << GPIO_PIN_CNF_DRIVE_Pos)
        | ((uint32_t)GPIO_PIN_CNF_SENSE_Disabled   << GPIO_PIN_CNF_SENSE_Pos);
}

void shutdown() {
    //sleep flash
    sleepFlash();
    //disconnect any pins used
    disconnectPin(D1);
    disconnectPin(D2);
    //setup pin for wakeup
    nrf_gpio_cfg_sense_input(g_ADigitalPinMap[D3], NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);
    //power off
    sd_power_system_off();
}
5 Likes

Can you sleepflash, disconnectpin, and still advertise ?

Hi,
I patched Circuitpython source for QSPI deep sleep with power consumption 3.6 uA in 5s loop.
From Nano_BLE_MCU-nRF52840_PS_v1.1.pdf datasheet 5.2.1.1 Sleep : ION_RAMON_RTC System ON, full 256 kB RAM retention, wake on RTC(running from LFRC clock) 3.16 μA

— a/ports/nrf/Makefile
+++ b/ports/nrf/Makefile
@@ -161,6 +161,9 @@ ifdef EXTERNAL_FLASH_DEVICES
ifeq ($(QSPI_FLASH_FILESYSTEM),1)
SRC_NRFX += nrfx/drivers/src/nrfx_qspi.c
endif

  •   ifeq ($(QSPI_FLASH_POWERDOWN), 1)
    
  •     CFLAGS += -DQSPI_FLASH_POWERDOWN=1
    
  •   endif
    

endif

— a/ports/nrf/boards/Seeed_XIAO_nRF52840_Sense/mpconfigboard.mk
+++ b/ports/nrf/boards/Seeed_XIAO_nRF52840_Sense/mpconfigboard.mk
@@ -7,4 +7,5 @@ USB_MANUFACTURER = “Seeed”
MCU_CHIP = nrf52840

QSPI_FLASH_FILESYSTEM = 1
+QSPI_FLASH_POWERDOWN = 1
EXTERNAL_FLASH_DEVICES = “P25Q16H”
— a/ports/nrf/supervisor/qspi_flash.c
+++ b/ports/nrf/supervisor/qspi_flash.c
@@ -290,6 +290,8 @@ void qspi_flash_enter_sleep(void) {
mp_printf(&mp_plat_print, “qspi flash: DPM failed\r\n”);
#endif
}

  • spi_flash_command(CMD_DEEP);

  • NRFX_DELAY_US(WAIT_AFTER_DPM_ENTER);
    #endif

    qspi_disable();
    @@ -300,6 +302,7 @@ void qspi_flash_exit_sleep(void) {

    #ifdef QSPI_FLASH_POWERDOWN
    if (NRF_QSPI->STATUS & QSPI_STATUS_DPM_Msk) {

  •    spi_flash_command(CMD_WAKE);
       // exit deep power-down mode
       NRF_QSPI->IFCONFIG1 &= ~QSPI_IFCONFIG1_DPMEN_Msk;
       NRFX_DELAY_US(WAIT_AFTER_DPM_EXIT);
    

— a/supervisor/shared/external_flash/common_commands.h
+++ b/supervisor/shared/external_flash/common_commands.h
@@ -44,5 +44,6 @@
#define CMD_ENABLE_RESET 0x66
#define CMD_RESET 0x99
#define CMD_WAKE 0xab
+#define CMD_DEEP 0xb9

#endif // MICROPY_INCLUDED_ATMEL_SAMD_EXTERNAL_FLASH_COMMON_COMMANDS_H

and with that code.py:

import time
import alarm

i=0
while True:
time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + 5)
al = alarm.light_sleep_until_alarms(time_alarm)
i+=1
print(i)

Hi, anyone know how to wake the xiao ble from deep sleep after running the command without external interrupts?

1 Like

Hmmm, I’m thinking a RESET or from an RTC. is the only options left?
HTH
GL :wink:

Yes of course, none of my code is using the flash chip.