Sleep Current of XIAO nRF52840, Deep Sleep vs. Light Sleep

I compared the sleep current between SystemOFFSleep(DeepSleep) and SystemONSleep(LightSleep). The board service package is “Seeed nRF52 Boards 1.1.1”. To enter sleep mode, I used NRF_POWER->SYSTEMOFF = 1; for DeepSleep and RTOS delay() function for LightSleep.
The pppk2 was used to measure the sleep current by applying 3.6V to the BAT pad on the backside of XIAO. The measured results were 2.4uA and 5.4A, with no significant difference.
Note that the quiescent current of the 3.3V LDO is measured even when the MPU enters sleep mode.

I hope this helps those considering sleep mode!

Flash_Light_Deep_Sleep.zip (1.1 KB)

3 Likes

I studied the variation of the sleep current. We took a total of 39 measurements with 3 devices at different times and on different days. The results showed a variation of nearly 1uA for LightSleep, probably due to the RTOS running, but very little variation was seen for DeepSleepd.
(Note that the sleep currents shown here include the current drawn by the on-board 3.3V LDO).

Thanks for all thus tests. Although I’m not working on it now, this will help me reaching my goals for my datalogger.

I am very new to this domain, and I am facing some problems putting my nRF52840 Sense board into deep sleep and waking it up again. I have tried different code snippets from various sources, but my problem is still not resolved. I want to implement a very simple system in which pressing a push button for 3 seconds puts the board into deep sleep mode, indicated by the red LED blinking twice. Then, when in deep sleep, pressing the same push button for 3 seconds (if timing is not possible in deep sleep, a double-tap is acceptable) should wake the board up, indicated by the blue LED blinking twice.

I am using the non-Mbed BSP, version 1.1.10. The code I am currently using somewhat performs the desired behavior, but not as expected. When I press the button for 3 seconds, the red LED blinks twice, and the blue LED (which was continuously blinking as part of the BLE function) stops blinking and stays lit. When I press the push button again for 3 seconds, the red LED blinks twice again, and only then does the board enter deep sleep.

Ideally, when the board is in deep sleep, pressing the push button for 3 seconds should wake it up, but that doesn’t happen. I have to press the onboard reset button to activate the board. Please help me fix this issue. I have included the code I am currently using below:

#include <bluefruit.h>
#include <LSM6DS3.h>
#include <Wire.h>
#include <Air_mouse_gesture_complete_inferencing.h> // Edge Impulse model

#include <nrf.h>
#include “nrf_nvic.h”
#include “nrf_soc.h”

// ---- Pin Definitions ----
#define BUTTON_PIN 0 // D0 (used for deep sleep wake-up)
#define SAMPLE_INTERVAL_MS 10
#define CONFIDENCE_THRESHOLD 0.7

// IMU and BLE
LSM6DS3 imu(I2C_MODE, 0x6A);
BLEUart bleuart;

// Normalization
const float MEANS = {-0.3726, -0.6139, -0.1795, 7.1494, -4.6447, 2.9262};
const float STD_DEVS = { 0.708, 0.5731, 0.6498, 148.9435, 214.0082, 130.2576};

float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];
size_t feature_ix = 0;

// Button handling
unsigned long last_sample_time = 0;
unsigned long buttonPressStart = 0;
bool buttonHeld = false;
bool isSleeping = false;

// LED blink helper
void blinkLED(int pin, int times, int duration_ms) {
pinMode(pin, OUTPUT);
for (int i = 0; i < times; i++) {
digitalWrite(pin, LOW); // ON (active LOW)
delay(duration_ms);
digitalWrite(pin, HIGH); // OFF
delay(duration_ms);
}
}

// Normalize IMU data
float normalize(float x, float mean, float stddev) {
return (x - mean) / stddev;
}

// BLE chunked transmission
void bleuart_send_block(String &data) {
int len = data.length();
for (int i = 0; i < len; i += 20) {
bleuart.write((const uint8_t*)(data.c_str() + i), min(20, len - i));
delay(5);
}
}

// Initialize BLE
void initBLE() {
Bluefruit.begin();
Bluefruit.setTxPower(4);
Bluefruit.setName(“XIAO_Inference”);

bleuart.begin();

Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
Bluefruit.Advertising.addService(bleuart);
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start();
}

// Deep sleep
void goToDeepSleep() {
Serial.println(“Going to deep sleep…”);
blinkLED(LED_RED, 2, 200); // Red LED blink

// Ensure LEDs off
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_BLUE, HIGH);

delay(50);

// Disconnect and stop BLE
Bluefruit.Advertising.stop();
Bluefruit.disconnect(0);
delay(100);

