I am starting to program an environmental datalogger system with multiple sensors + {RTC + SD-card shield} , where the interval between reading the sensors will be several hours; most likely 2 or 4 times per 24-hour period.
The intention is to base the project on an Arduino R4 WiFi. At some point, I may wish to add LoRa (probably 915 MHz) capability
It is desired that this datalogger system be capable of running reliably in a remote location for months at a time. Conserving battery power by putting the microprocessor into "sleep" or "hibernate" mode during the long intervals between readings would seem to be a good idea!
I have been reading that one should avoid the use of delay() because it causes a break in code execution. Looking forward, I may elect to have each of the 4 sensors reading at different intervals; avoiding breaks then sounds like a good idea.
The alternatives to using delay() I have found mention of, so far, include:
use of millis();
use of hardware counters (i.e., those built into the microprocessor);
using timer interrupts; and finally,
a rather vague suggestion about writing code that would rely on comparing readings from the RTC to trigger the reading-and-recording of sensor values. But that suggestion was so vague as to be useless.
Would anybody care to suggest an "ideal" direction to proceed in, before I get into the nitty-gritty ?
You are not going to be using delay(), millis, or any other type of timing that requires a running processor over that period of time. Obvious option is to use the alarm output of an RTC to wake the processor from the sleep state.
Do you need WiFi? That consumes a large amount of power.
The SD card also will consume considerable power, you might want to buffer the data to another type of storage, such as FRAM memory, and only periodically write to the SD card.
If saving power is essential rather than a nice to have, then you are going to need to put the board into deep sleep mode. In this mode loop() stops executing until the RTC interval expires, and then it wakes and restarts where it left off.
This means you don't need to have an external wake source such as an interrupt from your sensors - it's done purely on time. RAM contents are preserved during sleep so you can set and read flags like "last time I was awake I did X".
This could work for you if your intervals to read the sensors are defineable and repeatable.
The Arduino MKR 1310 has a deep sleep consumption of ~110 micro amps and will run for many weeks on a 3.7V LiPo, without the need for butchering it by removing components. It also has onboard LoRa radio. I use it for data logging a remote water meter. Works very well.
You cannot use either delay() or millis() to achieve low power sleep mode. Other functions are required which will be specific to the type of Arduino. I'm not familiar with Uno R4 and don't know what they would be.
A hardware counter would need some external clock signal for it to count. This could be provided by the 1Hz output of an RTC module, for example.
Timer interrupts (from the Arduino's internal hardware timers) can probably wake the Arduino from sleep mode. However, there may be a limit to how long the timed period can be, which means your code will need to sleep multiple times until the correct time for the next sensor reading.
Some RTC modules have alarm timers that can be set to send an interrupt signal to the Arduino to wake it from deep sleep at a set time.
you need to experiment to see how well the Arduino R4 WiFi sleeps, e.g.
can you put the Renesas RA4M1 microcontroller to sleep
can you put the ESP32-S3 radio microcontroller to sleep
are there other modules on board which need to sleep?
you may find that even when all the onboard devices are sleeping the onboard power supply may still draw significant current
EDIT: if you are considering LoRa it could be worth considering a LoRa development board with an onboard microcontroller which supports deep sleep modes, e.g. heltec wifi-lora-ESP32S3-v4/
see ESP32-S3 sleep modes
If you were to use the Arduino MKRWAN 1310 because of its LoRa radio and deep sleep / ultra-low current consumption, you could take advantage of the fact that it also has a built-in RTC peripheral.
The ArduinoLowPower library integrates the RTC with deep sleep mode. The maximum sleep time with this library is about 71 minutes, but you can chain this indefinitely in a for loop for much longer intervals.
Flashing an LED, putting the device to low consumption / deep sleep for a minute, waking it and repeating is literally as easy as this:
The longest INTERVAL using 32-bit time variables is 49 days and a fraction but Proper Use Of Unsigned Subtraction Completely Negates ANY ROLLOVER PROBLEMS.
If you want a longer interval, use 64 bit unsigned long long variables. [edit:add] Then you can time for literally billions of years if you had an immortal project, otherwise possibly decades long intervals on a future museum piece project.
The need for an RTC with an Uno or other board using a resonator (cheaper than a crystal and 2 capacitors) is that the resonator can lose minutes a day running slow. Depending on what you're doing, that may not matter at all.
The Arduino forum archives hold a number of examples of doing that. For a while back over 10 years ago the fad was packing data collectors in Altoid tins and some of those did run for months. Those had standalone AVR chips wired in, a board takes way more room! Yes, Arduino supports standalone chip projects.
If you don't write much per hour or day, a quick burst to SD won't take 1 whole second. How often do you need sampling? Can you buffer the data in RAM, write directly to the 512B (default) SD buffer and write when it's full.
Some projects included solar cells and had to get into power management.
You need to get rid of everything you do not need.
A board with 2 CPUs is at least questionable.
Need an onboard debugger? How about an USB-Serial chip?
A power LED?
Also some experimentation is required:
What needs more power? Turn on/off the SD or let it idle for a few seconds
When does the SD its housekeeping?
...
Have you looked at the STM32WL?
Your outer loop might look something like this:
Calculate when to run the next activity
Set alarm
Sleep
Wake up
Run activity
Rinse and repeat
With delay() you are busy watching grass grow at full speed. With millis() it is easier to build a cooperative scheduling. Take a look at the example "blink without delay".
And you could also catch the rollover and add another uint32_t to count the rollovers...
I think there will be no rollover of a 64 bit counter before the end of times.
Rtc may still be a good idea as the millis counters are not very accurate... if you schedule a measurement every day at 12:00 the event will drift away by minutes per day (depending on temperature and god knows what else).
Rtc can also be used to wake up controller from sleep mode...
And rtc will keep time in case of power failure...
Do the time math as all unsigned subtractions and THERE IS NO EFFING OVERFLOW!
Unsigned: elapsed time = end time - start time....
that WORKS EVERY TIME!
Big concern is are your unsigned time variables big enough to hold the longest Interval you would time. After that, for speed we want unsigned time variables that aren't so big that they have extra bytes to process:
With 32-bits you can time millis to 49.71-some Days or with micros a bit over 71 Minutes.
With 16-bits you can time millis to 65.535 Seconds or with micros you can time 65.535 millis --- twice as fast as using 32-bit values.
With 8 bits you can time millis but watch out for how Arduino skips 6 values in those 8 bits to get millis +/- 1 ms. IF you need precision, use micros, they work to the next 4 usecs which is 250x smaller than ms.
If this is new to you, it's going to take practice to get easy cause the code techniques are not like "common wisdom".
A video/article should drop today over at Dronebot Workshop. Jumping to the big reveal, an esp32 in hibernate mode draws 2.5uA, in deep sleep 25uA. I have had an UNO R4 under RTC test for months and it's cloc is very inaccurtae but probably good enough for your use case. You may benefit by looking at the SEEED Studio XIAO line of thumb nail sized ESP32 with dataloger, LORA, and more add-ons.