Low Power for Nucleo F401RE project: remote weather datalogger

AndrewMag
Tue Nov 20, 2018 7:11 pm
Hi,
I have bulit a project using a Nucleo F401RE, it is a weather station that records data on SD card every 20 mins. It works using BME 280 (Temperature, Humidity and Pressure sensors) and HC-SR04 for snow depth. In addition I have a SD card module and a DS3231 RTC.
Now my problem is that I need to run this project on batteries for almost one month (It will be mounted in high mountain for the winter period), It currently drows about 35 mA… I saw many libraries for Low Power but I wasn’t able to make them working. Anyone can help me?
Many thanks in advance!

Here’s the current project’s code (tested and working 100%):
#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Sodaq_DS3231.h"

Adafruit_BME280 bme;
#define SEALEVELPRESSURE_HPA (1013.25)

const int TRIG_PIN = 5;
const int ECHO_PIN = 6;

File data;

int meteoCount = 1;

void setup() {
pinMode(TRIG_PIN, OUTPUT); //dichiaro come output il pin trig
pinMode(ECHO_PIN, INPUT); //dichiaro come input il pin echo
bme.begin(0x76);
pinMode(10, OUTPUT);
SD.begin(4);

data = SD.open("MetData.txt", FILE_WRITE);
data.println("N:,Data e ora:,Temperatura:,U.R.:,Press.assoluta:,Altitudine:,Neve:");
data.close();

Wire.begin();
rtc.begin();
}

uint32_t old_ts;

void loop() {
float temperatura = bme.readTemperature();

float neve, durata; //dichiaro la variabile neve e la variabile durata
float cmPerMicrosecondi = 0.0331 + ( 0.000062 * temperatura); //calcolo i cm/ms del suono in base alla temperatura
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10); //faccio un impulso di dieci microsecondi sul pin trig
digitalWrite(TRIG_PIN, LOW);
durata = pulseIn(ECHO_PIN, HIGH); //mi metto in ascolto sul pin eco e calcolo la durata dell'impulso
neve = (durata*cmPerMicrosecondi/2.0); //calcolo la distanza con la formula durata*(cm/ms)/2, diviso due perchè il suo va, rimbalza contro un oggetto e ritorna, quindi compie due volte il tragitto
float menoNeve = 200.0 - neve;

delay(500);

DateTime now = rtc.now(); //get the current date-time
uint32_t ts = now.getEpoch();

if (old_ts == 0 || old_ts != ts) {
old_ts = ts;

data = SD.open("MetData.txt", FILE_WRITE);
data.print(meteoCount);
data.print(",");
data.print(now.date(), DEC);
data.print("/");
data.print(now.month(), DEC);
data.print("/");
data.print(now.year(), DEC);
data.print(" ");
data.print(now.hour(), DEC);
data.print(":");
data.print(now.minute(), DEC);
data.print(":");
data.print(now.second(), DEC);
data.print(",");
data.print(temperatura);
data.print(",");
data.print(bme.readHumidity());
data.print(",");
data.print(bme.readPressure() / 100.0F);
data.print(",");
data.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
data.print(",");
data.print(menoNeve);
data.println("");
data.close();
meteoCount++;

delay(1200000);
}
}


mrburnette
Tue Nov 20, 2018 7:29 pm
[AndrewMag – Tue Nov 20, 2018 7:11 pm] –
Hi,
I have bulit a project using a Nucleo F401RE, it is a weather station that records data on SD card every 20 mins. It works using BME 280 (Temperature, Humidity and Pressure sensors) and HC-SR04 for snow depth. In addition I have a SD card module and a DS3231 RTC.
Now my problem is that I need to run this project on batteries for almost one month (It will be mounted in high mountain for the winter period), It currently drows about 35 mA… I saw many libraries for Low Power but I wasn’t able to make them working.

Since you are using a Nucleo board, can I assume you are using the STM Official (HAL-based) core?

If so, the low-power library should work: https://github.com/stm32duino/Arduino_C … /issues/35

Ray


fpiSTM
Wed Nov 21, 2018 8:01 am
Something like this should work:

#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <STM32LowPower.h>
#include <STM32RTC.h>

Adafruit_BME280 bme;
#define SEALEVELPRESSURE_HPA (1013.25)

const int TRIG_PIN = 5;
const int ECHO_PIN = 6;

File data;
// Time in second between measure
static uint32_t atime = 1200000;

int meteoCount = 1;
/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

