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.
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;
* 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)
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.
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.
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
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).