XIAO BLE Sense in deep sleep mode

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.

Great discusson ! but for me as a beginner I understand that we are talking about deep-sleep that I understand as power-off /switch-off the device.
How about light-sleep mode ? What should I use ? delay ?
I’m making a project that is running: digitalread ADC pin 10 times per second and send a BLE packet every 1 second .
Should I use delay to lower battery consumtion ?
What light-sleep scenario should I use on XIAO BLE ?

1 Like

…does this mean that I will NOT be able to flash next sketch on the board ? It sounds that I will stay stuck with that program and the XIAO BLE will be useless ? :slight_smile:
…or maybe the reset button is waking up the flash device ?

No, you are getting confused. We are talking about the external flash chip that is on board the XIAO not the internal flash on the nrf that your code is written to.

If you dont need the external flash chip disable it like I showed. Then just use delay, the nrf core automatically turns off most things not in use. If you really care about low power you need to get a power profiler to test with.

1 Like

Thanks for reply - now a bit more clear for me :slight_smile:
sad that seeed is not publishing that info … looks like everyting must be “discovered” by users like You.
Thanks for help.

Hi! It’s so amazing you managed to figure out how to put it on sleep in platformio. I tried to follow the guide to build using adafruit 1.0.0 library and platformio. I created a small sketch to test this out. But I find I might miss some constants like D1, D2, D3 used in disconnectpin. Apart of flashTransport that seems I cannot find the proper library.

My platformio is similar to this one:

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
board = xiao_ble_sense
framework = arduino
lib_deps =
    olikraus/U8g2@^2.28.8
    SPI
    adafruit/Adafruit SPIFlash@^4.0.0
    adafruit/SdFat - Adafruit Fork@^2.2.1
    seeed-studio/Seeed Arduino LSM6DS3@^2.0.3

Could you please share the platformio.ini file you’re using including the main.cpp to test this? I’ve been searching this anywhere but seems there’s still little or no information.

Also it’s a discouraging that you managed to discover the flash issue even before of Seedstudio team releasing it for us. I was really happy with Xiao (SAMD) but this board seems to be a timewaster, no matter what you try to do on it as wearable, I’m alwayis hitting on a rock.

I appreciate so much your effort to uncover these hidden details.

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
board = xiao_ble_sense
framework = arduino
lib_deps = 
	adafruit/Adafruit SPIFlash@^4.0.0
	adafruit/SdFat - Adafruit Fork@^2.2.1

Platform.ini looks fine, maybe you are missing the Arduino.h include for your Dx pin defs. Note that when calling nrf api directly through nrf functions, macros or register manipulations you must use nrf pin numbers, these are mapped via the g_ADigitalPinMap global array, e.g. nrfPin = g_ADigitalPinMap[D0]

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

#define PIN_POT   A0
#define PIN_BTN   D7
#define PIN_POT_GND D10

Hey, quite a big discussion. I read a lot of it multiple times and still didn’t manage to get the XIAO nrf52840 to sleep and wake on interrupt.
Indeed it seems like everyone is just comparing their sleep current, but no-one ever made it work to wake up the board. At least there is no complete functioning code in this discussion.

@Rhys3logy Asked for the same thing that I’m about, but he never got an answer:

So I’ll give exact information on what I’m doing:
Board: XIAO nRF52840
Boards Manager: Speed nRF52 Board
IDE: Arduino IDE

This is my minimal code to turn the chip off, which doesn’t work:

void setup() {
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_RED, OUTPUT);
}

int c;
void loop() {
  c++;
  digitalWrite(LED_GREEN, c % 2 == 0 ? HIGH : LOW);
  delay(1000);
  if (c == 10) {
    digitalWrite(LED_RED, LOW);
    sd_power_system_off();
  }
}

This should put the board to sleep right? Well sd_power_system_off() does exactly nothing. It executes and the program just continues.
I managed to get the board to shut down via the mentioned NRF_POWER->SYSTEMOFF = 1; but I tried everything to wake it up, but the only way to do it seems to be a reset via reset button, or killing the power and plugging it back in.

So this sample is so minimal, and the task ist so basic. Is there anyone who actually got this to work and can post steps to reproduce? The fact that sd_power_system_off() doesn’t do anything on my side makes me suspicious, maybe this could hint something?

I’m new to hardware programming and this is my second project and the first with this chip. What I want to do in the very end (getting the thing to shut down and at least restart upon button press would be the first step) - is to:

  • Go into sleep
  • Wake up every now and again (just like delay() but non-blocking)
  • Check some variables and the time (they must persist)
  • Go back to sleep
  • Additional: Wake Up on button press and run the program normally

Maybe some of the experts here can help, we spent a whole day with 2 experienced programmers and didn’t manage to do that.

Greetings from Germany,
Noblauch

1 Like