void setup() {
pinMode(TRIG_PIN, OUTPUT); //dichiaro come output il pin trig
pinMode(ECHO_PIN, INPUT); //dichiaro come input il pin echo
bme.begin(0x76);
pinMode(10, OUTPUT);
SD.begin(4);

data = SD.open("MetData.txt", FILE_WRITE);
data.println("N:,Data e ora:,Temperatura:,U.R.:,Press.assoluta:,Altitudine:,Neve:");
data.close();

Wire.begin();
// By default the LSI is selected as source. Use LSE for better accuracy
rtc.setClockSource(STM32RTC::LSE_CLOCK);
rtc.begin();
// Configure low power
LowPower.begin();
LowPower.enableWakeupFrom(&rtc, alarmMatch, &atime);
// Configure first alarm in 2 second then it will be done in the rtc callback
rtc.setAlarmEpoch( rtc.getEpoch() + atime );
}

void loop() {
float temperatura = bme.readTemperature();

float neve, durata; //dichiaro la variabile neve e la variabile durata
float cmPerMicrosecondi = 0.0331 + ( 0.000062 * temperatura); //calcolo i cm/ms del suono in base alla temperatura
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10); //faccio un impulso di dieci microsecondi sul pin trig
digitalWrite(TRIG_PIN, LOW);
durata = pulseIn(ECHO_PIN, HIGH); //mi metto in ascolto sul pin eco e calcolo la durata dell'impulso
neve = (durata * cmPerMicrosecondi / 2.0); //calcolo la distanza con la formula durata*(cm/ms)/2, diviso due perchè il suo va, rimbalza contro un oggetto e ritorna, quindi compie due volte il tragitto
float menoNeve = 200.0 - neve;

delay(500);

data = SD.open("MetData.txt", FILE_WRITE);
data.print(meteoCount++);
data.print(",");
data.print(rtc.getDay(), DEC);
data.print("/");
data.print(rtc.getMonth(), DEC);
data.print("/");
data.print(rtc.getYear(), DEC);
data.print(" ");
print2digits(rtc.getHours());
data.print(":");
print2digits(rtc.getMinutes());
data.print(":");
print2digits(rtc.getSeconds());
data.print(",");
data.print(temperatura);
data.print(",");
data.print(bme.readHumidity());
data.print(",");
data.print(bme.readPressure() / 100.0F);
data.print(",");
data.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
data.print(",");
data.print(menoNeve);
data.println("");
data.close();

LowPower.deepSleep();

}

void print2digits(int number) {
if (number < 10) {
data.print("0"); // print a 0 before if the number is < than 10
}
data.print(number);
}

void alarmMatch(void* data)
{
// This function will be called once on device wakeup
// You can do some little operations here (like changing variables which will be used in the loop)
// Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
uint32_t sec = 1;
if (data != NULL) {
sec = *(uint32_t*)data;
// Minimum is 1 second
if (sec == 0) {
sec = 1;
}
}
rtc.setAlarmEpoch( rtc.getEpoch() + sec);
}


zoomx
Wed Nov 21, 2018 9:06 am
@AndrewMag
if you are using the DS3231 chinese modules read this
https://thecavepearlproject.org/2014/05 … from-ebay/
It says to remove some components to achieve low power on this module starting from the power led.
Cave Pearl datalogger, that is Arduino based, has a lot of low power tips because this project is not a proof of concept but is used since years in remote underwater caves.

AndrewMag
Wed Nov 21, 2018 6:16 pm
Many thanks to everybody! Yes I’m using the STM Official (HAL-based) core with sucess!

[fpiSTM – Wed Nov 21, 2018 8:01 am] –
Something like this should work:
[…] Note I’ve used the RTC from the board.
You should be able to mount a coin cell battery on vbat by removing one soldier bridge
See: https://os.mbed.com/users/gregeric/note … n-stm32-n/

For your use case it’s should be better to use shutdown mode but I do not implement yet the RTC back register to restore the time.

Many thanks for the code and the advices! Unfortunately It gives me this error, that I had also when I tried in the past to use the low power library… what did I do wrong?

Arduino:1.8.5 (Windows 7), Scheda:"Nucleo-64, Nucleo F401RE, STLink, Enabled with generic Serial, None, Smallest (-Os default)"

In file included from C:\Users\Andrea\Documents\Arduino\libraries\STM32LowPower-master\src/STM32LowPower.h:49:0,

from C:\Users\Andrea\Desktop\DATI ANDREA\ardUINO\sketch_nov21a\sketch_nov21a.ino:6:

C:\Users\Andrea\Documents\Arduino\libraries\STM32RTC-master\src/STM32RTC.h:66:10: error: 'HOUR_AM' was not declared in this scope

AM = HOUR_AM,

^~~~~~~