// Fully disable SoftDevice (BLE)
sd_softdevice_disable();
delay(50);

// Configure wake-up button
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE); // Use PULLUP_SENSE for wake-up
NRF_P0->PIN_CNF[BUTTON_PIN] = (GPIO_PIN_CNF_SENSE_Low << GPIO_PIN_CNF_SENSE_Pos);

// Enable wake-up on button press
sd_power_system_off();

}

// Setup
void setup() {
// Enable DC-DC for stable battery power
NRF_POWER->DCDCEN = 1;

// Initialize serial safely (no blocking)
Serial.begin(115200);
delay(100); // Replace while(!Serial) to avoid USB dependency

pinMode(BUTTON_PIN, INPUT_PULLUP);

pinMode(LED_RED, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_BLUE, HIGH);

// Check if we’re waking from sleep
if (NRF_POWER->RESETREAS & POWER_RESETREAS_OFF_Msk) {
// Clear the reset reason
NRF_POWER->RESETREAS = POWER_RESETREAS_OFF_Msk;
isSleeping = false;
blinkLED(LED_BLUE, 2, 200); // BLUE LED blink on wake
}

// IMU
if (imu.begin() != 0) {
Serial.println(“IMU failed”);
while (1);
}

// Initialize BLE
initBLE();

Serial.println(“Device ready”);
}

// Loop
void loop() {
// — Button check for deep sleep toggle —
int buttonState = digitalRead(BUTTON_PIN);
unsigned long currentMillis = millis();

if (buttonState == LOW) {
if (!buttonHeld) {
buttonHeld = true;
buttonPressStart = currentMillis;
} else if (currentMillis - buttonPressStart >= 2000) {
goToDeepSleep();
// After waking from sleep, we’ll start fresh in setup()
}
} else {
buttonHeld = false;
}

// — IMU Sampling & Inference —
if (millis() - last_sample_time >= SAMPLE_INTERVAL_MS) {
last_sample_time = millis();

float ax = imu.readFloatAccelX();
float ay = imu.readFloatAccelY();
float az = imu.readFloatAccelZ();
float gx = imu.readFloatGyroX();
float gy = imu.readFloatGyroY();
float gz = imu.readFloatGyroZ();

features[feature_ix++] = normalize(ax, MEANS[0], STD_DEVS[0]);
features[feature_ix++] = normalize(ay, MEANS[1], STD_DEVS[1]);
features[feature_ix++] = normalize(az, MEANS[2], STD_DEVS[2]);
features[feature_ix++] = normalize(gx, MEANS[3], STD_DEVS[3]);
features[feature_ix++] = normalize(gy, MEANS[4], STD_DEVS[4]);
features[feature_ix++] = normalize(gz, MEANS[5], STD_DEVS[5]);

if (feature_ix >= EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
  ei_impulse_result_t result;
  signal_t signal;

  if (numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal) == 0) {
    if (run_classifier(&signal, &result, false) == EI_IMPULSE_OK) {
      if (bleuart.notifyEnabled()) {
        String topLabel = "";
        float topValue = 0.0;

        for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
          float val = result.classification[ix].value;
          if (val > topValue) {
            topValue = val;
            topLabel = result.classification[ix].label;
          }
        }

        if (topValue > CONFIDENCE_THRESHOLD) {
          String resultStr = topLabel + ": " + String(topValue, 5);
          bleuart_send_block(resultStr);
        }
      }
    }
  }
  feature_ix = 0;
}

}
}

Hi Devarshi_Bohra,
If you try it with a sketch that contains a lot of content at once, debugging will become difficult.
Why don’t you try it with a sketch that only includes DeepSleep, Wakeup, and LED flashing to see if it works the way you want it to?

When posting code, please use </> to post it.

Hi there,

And Welcome here,

If I may, Definitely do and take @msfujino’s Excellent suggestion to break it down to basics first, you have a lot of moving parts there, it definitely will make troubleshooting easier.
also I notice you would benefit from having a look at this thread and the code example, You will see the proper pin connection and logic needed to wake up and sleep the nrf52840, some extra stuff as it relates to IMU. The Xiao Grove Expansion Board Video demonstration, w/ code. 🎅

HTH
GL :slight_smile: PJ :v:

You’re close you will get it, take a one step at a time approach and edit your post (fix code tags “</>” ) makes it easier to read. :+1:

Thanks to @PJ_Glasso and @msfujino for your replies and suggestions. I broke down my project to the basics and tried putting the board to sleep and waking it up using a push button. Using @PJ_Glasso’s Xiao Grove Expansion code, I created this basic version, and it works! I haven’t tested the current consumption in sleep mode yet, but the serial monitor output and disconnection during sleep suggest that the device successfully enters and exits sleep mode. Also, if you have time, please review my code and let me know if there are any problems or mistakes. It would be really helpful. Below is the code I used:

#include <Arduino.h>
#include <Adafruit_TinyUSB.h>  // For USB serial support, version: 1.7.0

// Pin definitions
#define BUTTON_PIN 2          // Pushbutton connected to D2 (other side to GND)

// Interrupt Service Routine (ISR) for button press
volatile bool buttonPressed = false; // Flag to track button press
void buttonISR() {
  buttonPressed = true;  // Set flag on falling edge
}

// Function to blink a specific LED
void blinkLED(uint8_t ledPin, int times, int delayMs) {
  for (int i = 0; i < times; i++) {
    digitalWrite(ledPin, LOW);  // On (active-low)
    delay(delayMs);
    digitalWrite(ledPin, HIGH); // Off
    delay(delayMs);
  }
}

// Setup function
void setup() {
  // Initialize LEDs
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
  digitalWrite(LED_RED, HIGH);  // Off (active-low)
  digitalWrite(LED_BLUE, HIGH); // Off (active-low)

  // Initialize serial for debugging
  Serial.begin(9600);
  // Wait for serial connection (up to 5 seconds)
  unsigned long startTime = millis();
  while (!Serial && millis() - startTime < 5000) {
    delay(10);
  }
  Serial.println("Program started. Hold button (D2 to GND) for 3 seconds to sleep.");

  // Configure button pin with pull-up resistor
  pinMode(BUTTON_PIN, INPUT_PULLUP);  // HIGH when not pressed, LOW when pressed

  // Attach interrupt for button press (falling edge to detect press)
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);

  // Indicate wake-up with 3 blue LED blinks
  Serial.println("Device awake (reset from sleep or power-on).");
  blinkLED(LED_BLUE, 3, 200);  // Blink blue LED 3 times
}

// Loop function
void loop() {
  // Blink red LED to indicate device is awake
  digitalWrite(LED_RED, LOW);  // On (active-low)
  delay(500);
  digitalWrite(LED_RED, HIGH); // Off
  delay(500);

  // Check for button press via ISR
  if (buttonPressed) {
    buttonPressed = false;  // Clear ISR flag

    // Check if button is held for 3 seconds
    unsigned long pressStart = millis();
    while (digitalRead(BUTTON_PIN) == LOW && millis() - pressStart < 3000) {
      // Wait until button is released or 3 seconds pass
      delay(10);
    }
    
    if (millis() - pressStart >= 3000 && digitalRead(BUTTON_PIN) == LOW) {
      // Button held for 3 seconds
      Serial.println("Button held for 3 seconds. Going to deep sleep...");
      
      // Blink red LED 3 times before sleep
      blinkLED(LED_RED, 3, 200);
      
      // Turn off both LEDs
      digitalWrite(LED_RED, HIGH);
      digitalWrite(LED_BLUE, HIGH);

      // Configure button pin for wake-up (sense low in system OFF mode)
      uint32_t pin = BUTTON_PIN;
      pin = g_ADigitalPinMap[pin];  // Map Arduino pin to nRF pin
      nrf_gpio_cfg_sense_input(pin, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW);

      delay(100);  // Ensure settings are applied

      // Enter system OFF mode (deep sleep)
      sd_power_system_off();
      NRF_POWER->SYSTEMOFF = 1;

      // Code will not reach here until woken up (resets to setup)
      Serial.println("Woken up from deep sleep!");
    }
  }

  // Enter low-power mode during idle (system ON)
  sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
  sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
  __WFE();  // Wait for event
  __WFI();  // Wait for interrupt
  sd_app_evt_wait();  // SoftDevice low-power wait
}

1 Like

Hi,

I have the base xiao nrf52840 and have been testing power draw. Using the above code from @msfujino - “Flash_Light_Deep_Sleep.ino” - I get 338uA during deep sleep. I see the same in my own code. Nothing I do seems to get the power draw below 338uA. This feels quite high as power draw sits at about 750uA in use while connected to my computer over BLE, and way higher than in the graph above. Any ideas what might be going on here? I am using the “Seeed nRF52 Boards” v1.1.12 (the non mbed version). Thanks in advance!

Edit:

Additionally, I have tried uninstalling v1.1.12 of the board library and installed 1.1.1 as mentioned in the original post (also needed to install Adafruit_SPIFlash 4.1.3 in order to get the code to compile), but I got the same result with 338uA during deep sleep with the original code.