Hi , this may not be exactly what you want but I’m using Sense version and double tap to wake up from sleep; here is a snip from the link here, I have a video on here showing it work as well.

void goToPowerOff() {
  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");
  u8x8.clearDisplay();
  u8x8.setCursor(0, 3);
  u8x8.print("SLEEP");
  setupDoubleTapInterrupt(); // not needed here, if already applied..
  delay(1000); // delay seems important to apply settings, before going to System OFF
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(int1Pin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
  delay(2000); // delay seems important to apply settings, before going to System OFF 
  NRF_POWER->SYSTEMOFF = 1;
}

The Nrf52840 supports a couple levels of sleep and even more when you turn off peripherals etc.
the Nordic forum has more on the topic as well.
HTH
GL :slight_smile:

As I said somewhere above unless you start the soft device none of your sd_* calls will do anything, it isn’t started automagically by the arduino core…

One way to start the soft device is Bluefruit.begin()

what is it “soft device” .
I found that term several times but can not find the explanation of it.
is it an adafruit library ? ble library ?

SoftDevice is the Nordic supplied software binary that implements the wireless protocols and other functions ontop of the nrf hardware. A bit like the binary blob on the RPI. If you never use any sd_* calls then you don’t need to start it, however to do anything wireless other than messing directly with the radio via registers you go via SD.

Hey @PJ_Glasso & @devmonkey thanks for your replies!

I already tried some hours in my freetime, but your code also doesn’t work for the XIAO nRF52840 :confused:

Thanks for talking about the “soft device”. I’m not using that and atm I don’t need to. Also I googled Bluefruit and saw this is kind of a breakout board. I don’t have all this, I only have the bare XIAO nRF52840 and plain Arduino IDE, nothing else.

@PJ_Glasso I destilled your code to the essential parts to make it compile (don’t have the display and so on) and tried it out, without success. I see the important parts are potentially:

  1. Set pinMode to INPUT for the wakeup pin
  2. Attach Interrupt for this pin RISING
  3. Use nrf_gpio_cfg_sense_input to attach a second interrupt for the chip to recognize this pin also for the wakeup?
  4. Use NRF_POWER->SYSTEMOFF = POWER_SYSTEMOFF_SYSTEMOFF_Enter to go to sleep

I was not able to wake the controller up, here is my final code:

#include "Wire.h"

const int wakeupPin = 10;        // the number of the pushbutton pin
uint8_t interruptCount = 0;      // Amount of received interrupts
uint8_t prevInterruptCount = 0;  // Interrupt Counter from last loop

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("Processor came out of reset.\n");

  pinMode(LED_RED, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
  setLedRGB(false, false, true);  // set blue led

  pinMode(wakeupPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(wakeupPin), interrupt, RISING);
}

void interrupt() {
  Serial.println("Interrupt");
  interruptCount++;
}

void loop() {
  setLedRGB(false, false, true);
  Serial.print("Iterrupt Counter: ");
  Serial.println(interruptCount);

  // if interrupt was received in this cycle
  if (interruptCount > prevInterruptCount) {
    Serial.println("Interrupt received!");
    setLedRGB(false, true, false);  // set green only
  }

  prevInterruptCount = interruptCount;

  delay(500);
  if (interruptCount >= 5) {
    // Trigger System OFF after 5 interrupts
    goToPowerOff();
  }
}

void goToPowerOff() {
  setLedRGB(false, false, false);
  Serial.println("Going to System OFF");
  delay(1000);
  //Ensure interrupt pin from IMU is set to wake up device
  nrf_gpio_cfg_sense_input(digitalPinToInterrupt(wakeupPin), NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);

  // Trigger System OFF
  NRF_POWER->SYSTEMOFF = POWER_SYSTEMOFF_SYSTEMOFF_Enter;
}

void setLedRGB(bool red, bool green, bool blue) {
  if (!blue) {
    digitalWrite(LED_BLUE, HIGH);
  } else {
    digitalWrite(LED_BLUE, LOW);
  }
  if (!green) {
    digitalWrite(LED_GREEN, HIGH);
  } else {
    digitalWrite(LED_GREEN, LOW);
  }
  if (!red) {
    digitalWrite(LED_RED, HIGH);
  } else {
    digitalWrite(LED_RED, LOW);
  }
}

Also what makes me wonder is the fact that you didn’t set the button pinmode input is not set to pullup or down, which makes the interrupt trigger as crazy automatically, but hey it shouldnt affect the rest of the code. However its not doing anything but shutting the chip down :frowning:

Not sure why it seems so impossible, but I really can’t get this to work, just plain vanilla Arduino and the bare XIAO. I’m simultaniously on different platforms and dedicated to get this to work. If I ever do, I’ll post my findings here so other people can get the sleep to work too!

Best,
Noblauch

2 Likes