C:\Users\Andrea\Documents\Arduino\libraries\STM32RTC-master\src/STM32RTC.h:67:10: error: 'HOUR_PM' was not declared in this scope

PM = HOUR_PM

^~~~~~~

exit status 1
Errore durante la compilazione per la scheda Nucleo-64.


fpiSTM
Wed Nov 21, 2018 7:10 pm
You didn’t have the last version of the libraries. Please remove the STM32RTC and LowPower then install them thanks the library manager,
It is better to use the official releases instead of the zip

AndrewMag
Wed Nov 21, 2018 7:39 pm
Ok. Removed both of them and re-installed the STM32duino RTC 1.0.2 version and the STM32duino Low Power 1.0.1 version
unfortunately nothing changed… same error!

fpiSTM
Wed Nov 21, 2018 8:18 pm
Yes, I’ve just think you are not on the last core 1.4.0. That’s why you get this error.

AndrewMag
Thu Nov 22, 2018 10:00 am
Ok thanks, how can I upgrade the core to 1.4.0?

fpiSTM
Thu Nov 22, 2018 1:08 pm
Thanks the boards manager with the JSON file.
See:
https://github.com/stm32duino/Arduino_C … ng-started

AndrewMag
Fri Nov 23, 2018 9:48 pm
Ok Thanks. Code charged successfully even if unfortunately it doesn’t work correctly because it makes just one measurement and then nothing more (maybe it doesn’t wake up from deep sleep?)
I also noticed that your code no longer uses the DS3231’s RTC… due to the fact I haven’t a back-up battery for the Nucleo board RTC, wouldn’t be possible to use the DS3231 instead of the on-board RTC also to wake up the board from low power? Maybe with an external alarm function from DS3231?
Thanks in advance

zoomx
Mon Nov 26, 2018 10:25 am
I believe it is possible like in Arduino but code should be changed.
The MCU shold go to sleep and awakened when the line connected to the DS3231 fall.
Unfortunately I am not able to write a code for this.

mrburnette
Mon Nov 26, 2018 1:07 pm
Check out Andy’s camera controller:

https://www.stm32duino.com/viewtopic.php?t=658


fpiSTM
Mon Nov 26, 2018 1:09 pm
Yes, as mentioned I’ve remove the use of the external RTC and provide a way to add a coin cell battery.
Anyway, it is possible to use an external one and use one of the SYSWKUP pins to wake up the board from sleep, for F401: PA0.
attachInterruptWakeup()

AndrewMag
Mon Nov 26, 2018 7:41 pm
Thanks
I’ve writed a new code… this time using the “timed deep sleep” current dropped from 35 mA to about 26.
Not bad but i think it should be possible to do more, by removing the LEDs (or disabling them? is it possible? I’m talking about LD1 e LD3) I hope to save a few more mA…
Does anyone know if it is possible to disable the current to sensors (VCC) while the board is in deep sleep? This would defintively give me much less power waste!

updated code (tested):
#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Sodaq_DS3231.h"
#include "STM32LowPower.h"

Adafruit_BME280 bme;
#define SEALEVELPRESSURE_HPA (1013.25)

const int TRIG_PIN = 5;
const int ECHO_PIN = 6;

File data;

int meteoCount = 1;

void setup() {
pinMode(TRIG_PIN, OUTPUT); //dichiaro come output il pin trig
pinMode(ECHO_PIN, INPUT); //dichiaro come input il pin echo
bme.begin(0x76);
pinMode(10, OUTPUT);
SD.begin(4);

data = SD.open("MetData.txt", FILE_WRITE);
data.println("N:,Data e ora:,Temperatura:,U.R.:,Press.assoluta:,Altitudine:,Neve:");
data.close();

Wire.begin();
rtc.begin();
LowPower.begin();
}

uint32_t old_ts;

void loop() {
float temperatura = bme.readTemperature();

float neve, durata; //dichiaro la variabile neve e la variabile durata
float cmPerMicrosecondi = 0.0331 + ( 0.000062 * temperatura); //calcolo i cm/ms del suono in base alla temperatura
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10); //faccio un impulso di dieci microsecondi sul pin trig
digitalWrite(TRIG_PIN, LOW);
durata = pulseIn(ECHO_PIN, HIGH); //mi metto in ascolto sul pin eco e calcolo la durata dell'impulso
neve = (durata*cmPerMicrosecondi/2.0); //calcolo la distanza con la formula durata*(cm/ms)/2, diviso due perchè il suo va, rimbalza contro un oggetto e ritorna, quindi compie due volte il tragitto
float menoNeve = 200.0 - neve;

