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)

2 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
}