XIAO_BLE wakes up from System_ON_Sleep and Writes weather data to on-board Flash

In a project to periodically write data to an on-board flash memory, I used System_ON_Sleep to reduce the standby current to 1/60th of its default value.

As a practical example, described here is that XIAO wakes up from System_ON_Sleep every hour and records temperature, humidity, and barometric pressure data measured by the BME280 into 2MB of > on-board flash memory.Measures to reduce current consumption include…

nRF52840: REG 1 in DC/DC mode, System_ON_Sleep mode, and wake-up with RTC interrupt
On-board flash memory: Deep Power-down mode
BME280: Forced mode (sleep until triggered)

The result was a 1/60 reduction in standby current from 6.65mA to 110uA and a voltage drop of 110mV on a 220mAh battery for a week. During the week-long test, the hourly weather data measured by the BME280 was recorded without any problems. It is possible to use it for a system that can record weather data for a month.

The sketches used in the experiment can be found here.
XIAO_LightSleep_Flash.zip (73.1 KB)

The following link and libraries made it easy to write sketches. Thanks.
Files · master · arduino / seeed_xiao_nrf52840 / flash_speedtest · GitLab
Adafruit SPIFlash 4.1.1 by Adafruit
SparkFun BME280 2.0.9 by SparkFun Electronics

The following link may be helpful for current reduction when sending measured data via BLE.
System_ON_Sleep of XIAO BLE

3 Likes

I bought the XIAO BLE board just for this application, with addition of rain gauge bucket tips recording. By pressing a bouton, it starts a BT connexion allowing me to gather datas on a smartphone (and make configuration). I started first with the adafruit feather nrf52840 and latter with the itsybitsy. Nevertheless, power consumption was too high, around 1 mA, due to onboard neo-leds that have a high current drain even when dark.

So, I want to build a weather data logger for very remote places (oversea, cave). I want to have a working duration of 3 years. A AA LiSOCl2 battery has a capacity 2.6 Ah. With your sleep current, it is not so far.

I will follow your project carefully.

Eric_S,
I think 100uA is the limit for System_ON_Sleep. If you are looking for a lower current consumption, there is also a method called “System Off Sleep or Deep Sleep”. Search for “Deep Sleep” in this forum and you will find some useful threads.

@msfujino Hi, recently I achieved systemOn mode under the Mbed version, and the current is roughly 260 uA. But I am not sure if this value is correct (since on the NRF52840 datasheet, it also says system on mode with RAM costing 3 uA). Do you have any experience with that?

Hi Hao_Liu,
As you can see in post 1 and post 31, the sleep current for non-mbed using RTOS is less than 5uA. On the other hand, mbed is very disadvantageous with respect to sleep current reduction.

System_ON_Sleep of XIAO BLE
System_ON_Sleep of XIAO BLE - #31 by msfujino

Since Post 1, the following changes have been made to reduce the sleep current from 110uA to 26uA, and the battery state can be monitored for a long time.

  1. Instead of Sleep/Wake-up by RTC interrupt, use RTOS delay() which consumes less current.
  2. Do not let Flash sleep because we do not know how to recover from DeepPowerDown mode.
  3. Periodically display the battery voltage status with LEDs.

nRF52_XIAO_LightSleep_Flash_BME280_LED.zip (73.0 KB)

To recover the flash from DeepPowerDown, use the command 0xAB.
Reference:
PUYA P25Q16H p.44
Gigadevice GD25Q16C p.29

I’ve been working on this issue for about a month now, trying the command ABh, or command 66h,99h while measuring current with pppk2, but still can’t get it to release from DeepPowerDown. Maybe there is some condition that I have not thought of.
There is another example at the following link
Xiao nrf52 exit qspi flash deep sleep

Do you mean after sending the command 0xAB, there is no change in the operating current and the flash also does not respond to further read/write operations?

0xAB is also a command to read the 8-bit electronic ID, below is not the exact code, just for illustration only:

SPI.transfer(0xAB);
SPI.transfer(0xFF);
SPI.transfer(0xFF);
SPI.transfer(0xFF);
byte id;
id = SPI.transfer(0xFF);

Could you verify if the flash accepts the command and returns the ID (0x14)?

Schematic description of the experiment so far,

#define PowerDownTime 285           // 285:OK 286:NG

flashTransport.runCommand(0xB9);    // enter
delay(PowerDownTime);

flashTransport.runCommand(0xAB);    // release
delay(500);

flashTransport.runCommand(0xB9);    // enter
delay(PowerDownTime);

flashTransport.runCommand(0xAB);    // release
delay(500);

flash.begin(&P25Q16H, 1);           // initialize

If PowerDownTime is smaller than 285mS, it can be released; if it is larger than 286mS, it cannot be released. The current value is also measured, but if it cannot be released, an error occurs in flash.begin(). I can’t imagine why it is related to the length of DeepPowerDown at the moment.

The symptoms are strange. However since the pins are not exposed, probing with logic analyzer is not possible. Will need to run test codes specifically. By the way have you tried other libraries? I suppose there should be other libraries for generic serial/SPI flash on Github. We may also treat it as a simple SPI device and run SPI commands to test it.
Flash pin 1 => CS
Flash pin 2 => MISO
Flash pin 5 => MOSI
Flash pin 6 => SCLK
However I am unsure if the SPI port can be remapped.

