Light / Deep Sleep Current Comparison of XIAO_MG24 and XIAO_nRF52840

I’m confused about the flash storage requirement? It can be shut down when not in use, ie “Deep Sleep”. The IMU is “de-powered”, along with some other devices that can’t be used in deep sleep.

Deep Sleep current is in the ~40uA range on the XIAO MG24 - I think that’s (very high) due to the SAMD21 (OpenOCD-SWD) “interference”.

As a comparison.
I normally get ~1uA with an 'MG24 and additional components in deep sleep (EM4) and ~5uA in “Light Sleep” (EM2).

On the XIAO MG24
EM4 = ~40uA
EM2 = ~150uA

EDIT>
Corrected value
EM4 = ~1.5uA

EM4 starts the application from the beginning (ie a reset).
EM2 allows the application to continue from where it entered sleep mode.

If 40uA is too high (deep sleep), then I’d suggest using something other than the XIAO MG24. Just depends on your requirements.

How much EEProm do you require for your application?
How often are the read/writes?

Hi there,

Well sounds to me like you’ve put in the work for sure. Check out what @msfujino suggest, if anyone can hold up a light in that darkness he can… so look that way. :+1:

If I may what is your application? Maybe there is some ideas we can see.

HTH
GL :slight_smile: PJ :v:

Yeah, let me show what I’ve got so far. The relevant bits are:

#include <ArduinoLowPower.h>
#include <EEPROM.h>

#define SLEEP_TIMEOUT 8000
#define SLEEP_DURATION 120000 // 2 minutes
#define WAKE_STATE FALLING
#define WAKE_UP_BTN PA5
#define WAKE_UP_TILT PA5
#define MINIMUM_TILT 9

// on board Flush SPI_1 pins
#define CS1 PA6   // (21)
#define CLK1 PA0  // (17), D17   
#define MOSI1 PB0 // (15), D15
#define MISO1 PB1 // (16), D16

// on board peripherals pins
#define IMU_EN PD5  // (19)
#define MIC_EN PC8  // (22)
#define VBAT_EN PD3 // (25)
#define RFSW_EN PB5 // (27)

// Flash commands
#define READ_DATA 0x03
#define WRITE_ENABLE 0x06
#define PAGE_PROGRAM 0x02
#define SECTOR_ERASE 0x20

uint32_t cause;
unsigned long startTimeoutTime = 0;

//-----------------------------------------------------------------------------

// Flash functions
void sendSPI(byte data) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(MOSI1, data & 0x80);
    data <<= 1;
    digitalWrite(CLK1, HIGH);
    delayMicroseconds(1);
    digitalWrite(CLK1, LOW);
    delayMicroseconds(1);
  }
}

void writeEnable() {
  digitalWrite(CS1, LOW);
  sendSPI(WRITE_ENABLE);
  digitalWrite(CS1, HIGH);
}

//-----------------------------------------------------------------------------

void setup() {
  // on board flash pins
  pinMode(CS1, OUTPUT);
  pinMode(CLK1, OUTPUT);
  pinMode(MOSI1, OUTPUT);
  pinMode(MISO1, INPUT);
  digitalWrite(CS1, HIGH); 

  // on board peripherals OFF
  pinMode(IMU_EN, OUTPUT);
  pinMode(MIC_EN, OUTPUT);
  pinMode(VBAT_EN, OUTPUT);
  pinMode(RFSW_EN, OUTPUT);
  // digitalWrite(IMU_EN, LOW);
  digitalWrite(MIC_EN, LOW);   // MIC Power OFF
  digitalWrite(VBAT_EN, LOW);  // VBAT Power OFF
  digitalWrite(RFSW_EN, LOW);  // RFSW Power OFF

  pinMode(WAKE_UP_BTN, INPUT_PULLUP);

  cause = GPIO_EM4GetPinWakeupCause();

  EEPROM.get(eeAddress, appState);
}

void loop() {
 const float baseX = myIMU.readFloatAccelX();
  const float baseY = myIMU.readFloatAccelY();
  const float baseZ = myIMU.readFloatAccelZ();
  const int angleZ = atan2(baseZ, sqrt(baseX * baseX + baseY * baseY)) * 180 / PI;

  // if there is no tilt, but it's been woken up due to sleep timer
  if (angleZ < appState.baseAngleZ + MINIMUM_TILT) {
    if (appState.asleep == 1) {
      // if the wake up cause was from the WAKE_UP_TILT pin, wake up and stay awake
      if (cause & GPIO_IEN_EM4WUIEN0) {
        wakeUp();
      // else, go back to sleep
      } else {
        enterStandbySleep();
      }
    }
  } else {
    // wake up and stay awake
    wakeUp();
  }

  // wait 8 seconds before going to sleep
  if (startTimeoutTime > 0 && ((millis() - startTimeoutTime) > SLEEP_TIMEOUT)) {
    startTimeoutTime = 0;
    enterStandbySleep();
  }
}