Hi there,
Did you notice this part ?

I would try with the same BSP, first.
Also are you using any other peripherals?

HTH
GL :slight_smile: PJ :v:

The Flash is only part of the low-power puzzle. :+1:

Please tell us your method for measuring current.
Are any peripheral devices connected to XIAO?
Please post the sketch you are actually using as-is.

Yes sorry, I edited my comment to include that I tried using v1.1.1 instead of 1.1.12 but nothing changed.

I have a multimeter connected in series to my battery connected to the battery pads of the nRF52840. I would include an image but I can’t post images or links it seems in this reply. I have an LED hooked up to the 3v3 pin but it is not active. I have tried many different deep sleep sketches I have found as well as my own code and I have the same result with them all (338uA during deep sleep). I used your code as-is with no changes, which I will add again here for ease:

//----------------------------------------------------------------------------------------------
// BSP : Seeed nRF52 Borads 1.1.1
// Board : Seeed nRF52 Borads / Seeed XIAO nRF52840 Sense 
//----------------------------------------------------------------------------------------------
// 2023/08/11

#include <bluefruit.h>          // /Arduino15/packages/Seeeduino/hardware/nrf52/1.1.1/libraries/Bluefruit52Lib/src
#include <Adafruit_SPIFlash.h>  // Need to be deleted /Documents/Arduino/libraries/SdFat

// Built from the P25Q16H datasheet.
// https://gitlab.com/arduino5184213/seeed_xiao_nrf52840/flash_speedtest/-/tree/master
SPIFlash_Device_t const P25Q16H {
  .total_size = (1UL << 21), // 2MiB
  .start_up_time_us = 10000, // Don't know where to find that value
  
  .manufacturer_id = 0x85,
  .memory_type = 0x60,
  .capacity = 0x15,

  .max_clock_speed_mhz = 55,
  .quad_enable_bit_mask = 0x02, // Datasheet p. 27
  .has_sector_protection = 1,   // Datasheet p. 27
  .supports_fast_read = 1,      // Datasheet p. 29
  .supports_qspi = 1,           // Obviously
  .supports_qspi_writes = 1,    // Datasheet p. 41
  .write_status_register_split = 1, // Datasheet p. 28
  .single_status_byte = 0,      // 2 bytes
  .is_fram = 0,                 // Flash Memory
};
Adafruit_FlashTransport_QSPI flashTransport;
Adafruit_SPIFlash flash(&flashTransport);

void setup()
{
  // Enable DC-DC converter Mode
  NRF_POWER->DCDCEN = 1;            // Enable DC/DC converter for REG1 stage

  delay(1000);
  
  // on board Flash enter to Deep Power-Down Mode
  flashTransport.begin();
  flashTransport.runCommand(0xB9);  // enter deep power-down mode
  delayMicroseconds(5);             // tDP=3uS
  flashTransport.end();

  // Light Sleep Mode (RTOS delay function)
  delay(1000);

  // Deep Sleep Mode
  NRF_POWER->SYSTEMOFF = 1;

  // Even after entering deep sleep mode, 
  // 3.3V LOD's "quiescent current 2.5uA" remains. 
}

void loop()
{
  // nothing to do
}

What is the multimeter’s resolution?
What happens if you remove the LED?

It was the LED! Thank you so much! Not sure why the LED is drawing so much current since its off? Do you have any ideas why that might be the case or a direction you can point me in to figure it out? Thank you so much!!

LEDs consume current even when not lit, but 300uA seems a bit too high. By the way, why isn’t the LED lit even though it’s connected to the 3V3 pin?

It is a single addressable RGB LED (ws2812b) with a data pin which I am using the Adafruit Neopixel library to control. Maybe thats why it’s using so much current?

Is the Din pin of the WS2812B connected to XIAO? If nothing is connected, it may be driven by noise and consuming current.

Unfortunately, the data sheet did not specify the current consumption of the WS2812B.

When idle but connected to 3v3, a gpio pin set to output, and GND, the led seems to draw about ~335.5uA (338uA during deep sleep with the LED connected and ~2.5uA when I disconnected the 3v3 line but kept Din and GND connected to the xiao).

Would the data sheet for most components specify something like idle current draw? I was wondering if I could maybe put a mosfet between 3v3 pin and LED to toggle power to it?

I had to do that… :+1:

Generally, datasheets include a Supply Current section listing the static current consumption value.

I believe it is possible to use a Load Switch or PMOS FET for shutdown. I also use a switch with a PMOS FET to cut off current to the display device.

EDIT: The WS2812B may function experimentally at 3.3V, but it is a device designed to operate at 5V.

1 Like