The value of PowerDownTime when release is possible is not stable at all. However, if it is short, it can be released, and if it is long, it cannot be released. Somehow, it seems to be related to Serial.print().
I have ordered an external Flash so that the signal can be observed on a digital oscilloscope.
This is going to be a long term project.

An untested sketch which issues commands to the flash over SPI:

#include “SPI.h”
#include <Adafruit_TinyUSB.h>

#define QSPI_IO3 29
#define QSPI_IO2 28
#define QSPI_IO1 27
#define QSPI_IO0 26
#define QSPI_SCK 24
#define QSPI_CS 25

SPIClass SPI_2(NRF_SPIM2, QSPI_IO1, QSPI_SCK, QSPI_IO0);

void setup() {

Serial.begin(115200);
while ( !Serial ) delay(10);
Serial.println(“FLASH TESTING”);
pinMode(QSPI_CS, OUTPUT);
pinMode(QSPI_IO2, OUTPUT);
pinMode(QSPI_IO3, OUTPUT);
digitalWrite(QSPI_CS, HIGH);
digitalWrite(QSPI_IO2, HIGH);
digitalWrite(QSPI_IO3, HIGH);

SPI_2.begin();

digitalWrite(QSPI_CS, LOW);
SPI_2.transfer(0xAB);
SPI_2.transfer(0xFF);
SPI_2.transfer(0xFF);
SPI_2.transfer(0xFF);
byte deviceID;
deviceID = SPI_2.transfer(0xFF);
digitalWrite(QSPI_CS, HIGH);
Serial.print("Device ID = ");
Serial.println(deviceID, HEX);

SPI_2.end();

suspendLoop();
}

If it can print out the device ID then you may issue 0xB9, 0xAB, etc to see if it can wake up after deep sleep and perform read/write operation.

When I wrote directly in SPI, both “enter” and “release” worked. I also confirmed it with current. This is a report for now.
Maybe there is a problem with “flashTransport.runCommand()”. Because it often happens that this command hangs waiting for a response.
I will take some more time tomorrow to check.

Perhaps you can try to use other libraries, e.g.
SPIMemory
SerialFlash

The Adafruit library is too difficult for me. Anyway it supports QSPI mode and SPI mode. What if you use SPI mode and see if the results are the same?

//Adafruit_FlashTransport_QSPI flashTransport;
SPIClass SPI_2(NRF_SPIM2, PIN_QSPI_IO1, PIN_QSPI_SCK, PIN_QSPI_IO0);
Adafruit_FlashTransport_SPI flashTransport(PIN_QSPI_CS, SPI_2);

Adafruit_SPIFlash flash(&flashTransport);

I connected Flash externally and observed the SPI signal. This is still my imagination.

The roles of the three functions needed to send commands are
flashTransport.begin();
CS–>HIGH, CS–>LOW, OUT 0x05, IN StatusRegister, CS–>HIGH
flashTransport.runCommand();
CS–>LOW, OUT 0xAB, CS–>HIGH
flashTransport.end(); flashTransport.
CS–>OPEN

Flash only accepts command 0xAB if it is in Deep PowerDown mode.
See datasheet 10.30 Deep Power-down (DP)
“Once the DP instruction is set, all instructions will be ignored except the Release from Deep Power-down mode (RDP) and Read Electronic Signature (RES) instruction.”

The Adafruit library does not support either command 0xB9 or 0xAB. Flash may receive 0x05 and hang up.

SPI_2.transfer(0xAB); is working fine.
I have not yet gotten around to the tips and research you gave me.

I don’t have an external flash so I ran the test on a STM32 board with a Windbond memory chip. Although the test was in SPI mode, the same observation might appear in QSPI mode on nRF52840.

I used the “flash_speedtest” example sketch of Adafruit SPIFlash library. I noticed the clock was very strange. It was not steady and the duty cycle and speed varied drastically during the test. Below is the most “handsome” clock waveform and the speed was extremely low. Although the flash memory was recognized by the sketch, the erase test failed.

After changing the clock setting, the test passed and the clock waveform became much better.

  flash.begin();
  flashTransport.setClockSpeed(10000000, 10000000);

Would you change the speed and retry? nRF52840 supports up to 8MHz for SPI. I don’t know why the writing time looked more or less in the same range before and after changing the clock speed but at least the erase test passed. The library is beyond my capability to understand. Also the results from STM32 may not apply to nRF52840.

I spent all day today struggling with external Flash, but I only had more questions and did not get any good results.

The result is that both internal Flash and external Flash, DeepPowerDown mode works fine with SPI.transfer(), but not with flashTransport.runCommand(). I tried not outputting the command 0x05, which I was concerned about, and tried to make the SPI bus waveform the same as SPI.transfer(), but DeepPowerDown mode does not work.
The clock frequency was running at 2MHz until the initialization of release, begin, etc. and 16MHz after that.

The SerialFlash library you mentioned supports the DeepPowerDown command, so I loaded the Adafruit library with this as a reference, but it is too complicated for a holiday programmer.Tomorrow I will try to run the Adafruit library with SPI instead of QSPI.

Fortunately, the Adafruit library works fine if I use SPI.transfer() only with respect to DeepPowerDown.

Just take it easy, it is just a hobby anyway. Check this out. Go back to the basics and perhaps it can inspire you.