Generally, is there a way to set up a timed deep sleep with either Arduino low power library, or nrf_power, doesn’t matter for the purposes of this question, and also wake it up mid way into that sleep with the push of a button? In my tests, it seems if you use the Arduino low power method of setting it to sleep for say, 10 seconds, and you add an interrupt, even if you trigger the interrupt at 5 seconds, the microcontroller will still sleep for an additional 5 seconds before saying yep, there was an interrupt that happened. Is this just the way it works? The way I interpret the term interrupt makes me think it should literally interrupt a process that is currently happening and force it do something else.
Hi there,
So, it’s a matter of WHich Pin you use, The GPIO’s connected to the RTC are what would be needed. NOT all of the GPIO’s can do this.
Can you post the code using the code tags above </> paste it in there what you are using currently.
HTH
GL PJ
Sure, since I’m asking about either ArduinoLowPower or NRF I guess it could be two things.
If we’re talking about ArduinoLowPower, I’d be using the MG24 and the code I’ve tried that does not interrupt and wake up the sleep in the middle if it’s 10 second sleep would look like this:
#include <Arduino.h>
#include <ArduinoLowPower.h>
#define PIN_WAKEUP D3 // I tried D0, D1, D2, D3, D7, D8, D9, & D10
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
Serial.println("Sleep with timed wakeup");
}
void loop() {
digitalWrite(LED_BUILTIN, LED_BUILTIN_ACTIVE);
delay(5000);
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
delay(500);
Serial.printf("Going to sleep at %lu\n", millis());
LowPower.attachInterruptWakeup(PIN_WAKEUP, nullptr, RISING);
LowPower.sleep(10000);
}
If we’re talking about the NRF methods, I’d be using the nRF52840. That one I’ve seen the interrupt work well, but not with a timed sleep. Maybe the question is can you even do a timed sleep with the nRF52840:
#include <Adafruit_SPIFlash.h>
Adafruit_FlashTransport_QSPI flashTransport;
#define SLEEP_TIMEOUT 5000
#define PIN_WAKEUP (28)
unsigned long startTimeoutTime = 0;
void QSPIF_sleep(void) {
flashTransport.begin();
flashTransport.runCommand(0xB9);
flashTransport.end();
}
void setupWakeUpInterrupt() {
myIMU.settings.gyroEnabled = 0;
myIMU.settings.tempEnabled = 0;
myIMU.settings.accelEnabled = 0;
myIMU.begin();
myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00); // No duration
myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x02); // Set wake-up threshold
myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x80); // Enable interrupts and apply slope filter; latch mode disabled
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x70); // Turn on the accelerometer
// ODR_XL = 833 Hz, FS_XL = ±2 g
delay(4); // Delay time per application note
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0xB0); // ODR_XL = 1.6 Hz
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL6_G, 0x10); // High-performance operating mode disabled for accelerometer
myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x20); // Wake-up interrupt driven to INT1 pin
pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE);
return;
}
void setup() {
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, HIGH);
QSPIF_sleep();
digitalWrite(LED_GREEN, LOW);
delay(1000);
digitalWrite(LED_GREEN, HIGH);
NRF_POWER->DCDCEN = 1;
}
void loop() {
if (startTimeoutTime == 0) startTimeoutTime = millis();
if (startTimeoutTime > 0 && ((millis() - startTimeoutTime) > SLEEP_TIMEOUT)) {
startTimeoutTime = 0;
enterStandbySleep();
}
}
void enterStandbySleep() {
digitalWrite(LED_RED, LOW);
delay(1000);
digitalWrite(LED_RED, HIGH);
pinMode(button1Pin, INPUT_SENSE_HIGH);
setupWakeUpInterrupt();
NRF_POWER->SYSTEMOFF = 1;
}
Hi there,
So the Nrf52840 I’m very familiar with so I would say this after looking at what you have and testing it. For both timed wake-up and push-button wake-up from sleep on the Xiao nRF52840. The current approach has some missing elements that need to be fixed:
Observations:
- No RTC (Real-Time Clock) wake-up
NRF_POWER->SYSTEMOFF = 1;
fully powers down the MCU, but there is no provision for a timed wake-up (e.g., from RTC).
- Wake-up via push button is missing a proper interrupt setup
- The pin
button1Pin
is used but not declared or attached to an interrupt.
- Wake-up via IMU (LSM6DS3) setup is incomplete
- The interrupt is initialized, but it doesn’t connect to an actual wake-up mechanism.
- Use RTC (Real-Time Counter) for timed wake-up
- The nRF52840 has the RTC1 peripheral, which can trigger an interrupt after a defined period.
- Set up GPIO interrupt for manual button wake-up
- Enable GPIOTE (GPIO Task and Event module) and enable SENSE_HIGH on the button pin.
Try it this way…
#include <Adafruit_SPIFlash.h>
#include <nrf_rtc.h>
#include <nrf_power.h>
#include <nrf_gpio.h>
#include <nrf_drv_gpiote.h>
#include <nrf_delay.h>
Adafruit_FlashTransport_QSPI flashTransport;
#define SLEEP_TIMEOUT_MS 5000
#define BUTTON_WAKEUP_PIN 28 // Adjust based on your wiring
#define IMU_INT_PIN 2 // Adjust based on actual LSM6DS3 INT1 wiring
unsigned long startTimeoutTime = 0;
void QSPIF_sleep(void) {
flashTransport.begin();
flashTransport.runCommand(0xB9); // Put QSPI flash into sleep mode
flashTransport.end();
}
void setupRTCWakeUp() {
NRF_RTC1->TASKS_STOP = 1;
NRF_RTC1->PRESCALER = 32; // 32 kHz / (32 + 1) gives ~1 ms resolution
NRF_RTC1->CC[0] = SLEEP_TIMEOUT_MS;
NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
NRF_RTC1->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
NVIC_EnableIRQ(RTC1_IRQn);
NRF_RTC1->TASKS_START = 1;
}
void setupButtonWakeUp() {
nrf_gpio_cfg_sense_input(BUTTON_WAKEUP_PIN, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
}
void setupIMUWakeUp() {
pinMode(IMU_INT_PIN, INPUT_PULLDOWN);
myIMU.settings.gyroEnabled = 0;
myIMU.settings.tempEnabled = 0;
myIMU.settings.accelEnabled = 1;
myIMU.begin();
myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_DUR, 0x00); // No duration
myIMU.writeRegister(LSM6DS3_ACC_GYRO_WAKE_UP_THS, 0x02); // Wake-up threshold
myIMU.writeRegister(LSM6DS3_ACC_GYRO_TAP_CFG1, 0x80); // Enable interrupts
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0x70); // Enable accelerometer
delay(4);
myIMU.writeRegister(LSM6DS3_ACC_GYRO_CTRL1_XL, 0xB0); // Lower ODR for power saving
myIMU.writeRegister(LSM6DS3_ACC_GYRO_MD1_CFG, 0x20); // Wake-up interrupt on INT1
nrf_gpio_cfg_sense_input(IMU_INT_PIN, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);
}
void setup() {
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_GREEN, HIGH);
QSPIF_sleep();
digitalWrite(LED_GREEN, LOW);
delay(1000);
digitalWrite(LED_GREEN, HIGH);
NRF_POWER->DCDCEN = 1; // Enable DC-DC converter for power efficiency
}
void loop() {
if (startTimeoutTime == 0) startTimeoutTime = millis();
if (startTimeoutTime > 0 && ((millis() - startTimeoutTime) > SLEEP_TIMEOUT_MS)) {
startTimeoutTime = 0;
enterStandbySleep();
}
}
void enterStandbySleep() {
digitalWrite(LED_RED, LOW);
delay(1000);
digitalWrite(LED_RED, HIGH);
setupRTCWakeUp();
setupButtonWakeUp();
setupIMUWakeUp();
NRF_POWER->SYSTEMOFF = 1; // Enter deep sleep
}
extern "C" void RTC1_IRQHandler(void) {
if (NRF_RTC1->EVENTS_COMPARE[0]) {
NRF_RTC1->EVENTS_COMPARE[0] = 0;
}
}
Keeping in mind the Nrf5_sdk is only supporting this method using #include <nrf_drv_gpiote.h>
For Arduino IDE:
I had to ditch the flash sleep stuff, for now. The Adafruit Flash LIB doesn’t like the New BSP.
This works with the Xiao Dev board (button D1)
#include <Arduino.h>
// Define pins
#define WAKEUP_PIN D1 // Change to your actual button pin
#define LED_PIN LED_BUILTIN // Built-in LED
#define SLEEP_TIMEOUT 5000 // Time before sleep (ms)
// Timeout tracking
unsigned long startTimeoutTime = 0;
void enterDeepSleep() {
digitalWrite(LED_PIN, HIGH); // Turn off LED before sleep
// Configure button as wake-up source
pinMode(WAKEUP_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), wakeUpHandler, FALLING);
// Enable deep sleep
NRF_POWER->SYSTEMOFF = 1;
}
void wakeUpHandler() {
// Empty interrupt handler (wakes up MCU)
}
void setup() {
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Turn LED on to indicate power-on
pinMode(WAKEUP_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), wakeUpHandler, FALLING);
delay(2000); // Startup delay
digitalWrite(LED_PIN, LOW); // Turn off LED
}
void loop() {
if (startTimeoutTime == 0) {
startTimeoutTime = millis();
}
// Check timeout for entering sleep
if ((millis() - startTimeoutTime) > SLEEP_TIMEOUT) {
startTimeoutTime = 0;
enterDeepSleep();
}
}
compiles and Test AOK…
HTH
GL PJ
I’ll look at the mg24 next and see if it will do the same. I have examples posted that use the IMU to wake up on a Double Tap.
Have a look at that as well.
A few HOURS LATER…
BRICKED(locked out) of the Mg24 after some testing no more uploads , see the other thread for the code, btw.
Please carefully read the comments at the beginning of the sketch below and the wakeup pin settings before trying this.
Unfortunately, the current ArduinoLowPower.cpp has a bug that needs to be fixed.
------------------------------------------------------------------------------------------
// BoardBoard Library : Silicon Labs 2.2.0
// Board Select : Seeed Studio XIAO MG24 (Sense)
// Plotocol stack : None
//----------------------------------------------------------------------------------------------
// 2025/02/17
// IMPORTANT NOTE:Cannot upload new sketches during DeepSleep
// When PC0(D0)(0)==LOW and reset, enable to upload. After uploading, be sure to set PC0==HIGH and Reset.
// \Arduino15\packages\SiliconLabs\hardware\silabs\2.2.0\libraries\ArduinoLowPower\src\ArduinoLowPower.cpp:L234
// Overrides the WEAK escapeHatch() function in main.cpp to provide a way to prevent
// the device from going to EM4 too quickly. If the device enters EM4 sleep the programmer
// is not able to communicate with it - and without this mechanism it can easily be bricked.
// This function will run before any user code and keep the device awake when the built-in button
// (or the configured escape pin) is pressed during startup so that the programmer has a chance to
// communicate with it.
// void escape_hatch()
// \Arduino15\packages\SiliconLabs\hardware\silabs\2.2.0\variants\xiao_mg24\pins_arduino.h:L114
// #define DEEP_SLEEP_ESCAPE_PIN D0 // PC0
// Reference Manual, 24.3.12.3 Pin Function Tables, Table 24.3. GPIO Alternate Function Table
// WAKE_UP PC0(ESCAPE), PC5(SCL), PC7(RX), PA5(MOSI), back side : PB1, PB3, PD2, PD5
// When PC0 is used for the WAKE_UP function, set it LOW for a short time to distinguish it from the ESCAPR function.
// If set timer and external pin at the same time, wake up immediately
// Need to modify \Arduino15\packages\SiliconLabs\hardware\silabs\2.2.0\libraries\ArduinoLowPower\src\ArduinoLowPower.cpp
// LINE130 ~ LINE134
/*
// this->setupDeepSleepWakeUpPin();
//
// EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT;
// EMU_EM4Init(&em4_init);
// EMU_EnterEM4();
EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT;
em4_init.pinRetentionMode = emuPinRetentionEm4Exit;
EMU_EM4Init(&em4_init);
this->setupDeepSleepWakeUpPin();
EMU_EnterEM4();
*/
#include <Arduino.h>
#include "ArduinoLowPower.h"
#define WAKE_UP_PIN PC0 // PC0(ESCAPE), PC5(SCL), PC7(RX), PA5(MOSI), back side : PB1, PB3, PD2, PD5
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
Serial.println("Deep sleep with external or timed wakeup");
}
void loop()
{
digitalWrite(LED_BUILTIN, LED_BUILTIN_ACTIVE);
delay(500);
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
delay(500);
Serial.printf("Going to deep sleep for 10s at %\lu\n", millis());
delay(10);
// enter Deep Sleep
LowPower.attachInterruptWakeup(WAKE_UP_PIN, nullptr, FALLING); // available GPIO wakeup only while deep sleep
LowPower.deepSleep(10000); // not abailable GPIO wakeup while light sleep
}
Hi there,
SOLID information there my guy
I was able to recover said Bricking (really not bricked just no time to DFU, code was still waking and sleeping and LED was toggling) Last night after a few tries with the PC0 jumped to GND it finally took the code posted without the “nullptr” non-sense. YOI I don’t like how it handles the serial port either when sleeping.
HTH
GL PJ:v:
It’s been over 2 months since BSP2.2.0 started supporting XIAO_MG24 and I wonder why it has not been updated with the basic bug?
What is the selling point of MG24, which tries to imitate nRF52840 but inevitably cannot become nRF52840? I don’t understand. I just can’t bring myself to use MG24. Here are my impressions.
Thanks for all this, good stuff!
I was reading about the RTC, but everything I’d seen said you had to add an external module, so that is neat that it has that built in. I do have follow up questions on this.
The first sketch looks really promising but I can’t get it to work. First off, I’m told by the IDE that I don’t have nrf_drv_gpiote.h
or nrf_delay.h
are they part of a different BSP maybe than what I have installed? Also, getting errors related to the RTC1_IRQHandler
function, but maybe that’s also because of a different BSP version, I don’t know. Also you have a note in there about wiring for the LSM6DS3, but I’m not sure what you mean. Wouldn’t all nRF52840’s have the same?
The second sketch just doesn’t work for some reason. The pin I’m using is correct, and the LED does turn on, but it doesn’t wake up after it goes to sleep. Also, why do you say that one is for the Arduino IDE, is the first sketch not for Arduino IDE or something?
Again, thanks so much. I’m just trying to keep up and understand everything.
Thanks @msfujino! You freaked me out a little bit with the comments there about bricking, so I will try this tomorrow. I do have 4 MG24’s, so maybe I can chance bricking one on accident. Are you saying that the sketch you provided would work if the low power lib didn’t have a bug, or is that a workaround?
Thanks again!
Edit: Oh sorry, I see you added the patch to the lib code right there in the comment. I will try it.
Hi there,
So the first sketch is for Nrf_5 _sdk ONLY, It was an Older Nordic SDK under PLIO,(there is a note under the code post.)
Second one is for compiling in Arduino IDE, THE BSP you use for it is 2.9.2 (rollback 1 version) currently IDE installs the latest 2.9.3.
The LSM6DS3 is internal (under the can) LOGICALLY it needs to be connected in the code to a pin for the interrupt to work and be registered by the Xiao’s m4 mcu.
" pinMode(PIN_LSM6DS3TR_C_INT1, INPUT_PULLDOWN_SENSE); "
i.e. needs attached somewhere…
The GPIO method " nrf_drv_gpiote.h
" is only for NRF5_SDK…
HTH
GL PJ
Check you pins, be sure it’s connected to physical pin 2 of the Xiao (D1) logically when pushed connected to GND. device will wake-up.
The FLOW is roughly THIS: It boots, sets wake pin interrupt, sets timer, checks timer , Sleeps and wakes when you push button.
@msfujino, I got that sketch to work successfully - thank you! That could be a great option for what I’m trying to do. Still working on @PJ_Glasso’s nRF52840 solution as another option as well. Thanks guys!
@msfujino Your solution is great and still working well. I had one follow up question about the attachInterruptWakeup callback. I don’t think I really need one, but it might actually be nice to have one. I can’t get it to work though. Does it by chance only work with light sleep or something?
Thanks
Since wake-up from DeepSleep is the same as RESET, I don’t think the callback function will work.
Have you tried this for LightSleep?
Why don’t you post it to the link below