Managing time is a fundamental element of interactive computing. This chapter covers built-in Arduino functions and introduces many additional techniques for handling time delays, time measurement, and real-world times and dates. You’ll learn about Arduino’s built-in function for introducing delays into your sketch, as well as more advanced techniques for intermittently performing operations. Other recipes in this chapter cover how to measure time as it passes, and even how to use an external real-time clock for tracking time and dates.
You want to know how much time has elapsed since an event happened; for example, how long a switch has been held down.
The following sketch uses the millis()
function to print how long a button was pressed (see Recipe 5.2 for details on how to connect the switch).
/* millisDuration sketch returns the number of milliseconds that a button has been pressed */ const int switchPin = 2; // the number of the input pin unsigned long startTime; // the value returned from millis when the switch is pressed unsigned long duration; // variable to store the duration void setup() { pinMode(switchPin, INPUT_PULLUP); Serial.begin(9600); } void loop() { if(digitalRead(switchPin) == LOW) { // here if the switch is pressed startTime = millis(); while(digitalRead(switchPin) == LOW) ; // wait while the switch is still pressed unsigned long duration = millis() - startTime; Serial.println(duration); } }
The millis
function returns the number of milliseconds since the current sketch started running.
The millis
function will overflow (go back to zero) after approximately 50 days. See Recipes 12.4 and 12.5 for information about using the Time library for handling intervals from seconds to years.
By storing the start time for an event, you can determine the duration of the event by subtracting the start time from the current time, as shown here:
unsigned long duration = millis() - startTime;
The Arduino reference for millis
: https://www.arduino.cc/reference/en/language/functions/time/millis/
See Recipes 12.4 and 12.5 for information about using the Time library to handle intervals from seconds to years.
You want your sketch to pause for some period of time. This may be some number of milliseconds, or a time given in seconds, minutes, hours, or days.
The Arduino delay
function is used in many sketches throughout this book. It pauses a sketch for the number of milliseconds specified as a parameter. (There are 1,000 milliseconds in one second.) The sketch that follows shows how you can use delay
to get almost any interval:
/* * delay sketch */ const unsigned long oneSecond = 1000; // a second is a thousand milliseconds const unsigned long oneMinute = oneSecond * 60; const unsigned long oneHour = oneMinute * 60; const unsigned long oneDay = oneHour * 24; void setup() { Serial.begin(9600); while(!Serial); // Needed on Leonardo and ARM-based boards } void loop() { Serial.println("delay for 1 millisecond"); delay(1); Serial.println("delay for 1 second"); delay(oneSecond); Serial.println("delay for 1 minute"); delay(oneMinute); Serial.println("delay for 1 hour"); delay(oneHour); Serial.println("delay for 1 day"); delay(oneDay); Serial.println("Ready to start over"); }
Because it is limited by the maximum value of an integer, the delay
function has a range from one one-thousandth of a second to around 25 days when you use a long integer. If you used an unsigned long, it will reach just under 50 days; see Chapter 2 for more on variable types).
You can use delayMicroseconds
to delay short periods. There are 1,000 microseconds in one millisecond, and 1 million microseconds in one second. delayMicroseconds
will pause from one microsecond to around 16 milliseconds, but for delays longer than a few thousand microseconds you should use delay
instead:
delayMicroseconds(10); // delay for 10 microseconds
delay
and delayMicroseconds
will delay for at least the amount of time given as the parameter, but they could delay a little longer if interrupts occur within the delay time.
The drawback of using the delay
function is that your sketch can’t do anything else during the delay period. You can find an alternative approach in the BlinkWithoutDelay example (File→Examples→02. Digital→BlinkWithoutDelay). This approach uses a variable, previousMillis
, to store the time at which an action was last performed. The sketch then checks the value of millis()
(which is based on an internal clock that ticks once every millisecond the sketch is running). When the difference between the current value of millis()
and previousMillis
reaches or exceeds a given interval, it performs an action, such as blinking an LED. Here is an abbreviated version of that sketch:
int ledState = LOW; unsigned long previousMillis = 0; const long interval = 1000; void setup() { pinMode(LED_BUILTIN, OUTPUT); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; } digitalWrite(LED_BUILTIN, ledState); } // You can perform other actions here. }
Here is a way to package this logic into a function named myDelay
that will delay the code in loop
but can perform some action during the delay period. You can customize the functionality for your application, but in this example, an LED is blinked on or off every 250 milliseconds:
/* * myDelay example sketch to blink an LED for a set amount of time */ const int ledPin = LED_BUILTIN; // the number of the LED pin int ledState = LOW; // ledState used to set the LED unsigned long previousMillis = 0; // will store last time LED was updated void setup() { pinMode(ledPin, OUTPUT); Serial.begin(9600); } void loop() { if (myDelay(blink, 250)) { Serial.println(millis() / 1000.0); // print elapsed time in seconds } } /* * Perform the specified function, return true if it was performed */ bool myDelay(void (*func)(void), long interval) { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; func(); // invoke the function return true; } return false; } void blink() { // if the LED is off turn it on and vice versa: if (ledState == LOW) { ledState = HIGH; } else { ledState = LOW; } digitalWrite(ledPin, ledState); }
The void (*func)(void)
parameter in the definition of myDelay
indicates that the func
argument is a pointer to a void
function that takes no ((void)
) arguments. So, every time func()
is invoked in myDelay
, it’s really calling blink()
. When the interval is reached, myDelay
resets previousMillis
, calls blink
, and returns true.
Arduino style is to avoid the use of pointers in sketches and to reserve those for use in libraries, which avoids confusing beginners who use your sketch. So, another approach is to use a third-party library available from the Library Manager, such as Tasker (https://github.com/joysfera/arduino-tasker). This example blinks LEDs on two pins at different rates, the built-in LED and an LED connected to pin 10. The sketch avoids needing to store the state of each pin in a separate variable by using digitalRead
to determine whether the LED is currently on or off:
/* * Tasker demo sketch */ #define TASKER_MAX_TASKS 2 // Set this to the number of tasks you need #include <Tasker.h> // Declare the Tasker object Tasker tasker; void setup() { pinMode(LED_BUILTIN, OUTPUT); pinMode(10, OUTPUT); // Blink the built-in LED every second tasker.setInterval(blink, 1000, LED_BUILTIN); // Blink the LED on pin 10 twice per second tasker.setInterval(blink, 500, 10); } void loop() { tasker.loop(); // Run any pending tasks } void blink(int pinNumber) { bool ledState = !digitalRead(pinNumber); // Toggle the current pin state if (ledState) { digitalWrite(pinNumber, HIGH); } else { digitalWrite(pinNumber, LOW); } }
The Arduino reference for delay
: http://www.arduino.cc/en/Reference/Delay
You want to determine the duration of a pulse (when a digital signal transitions from low to high back to low again, or high to low back to high) with microsecond accuracy; for example, to measure the exact duration of HIGH
or LOW
pulses on a pin.
The pulseIn
function returns the duration in microseconds for a changing signal on a digital pin. This sketch prints the time in microseconds of the HIGH
and LOW
pulses generated by analogWrite
(see the section on “Analog Output” in Chapter 7). Because the analogWrite
pulses are generated internally by Arduino, no external wiring is required:
/* PulseIn sketch displays duration of high and low pulses from analogWrite */ const int inputPin = 3; // analog output pin to monitor unsigned long val; // this will hold the value from pulseIn void setup() { Serial.begin(9600); } void loop() { analogWrite(inputPin, 128); Serial.print("Writing 128 to pin "); Serial.print(inputPin); printPulseWidth(inputPin); analogWrite(inputPin, 254); Serial.print("Writing 254 to pin "); Serial.print(inputPin); printPulseWidth(inputPin); delay(3000); } void printPulseWidth(int pin) { val = pulseIn(pin, HIGH); Serial.print(": High Pulse width = "); Serial.print(val); val = pulseIn(pin, LOW); Serial.print(", Low Pulse width = "); Serial.println(val); }
The Serial monitor will display :
Writing 128 to pin 3: High Pulse width = 989, Low Pulse width = 997 Writing 254 to pin 3: High Pulse width = 1977, Low Pulse width = 8
pulseIn
can measure how long a pulse is either HIGH
or LOW
:
pulseIn(pin, HIGH); // returns microseconds that pulse is HIGH pulseIn(pin, LOW); // returns microseconds that pulse is LOW
The pulseIn
function waits for the pulse to start (or for a timeout if there is no pulse). By default, it will stop waiting after one second, but you can change that by specifying the time to wait in microseconds as a third parameter (note that 1,000 microseconds equals 1 millisecond):
pulseIn(pin, HIGH, 5000); // wait 5 milliseconds for the pulse to start
The timeout value only matters if the pulse does not start within the given period. Once the start of a pulse is detected, the function will start timing and will not return until the pulse ends.
pulseIn
can measure values between around 10 microseconds to three minutes in duration, but the value of long pulses may not be very accurate.
The Arduino reference for pulseIn
: https://www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/
Recipe 6.5 shows pulseIn
used to measure the pulse width of an ultrasonic distance sensor.
Recipe 18.2 provides more information on using hardware interrupts.
You want to use the time of day (hours, minutes, and seconds) in a sketch, and you don’t want to connect external hardware.
This sketch uses the Time library to display the time of day. The Time library can be installed using the Arduino Library Manager (if you have trouble finding it, try searching the Arduino Library Manager for “timekeeping”).
/* * Time sketch */ #include <TimeLib.h> void setup() { Serial.begin(9600); setTime(12,0,0,1,1,2020); // set time to noon Jan 1 2020 } void loop() { digitalClockDisplay(); delay(1000); } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); }
The Time library enables you to keep track of the date and time. Many Arduino boards use a quartz crystal for timing, and this is accurate to a couple of seconds per day, but it does not have a battery to remember the time when power is switched off. Therefore, time will restart from 0 each time a sketch starts, so you need to set the time using the setTime
function. The sketch sets the time to noon on January 1, 2020 each time it starts.
The Time library uses a standard known as Unix time (also called POSIX time or Epoch time). The values represent the number of elapsed seconds since January 1, 1970. Experienced C programmers may recognize that this is the same as the time_t
used in the ISO standard C library for storing time values.
Of course, it’s more useful to set the time to your current local time instead of a fixed value. The following sketch gets the numerical time value (the number of elapsed seconds since January 1, 1970) from the serial port to set the time. You can enter a value using the Serial Monitor (the current Unix time can be found on a number of websites including https://www.epochconverter.com):
/* * SetTimeSerial sketch * Set the time from the serial port. Simplified version of TimeSerial example * from the Time library. * * Set the time by sending the letter T followed by ten digits indicating * number of seconds since January 1, 1970, for example T1569888000 would * represent 12am on October 1, 2019. */ #include <TimeLib.h> #define TIME_HEADER 'T' // Header tag for serial time sync message void setup() { Serial.begin(9600); Serial.println("Waiting for time sync message"); } void loop(){ if(Serial.available()) { processSyncMessage(); } if(timeStatus() != timeNotSet) { // Display the time and date digitalClockDisplay(); } delay(1000); } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); } // Parse the time message void processSyncMessage() { time_t pctime = 0; if(Serial.find(TIME_HEADER)) { pctime = Serial.parseInt(); setTime(pctime); // Set clock to the time received on serial port } }
The code to display the time and date is the same as before, but now the sketch waits to receive the time from the serial port. See the Discussion in Recipe 4.3 if you are not familiar with how to receive numeric data using the serial port.
A processing sketch named SyncArduinoClock is included with the Time library examples (it’s in the Time/Examples/Processing/SyncArduinoClock
folder). This Processing sketch will send the current time from your computer to Arduino at the click of a mouse. Run SyncArduinoClock in Processing, ensuring that the serial port is the one connected to Arduino (Chapter 4 describes how to run a Processing sketch that talks to Arduino). You should see the message Waiting for time sync message
sent by Arduino and displayed in the Processing text area (the black area for text messages at the bottom of the Processing IDE). Click the Processing application window (it’s a 200-pixel gray square) and you should see the text area display the time as printed by the Arduino sketch.
You can also set the clock from the Serial Monitor if you can get the current Unix time; http://www.epochconverter.com/ is one of many websites that provide the time in this format. Make sure the converter you use is configured for microseconds (a 10-digit value, at least until sometime in 2286); if it is configured for milliseconds the number will 1000 times too large. Copy the 10-digit number indicated as the current Unix time and paste this into the Serial Monitor Send window. Precede the number with the letter T and click Send. For example, if you send this:
T1282041639
Arduino should respond by displaying the time every second:
10:40:49 17 8 2019 10:40:50 17 8 2019 10:40:51 17 8 2019 10:40:52 17 8 2019 10:40:53 17 8 2019 10:40:54 17 8 2019 . . .
You can also set the time using buttons or other input devices such as tilt sensors, a joystick, or a rotary encoder.
The following sketch uses two buttons to move the clock “hands” forward or backward. Figure 12-1 shows the connections (see Recipe 5.2 if you need help using switches):
/* AdjustClockTime sketch buttons on pins 2 and 3 adjust the time */ #include <TimeLib.h> const int btnForward = 2; // button to move time forward const int btnBack = 3; // button to move time back unsigned long prevtime; // when the clock was last displayed void setup() { pinMode(btnForward, INPUT_PULLUP); // enable internal pull-up resistors pinMode(btnBack, INPUT_PULLUP); setTime(12,0,0,1,1,2020); // start with the time set to noon Jan 1 2020 Serial.begin(9600); } void loop() { prevtime = now(); // note the time while( prevtime == now() ) // stay in this loop till the second changes { // check if the set button pressed while waiting for second to roll over if(checkSetTime()) prevtime = now(); // time changed so reset start time } digitalClockDisplay(); } // functions checks to see if the time should be adjusted // returns true if time was changed bool checkSetTime() { int step; // the number of seconds to move (backwards if negative) bool isTimeAdjusted = false; // set to true if the time is adjusted step = 1; // ready to step forwards while(digitalRead(btnForward)== LOW) { adjustTime(step); isTimeAdjusted = true; // to tell the user that the time has changed step = step + 1; // next step will be bigger digitalClockDisplay(); // update clock delay(100); } step = -1; // negative numbers step backwards while(digitalRead(btnBack)== LOW) { adjustTime(step); isTimeAdjusted = true; // to tell the user that the time has changed step = step - 1; // next step will be a bigger negative number digitalClockDisplay(); // update clock delay(100); } return isTimeAdjusted; // tell the user if the time was adjusted } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); }
The sketch uses the same digitalClockDisplay
and printDigits
functions from Recipe 12.3, so copy those prior to running the sketch.
Here is a variation on this sketch that uses the position of a variable resistor to determine the direction and rate of adjustment when a switch is pressed:
#include <TimeLib.h> const int potPin = A0; // pot to determine direction and speed const int buttonPin = 2; // button enables time adjustment unsigned long prevtime; // when the clock was last displayed void setup() { digitalWrite(buttonPin, HIGH); // enable internal pull-up resistors setTime(12,0,0,1,1,2020); // start with the time set to noon Jan 1 2020 Serial.begin(9600); } void loop() { prevtime = now(); // note the time while( prevtime == now() ) // stay in this loop till the second changes { // check if the set button pressed while waiting for second to roll over if(checkSetTime()) prevtime = now(); // time has changed, so reset start time } digitalClockDisplay(); } // functions checks to see if the time should be adjusted // returns true if time was changed bool checkSetTime() { int value; // a value read from the pot int step; // the number of seconds to move (backwards if negative) bool isTimeAdjusted = false; // set to true if the time is adjusted while(digitalRead(buttonPin)== LOW) { // here while button is pressed value = analogRead(potPin); // read the pot value step = map(value, 0,1023, 10, -10); // map value to the desired range if( step != 0) { adjustTime(step); isTimeAdjusted = true; // to tell the user that the time has changed digitalClockDisplay(); // update clock delay(100); } } return isTimeAdjusted; }
The preceding sketch uses the same digitalClockDisplay
and printDigits
functions from Recipe 12.3, so copy those prior to running the sketch. Figure 12-2 shows how the variable resistor and switch are connected. If you are using a 3.3V board that is not 5-volt tolerant, connect the positive side of the variable resistor to 3.3V instead of 5V.
All these examples print to the serial port, but you can print the output to LEDs or LCDs. The download for the Graphical LCD covered in Recipe 11.9 contains example sketches for displaying and setting time using an analog clock display drawn on the LCD.
The Time library includes convenience functions for converting to and from various time formats. For example, you can find out how much time has elapsed since the start of the day and how much time remains until the day’s end.
You can look in TimeLib.h
in the libraries
folder for the complete list. More details are available in Chapter 16:
dayOfWeek( now() ); // the day of the week (Sunday is day 1) elapsedSecsToday( now() ); // returns the number of seconds since the start // of today nextMidnight( now() ); // how much time to the end of the day elapsedSecsThisWeek( now() ); // how much time has elapsed since the start of // the week
You can also print text strings for the days and months; here is a variation on the digital clock display code that prints the names of the day and month:
void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(dayStr(weekday())) + ", " + String(monthShortStr(month())) + " " + String(year()); Serial.println(datestr); }
Time library reference: https://github.com/PaulStoffregen/Time
Wikipedia article on Unix time: http://en.wikipedia.org/wiki/Unix_time
http://www.epochconverter.com/ and http://www.onlineconversion.com/unix_time.htm are two popular Unix time conversion tools.
You want to perform some action on specific days and at specific times of the day.
TimeAlarms is a companion to the Time library discussed in Recipe 12.4. Install the TimeAlarms library using the Arduino Library Manager (and install the Time library also if not already installed). TimeAlarms makes it easy to create time and date alarms:
/* * TimeAlarmsExample sketch * * This example calls alarm functions at 8:30 am and at 5:45 pm (17:45) * and simulates turning lights on at night and off in the morning * * A timer is called every 15 seconds * Another timer is called once only after 10 seconds * * At startup the time is set to Jan 1 2020 8:29 am */ #include <TimeLib.h> #include <TimeAlarms.h> void setup() { Serial.begin(9600); while(!Serial); Serial.println("TimeAlarms Example"); Serial.println("Alarms are triggered daily at 8:30 am and 17:45 pm"); Serial.println("One timer is triggered every 15 seconds"); Serial.println("Another timer is set to trigger only once after 10 seconds"); Serial.println(); setTime(8,29,40,1,1,2020); // set time to 8:29:40am Jan 1 2020 Alarm.alarmRepeat(8,30,0, MorningAlarm); // 8:30am every day Alarm.alarmRepeat(17,45,0,EveningAlarm); // 5:45pm every day Alarm.timerRepeat(15, RepeatTask); // timer for every 15 seconds Alarm.timerOnce(10, OnceOnlyTask); // called once after 10 seconds } void MorningAlarm() { Serial.println("Alarm: - turn lights off"); } void EveningAlarm() { Serial.println("Alarm: - turn lights on"); } void RepeatTask() { Serial.println("15 second timer"); } void OnceOnlyTask() { Serial.println("This timer only triggers once"); } void loop() { digitalClockDisplay(); Alarm.delay(1000); // wait one second between clock display } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); }
You can schedule tasks to trigger at a particular time of day (these are called alarms) or schedule tasks to occur after an interval of time has elapsed (called timers). Each of these tasks can be created to continuously repeat or to occur only once.
To specify an alarm to trigger a task repeatedly at a particular time of day use:
Alarm.alarmRepeat(8,30,0, MorningAlarm);
This calls the function MorningAlarm
at 8:30 a.m. every day.
If you want the alarm to trigger only once, you can use the alarmOnce
method:
Alarm.alarmOnce(8,30,0, MorningAlarm);
This calls the function MorningAlarm
a single time only (the next time it is 8:30 a.m.) and will not trigger again.
Timers trigger tasks that occur after a specified interval of time has passed rather than at a specific time of day. The timer interval can be specified in any number of seconds, or in hour, minutes, and seconds:
Alarm.timerRepeat(15, Repeats); // timer task every 15 seconds
This calls the Repeats
function in your sketch every 15 seconds.
If you want a timer to trigger once only, use the timerOnce
method:
Alarm.timerOnce(10, OnceOnly); // called once after 10 seconds
This calls the onceOnly
function in a sketch 10 seconds after the timer is created.
Your code needs to call Alarm.delay
regularly because this function checks the state of all the scheduled events. Failing to regularly call Alarm.delay
will result in the alarms not being triggered. You can call Alarm.delay(0)
if you need to service the scheduler without a delay. Always use Alarm.delay
instead of delay
when using TimeAlarms in a sketch.
The TimeAlarms library requires the Time library to be installed—see Recipe 12.4. No internal or external hardware is required to use the TimeAlarms library. The scheduler does not use interrupts, so the task-handling function is the same as any other functions you create in your sketch (code in an interrupt handler has restrictions that are discussed in Chapter 18, but these do not apply to TimeAlarms functions).
Timer intervals can range from one second to several years. (If you need timer intervals shorter than one second, the Tasker library may be more suitable; see https://github.com/joysfera/arduino-tasker.)
Tasks are scheduled for specific times designated by the system clock in the Time library (see Recipe 12.4 for more details). If you change the system time (e.g., by calling setTime
), the trigger times are not adjusted. For example, if you use setTime
to move one hour ahead, all alarms and timers will occur one hour sooner. In other words, if it’s 1:00 and a task is set to trigger in two hours (at 3:00), and then you change the current time to 2:00, the task will trigger in one hour. If the system time is set backward—for example, to 12:00—the task will trigger in three hours (i.e., when the system time indicates 3:00). If the time is reset to earlier than the time at which a task was scheduled, the task will be triggered immediately (actually, on the next call to Alarm.delay
).
This is the expected behavior for alarms—tasks are scheduled for a specific time of day and will trigger at that time—but the effect on timers may be less clear. If a timer is scheduled to trigger in five minutes’ time and then the clock is set back by one hour, that timer will not trigger until one hour and five minutes have elapsed (even if it is a repeating timer—a repeat does not get rescheduled until after it triggers).
Up to six alarms and timers can be scheduled to run at the same time. You can modify the library to enable more tasks to be scheduled; Recipe 16.3 shows you how to do this.
onceOnly
alarms and timers are freed when they are triggered, and you can reschedule these as often as you want so long as there are no more than six pending at one time. The following code gives one example of how a timerOnce
task can be rescheduled:
Alarm.timerOnce(random(10), randomTimer); // trigger after random // number of seconds void randomTimer(){ int period = random(2,10); // get a new random period Alarm.timerOnce(period, randomTimer); // trigger for another random period }
You want to use the time of day provided by a real-time clock (RTC) such as the DS1307. External boards usually have battery backup, so the time will be correct even when Arduino is reset or turned off.
The simplest way to use an RTC is with a companion library to the Time library, named DS1307RTC.h
. Install the DS1307RTC library using the Arduino Library Manager (and install the Time library also if not already installed). This recipe is for the widely used DS1307 and DS1337 RTC chips:
/* * TimeRTC sketch * example code illustrating Time library with real-time clock. * */ #include <TimeLib.h> #include <Wire.h> #include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t void setup() { Serial.begin(9600); while(!Serial); // For Leonardo and 32-bit boards setSyncProvider(RTC.get); // the function to get the time from the RTC if(timeStatus()!= timeSet) Serial.println("Unable to sync with the RTC"); else Serial.println("RTC has set the system time"); } void loop() { digitalClockDisplay(); delay(1000); } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); }
Most RTC boards for Arduino use the I2C protocol for communicating (see Chapter 13 for more on I2C). Connect the line marked “SCL” (or “Clock”) to Arduino analog pin 5 and “SDA” (or “Data”) to analog pin 4, as shown in Figure 12-3. (Analog pins 4 and 5 are used for I2C; see Chapter 13). Take care to ensure that you connect the +5V power line and Gnd pins correctly.
The code is similar to other recipes using the Time library, but it gets its value from the RTC rather than from the serial port or hardcoded value. The only additional line needed is this:
setSyncProvider(RTC.get); // the function to get the time from the RTC
The setSyncProvider
function tells the Time library how it should get information for setting (and updating) the time. RTC.get
is a method within the RTC library that returns the current time in the format used by the Time library (Unix time).
Each time Arduino starts, the setup
function will call RTC.get
to set the time from the RTC hardware.
Before you can get the correct time from the module, you need to set its time. Here is a sketch that enables you to set the time on the RTC hardware—you only need to do this when you first attach the battery to the RTC, when replacing the battery, or if the time needs to be changed:
/* * Set RTC time sketch * Set the RTC from the serial port. Simplified version of TimeSerial example * from the Time library. * * Set the time by sending the letter T followed by ten digits indicating * number of seconds since January 1, 1970, for example T1569888000 would * represent 12am on October 1, 2019. */ #include <TimeLib.h> #include <Wire.h> #include <DS1307RTC.h> // a basic DS1307 library that returns time as a time_t void setup() { Serial.begin(9600); setSyncProvider(RTC.get); // the function to get the time from the RTC if(timeStatus()!= timeSet) Serial.println("Unable to sync with the RTC"); else Serial.println("RTC has set the system time"); } void loop() { if(Serial.available()) { processSyncMessage(); } digitalClockDisplay(); delay(1000); } // Pad digits with a leading 0 String padDigits(int digit) { String str = String("0") + digit; // Put a zero in front of the digit return str.substring(str.length() - 2); // Remove all but the last two characters } void digitalClockDisplay(){ String timestr = String(hour()) + ":" + padDigits(minute()) + ":" + padDigits(second()); Serial.println(timestr); String datestr = String(year()) + "-" + padDigits(month()) + "-" + padDigits(day()); Serial.println(datestr); } #define TIME_HEADER 'T' // Header tag for serial time sync message // Parse the time message void processSyncMessage() { time_t pctime = 0; if(Serial.find(TIME_HEADER)) { pctime = Serial.parseInt(); setTime(pctime); // Set clock to the time received on serial port RTC.set(pctime); // Set the RTC too } }
This sketch is almost the same as the TimeSerial sketch in Recipe 12.4 for setting the time from the serial port, but here the RTC.set
function is also called when a time message is received from the computer to set the RTC:
setTime(pctime); // Set clock to the time received on serial port RTC.set(pctime); // Set the RTC too
The RTC chip uses I2C to communicate with Arduino. I2C is explained in Chapter 13.
With the Adafruit_RTCLib library, you can set the RTC time using the compilation time of your sketch with rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
. See https://learn.adafruit.com/ds1307-real-time-clock-breakout-board-kit for more information.
Some of the more recent Arduino boards have RTC capability built in (Zero, MKRZero or MKR1000), and just need a battery backup to be connected to maintain the time when the main board is not powered. See https://www.arduino.cc/en/Reference/RTC for more information.
The SparkFun Real Time Clock Module (BOB-00099).
The Adafruit DS1307 Real Time Clock breakout board (product ID 3296).
18.217.8.82