void wakeUp() {
  startTimeoutTime = millis();
  appState.asleep = 0;
  appState.timeSpentSleeping = 0;

  // this stuff is commented out because I'm not sure if this is right or even helpful
  // digitalWrite(IMU_EN, HIGH); // IMU Power ON
  
  // Flash power up
  // digitalWrite(CS1, LOW);
  // sendSPI(0xAB);
  // digitalWrite(CS1, HIGH);
  // delayMicroseconds(30);
}

void enterStandbySleep() {
  // digitalWrite(IMU_EN, LOW); // IMU Power OFF
  appState.asleep = 1;
  EEPROM.put(eeAddress, appState);
  delay(10);

  // Flash Deep Power Down
  // writeEnable();
  // digitalWrite(CS1, LOW);
  // sendSPI(0xB9);
  // digitalWrite(CS1, HIGH);
  // delay(1);

  LowPower.attachInterruptWakeup(WAKE_UP_TILT, nullptr, WAKE_STATE);
  LowPower.deepSleep(SLEEP_DURATION);
}

The EEPROM state I’m getting a putting is kind of an object with a dozen or so numbers to represent status of different things in the app.

I wanted to measure the current, but your code had an error and could not be compiled.
Could you please provide us with a code that can test here and the current value you are having trouble with?

Yeah I didn’t do a great job cherry picking. I’ve tested this and it works. This is interesting, in this more contrived example the battery usage is actually higher. My multimeter is reading over 5mA when in deep sleep!

#include <ArduinoLowPower.h>
#include <EEPROM.h>
#include <LSM6DS3.h>

LSM6DS3 myIMU(I2C_MODE, 0x6A);

#define SLEEP_TIMEOUT 8000
#define SLEEP_DURATION 2000 // 2 sec
#define WAKE_STATE FALLING
#define WAKE_UP_BTN PA5
#define MINIMUM_TILT 9

// on board Flush SPI_1 pins
#define CS1 PA6   // (21)
#define CLK1 PA0  // (17), D17   
#define MOSI1 PB0 // (15), D15
#define MISO1 PB1 // (16), D16

// on board peripherals pins
#define IMU_EN PD5  // (19)
#define MIC_EN PC8  // (22)
#define VBAT_EN PD3 // (25)
#define RFSW_EN PB5 // (27)

// Flash commands
#define READ_DATA 0x03
#define WRITE_ENABLE 0x06
#define PAGE_PROGRAM 0x02
#define SECTOR_ERASE 0x20

uint32_t cause;
unsigned long startTimeoutTime = 0;

const int eeAddress = 0;
struct SavedState {
  int asleep;
  int baseAngleZ;
};

SavedState defaultState = {
  1,    // asleep
  90,   // baseAngleZ
};

SavedState appState;

//-----------------------------------------------------------------------------

// Flash functions
void sendSPI(byte data) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(MOSI1, data & 0x80);
    data <<= 1;
    digitalWrite(CLK1, HIGH);
    delayMicroseconds(1);
    digitalWrite(CLK1, LOW);
    delayMicroseconds(1);
  }
}

void writeEnable() {
  digitalWrite(CS1, LOW);
  sendSPI(WRITE_ENABLE);
  digitalWrite(CS1, HIGH);
}

//-----------------------------------------------------------------------------

void setup() {
  cause = GPIO_EM4GetPinWakeupCause();

  // on board flash pins
  pinMode(CS1, OUTPUT);
  pinMode(CLK1, OUTPUT);
  pinMode(MOSI1, OUTPUT);
  pinMode(MISO1, INPUT);
  digitalWrite(CS1, HIGH); 

  // on board peripherals OFF
  pinMode(IMU_EN, OUTPUT);
  pinMode(MIC_EN, OUTPUT);
  pinMode(VBAT_EN, OUTPUT);
  pinMode(RFSW_EN, OUTPUT);
  // digitalWrite(IMU_EN, LOW);
  digitalWrite(MIC_EN, LOW);   // MIC Power OFF
  digitalWrite(VBAT_EN, LOW);  // VBAT Power OFF
  digitalWrite(RFSW_EN, LOW);  // RFSW Power OFF

  myIMU.settings.gyroEnabled = 0;
  myIMU.settings.tempEnabled = 0;
  myIMU.settings.accelSampleRate = 208;
  myIMU.settings.accelFifoEnabled = 1;
  if (myIMU.begin() != 0) {
      // Serial.println("Device error");
  }

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(WAKE_UP_BTN, INPUT_PULLUP);

  EEPROM.get(eeAddress, appState);
  if (appState.baseAngleZ < 0) appState = defaultState;
}

