I am trying to create a low-power climate sensor that will sleep for some time (preferably 1 minute, maybe 5) and wake up to take measurements before going back to sleep. However, I measured my power consumption and the current was 24/25mA even when it should be in "deep sleep". Am I doing something wrong in the code? I have an Arduino Nano Every, RTC DS3231, AHT20 temp sensor, LTR390 light sensor, SD card module, and an anemometer that just creates a voltage output without requiring any power.
Here is my code:
#include <SD.h>
#include "avr/sleep.h"
#include "DS3231.h"
#include <Adafruit_AHTX0.h>
#include "Adafruit_LTR390.h"
#include <Wire.h>
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
Adafruit_AHTX0 aht; //temp and humidity sensor
Adafruit_LTR390 ltr = Adafruit_LTR390(); //light sensor
DS3231 rtc; //real time clock
//Constants
const int anemometerPin = 7; //analog pin 7 (A7)
const int intPin = 2;
const int chipSelect = 10; // set the chip select pin for the SD card
const char DATA_FILENAME[] = "vsdata.txt";
void wakeUp() {
} // This will be used to wake up the Arduino from sleep mode
DateTime now;
void setup() {
delay (500);
Serial.begin(9600); // Begin reading to the serial monitor in Baud 9600
Serial.println(F("Begin measurements"));
pinMode(anemometerPin, INPUT);
pinMode(intPin, INPUT);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
pinMode(chipSelect, OUTPUT);
digitalWrite(chipSelect, HIGH);
if (! aht.begin()) {
Serial.println(F("Could not find AHT? Check wiring")); //Say the AHT is connecting if it's not
while (1) delay(10); //Stpo here if there is an issue
}
Serial.println(F("AHT20 found")); //Let me know if there is an issue with the AHT20
if ( ! ltr.begin()) {
Serial.println(F("Couldn't find LTR sensor!"));
while (1) delay (10);
}
Serial.println(F("Found LTR sensor!"));
ltr.setGain(LTR390_GAIN_3);
Serial.print(F("Gain : "));
switch (ltr.getGain()) {
case LTR390_GAIN_1: Serial.println(F("1")); break;
case LTR390_GAIN_3: Serial.println(F("3")); break;
case LTR390_GAIN_6: Serial.println(F("6")); break;
case LTR390_GAIN_9: Serial.println(F("9")); break;
case LTR390_GAIN_18: Serial.println(F("18")); break;
}
ltr.setResolution(LTR390_RESOLUTION_16BIT);
Serial.println(F("Resolution : "));
switch (ltr.getResolution()) {
case LTR390_RESOLUTION_13BIT: Serial.println(F("13")); break;
case LTR390_RESOLUTION_16BIT: Serial.println(F("16")); break;
case LTR390_RESOLUTION_17BIT: Serial.println(F("17")); break;
case LTR390_RESOLUTION_18BIT: Serial.println(F("18")); break;
case LTR390_RESOLUTION_19BIT: Serial.println(F("19")); break;
case LTR390_RESOLUTION_20BIT: Serial.println(F("20")); break;
}
if (! rtc.begin())
{
Serial.println(F("Couldn't find RTC Module"));
while (1);
}
rtc.begin();
rtc.setDateTime(__DATE__, __TIME__);
rtc.setAlarm1(0, 0, 0, 0, DS3231_MATCH_S);
rtc.enableOutput(false);
if (!SD.begin(chipSelect)) {
Serial.println(F("initialisation failed"));
while (1) delay(10);
}
Serial.println(F("initialisation done"));
}
void loop() {
attachInterrupt(digitalPinToInterrupt(intPin), wakeUp, LOW);
Serial.println(F("sleeping..."));
delay(100); //finish printing
sleep_cpu(); // <= go to sleep
//waking up here when interrupt detected
detachInterrupt(digitalPinToInterrupt(intPin));
rtc.clearAlarm1();
delay(10); //thinking time
File dataFile = SD.open("vsdata", FILE_WRITE); // open file for writing
if(dataFile) {
RTCDateTime dt = rtc.getDateTime();
dataFile.println(rtc.dateFormat("d-m-Y H:i:s", dt));
delay(100);
//Read analog value from anemometer (Voltage value between 0-3.3V on Arduino)
int analogValue = analogRead(anemometerPin);
float voltage = analogValue * 5 / 1023.0; // Convert to volts
float windSpeedMS = voltage * 14.0; //Wind speed in m/s
float windSpeedKMH = voltage * 50.0; //Windspeed in km/s=h
dataFile.print("Wind Speed:");
dataFile.print(windSpeedMS);
dataFile.print(" m/s | ");
dataFile.print(windSpeedKMH);
dataFile.println(" km/h");
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp); //populate temp and humidity objects with fresh data
dataFile.print("Temperature: "); dataFile.print(temp.temperature); dataFile.println(" degrees C");
dataFile.print("Humidity: "); dataFile.print(humidity.relative_humidity); dataFile.println("% rH");
ltr.setMode(LTR390_MODE_UVS);
if (ltr.getMode() == LTR390_MODE_ALS) {
dataFile.println(F("In ALS mode"));
} else {
dataFile.println(F("In UVS mode"));
}
delay(100);
if (ltr.newDataAvailable()) {
dataFile.print(F("UV data: "));
dataFile.println(ltr.readUVS());
}
delay(100);
ltr.setMode(LTR390_MODE_ALS);
if (ltr.getMode() == LTR390_MODE_ALS) {
dataFile.println(F("In ALS mode"));
} else {
dataFile.println(F("In UVS mode"));
}
delay(100);
if (ltr.newDataAvailable()) {
dataFile.print(F("ALS data: "));
dataFile.println(ltr.readALS());
delay(100);
}
dataFile.close();
}
else{
Serial.println(F("error opening vsdata"));
}
}
The first step is to put the "bare" Nano Every (no sensors, no RTC, no SD module, etc.) to deep sleep with the minimal required code, and measure the supply current to verify the sleep state.
Have you successfully done that?
Keep in mind that the USB bridge processor will be drawing current unless it is sleeping or somehow disconnected.
The Nano Every is not a good board for low power use. Its ATmega4809 can sleep, but the board itself has a USB chip, regulator, and LEDs that keep drawing several milliamps even in sleep mode if I remember well. You can lower the current by removing components, but probably not to microamp levels.
You set wakeup interrupt to fire while pin became LOW. But your pin always LOW, so your controller going to sleep and than wake-up immediately.
Did you forget to pull up interrupt pin?
With all that gear, I think it's unlikely that you will be able to get "sleep" current down very much. But since you already have the RTC, assuming it has its own coin cell backup, you might consider the possibility of using it to control power to the entire circuit, switching on the power every 5 minutes, or whatever. You would need a P-channel mosfet and a resistor. Assuming your power supply is 5.5V or less, this circuit would let the DS3231 switch on the power:
The RTC's /INT pin is open drain, active low. So when the alarm time is met, that pin goes low, which turns on the mosfet. The processor will then take and save the reading, set the next alarm time, and then finally send a command to the RTC to clear the alarm flag. That deactivates the /INT pin, and the mosfet turns off.
If your power supply is greater than 5.5V, you would need to add an N-channel mosfet.
The RTC will be running on its coin cell most of the time, but it will run for years that way. And turning off the power completely when nothing is happening would have a very major effect on battery life.
For the devices that don't draw a lot of current, you can try powering them from a GPIO port. Then you can turn them on and off as needed. But something like an SD card draws way more current when writing than a GPIO can supply, so you would need a mosfet that is controlled by a GPIO port. But it looks like everything else could be powered by a GPIO pin. I have done this with a DS3231, and it works fine.
But remember, if you turn off the power to a device, you also need to be sure the GPIOs connected to it are either in INPUT mode or OUTPUT, LOW. If they are INPUT_PULLUP or OUTPUT, HIGH, current will flow out of those GPIO pins into the device that's been turned off, which defeats the purpose of turning them off.
I think you will find that removing all the LEDs will make a big difference. But I suspect the Every will still not sleep very soundly.
Edit: I see on the Every schematic that the power indicator LED, which is on all the time, has a 330R series resistor. It must be awfully bright.
The recommended pattern for placing sleep-related codes inside the loop() function is shown below. This is to prevent the CPU from entering sleep accidentally and becoming unresponsive.
The sleep code I have used is based on interrupts that come from the DS3231 at intervals of one minute. I haven't had any issues with the system taking measurements every one minute and sleeping in between. See the "wake up from sleep using DS3231 interrupts" section of this article: Alarms with Real-Time-Clock DS3231
If the DS3231 RTC is used only to generate a wake-up signal for the CPU, you can omit it to save power and instead use the CPU’s WDT timer to generate a wake-up signal at a 1-minute interval.
I am also using the DS3231 to time stamp each of my sensor readings. Is there a way to record the time of the readings through the Arduino's internal clock? And does the Arduino's clock drift more over the long term than the DS3231?
The Nano R4 has an internal RTC.
Had one on a time test for several months now and it loses about 46 seconds per day.
DS3231 about +/- 3 seconds a month.