I am designing an incubator system and I need to drive a motor forward and reverse every 6 hours. Is there any better choice other than using delay()
? A 6-hours delay seems a little unprofessional.
-
2Use an RTC chip.– MajenkoCommented Dec 13, 2020 at 11:22
-
1Explanation please if you don't mind– henzupCommented Dec 13, 2020 at 11:24
-
1circuitbasics.com/…– MajenkoCommented Dec 13, 2020 at 11:26
-
8> A 6 hours delay seems a little unprofessional. The unprofessional attitude comes from the more important, but ignored questions: - Does it matter when/if the Arduino was restarted/reset while running? - Is it useful to have any feedback about the actual state of the cycle? - Which is the required accuracy for those 6 hours? - Is it useful/required to synchronize that period to some real world clock?– DataFiddlerCommented Dec 13, 2020 at 19:07
-
How accurate do you need the 6 hours to be, and what processor/plaftorm are you using? Many processors will have sleep modes where you can just tell it to sleep for a given time (though in many instances that will look like a reset it wakes up, so you need to save some state and check the boot reason on startup), but precision is often not very good.– jcaronCommented Dec 14, 2020 at 15:06
5 Answers
Several options here, and a couple folks have pointed out some challenges.
"Best" answer -- probably to use a real-time clock (RTC) board to assist your timing, and effectively set a target time (next motor run is at 23:14...) each time the cycle restarts. By saving this target off (to the EEPROM or an SD card for instance), you'd protect against a potential shutdown and reset of the arduino in the middle.
Secondary answer; set your loop() to include a relatively short delay() statement, then check the elapsed time since the last motor engagement. This would allow you to have other items processing, as someone else noted in this thread. This I've pseudocoded below.
unsigned long lastMotorRunTime;
void loop()
{
/*
* do whatever
*/
if(millis()-lastMotorRunTime > 21600000) // 6 hours have passed
{
runTheMotor(); // placeholder for the motor movements you need
lastMotorRunTime = millis();
}
/*
* do other stuff
*/
delay(100); // loop about every 1/10 second, or whatever works
}
Given you don't seem to need detailed precision -- a few seconds either way wouldn't matter -- this should work reasonably. Note it WILL fail if the Ardiuno is reset -- then it will only (in my pseudocode) rotate the motor 6 hours after the last Arduino reset. Probably better to assume that an arduino reset means move the motor 'now', if the point is to ensure periodic motion rather than precise timing.
For precise timing, as noted, the RTC would be required.
(Updated: Changed lastMotorRunTime to unsigned long)
-
3Should be
unsigned long lastMotorRunTime
to avoid issue with rollover; see arduino.stackexchange.com/questions/12587/…– MarkUCommented Dec 14, 2020 at 1:27 -
2
-
@EdgarBonet delay is probably useless, but it points out the idea, and can be replaced with something more useful like: arduino.cc/en/Reference/LowPowerSleep Commented Dec 14, 2020 at 9:58
-
1Re “it points out the idea, and can be replaced with something more useful”: The comment
do other stuff
perfectly fills this role. Commented Dec 14, 2020 at 12:42 -
2You will also want to incorporate some sort of state mechanism and/or a positional readout to ensure that the motor ran the appropriate time. I've worked on encoders that jittered and caused 'more motion' resulting in time turned off too soon. Power failures, intermittent glitches or spikes- all can wreck movement. There are lots of ideas here and you're showing great pre-planning to solve them.– J.HirschCommented Dec 14, 2020 at 21:54
Since this is for an incubator, I suspect that you don't need high precision. So delay() will work perfectly well, if you don't want to do anything else in the meantime (like maybe monitor temperature, control a heater, and/or light LEDs for over/under temperature). In that case you could use a loop that handled all those operations at some convenient rate like once per second or once per minute, using delay() in the loop. (I assume that your incubator is slow to respond to the heater, so slow sampling will be fine.) Then let the loop run for 6 hours, which you can determine by simple count of the 1-minute (or whatever) loop delays. And if you find that your 6-hour time is consistently high or low, adjust the count limit to compensate.
Using delay()
for a 6-hour delay is a bit awkward, but perfectly doable. It will, however, not be very accurate, because the accuracy depends on the accuracy of the Arduino's clock, and it will block all other code you may wish to run.
Using timing with millis()
would allow your Arduino to do other things while waiting. It will be just as inaccurate, though.
For long periods, and especially if you want things to be more accurate, you could use an external Real Time Clock, like the DS3231. You can get these as modules on break-out boards, and libraries are also available.
These RTCs have a backup battery, so they also keep running a while without external power, and will also keep time when the Arduino resets. Most also have settable alarm times you could use.
For your application, I would also implement a simple watchdog timer to make sure the Arduino is still waiting and working, and hasn't crashed. Note that a watchdog timer won't play nice with delay()
; a simple 6-hour delay will always trigger it, and timing with millis()
or an external RTC is the way to go here.
-
2Some of these modules support the alarm function. Let your code set the time when compiling then forget about it. LOOP: When starting the cycle read the clock and calculate the alarm time needed for reversal and set it. When the alarm triggers reverse the motor and repeat the LOOP. If you put the battery in the module this will keep time even if the power fails.– GilCommented Dec 13, 2020 at 20:04
I have an automatic watering system that (among other things) trigger on time. I use the excellent Timer Library for this. It allows several useful timed interactions such as Pulse
or Oscillate
in addition to After
which would solve your issue - or Every
if you want a recurring 6 hour code block to execute
In the end - behind the screen - it is just as unprofessional as millis()
- the library itself uses millis()
and a scheduler. But the code looks cleaner and is easier to write and update.
There's nothing wrong with using delay here.
There is only a problem if you want to make the Arduino do something else as well. I that case you'd want to use millis
, and use that to calculate how much time has passed (see blink without delay).