void loop() {
  if (appState.asleep == 1) {
    // if the wake up cause was from the WAKE_UP_BTN pin, wake up and stay awake
    if (cause & GPIO_IEN_EM4WUIEN0) {
      wakeUp();
    // else, go back to sleep
    } else {
      enterStandbySleep();
    }
  }

  const float baseX = myIMU.readFloatAccelX();
  const float baseY = myIMU.readFloatAccelY();
  const float baseZ = myIMU.readFloatAccelZ();
  const int angleZ = atan2(baseZ, sqrt(baseX * baseX + baseY * baseY)) * 180 / PI;
  if (angleZ < appState.baseAngleZ - MINIMUM_TILT) {
    // if tilted, it should stay awake
    wakeUp();
  }

  digitalWrite(LED_BUILTIN, LOW); // LED on

  // wait 8 seconds before going to sleep
  if (startTimeoutTime > 0 && ((millis() - startTimeoutTime) > SLEEP_TIMEOUT)) {
    startTimeoutTime = 0;
    enterStandbySleep();
  }
}

void wakeUp() {
  startTimeoutTime = millis();
  appState.asleep = 0;

  // this stuff is commented out because I'm not sure if this is right or even helpful
  // digitalWrite(IMU_EN, HIGH); // IMU Power ON
  
  // Flash power up
  // digitalWrite(CS1, LOW);
  // sendSPI(0xAB);
  // digitalWrite(CS1, HIGH);
  // delayMicroseconds(30);
}

void enterStandbySleep() {
  // digitalWrite(IMU_EN, LOW); // IMU Power OFF
  digitalWrite(LED_BUILTIN, HIGH); // LED off
  appState.asleep = 1;
  EEPROM.put(eeAddress, appState);
  delay(10);

  // Flash Deep Power Down
  // writeEnable();
  // digitalWrite(CS1, LOW);
  // sendSPI(0xB9);
  // digitalWrite(CS1, HIGH);
  // delay(1);

  LowPower.attachInterruptWakeup(WAKE_UP_BTN, nullptr, WAKE_STATE);
  LowPower.deepSleep(SLEEP_DURATION);
}

I need to make a correction above.

The 40uA I mentioned is the Light Sleep current on an MG24 when built with the Simplicity Studio development tools.

The correct current for the MG24 with (Arduino) Deep Sleep is ~1.0uA

I have just checked your code and it runs at 4.7mA and sleeps at ~1.05uA on a standalone XIAO MG24 Sense.

1 Like

Huh, maybe I’m hooking things up to my multimeter wrong then? I don’t know. I am building it with the Arduino IDE. Essentially this code is burning through (supposed) 2700mAh batteries in like 12 days. The only difference between this code and what I’ve been using is that it actually deep sleeps for much longer.

The wake time is quite high in your app.
I try keep wake times to uS or mS as that’s when the most power is being used.
2700mAh usually lasts me about a year…

1 Like

Yeah I want to be able to read a display for a few seconds. Maybe I can lower that. I am actually thinking the culprit might be how I’m hooking up the battery. It might be coming back to this buck converter. How are you using batteries?

I just connect to the battery input (underside).

I’m currently testing on a 30F UltraCAP.

1 Like

Are you using a lithium ion battery? I am trying to keep my project to conventional batteries, and picked a 9v to start with. I don’t think you can hook that straight up to it. Guessing this is the source of all my issues

I haven’t connected the display (wasn’t in your code) for checking current/power, but I can write my own and connect to a display I have here… but have other stuff to do :slight_smile:

I can use “conventional lithium ion” battery if required. I have a few lying about but not sure if I have a 2700mAh - will have to “dig around”…
I couldn’t quite make out your connections on your circuit but make sure you have a pullup resistor on the wake pin as well. I have 100k.

Thanks! I do have a resistor there so that should be good. If you to test with some conventional battery, how would you hook it up? I am using this and I have a suspicion based on this conversation that it is a battery hog.

Edit: oh wow, it is the buck converter. I hooked it up to the multimeter with no other load on it and it was drawing over 5mA! They seem to be very inconsistent between chips. I have more than one and a different one drew a little over 900uA. Guess I really need to figure out a different solution here, but I’d still really like to use a conventional battery :thinking:

You could try the regulator “standalone” to check but I suspect it’s not very “efficient”.

In my case, since (mostly) the power is very low, for a non-battery (range) voltage, ie > 4.2V I just use an LDO (if voltage is low enough eg < 12V).
A good quality LDO draws ~ 1uA (above circuit usage).

But for standard battery voltage ranges I just use the battery inputs.
With potential for “current spikes” on wake, use a decent capacitor, ie > 100uF? Dependent on the battery chemistry.

The OLed seems to be a little power hungry (testing now) so should be turned off during sleep (Which I think you did based on your original firmware that I tested)?

1 Like

Yep, you are right the display is turned off.

This is awesome. So sounds like I can try two options, an LDO and a capacitor. So either one of those would just sit in between the battery and the battery pads? Gonna give this a go. Thank you!