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);
}
}
[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
#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);
}
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.
[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.
It is better to use the official releases instead of the zip
unfortunately nothing changed… same error!
See:
https://github.com/stm32duino/Arduino_C … ng-started
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
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.
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()
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);
}
}
[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.
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.
#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);
}
}
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.
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)
[fpiSTM – Wed Nov 28, 2018 4:14 pm] –
Her an example how to use the external wakeup
https://github.com/stm32duino/STM32LowP … up.ino#L32I 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 ");
}
[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.
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?
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.