delay(500);

DateTime now = rtc.now(); //get the current date-time
uint32_t ts = now.getEpoch();

if (old_ts == 0 || old_ts != ts) {
old_ts = ts;

data = SD.open("MetData.txt", FILE_WRITE);
data.print(meteoCount);
data.print(",");
data.print(now.date(), DEC);
data.print("/");
data.print(now.month(), DEC);
data.print("/");
data.print(now.year(), DEC);
data.print(" ");
data.print(now.hour(), DEC);
data.print(":");
data.print(now.minute(), DEC);
data.print(":");
data.print(now.second(), DEC);
data.print(",");
data.print(temperatura);
data.print(",");
data.print(bme.readHumidity());
data.print(",");
data.print(bme.readPressure() / 100.0F);
data.print(",");
data.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
data.print(",");
data.print(menoNeve);
data.println("");
data.close();
meteoCount++;

digitalWrite(LED_BUILTIN, LOW);
LowPower.deepSleep(1200000);
}
}


zoomx
Tue Nov 27, 2018 8:40 am
[AndrewMag – Mon Nov 26, 2018 7:41 pm] –
Does anyone know if it is possible to disable the current to sensors (VCC) while the board is in deep sleep? This would defintively give me much less power waste!

It depends on sensors!
If you use a BME280, maybe it is possible to power it from a pin or use the power down function.


fpiSTM
Tue Nov 27, 2018 8:55 am
Low Power library handle the MCU power mode. User have also to handle all connected peripheral to get the best low power.
More over if you use the STLink it consume also some current.

In your case if you kept the external RTC. I advise to use shutdown() and attachIntterupt on PA0 (SYSWKUP pin).
Then program the DS3231 to generate an interrupt each period you want.


AndrewMag
Tue Nov 27, 2018 7:58 pm
Just tried with this one (trying to disabilitate sensors’ VCC by pins 9 and 2) but it’s not working: sensors’ values appeared in txt file are “nan”…

#include <SD.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "Sodaq_DS3231.h"
#include "STM32LowPower.h"

Adafruit_BME280 bme;
#define SEALEVELPRESSURE_HPA (1013.25)

const int TRIG_PIN = 5;
const int ECHO_PIN = 6;
int nevePin = 9;
int bmePin = 2;
File data;

int meteoCount = 1;

void setup() {
pinMode(TRIG_PIN, OUTPUT); //dichiaro come output il pin trig
pinMode(ECHO_PIN, INPUT); //dichiaro come input il pin echo
bme.begin(0x76);
pinMode(10, OUTPUT);
SD.begin(4);
pinMode(bmePin, OUTPUT);
pinMode(nevePin, OUTPUT);

data = SD.open("MetData.txt", FILE_WRITE);
data.println("N:,Data e ora:,Temperatura:,U.R.:,Press.assoluta:,Altitudine:,Neve:");
data.close();

Wire.begin();
rtc.begin();
LowPower.begin();
}

uint32_t old_ts;

void loop() {
digitalWrite(nevePin, HIGH);
digitalWrite(bmePin, HIGH);
float temperatura = bme.readTemperature();

float neve, durata; //dichiaro la variabile neve e la variabile durata
float cmPerMicrosecondi = 0.0331 + ( 0.000062 * temperatura); //calcolo i cm/ms del suono in base alla temperatura
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10); //faccio un impulso di dieci microsecondi sul pin trig
digitalWrite(TRIG_PIN, LOW);
durata = pulseIn(ECHO_PIN, HIGH); //mi metto in ascolto sul pin eco e calcolo la durata dell'impulso
neve = (durata*cmPerMicrosecondi/2.0); //calcolo la distanza con la formula durata*(cm/ms)/2, diviso due perchè il suo va, rimbalza contro un oggetto e ritorna, quindi compie due volte il tragitto
float menoNeve = 200.0 - neve;

delay(500);

DateTime now = rtc.now(); //get the current date-time
uint32_t ts = now.getEpoch();

if (old_ts == 0 || old_ts != ts) {
old_ts = ts;

data = SD.open("MetData.txt", FILE_WRITE);
data.print(meteoCount);
data.print(",");
data.print(now.date(), DEC);
data.print("/");
data.print(now.month(), DEC);
data.print("/");
data.print(now.year(), DEC);
data.print(" ");
data.print(now.hour(), DEC);
data.print(":");
data.print(now.minute(), DEC);
data.print(":");
data.print(now.second(), DEC);
data.print(",");
data.print(temperatura);
data.print(",");
data.print(bme.readHumidity());
data.print(",");
data.print(bme.readPressure() / 100.0F);
data.print(",");
data.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
data.print(",");
data.print(menoNeve);
data.println("");
data.close();
meteoCount++;

digitalWrite(LED_BUILTIN, LOW);
digitalWrite(nevePin, LOW);
digitalWrite(bmePin, LOW);
LowPower.deepSleep(1200000);
}
}


zoomx
Wed Nov 28, 2018 8:07 am
Remove all led that you don’t need.
Power consumption of DS3231 seems too high, have you removed the diode used for recharging battery?
If the Nucleo is powered from USB maybe there is a power waste from the LDO.

Are you using HCSR-04 to measure snow height?

[fpiSTM – Tue Nov 27, 2018 8:55 am] –
In your case if you kept the external RTC. I advise to use shutdown() and attachIntterupt on PA0 (SYSWKUP pin).
Then program the DS3231 to generate an interrupt each period you want.

The DS3231 has a line alarm that usually is high and when alarm fires it goes low.
Like on Arduino UNO you have to put the MCU in deep sleep and configure a pin (2 and 3 on Arduino UNO, PA0 on the STM32 like suggested by fpiSTM) to wake the MCU when the line goes from HIGH to LOW.


fpiSTM
Wed Nov 28, 2018 4:14 pm
Her an example how to use the external wakeup
https://github.com/stm32duino/STM32LowP … up.ino#L32

I saw in the DS library example how to configure alarm, so you should be able to configure the alarm and wakeup thanks PA0.
And use shutdown() instead of deepsleep() (without argument as you will be wake up thanks the external RTC alarm)


AndrewMag
Wed Nov 28, 2018 6:15 pm
[fpiSTM – Wed Nov 28, 2018 4:14 pm] –
Her an example how to use the external wakeup
https://github.com/stm32duino/STM32LowP … up.ino#L32

I saw in the DS library example how to configure alarm, so you should be able to configure the alarm and wakeup thanks PA0.
And use shutdown() instead of deepsleep() (without argument as you will be wake up thanks the external RTC alarm)

In SodaqDS3231 RTC library I found this exmple:
//Interrupts for Battery management/saving using MCU power down mode. /INT from DS3231 is connected to INT0 of MCU.

#include <avr/sleep.h>
#include <Wire.h>
#include "Sodaq_DS3231.h"

static uint8_t prevSecond=0;

void setup ()
{
/*Initialize INT0 for accepting interrupts */
PORTD |= 0x04;
DDRD &=~ 0x04;

Serial.begin(57600);
Wire.begin();

rtc.begin();
attachInterrupt(0, INT0_ISR, FALLING);

//Enable Interrupt
rtc.enableInterrupts(EveryMinute); //interrupt at EverySecond, EveryMinute, EveryHour
// or this
//rtc.enableInterrupts(18,4,0); // interrupt at (h,m,s)
}

void loop ()
{

DateTime now = rtc.now(); //get the current date-time
if((now.second()) != prevSecond )
{
//print only when there is a change in seconds
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.date(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println(' ');
}
prevSecond = now.second();
rtc.clearINTStatus();

}

//Interrupt service routine for external interrupt on INT0 pin conntected to /INT
void INT0_ISR()
{
//Keep this as short as possible. Possibly avoid using function calls

Serial.println(" External Interrupt detected ");
}


zoomx
Thu Nov 29, 2018 10:45 am
[AndrewMag – Wed Nov 28, 2018 6:15 pm] –

How I can configure the PA0 pin? Do you have an example?

Maybe this one?
https://github.com/stm32duino/STM32LowP … Wakeup.ino

As I can see it is the same on a Arduino UNO, different pin and different function but other things are the same.


AndrewMag
Sat Dec 08, 2018 9:29 am
Hello everyone,
I did some tests but I was not able to set correctly the wake up of the board via the DS3231 … as a result I have that the board does not go to sleep and continues to run normally at about 21 mA… Anyone can help me?
I’m sure it’s a coding problem and mine is probably completely wrong. The only thing I was able to set is the alarm on the DS3231 (even that i wanted it every 20 mins, but it seem that is possible only every hour) but the part of the code for sending to sleep and waking up the board is probably incorrect… is there an example for the wakeup code?

zoomx
Mon Dec 10, 2018 10:22 am
Have you tested the one that I linked before?

On DS3231 to wake up every 20 minutes you should use the wake up with minute match and set a new alarm adding 20 minutes. As far as I remember (it’s monday morning!) this chip has not an alarm at a fixed time interval so the alarm every hour is made using minute match, since this match happens only one time in a hour.


Leave a Reply

Your email address will not be published. Required fields are marked *