From cb7bd533cb2b505441ca9a35c9897db358a30b47 Mon Sep 17 00:00:00 2001 From: Mike Beattie Date: Tue, 20 Mar 2012 23:25:17 +1300 Subject: Add Time library Signed-off-by: Mike Beattie --- DS1307RTC/DS1307RTC.cpp | 124 +++++++++ DS1307RTC/DS1307RTC.h | 31 +++ DS1307RTC/keywords.txt | 22 ++ DS1307RTC/readme.txt | 7 + Time/DateStrings.cpp | 80 ++++++ .../SyncArduinoClock/SyncArduinoClock.pde | 70 +++++ .../Processing/SyncArduinoClock/readme.txt | 9 + Time/Examples/TimeGPS/TimeGPS.pde | 82 ++++++ Time/Examples/TimeNTP/TimeNTP.pde | 120 ++++++++ Time/Examples/TimeRTC/TimeRTC.pde | 47 ++++ Time/Examples/TimeRTCLog/TimeRTCLog.pde | 106 +++++++ Time/Examples/TimeRTCSet/TimeRTCSet.pde | 82 ++++++ Time/Examples/TimeSerial/TimeSerial.pde | 82 ++++++ .../TimeSerialDateStrings.pde | 80 ++++++ Time/Readme.txt | 131 +++++++++ Time/Time.cpp | 307 +++++++++++++++++++++ Time/Time.h | 126 +++++++++ Time/keywords.txt | 33 +++ .../Examples/TimeAlarmExample/TimeAlarmExample.pde | 77 ++++++ TimeAlarms/TimeAlarms.cpp | 150 ++++++++++ TimeAlarms/TimeAlarms.h | 127 +++++++++ TimeAlarms/keywords.txt | 25 ++ TimeAlarms/readme.txt | 220 +++++++++++++++ 23 files changed, 2138 insertions(+) create mode 100644 DS1307RTC/DS1307RTC.cpp create mode 100644 DS1307RTC/DS1307RTC.h create mode 100644 DS1307RTC/keywords.txt create mode 100644 DS1307RTC/readme.txt create mode 100644 Time/DateStrings.cpp create mode 100644 Time/Examples/Processing/SyncArduinoClock/SyncArduinoClock.pde create mode 100644 Time/Examples/Processing/SyncArduinoClock/readme.txt create mode 100644 Time/Examples/TimeGPS/TimeGPS.pde create mode 100644 Time/Examples/TimeNTP/TimeNTP.pde create mode 100644 Time/Examples/TimeRTC/TimeRTC.pde create mode 100644 Time/Examples/TimeRTCLog/TimeRTCLog.pde create mode 100644 Time/Examples/TimeRTCSet/TimeRTCSet.pde create mode 100644 Time/Examples/TimeSerial/TimeSerial.pde create mode 100644 Time/Examples/TimeSerialDateStrings/TimeSerialDateStrings.pde create mode 100644 Time/Readme.txt create mode 100644 Time/Time.cpp create mode 100644 Time/Time.h create mode 100644 Time/keywords.txt create mode 100644 TimeAlarms/Examples/TimeAlarmExample/TimeAlarmExample.pde create mode 100644 TimeAlarms/TimeAlarms.cpp create mode 100644 TimeAlarms/TimeAlarms.h create mode 100644 TimeAlarms/keywords.txt create mode 100644 TimeAlarms/readme.txt diff --git a/DS1307RTC/DS1307RTC.cpp b/DS1307RTC/DS1307RTC.cpp new file mode 100644 index 0000000..bf0b46e --- /dev/null +++ b/DS1307RTC/DS1307RTC.cpp @@ -0,0 +1,124 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + + Copyright (c) Michael Margolis 2009 + This library is intended to be uses with Arduino Time.h library functions + + The library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + 30 Dec 2009 - Initial release + 5 Sep 2011 updated for Arduino 1.0 + */ + +#include +#include "DS1307RTC.h" + +#define DS1307_CTRL_ID 0x68 + +DS1307RTC::DS1307RTC() +{ + Wire.begin(); +} + +// PUBLIC FUNCTIONS +time_t DS1307RTC::get() // Aquire data from buffer and convert to time_t +{ + tmElements_t tm; + read(tm); + return(makeTime(tm)); +} + +void DS1307RTC::set(time_t t) +{ + tmElements_t tm; + breakTime(t, tm); + tm.Second |= 0x80; // stop the clock + write(tm); + tm.Second &= 0x7f; // start the clock + write(tm); +} + +// Aquire data from the RTC chip in BCD format +void DS1307RTC::read( tmElements_t &tm) +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); +#else + Wire.send(0x00); +#endif + Wire.endTransmission(); + + // request the 7 data fields (secs, min, hr, dow, date, mth, yr) + Wire.requestFrom(DS1307_CTRL_ID, tmNbrFields); +#if ARDUINO >= 100 + tm.Second = bcd2dec(Wire.read() & 0x7f); + tm.Minute = bcd2dec(Wire.read() ); + tm.Hour = bcd2dec(Wire.read() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.read() ); + tm.Day = bcd2dec(Wire.read() ); + tm.Month = bcd2dec(Wire.read() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.read()))); +#else + tm.Second = bcd2dec(Wire.receive() & 0x7f); + tm.Minute = bcd2dec(Wire.receive() ); + tm.Hour = bcd2dec(Wire.receive() & 0x3f); // mask assumes 24hr clock + tm.Wday = bcd2dec(Wire.receive() ); + tm.Day = bcd2dec(Wire.receive() ); + tm.Month = bcd2dec(Wire.receive() ); + tm.Year = y2kYearToTm((bcd2dec(Wire.receive()))); +#endif +} + +void DS1307RTC::write(tmElements_t &tm) +{ + Wire.beginTransmission(DS1307_CTRL_ID); +#if ARDUINO >= 100 + Wire.write((uint8_t)0x00); // reset register pointer + Wire.write(dec2bcd(tm.Second)) ; + Wire.write(dec2bcd(tm.Minute)); + Wire.write(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.write(dec2bcd(tm.Wday)); + Wire.write(dec2bcd(tm.Day)); + Wire.write(dec2bcd(tm.Month)); + Wire.write(dec2bcd(tmYearToY2k(tm.Year))); +#else + Wire.send(0x00); // reset register pointer + Wire.send(dec2bcd(tm.Second)) ; + Wire.send(dec2bcd(tm.Minute)); + Wire.send(dec2bcd(tm.Hour)); // sets 24 hour format + Wire.send(dec2bcd(tm.Wday)); + Wire.send(dec2bcd(tm.Day)); + Wire.send(dec2bcd(tm.Month)); + Wire.send(dec2bcd(tmYearToY2k(tm.Year))); +#endif + Wire.endTransmission(); +} +// PRIVATE FUNCTIONS + +// Convert Decimal to Binary Coded Decimal (BCD) +uint8_t DS1307RTC::dec2bcd(uint8_t num) +{ + return ((num/10 * 16) + (num % 10)); +} + +// Convert Binary Coded Decimal (BCD) to Decimal +uint8_t DS1307RTC::bcd2dec(uint8_t num) +{ + return ((num/16 * 10) + (num % 16)); +} + +DS1307RTC RTC = DS1307RTC(); // create an instance for the user + diff --git a/DS1307RTC/DS1307RTC.h b/DS1307RTC/DS1307RTC.h new file mode 100644 index 0000000..e3a1bc1 --- /dev/null +++ b/DS1307RTC/DS1307RTC.h @@ -0,0 +1,31 @@ +/* + * DS1307RTC.h - library for DS1307 RTC + * This library is intended to be uses with Arduino Time.h library functions + */ + +#ifndef DS1307RTC_h +#define DS1307RTC_h + +#include + +// library interface description +class DS1307RTC +{ + // user-accessible "public" interface + public: + DS1307RTC(); + static time_t get(); + static void set(time_t t); + static void read(tmElements_t &tm); + static void write(tmElements_t &tm); + + private: + static uint8_t dec2bcd(uint8_t num); + static uint8_t bcd2dec(uint8_t num); +}; + +extern DS1307RTC RTC; + +#endif + + diff --git a/DS1307RTC/keywords.txt b/DS1307RTC/keywords.txt new file mode 100644 index 0000000..be96f1e --- /dev/null +++ b/DS1307RTC/keywords.txt @@ -0,0 +1,22 @@ +####################################### +# Syntax Coloring Map For DS1307RTC +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +get KEYWORD2 +set KEYWORD2 +read KEYWORD2 +write KEYWORD2 +####################################### +# Instances (KEYWORD2) +####################################### +RTC +####################################### +# Constants (LITERAL1) +####################################### diff --git a/DS1307RTC/readme.txt b/DS1307RTC/readme.txt new file mode 100644 index 0000000..5978295 --- /dev/null +++ b/DS1307RTC/readme.txt @@ -0,0 +1,7 @@ +Readme file for DS1307RTC Library + +The DS1307RTC library is provided to demonstrate the Arduino Time library. + +See the TimeRTC example sketches privided with the Time library download for usage + + diff --git a/Time/DateStrings.cpp b/Time/DateStrings.cpp new file mode 100644 index 0000000..7610c8f --- /dev/null +++ b/Time/DateStrings.cpp @@ -0,0 +1,80 @@ +/* DateStrings.cpp + * Definitions for date strings for use with the Time library + * + * No memory is consumed in the sketch if your code does not call any of the string methods + * You can change the text of the strings, make sure the short strings are each exactly 3 characters + * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in Time.h + * + */ + +#include +#include "Time.h" + +// the short strings for each day or month must be exactly dt_SHORT_STR_LEN +#define dt_SHORT_STR_LEN 3 // the length of short strings + +static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null + +char monthStr1[] PROGMEM = "January"; +char monthStr2[] PROGMEM = "February"; +char monthStr3[] PROGMEM = "March"; +char monthStr4[] PROGMEM = "April"; +char monthStr5[] PROGMEM = "May"; +char monthStr6[] PROGMEM = "June"; +char monthStr7[] PROGMEM = "July"; +char monthStr8[] PROGMEM = "August"; +char monthStr9[] PROGMEM = "September"; +char monthStr10[] PROGMEM = "October"; +char monthStr11[] PROGMEM = "November"; +char monthStr12[] PROGMEM = "December"; + +PGM_P monthNames_P[] PROGMEM = +{ + "",monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6, + monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12 +}; + +char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec"; + +char dayStr0[] PROGMEM = "Err"; +char dayStr1[] PROGMEM = "Sunday"; +char dayStr2[] PROGMEM = "Monday"; +char dayStr3[] PROGMEM = "Tuesday"; +char dayStr4[] PROGMEM = "Wednesday"; +char dayStr5[] PROGMEM = "Thursday"; +char dayStr6[] PROGMEM = "Friday"; +char dayStr7[] PROGMEM = "Saturday"; + +PGM_P dayNames_P[] PROGMEM = { dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7}; +char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThrFriSat"; + +/* functions to return date strings */ + +char* monthStr(uint8_t month) +{ + strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month]))); + return buffer; +} + +char* monthShortStr(uint8_t month) +{ + for (int i=0; i < dt_SHORT_STR_LEN; i++) + buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)])); + buffer[dt_SHORT_STR_LEN] = 0; + return buffer; +} + +char* dayStr(uint8_t day) +{ + strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day]))); + return buffer; +} + +char* dayShortStr(uint8_t day) +{ + uint8_t index = day*dt_SHORT_STR_LEN; + for (int i=0; i < dt_SHORT_STR_LEN; i++) + buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i])); + buffer[dt_SHORT_STR_LEN] = 0; + return buffer; +} diff --git a/Time/Examples/Processing/SyncArduinoClock/SyncArduinoClock.pde b/Time/Examples/Processing/SyncArduinoClock/SyncArduinoClock.pde new file mode 100644 index 0000000..4c74d4b --- /dev/null +++ b/Time/Examples/Processing/SyncArduinoClock/SyncArduinoClock.pde @@ -0,0 +1,70 @@ +/** + * SyncArduinoClock. + * + * portIndex must be set to the port connected to the Arduino + * + * The current time is sent in response to request message from Arduino + * or by clicking the display window + * + * The time message is 11 ASCII text characters; a header (the letter 'T') + * followed by the ten digit system time (unix time) + */ + + +import processing.serial.*; + +public static final short portIndex = 1; // select the com port, 0 is the first port +public static final char TIME_HEADER = 'T'; //header byte for arduino serial time message +public static final char TIME_REQUEST = 7; // ASCII bell character +public static final char LF = 10; // ASCII linefeed +public static final char CR = 13; // ASCII linefeed +Serial myPort; // Create object from Serial class + +void setup() { + size(200, 200); + println(Serial.list()); + println(" Connecting to -> " + Serial.list()[portIndex]); + myPort = new Serial(this,Serial.list()[portIndex], 9600); +} + +void draw() +{ + if ( myPort.available() > 0) { // If data is available, + char val = char(myPort.read()); // read it and store it in val + if(val == TIME_REQUEST){ + long t = getTimeNow(); + sendTimeMessage(TIME_HEADER, t); + } + else + { + if(val == LF) + ; //igonore + else if(val == CR) + println(); + else + print(val); // echo everying but time request + } + } +} + +void mousePressed() { + sendTimeMessage( TIME_HEADER, getTimeNow()); +} + + +void sendTimeMessage(char header, long time) { + String timeStr = String.valueOf(time); + myPort.write(header); // send header and time to arduino + myPort.write(timeStr); +} + +long getTimeNow(){ + // java time is in ms, we want secs + GregorianCalendar cal = new GregorianCalendar(); + cal.setTime(new Date()); + int tzo = cal.get(Calendar.ZONE_OFFSET); + int dst = cal.get(Calendar.DST_OFFSET); + long now = (cal.getTimeInMillis() / 1000) ; + now = now + (tzo/1000) + (dst/1000); + return now; +} diff --git a/Time/Examples/Processing/SyncArduinoClock/readme.txt b/Time/Examples/Processing/SyncArduinoClock/readme.txt new file mode 100644 index 0000000..985bd80 --- /dev/null +++ b/Time/Examples/Processing/SyncArduinoClock/readme.txt @@ -0,0 +1,9 @@ +SyncArduinoClock is a Processing sketch that responds to Arduino requests for +time synchronization messages. + +The portIndex must be set the Serial port connected to Arduino. + +Download TimeSerial.pde onto Arduino and you should see the time +message displayed when you run SyncArduinoClock in Processing. +The Arduino time is set from the time on your computer through the +Processing sketch. \ No newline at end of file diff --git a/Time/Examples/TimeGPS/TimeGPS.pde b/Time/Examples/TimeGPS/TimeGPS.pde new file mode 100644 index 0000000..1c7b25e --- /dev/null +++ b/Time/Examples/TimeGPS/TimeGPS.pde @@ -0,0 +1,82 @@ +/* + * TimeGPS.pde + * example code illustrating time synced from a GPS + * + */ + +#include +#include //http://arduiniana.org/libraries/TinyGPS/ +#include //http://arduiniana.org/libraries/newsoftserial/ +// GPS and NewSoftSerial libraries are the work of Mikal Hart + +TinyGPS gps; +NewSoftSerial serial_gps = NewSoftSerial(3, 2); // receive on pin 3 + +const int offset = 1; // offset hours from gps time (UTC) +time_t prevDisplay = 0; // when the digital clock was displayed + +void setup() +{ + Serial.begin(9600); + serial_gps.begin(4800); + Serial.println("Waiting for GPS time ... "); + setSyncProvider(gpsTimeSync); +} + +void loop() +{ + while (serial_gps.available()) + { + gps.encode(serial_gps.read()); // process gps messages + } + if(timeStatus()!= timeNotSet) + { + if( now() != prevDisplay) //update the display only if the time has changed + { + prevDisplay = now(); + digitalClockDisplay(); + } + } +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +time_t gpsTimeSync(){ + // returns time if avail from gps, else returns 0 + unsigned long fix_age = 0 ; + gps.get_datetime(NULL,NULL, &fix_age); + unsigned long time_since_last_fix; + if(fix_age < 1000) + return gpsTimeToArduinoTime(); // return time only if updated recently by gps + return 0; +} + +time_t gpsTimeToArduinoTime(){ + // returns time_t from gps date and time with the given offset hours + tmElements_t tm; + int year; + gps.crack_datetime(&year, &tm.Month, &tm.Day, &tm.Hour, &tm.Minute, &tm.Second, NULL, NULL); + tm.Year = year - 1970; + time_t time = makeTime(tm); + return time + (offset * SECS_PER_HOUR); +} diff --git a/Time/Examples/TimeNTP/TimeNTP.pde b/Time/Examples/TimeNTP/TimeNTP.pde new file mode 100644 index 0000000..11927c6 --- /dev/null +++ b/Time/Examples/TimeNTP/TimeNTP.pde @@ -0,0 +1,120 @@ +/* + * Time_NTP.pde + * Example showing time sync to NTP time source + * + * This sketch uses the Ethenet library with the user contributed UdpBytewise extension + */ + +#include +#include +#include // UDP library from: bjoern@cs.stanford.edu 12/30/2008 +#if UDP_TX_PACKET_MAX_SIZE <64 || UDP_RX_PACKET_MAX_SIZE < 64 +#error : UDP packet size to small - modify UdpBytewise.h to set buffers to 64 bytes +#endif +/* + * + * You may need to modify the UdpBytewise.h library to allow enough space in the buffers for the NTP packets. + * Open up UdpBytewse.h and set the following buffers to 64 bytes: + * #define UDP_TX_PACKET_MAX_SIZE 64 + * #define UDP_RX_PACKET_MAX_SIZE 64 + */ + + +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +byte ip[] = { 192, 168, 1, 44 }; // set the IP address to an unused address on your network + +byte SNTP_server_IP[] = { 192, 43, 244, 18}; // time.nist.gov +//byte SNTP_server_IP[] = { 130,149,17,21}; // ntps1-0.cs.tu-berlin.de +//byte SNTP_server_IP[] = { 192,53,103,108}; // ptbtime1.ptb.de + + +time_t prevDisplay = 0; // when the digital clock was displayed +const long timeZoneOffset = 0L; // set this to the offset in seconds to your local time; + +void setup() +{ + Serial.begin(9600); + Ethernet.begin(mac,ip); + Serial.println("waiting for sync"); + setSyncProvider(getNtpTime); + while(timeStatus()== timeNotSet) + ; // wait until the time is set by the sync provider +} + +void loop() +{ + if( now() != prevDisplay) //update the display only if the time has changed + { + prevDisplay = now(); + digitalClockDisplay(); + } +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +/*-------- NTP code ----------*/ + +unsigned long getNtpTime() +{ + sendNTPpacket(SNTP_server_IP); + delay(1000); + if ( UdpBytewise.available() ) { + for(int i=0; i < 40; i++) + UdpBytewise.read(); // ignore every field except the time + const unsigned long seventy_years = 2208988800UL + timeZoneOffset; + return getUlong() - seventy_years; + } + return 0; // return 0 if unable to get the time +} + +unsigned long sendNTPpacket(byte *address) +{ + UdpBytewise.begin(123); + UdpBytewise.beginPacket(address, 123); + UdpBytewise.write(B11100011); // LI, Version, Mode + UdpBytewise.write(0); // Stratum + UdpBytewise.write(6); // Polling Interval + UdpBytewise.write(0xEC); // Peer Clock Precision + write_n(0, 8); // Root Delay & Root Dispersion + UdpBytewise.write(49); + UdpBytewise.write(0x4E); + UdpBytewise.write(49); + UdpBytewise.write(52); + write_n(0, 32); //Reference and time stamps + UdpBytewise.endPacket(); +} + +unsigned long getUlong() +{ + unsigned long ulong = (unsigned long)UdpBytewise.read() << 24; + ulong |= (unsigned long)UdpBytewise.read() << 16; + ulong |= (unsigned long)UdpBytewise.read() << 8; + ulong |= (unsigned long)UdpBytewise.read(); + return ulong; +} + +void write_n(int what, int how_many) +{ + for( int i = 0; i < how_many; i++ ) + UdpBytewise.write(what); +} diff --git a/Time/Examples/TimeRTC/TimeRTC.pde b/Time/Examples/TimeRTC/TimeRTC.pde new file mode 100644 index 0000000..1a1ed80 --- /dev/null +++ b/Time/Examples/TimeRTC/TimeRTC.pde @@ -0,0 +1,47 @@ +/* + * TimeRTC.pde + * example code illustrating Time library with Real Time Clock. + * + */ + +#include +#include +#include // 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() +{ + digitalClockDisplay(); + delay(1000); +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + diff --git a/Time/Examples/TimeRTCLog/TimeRTCLog.pde b/Time/Examples/TimeRTCLog/TimeRTCLog.pde new file mode 100644 index 0000000..76ed17d --- /dev/null +++ b/Time/Examples/TimeRTCLog/TimeRTCLog.pde @@ -0,0 +1,106 @@ +/* + * TimeRTCLogger.pde + * example code illustrating adding and subtracting Time. + * + * this sketch logs pin state change events + * the time of the event and time since the previous event is calculated and sent to the serial port. + */ + +#include +#include +#include // a basic DS1307 library that returns time as a time_t + +const int nbrInputPins = 6; // monitor 6 digital pins +const int inputPins[nbrInputPins] = {2,3,4,5,6,7}; // pins to monitor +boolean state[nbrInputPins] ; // the state of the monitored pins +time_t prevEventTime[nbrInputPins] ; // the time of the previous event + +void setup() { + Serial.begin(9600); + setSyncProvider(RTC.get); // the function to sync the time from the RTC + for(int i=0; i < nbrInputPins; i++){ + pinMode( inputPins[i], INPUT); + // digitalWrite( inputPins[i], HIGH); // uncomment these lines if + // state[i] = HIGH; // pull-up resistors are wanted + } +} + +void loop() +{ + for(int i=0; i < nbrInputPins; i++) + { + boolean val = digitalRead(inputPins[i]); + if(val != state[i]) + { + time_t duration = 0; // the time since the previous event + state[i] = val; + time_t timeNow = now(); + if(prevEventTime[i] > 0) + // if this was not the first state change, calculate the time from the previous change + duration = duration = timeNow - prevEventTime[i]; + logEvent(inputPins[i], val, timeNow, duration ); // log the event + prevEventTime[i] = timeNow; // store the time for this event + } + } +} + +void logEvent( int pin, boolean state, time_t timeNow, time_t duration) +{ + Serial.print("Pin "); + Serial.print(pin); + if( state == HIGH) + Serial.print(" went High at "); + else + Serial.print(" went Low at "); + showTime(timeNow); + if(duration > 0){ + // only display duration if greater than 0 + Serial.print(", Duration was "); + showDuration(duration); + } + Serial.println(); +} + + +void showTime(time_t t){ + // display the given time + Serial.print(hour(t)); + printDigits(minute(t)); + printDigits(second(t)); + Serial.print(" "); + Serial.print(day(t)); + Serial.print(" "); + Serial.print(month(t)); + Serial.print(" "); + Serial.print(year(t)); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +void showDuration(time_t duration){ +// prints the duration in days, hours, minutes and seconds + if(duration >= SECS_PER_DAY){ + Serial.print(duration / SECS_PER_DAY); + Serial.print(" day(s) "); + duration = duration % SECS_PER_DAY; + } + if(duration >= SECS_PER_HOUR){ + Serial.print(duration / SECS_PER_HOUR); + Serial.print(" hour(s) "); + duration = duration % SECS_PER_HOUR; + } + if(duration >= SECS_PER_MIN){ + Serial.print(duration / SECS_PER_MIN); + Serial.print(" minute(s) "); + duration = duration % SECS_PER_MIN; + } + Serial.print(duration); + Serial.print(" second(s) "); +} + diff --git a/Time/Examples/TimeRTCSet/TimeRTCSet.pde b/Time/Examples/TimeRTCSet/TimeRTCSet.pde new file mode 100644 index 0000000..48b696c --- /dev/null +++ b/Time/Examples/TimeRTCSet/TimeRTCSet.pde @@ -0,0 +1,82 @@ +/* + * TimeRTCSet.pde + * example code illustrating Time library with Real Time Clock. + * + * RTC clock is set in response to serial port time message + * A Processing example sketch to set the time is inclided in the download + */ + +#include +#include +#include // 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()) + { + time_t t = processSyncMessage(); + if(t >0) + { + RTC.set(t); // set the RTC and the system time to the received value + setTime(t); + } + } + digitalClockDisplay(); + delay(1000); +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +/* code to process time sync messages from the serial port */ +#define TIME_MSG_LEN 11 // time sync to PC is HEADER followed by unix time_t as ten ascii digits +#define TIME_HEADER 'T' // Header tag for serial time sync message + +time_t processSyncMessage() { + // return the time if a valid sync message is received on the serial port. + while(Serial.available() >= TIME_MSG_LEN ){ // time message consists of a header and ten ascii digits + char c = Serial.read() ; + Serial.print(c); + if( c == TIME_HEADER ) { + time_t pctime = 0; + for(int i=0; i < TIME_MSG_LEN -1; i++){ + c = Serial.read(); + if( c >= '0' && c <= '9'){ + pctime = (10 * pctime) + (c - '0') ; // convert digits to a number + } + } + return pctime; + } + } + return 0; +} + diff --git a/Time/Examples/TimeSerial/TimeSerial.pde b/Time/Examples/TimeSerial/TimeSerial.pde new file mode 100644 index 0000000..55c67a6 --- /dev/null +++ b/Time/Examples/TimeSerial/TimeSerial.pde @@ -0,0 +1,82 @@ +/* + * TimeSerial.pde + * example code illustrating Time library set through serial port messages. + * + * Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970) + * you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2010 + T1262347200 + * + * A Processing example sketch to automatically send the messages is inclided in the download + */ + +#include + +#define TIME_MSG_LEN 11 // time sync to PC is HEADER followed by unix time_t as ten ascii digits +#define TIME_HEADER 'T' // Header tag for serial time sync message +#define TIME_REQUEST 7 // ASCII bell character requests a time sync message + +void setup() { + Serial.begin(9600); + setSyncProvider( requestSync); //set function to call when sync required + Serial.println("Waiting for sync message"); +} + +void loop(){ + if(Serial.available() ) + { + processSyncMessage(); + } + if(timeStatus()!= timeNotSet) + { + digitalWrite(13,timeStatus() == timeSet); // on if synced, off if needs refresh + digitalClockDisplay(); + } + delay(1000); +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(month()); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +void processSyncMessage() { + // if time sync available from serial port, update time and return true + while(Serial.available() >= TIME_MSG_LEN ){ // time message consists of a header and ten ascii digits + char c = Serial.read() ; + Serial.print(c); + if( c == TIME_HEADER ) { + time_t pctime = 0; + for(int i=0; i < TIME_MSG_LEN -1; i++){ + c = Serial.read(); + if( c >= '0' && c <= '9'){ + pctime = (10 * pctime) + (c - '0') ; // convert digits to a number + } + } + setTime(pctime); // Sync Arduino clock to the time received on the serial port + } + } +} + +time_t requestSync() +{ + Serial.print(TIME_REQUEST,BYTE); + return 0; // the time will be sent later in response to serial mesg +} + diff --git a/Time/Examples/TimeSerialDateStrings/TimeSerialDateStrings.pde b/Time/Examples/TimeSerialDateStrings/TimeSerialDateStrings.pde new file mode 100644 index 0000000..dcff97e --- /dev/null +++ b/Time/Examples/TimeSerialDateStrings/TimeSerialDateStrings.pde @@ -0,0 +1,80 @@ +/* + * TimeSerialDateStrings.pde + * example code illustrating Time library date strings + * + * This sketch adds date string functionality to TimeSerial.pde + * + */ + +#include + +#define TIME_MSG_LEN 11 // time sync to PC is HEADER followed by unix time_t as ten ascii digits +#define TIME_HEADER 'T' // Header tag for serial time sync message +#define TIME_REQUEST 7 // ASCII bell character requests a time sync message + +void setup() { + Serial.begin(9600); + setSyncProvider( requestSync); //set function to call when sync required + Serial.println("Waiting for sync message"); +} + +void loop(){ + if(Serial.available() ) + { + processSyncMessage(); + } + if(timeStatus()!= timeNotSet) + { + digitalClockDisplay(); + } + delay(1000); +} + +void digitalClockDisplay(){ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.print(" "); + Serial.print(dayStr(weekday())); + Serial.print(" "); + Serial.print(day()); + Serial.print(" "); + Serial.print(monthShortStr(month())); + Serial.print(" "); + Serial.print(year()); + Serial.println(); +} + +void printDigits(int digits){ + // utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + +void processSyncMessage() { + // if time sync available from serial port, update time and return true + while(Serial.available() >= TIME_MSG_LEN ){ // time message consists of a header and ten ascii digits + char c = Serial.read() ; + Serial.print(c); + if( c == TIME_HEADER ) { + time_t pctime = 0; + for(int i=0; i < TIME_MSG_LEN -1; i++){ + c = Serial.read(); + if( c >= '0' && c <= '9'){ + pctime = (10 * pctime) + (c - '0') ; // convert digits to a number + } + } + setTime(pctime); // Sync Arduino clock to the time received on the serial port + } + } +} + +time_t requestSync() +{ + Serial.print(TIME_REQUEST,BYTE); + return 0; // the time will be sent later in response to serial mesg +} + diff --git a/Time/Readme.txt b/Time/Readme.txt new file mode 100644 index 0000000..22bc4aa --- /dev/null +++ b/Time/Readme.txt @@ -0,0 +1,131 @@ +Readme file for Arduino Time Library + +Time is a library that provides timekeeping functionality for Arduino. + +The code is derived from the Playground DateTime library but is updated +to provide an API that is more flexable and easier to use. + +A primary goal was to enable date and time functionality that can be used with +a variety of external time sources with minimum differences required in sketch logic. + +Example sketches illustrate how similar sketch code can be used with: a Real Time Clock, +internet NTP time service, GPS time data, and Serial time messages from a computer +for time synchronization. + +The functions available in the library include: + +hour(); // the hour now (0-23) +minute(); // the minute now (0-59) +second(); // the second now (0-59) +day(); // the day now (1-31) +weekday(); // day of the week, Sunday is day 0 +month(); // the month now (1-12) +year(); // the full four digit year: (2009, 2010 etc) + +there are also functions to return the hour in 12 hour format +hourFormat12(); // the hour now in 12 hour format +isAM(); // returns true if time now is AM +isPM(); // returns true if time now is PM + +now(); // returns the current time as seconds since Jan 1 1970 + +The time and date functions can take an optional parameter for the time. This prevents +errors if the time rolls over between elements. For example, if a new minute begins +between getting the minute and second, the values will be inconsistent. Using the +following functions eliminates this probglem + time_t t = now(); // store the current time in time variable t + hour(t); // returns the hour for the given time t + minute(t); // returns the minute for the given time t + second(t); // returns the second for the given time t + day(t); // the day for the given time t + weekday(t); // day of the week for the given time t + month(t); // the month for the given time t + year(t); // the year for the given time t + + +Functions for managing the timer services are: +setTime(t); // set the system time to the give time t +setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr (2010 or 10 sets year to 2010) +adjustTime(adjustment); // adjust system time by adding the adjustment value + +timeStatus(); // indicates if time has been set and recently synchronized + // returns one of the following enumerations: + timeNotSet // the time has never been set, the clock started at Jan 1 1970 + timeNeedsSync // the time had been set but a sync attempt did not succeed + timeSet // the time is set and is synced +Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but +the returned time may have drifted if the status is timeNeedsSync. + +setSyncProvider(getTimeFunction); // set the external time provider +setSyncInterval(interval); // set the number of seconds between re-sync + + +There are many convenience macros in the time.h file for time constants and conversion of time units. + +To use the library, copy the download to the Library directory. + +The Time directory contains the Time library and some example sketches +illustrating how the library can be used with various time sources: + +- TimeSerial.pde shows Arduino as a clock without external hardware. + It is synchronized by time messages sent over the serial port. + A companion Processing sketch will automatically provide these messages + if it is running and connected to the Arduino serial port. + +- TimeSerialDateStrings.pde adds day and month name strings to the sketch above + Short (3 character) and long strings are available to print the days of + the week and names of the months. + +- TimeRTC uses a DS1307 real time clock to provide time synchronization. + A basic RTC library named DS1307RTC is included in the download. + To run this sketch the DS1307RTC library must be installed. + +- TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock + +- TimeRTCLog demonstrates how to calculate the difference between times. + It is a vary simple logger application that monitors events on digtial pins + and prints (to the serial port) the time of an event and the time period since the previous event. + +- TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service. + The NTP protocol uses UDP and the UdpBytewise library is required, see: + http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/ + +-TimeGPS gets time from a GPS + This requires the TinyGPS and NewSoftSerial libraries from Mikal Hart: + http://arduiniana.org/libraries/TinyGPS and http://arduiniana.org/libraries/newsoftserial/ + +Differences between this code and the playground DateTime library +although the Time library is based on the DateTime codebase, the API has changed. +Changes in the Time library API: +- time elements are functions returning int (they are variables in DateTime) +- Years start from 1970 +- days of the week and months start from 1 (they start from 0 in DateTime) +- DateStrings do not require a seperate library +- time elements can be accessed non-atomically (in DateTime they are always atomic) +- function added to automatically sync time with extrnal source +- localTime and maketime parameters changed, localTime renamed to breakTime + +Technical notes: + +Internal system time is based on the standard Unix time_t. +The value is the number of seconds since Jan 1 1970. +System time begins at zero when the sketch starts. + +The internal time can be automatically synchronized at regular intervals to an external time source. +This is enabled by calling the setSyncProvider(provider) function - the provider argument is +the address of a function that returns the current time as a time_t. +See the sketches in the examples directory for usage. + +The default interval for re-syncing the time is 5 minutes but can be changed by calling the +setSyncInterval( interval) method to set the number of seconds between re-sync attempts. + +The Time library defines a structure for holding time elements that is a compact version of the C tm structure. +All the members of the Arduino tm structure are bytes and the year is offset from 1970. +Convenience macros provide conversion to and from the Arduino format. + +Low level functions to convert between system time and individual time elements are provided: + breakTime( time, &tm); // break time_t into elements stored in tm struct + makeTime( &tm); // return time_t from elements stored in tm struct + +The DS1307RTC library included in the download provides an example of how a time provider +can use the low level functions to interface with the Time library. \ No newline at end of file diff --git a/Time/Time.cpp b/Time/Time.cpp new file mode 100644 index 0000000..86bdcd5 --- /dev/null +++ b/Time/Time.cpp @@ -0,0 +1,307 @@ +/* + time.c - low level time and date functions + Copyright (c) Michael Margolis 2009 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + 6 Jan 2010 - initial release + 12 Feb 2010 - fixed leap year calculation error + 1 Nov 2010 - fixed setTime bug (thanks to Korman for this) +*/ + +#if ARDUINO >= 100 +#include +#else +#include +#endif + +#include "Time.h" + +static tmElements_t tm; // a cache of time elements +static time_t cacheTime; // the time the cache was updated +static time_t syncInterval = 300; // time sync will be attempted after this many seconds + +void refreshCache( time_t t){ + if( t != cacheTime) + { + breakTime(t, tm); + cacheTime = t; + } +} + +int hour() { // the hour now + return hour(now()); +} + +int hour(time_t t) { // the hour for the given time + refreshCache(t); + return tm.Hour; +} + +int hourFormat12() { // the hour now in 12 hour format + return hourFormat12(now()); +} + +int hourFormat12(time_t t) { // the hour for the given time in 12 hour format + refreshCache(t); + if( tm.Hour == 0 ) + return 12; // 12 midnight + else if( tm.Hour > 12) + return tm.Hour - 12 ; + else + return tm.Hour ; +} + +uint8_t isAM() { // returns true if time now is AM + return !isPM(now()); +} + +uint8_t isAM(time_t t) { // returns true if given time is AM + return !isPM(t); +} + +uint8_t isPM() { // returns true if PM + return isPM(now()); +} + +uint8_t isPM(time_t t) { // returns true if PM + return (hour(t) >= 12); +} + +int minute() { + return minute(now()); +} + +int minute(time_t t) { // the minute for the given time + refreshCache(t); + return tm.Minute; +} + +int second() { + return second(now()); +} + +int second(time_t t) { // the second for the given time + refreshCache(t); + return tm.Second; +} + +int day(){ + return(day(now())); +} + +int day(time_t t) { // the day for the given time (0-6) + refreshCache(t); + return tm.Day; +} + +int weekday() { // Sunday is day 1 + return weekday(now()); +} + +int weekday(time_t t) { + refreshCache(t); + return tm.Wday; +} + +int month(){ + return month(now()); +} + +int month(time_t t) { // the month for the given time + refreshCache(t); + return tm.Month; +} + +int year() { // as in Processing, the full four digit year: (2009, 2010 etc) + return year(now()); +} + +int year(time_t t) { // the year for the given time + refreshCache(t); + return tmYearToCalendar(tm.Year); +} + +/*============================================================================*/ +/* functions to convert to and from system time */ +/* These are for interfacing with time serivces and are not normally needed in a sketch */ + +// leap year calulator expects year argument as years offset from 1970 +#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) + +static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0 + +void breakTime(time_t time, tmElements_t &tm){ +// break the given time_t into time components +// this is a more compact version of the C library localtime function +// note that year is offset from 1970 !!! + + uint8_t year; + uint8_t month, monthLength; + unsigned long days; + + tm.Second = time % 60; + time /= 60; // now it is minutes + tm.Minute = time % 60; + time /= 60; // now it is hours + tm.Hour = time % 24; + time /= 24; // now it is days + tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 + + year = 0; + days = 0; + while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { + year++; + } + tm.Year = year; // year is offset from 1970 + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; // now it is days in this year, starting at 0 + + days=0; + month=0; + monthLength=0; + for (month=0; month<12; month++) { + if (month==1) { // february + if (LEAP_YEAR(year)) { + monthLength=29; + } else { + monthLength=28; + } + } else { + monthLength = monthDays[month]; + } + + if (time >= monthLength) { + time -= monthLength; + } else { + break; + } + } + tm.Month = month + 1; // jan is month 1 + tm.Day = time + 1; // day of month +} + +time_t makeTime(tmElements_t &tm){ +// assemble time elements into time_t +// note year argument is offset from 1970 (see macros in time.h to convert to other formats) +// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 + + int i; + time_t seconds; + + // seconds from 1970 till 1 jan 00:00:00 of the given year + seconds= tm.Year*(SECS_PER_DAY * 365); + for (i = 0; i < tm.Year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; // add extra days for leap years + } + } + + // add days for this year, months start from 1 + for (i = 1; i < tm.Month; i++) { + if ( (i == 2) && LEAP_YEAR(tm.Year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0 + } + } + seconds+= (tm.Day-1) * SECS_PER_DAY; + seconds+= tm.Hour * SECS_PER_HOUR; + seconds+= tm.Minute * SECS_PER_MIN; + seconds+= tm.Second; + return seconds; +} +/*=====================================================*/ +/* Low level system time functions */ + +static time_t sysTime = 0; +static time_t prevMillis = 0; +static time_t nextSyncTime = 0; +static timeStatus_t Status = timeNotSet; + +getExternalTime getTimePtr; // pointer to external sync function +//setExternalTime setTimePtr; // not used in this version + +#ifdef TIME_DRIFT_INFO // define this to get drift data +time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync +#endif + + +time_t now(){ + while( millis() - prevMillis >= 1000){ + sysTime++; + prevMillis += 1000; +#ifdef TIME_DRIFT_INFO + sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift +#endif + } + if(nextSyncTime <= sysTime){ + if(getTimePtr != 0){ + time_t t = getTimePtr(); + if( t != 0) + setTime(t); + else + Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; + } + } + return sysTime; +} + +void setTime(time_t t){ +#ifdef TIME_DRIFT_INFO + if(sysUnsyncedTime == 0) + sysUnsyncedTime = t; // store the time of the first call to set a valid Time +#endif + + sysTime = t; + nextSyncTime = t + syncInterval; + Status = timeSet; + prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) +} + +void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ + // year can be given as full four digit year or two digts (2010 or 10 for 2010); + //it is converted to years since 1970 + if( yr > 99) + yr = yr - 1970; + else + yr += 30; + tm.Year = yr; + tm.Month = mnth; + tm.Day = dy; + tm.Hour = hr; + tm.Minute = min; + tm.Second = sec; + setTime(makeTime(tm)); +} + +void adjustTime(long adjustment){ + sysTime += adjustment; +} + +timeStatus_t timeStatus(){ // indicates if time has been set and recently synchronized + return Status; +} + +void setSyncProvider( getExternalTime getTimeFunction){ + getTimePtr = getTimeFunction; + nextSyncTime = sysTime; + now(); // this will sync the clock +} + +void setSyncInterval(time_t interval){ // set the number of seconds between re-sync + syncInterval = interval; +} \ No newline at end of file diff --git a/Time/Time.h b/Time/Time.h new file mode 100644 index 0000000..e54f3b0 --- /dev/null +++ b/Time/Time.h @@ -0,0 +1,126 @@ +/* + time.h - low level time and date functions +*/ + +/* + July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this) + - fixed daysToTime_t macro (thanks maniacbug) +*/ + +#ifndef _Time_h +#define _Time_h + +#include + +typedef unsigned long time_t; + +typedef enum {timeNotSet, timeNeedsSync, timeSet +} timeStatus_t ; + +typedef enum { + dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday +} timeDayOfWeek_t; + +typedef enum { + tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields +} tmByteFields; + +typedef struct { + uint8_t Second; + uint8_t Minute; + uint8_t Hour; + uint8_t Wday; // day of week, sunday is day 1 + uint8_t Day; + uint8_t Month; + uint8_t Year; // offset from 1970; +} tmElements_t, TimeElements, *tmElementsPtr_t; + +//convenience macros to convert to and from tm years +#define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year +#define CalendarYrToTm(Y) ((Y) - 1970) +#define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000 +#define y2kYearToTm(Y) ((Y) + 30) + +typedef time_t(*getExternalTime)(); +//typedef void (*setExternalTime)(const time_t); // not used in this version + + +/*==============================================================================*/ +/* Useful Constants */ +#define SECS_PER_MIN (60UL) +#define SECS_PER_HOUR (3600UL) +#define SECS_PER_DAY (SECS_PER_HOUR * 24UL) +#define DAYS_PER_WEEK (7UL) +#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK) +#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL) +#define SECS_YR_2000 (946684800UL) // the time at the start of y2k + +/* Useful Macros for getting elapsed time */ +#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) +#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) +#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) +#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday +#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970 +#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight +// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 +// Always set the correct time before settting alarms +#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day +#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day +#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1 +#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time +#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time + + +/* Useful Macros for converting elapsed time to a time_t */ +#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) +#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) +#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 +#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) + +/*============================================================================*/ +/* time and date functions */ +int hour(); // the hour now +int hour(time_t t); // the hour for the given time +int hourFormat12(); // the hour now in 12 hour format +int hourFormat12(time_t t); // the hour for the given time in 12 hour format +uint8_t isAM(); // returns true if time now is AM +uint8_t isAM(time_t t); // returns true the given time is AM +uint8_t isPM(); // returns true if time now is PM +uint8_t isPM(time_t t); // returns true the given time is PM +int minute(); // the minute now +int minute(time_t t); // the minute for the given time +int second(); // the second now +int second(time_t t); // the second for the given time +int day(); // the day now +int day(time_t t); // the day for the given time +int weekday(); // the weekday now (Sunday is day 1) +int weekday(time_t t); // the weekday for the given time +int month(); // the month now (Jan is month 1) +int month(time_t t); // the month for the given time +int year(); // the full four digit year: (2009, 2010 etc) +int year(time_t t); // the year for the given time + +time_t now(); // return the current time as seconds since Jan 1 1970 +void setTime(time_t t); +void setTime(int hr,int min,int sec,int day, int month, int yr); +void adjustTime(long adjustment); + +/* date strings */ +#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) +char* monthStr(uint8_t month); +char* dayStr(uint8_t day); +char* monthShortStr(uint8_t month); +char* dayShortStr(uint8_t day); + +/* time sync functions */ +timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized +void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider +void setSyncInterval(time_t interval); // set the number of seconds between re-sync + +/* low level functions to convert to and from system time */ +void breakTime(time_t time, tmElements_t &tm); // break time_t into elements +time_t makeTime(tmElements_t &tm); // convert time elements into time_t + + +#endif /* _Time_h */ + diff --git a/Time/keywords.txt b/Time/keywords.txt new file mode 100644 index 0000000..f921672 --- /dev/null +++ b/Time/keywords.txt @@ -0,0 +1,33 @@ +####################################### +# Syntax Coloring Map For Time +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### +time_t KEYWORD1 +####################################### +# Methods and Functions (KEYWORD2) +####################################### +now KEYWORD2 +second KEYWORD2 +minute KEYWORD2 +hour KEYWORD2 +day KEYWORD2 +month KEYWORD2 +year KEYWORD2 +isAM KEYWORD2 +isPM KEYWORD2 +weekday KEYWORD2 +setTime KEYWORD2 +adjustTime KEYWORD2 +setSyncProvider KEYWORD2 +setSyncInteval KEYWORD2 +timeStatus KEYWORD2 +####################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/TimeAlarms/Examples/TimeAlarmExample/TimeAlarmExample.pde b/TimeAlarms/Examples/TimeAlarmExample/TimeAlarmExample.pde new file mode 100644 index 0000000..5084ffd --- /dev/null +++ b/TimeAlarms/Examples/TimeAlarmExample/TimeAlarmExample.pde @@ -0,0 +1,77 @@ +/* + * TimeAlarmExample.pde + * + * 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 weekly timer is set for Saturdays at 8:30:30 + * + * 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 2011 8:29 am + */ + +#include +#include + +void setup() +{ + Serial.begin(9600); + setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011 + // create the alarms + Alarm.alarmRepeat(8,30,0, MorningAlarm); // 8:30am every day + Alarm.alarmRepeat(17,45,0,EveningAlarm); // 5:45pm every day + Alarm.alarmRepeat(dowSaturday,8,30,30,WeeklyAlarm); // 8:30:30 every Saturday + + + Alarm.timerRepeat(15, Repeats); // timer for every 15 seconds + Alarm.timerOnce(10, OnceOnly); // called once after 10 seconds +} + +void loop(){ + digitalClockDisplay(); + Alarm.delay(1000); // wait one second between clock display +} + +// functions to be called when an alarm triggers: +void MorningAlarm(){ + Serial.println("Alarm: - turn lights off"); +} + +void EveningAlarm(){ + Serial.println("Alarm: - turn lights on"); +} + +void WeeklyAlarm(){ + Serial.println("Alarm: - its Monday Morning"); +} + +void ExplicitAlarm(){ + Serial.println("Alarm: - this triggers only at the given date and time"); +} + +void Repeats(){ + Serial.println("15 second timer"); +} + +void OnceOnly(){ + Serial.println("This timer only triggers once"); +} + +void digitalClockDisplay() +{ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.println(); +} + +void printDigits(int digits) +{ + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} + diff --git a/TimeAlarms/TimeAlarms.cpp b/TimeAlarms/TimeAlarms.cpp new file mode 100644 index 0000000..66b94f9 --- /dev/null +++ b/TimeAlarms/TimeAlarms.cpp @@ -0,0 +1,150 @@ +/* + TimeAlarms.cpp - Arduino Time alarms for use with Time library + Copyright (c) 208-2011 Michael Margolis. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + /* + 2 July 2011 - replaced alarm types implied from alarm value with enums to make trigger logic more robust + - this fixes bug in repeating weekly alarms - thanks to Vincent Valdy and draythomp for testing +*/ + +extern "C" { +#include // for memset +} + +#include +#include "TimeAlarms.h" +#include "Time.h" + +#define IS_ONESHOT true // constants used in arguments to create method +#define IS_REPEAT false + + +//************************************************************** +//* Alarm Class Constructor + +AlarmClass::AlarmClass() +{ + Mode.isEnabled = Mode.isOneShot = 0; + Mode.alarmType = dtNotAllocated; + value = nextTrigger = 0; + onTickHandler = NULL; // prevent a callback until this pointer is explicitly set +} + +//************************************************************** +//* Private Methods + + +void AlarmClass::updateNextTrigger() +{ + if( (value != 0) && Mode.isEnabled ) + { + time_t time = now(); + if( dtIsAlarm(Mode.alarmType) && nextTrigger <= time ) // update alarm if next trigger is not yet in the future + { + if(Mode.alarmType == dtExplicitAlarm ) // is the value a specific date and time in the future + { + nextTrigger = value; // yes, trigger on this value + } + else if(Mode.alarmType == dtDailyAlarm) //if this is a daily alarm + { + if( value + previousMidnight(now()) <= time) + { + nextTrigger = value + nextMidnight(time); // if time has passed then set for tomorrow + } + else + { + nextTrigger = value + previousMidnight(time); // set the date to today and add the time given in value + } + } + else if(Mode.alarmType == dtWeeklyAlarm) // if this is a weekly alarm + { + if( (value + previousSunday(now())) <= time) + { + nextTrigger = value + nextSunday(time); // if day has passed then set for the next week. + } + else + { + nextTrigger = value + previousSunday(time); // set the date to this week today and add the time given in value + } + } + else // its not a recognized alarm type - this should not happen + { + Mode.isEnabled = 0; // Disable the alarm + } + } + if( Mode.alarmType == dtTimer) + { + // its a timer + nextTrigger = time + value; // add the value to previous time (this ensures delay always at least Value seconds) + } + } + else + { + Mode.isEnabled = 0; // Disable if the value is 0 + } +} + +//************************************************************** +//* Time Alarms Public Methods + +TimeAlarmsClass::TimeAlarmsClass() +{ + isServicing = false; + for(uint8_t id = 0; id < dtNBR_ALARMS; id++) + free(id); // ensure all Alarms are cleared and available for allocation +} + +// this method creates a trigger at the given absolute time_t +// it replaces the call to alarmOnce with values greater than a week +AlarmID_t TimeAlarmsClass::triggerOnce(time_t value, OnTick_t onTickHandler){ // trigger once at the given time_t + if( value > 0) + return create( value, onTickHandler, IS_ONESHOT, dtExplicitAlarm ); + else + return dtINVALID_ALARM_ID; // dont't allocate if the time is greater than one day +} + +// this method will now return an error if the value is greater than one day - use DOW methods for weekly alarms +AlarmID_t TimeAlarmsClass::alarmOnce(time_t value, OnTick_t onTickHandler){ // trigger once at the given time of day + if( value <= SECS_PER_DAY) + return create( value, onTickHandler, IS_ONESHOT, dtDailyAlarm ); + else + return dtINVALID_ALARM_ID; // dont't allocate if the time is greater than one day +} + +AlarmID_t TimeAlarmsClass::alarmOnce(const int H, const int M, const int S,OnTick_t onTickHandler){ // as above with HMS arguments + return create( AlarmHMS(H,M,S), onTickHandler, IS_ONESHOT, dtDailyAlarm ); +} + +AlarmID_t TimeAlarmsClass::alarmOnce(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler){ // as above, with day of week + return create( (DOW-1) * SECS_PER_DAY + AlarmHMS(H,M,S), onTickHandler, IS_ONESHOT, dtWeeklyAlarm ); +} + +// this method will now return an error if the value is greater than one day - use DOW methods for weekly alarms +AlarmID_t TimeAlarmsClass::alarmRepeat(time_t value, OnTick_t onTickHandler){ // trigger daily at the given time + if( value <= SECS_PER_DAY) + return create( value, onTickHandler, IS_REPEAT, dtDailyAlarm ); + else + return dtINVALID_ALARM_ID; // dont't allocate if the time is greater than one day } AlarmID_t TimeAlarmsClass::alarmRepeat(const int H, const int M, const int S, OnTick_t onTickHandler){ // as above with HMS arguments return create( AlarmHMS(H,M,S), onTickHandler, IS_REPEAT, dtDailyAlarm ); } AlarmID_t TimeAlarmsClass::alarmRepeat(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler){ // as above, with day of week return create( (DOW-1) * SECS_PER_DAY + AlarmHMS(H,M,S), onTickHandler, IS_REPEAT, dtWeeklyAlarm ); } AlarmID_t TimeAlarmsClass::timerOnce(time_t value, OnTick_t onTickHandler){ // trigger once after the given number of seconds return create( value, onTickHandler, IS_ONESHOT, dtTimer ); } AlarmID_t TimeAlarmsClass::timerOnce(const int H, const int M, const int S, OnTick_t onTickHandler){ // As above with HMS arguments return create( AlarmHMS(H,M,S), onTickHandler, IS_ONESHOT, dtTimer ); } AlarmID_t TimeAlarmsClass::timerRepeat(time_t value, OnTick_t onTickHandler){ // trigger after the given number of seconds continuously return create( value, onTickHandler, IS_REPEAT, dtTimer); } AlarmID_t TimeAlarmsClass::timerRepeat(const int H, const int M, const int S, OnTick_t onTickHandler){ // trigger after the given number of seconds continuously return create( AlarmHMS(H,M,S), onTickHandler, IS_REPEAT, dtTimer); } void TimeAlarmsClass::enable(AlarmID_t ID) { if(isAllocated(ID)) { Alarm[ID].Mode.isEnabled = (Alarm[ID].value != 0) && (Alarm[ID].onTickHandler != 0) ; // only enable if value is non zero and a tick handler has been set Alarm[ID].updateNextTrigger(); // trigger is updated whenever this is called, even if already enabled } } void TimeAlarmsClass::disable(AlarmID_t ID) { if(isAllocated(ID)) Alarm[ID].Mode.isEnabled = false; } // write the given value to the given alarm void TimeAlarmsClass::write(AlarmID_t ID, time_t value) { if(isAllocated(ID)) { Alarm[ID].value = value; enable(ID); // update trigger time } } // return the value for the given alarm ID time_t TimeAlarmsClass::read(AlarmID_t ID) { if(isAllocated(ID)) return Alarm[ID].value ; else return dtINVALID_TIME; } // return the alarm type for the given alarm ID dtAlarmPeriod_t TimeAlarmsClass::readType(AlarmID_t ID) { if(isAllocated(ID)) return (dtAlarmPeriod_t)Alarm[ID].Mode.alarmType ; else return dtNotAllocated; } + void TimeAlarmsClass::free(AlarmID_t ID) + { + if(isAllocated(ID)) + { + Alarm[ID].Mode.isEnabled = false; + Alarm[ID].Mode.alarmType = dtNotAllocated; + Alarm[ID].onTickHandler = 0; + Alarm[ID].value = 0; + Alarm[ID].nextTrigger = 0; + } + } + // returns the number of allocated timers uint8_t TimeAlarmsClass::count() { uint8_t c = 0; for(uint8_t id = 0; id < dtNBR_ALARMS; id++) { if(isAllocated(id)) c++; } return c; } // returns true only if id is allocated and the type is a time based alarm, returns false if not allocated or if its a timer bool TimeAlarmsClass::isAlarm(AlarmID_t ID) { return( isAllocated(ID) && dtIsAlarm(Alarm[ID].Mode.alarmType) ); } // returns true if this id is allocated bool TimeAlarmsClass::isAllocated(AlarmID_t ID) { return( ID < dtNBR_ALARMS && Alarm[ID].Mode.alarmType != dtNotAllocated ); } AlarmID_t TimeAlarmsClass::getTriggeredAlarmId() //returns the currently triggered alarm id // returns dtINVALID_ALARM_ID if not invoked from within an alarm handler { if(isServicing) return servicedAlarmId; // new private data member used instead of local loop variable i in serviceAlarms(); else return dtINVALID_ALARM_ID; // valid ids only available when servicing a callback } // following functions are not Alarm ID specific. void TimeAlarmsClass::delay(unsigned long ms) { unsigned long start = millis(); while( millis() - start <= ms) serviceAlarms(); } void TimeAlarmsClass::waitForDigits( uint8_t Digits, dtUnits_t Units) { while(Digits != getDigitsNow(Units) ) { serviceAlarms(); } } void TimeAlarmsClass::waitForRollover( dtUnits_t Units) { while(getDigitsNow(Units) == 0 ) // if its just rolled over than wait for another rollover serviceAlarms(); waitForDigits(0, Units); } uint8_t TimeAlarmsClass::getDigitsNow( dtUnits_t Units) { time_t time = now(); if(Units == dtSecond) return numberOfSeconds(time); if(Units == dtMinute) return numberOfMinutes(time); if(Units == dtHour) return numberOfHours(time); if(Units == dtDay) return dayOfWeek(time); return 255; // This should never happen } //*********************************************************** //* Private Methods void TimeAlarmsClass::serviceAlarms() { if(! isServicing) { isServicing = true; for( servicedAlarmId = 0; servicedAlarmId < dtNBR_ALARMS; servicedAlarmId++) { if( Alarm[servicedAlarmId].Mode.isEnabled && (now() >= Alarm[servicedAlarmId].nextTrigger) ) { OnTick_t TickHandler = Alarm[servicedAlarmId].onTickHandler; if(Alarm[servicedAlarmId].Mode.isOneShot) free(servicedAlarmId); // free the ID if mode is OnShot else Alarm[servicedAlarmId].updateNextTrigger(); if( TickHandler != NULL) { (*TickHandler)(); // call the handler } } } isServicing = false; } } // returns the absolute time of the next scheduled alarm, or 0 if none time_t TimeAlarmsClass::getNextTrigger() { time_t nextTrigger = 0xffffffff; // the max time value for(uint8_t id = 0; id < dtNBR_ALARMS; id++) { if(isAllocated(id) ) { if(Alarm[id].nextTrigger < nextTrigger) nextTrigger = Alarm[id].nextTrigger; } } return nextTrigger == 0xffffffff ? 0 : nextTrigger; } // attempt to create an alarm and return true if successful AlarmID_t TimeAlarmsClass::create( time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType, uint8_t isEnabled) { if( ! (dtIsAlarm(alarmType) && now() < SECS_PER_YEAR)) // only create alarm ids if the time is at least Jan 1 1971 { for(uint8_t id = 0; id < dtNBR_ALARMS; id++) { if( Alarm[id].Mode.alarmType == dtNotAllocated ) { // here if there is an Alarm id that is not allocated Alarm[id].onTickHandler = onTickHandler; Alarm[id].Mode.isOneShot = isOneShot; Alarm[id].Mode.alarmType = alarmType; Alarm[id].value = value; isEnabled ? enable(id) : disable(id); return id; // alarm created ok } } } return dtINVALID_ALARM_ID; // no IDs available or time is invalid } // make one instance for the user to use TimeAlarmsClass Alarm = TimeAlarmsClass() ; \ No newline at end of file diff --git a/TimeAlarms/TimeAlarms.h b/TimeAlarms/TimeAlarms.h new file mode 100644 index 0000000..fd82b10 --- /dev/null +++ b/TimeAlarms/TimeAlarms.h @@ -0,0 +1,127 @@ +// TimeAlarms.h - Arduino Time alarms header for use with Time library + +#ifndef TimeAlarms_h +#define TimeAlarms_h + +#include + +#include "Time.h" + +#define dtNBR_ALARMS 6 // max is 255 + +#define USE_SPECIALIST_METHODS // define this for testing + +typedef enum { dtMillisecond, dtSecond, dtMinute, dtHour, dtDay } dtUnits_t; + +typedef struct { + uint8_t alarmType :4 ; // enumeration of daily/weekly (in future: biweekly/semimonthly/monthly/annual) + // note that the current API only supports daily or weekly alarm periods + uint8_t isEnabled :1 ; // the timer is only actioned if isEnabled is true + uint8_t isOneShot :1 ; // the timer will be de-allocated after trigger is processed + } + AlarmMode_t ; + +// new time based alarms should be added just before dtLastAlarmType +typedef enum {dtNotAllocated, dtTimer, dtExplicitAlarm, dtDailyAlarm, dtWeeklyAlarm, dtLastAlarmType } dtAlarmPeriod_t ; // in future: dtBiweekly, dtMonthly, dtAnnual + +// macro to return true if the given type is a time based alarm, false if timer or not allocated +#define dtIsAlarm(_type_) (_type_ >= dtExplicitAlarm && _type_ < dtLastAlarmType) + +typedef uint8_t AlarmID_t; +typedef AlarmID_t AlarmId; // Arduino friendly name + +#define dtINVALID_ALARM_ID 255 +#define dtINVALID_TIME 0L + +class AlarmClass; // forward reference +typedef void (*OnTick_t)(); // alarm callback function typedef + +// class defining an alarm instance, only used by dtAlarmsClass +class AlarmClass +{ +private: + +public: + AlarmClass(); + OnTick_t onTickHandler; + void updateNextTrigger(); + time_t value; + time_t nextTrigger; + AlarmMode_t Mode; +}; + +// class containing the collection of alarms +class TimeAlarmsClass +{ +private: + AlarmClass Alarm[dtNBR_ALARMS]; + void serviceAlarms(); + uint8_t isServicing; + uint8_t servicedAlarmId; // the alarm currently being serviced + AlarmID_t create( time_t value, OnTick_t onTickHandler, uint8_t isOneShot, dtAlarmPeriod_t alarmType, uint8_t isEnabled=true); + +public: + TimeAlarmsClass(); + // functions to create alarms and timers + + AlarmID_t triggerOnce(time_t value, OnTick_t onTickHandler); // trigger once at the given time_t + + AlarmID_t alarmRepeat(time_t value, OnTick_t onTickHandler); // trigger daily at given time of day + AlarmID_t alarmRepeat(const int H, const int M, const int S, OnTick_t onTickHandler); // as above, with hms arguments + AlarmID_t alarmRepeat(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler); // as above, with day of week + + AlarmID_t alarmOnce(time_t value, OnTick_t onTickHandler); // trigger once at given time of day + AlarmID_t alarmOnce( const int H, const int M, const int S, OnTick_t onTickHandler); // as above, with hms arguments + AlarmID_t alarmOnce(const timeDayOfWeek_t DOW, const int H, const int M, const int S, OnTick_t onTickHandler); // as above, with day of week + + AlarmID_t timerOnce(time_t value, OnTick_t onTickHandler); // trigger once after the given number of seconds + AlarmID_t timerOnce(const int H, const int M, const int S, OnTick_t onTickHandler); // As above with HMS arguments + + AlarmID_t timerRepeat(time_t value, OnTick_t onTickHandler); // trigger after the given number of seconds continuously + AlarmID_t timerRepeat(const int H, const int M, const int S, OnTick_t onTickHandler); // As above with HMS arguments + + void delay(unsigned long ms); + + // utility methods + uint8_t getDigitsNow( dtUnits_t Units); // returns the current digit value for the given time unit + void waitForDigits( uint8_t Digits, dtUnits_t Units); + void waitForRollover(dtUnits_t Units); + + // low level methods + void enable(AlarmID_t ID); // enable the alarm to trigger + void disable(AlarmID_t ID); // prevent the alarm from triggering + AlarmID_t getTriggeredAlarmId(); // returns the currently triggered alarm id + void write(AlarmID_t ID, time_t value); // write the value (and enable) the alarm with the given ID + time_t read(AlarmID_t ID); // return the value for the given timer + dtAlarmPeriod_t readType(AlarmID_t ID); // return the alarm type for the given alarm ID + +#ifndef USE_SPECIALIST_METHODS +private: // the following methods are for testing and are not documented as part of the standard library +#endif + void free(AlarmID_t ID); // free the id to allow its reuse + uint8_t count(); // returns the number of allocated timers + time_t getNextTrigger(); // returns the time of the next scheduled alarm + bool isAllocated(AlarmID_t ID); // returns true if this id is allocated + bool isAlarm(AlarmID_t ID); // returns true if id is for a time based alarm, false if its a timer or not allocated +}; + +extern TimeAlarmsClass Alarm; // make an instance for the user + +/*============================================================================== + * MACROS + *============================================================================*/ + +/* public */ +#define waitUntilThisSecond(_val_) waitForDigits( _val_, dtSecond) +#define waitUntilThisMinute(_val_) waitForDigits( _val_, dtMinute) +#define waitUntilThisHour(_val_) waitForDigits( _val_, dtHour) +#define waitUntilThisDay(_val_) waitForDigits( _val_, dtDay) +#define waitMinuteRollover() waitForRollover(dtSecond) +#define waitHourRollover() waitForRollover(dtMinute) +#define waitDayRollover() waitForRollover(dtHour) + +#define AlarmHMS(_hr_, _min_, _sec_) (_hr_ * SECS_PER_HOUR + _min_ * SECS_PER_MIN + _sec_) + + +#endif /* TimeAlarms_h */ + diff --git a/TimeAlarms/keywords.txt b/TimeAlarms/keywords.txt new file mode 100644 index 0000000..1843a88 --- /dev/null +++ b/TimeAlarms/keywords.txt @@ -0,0 +1,25 @@ +####################################### +# Syntax Coloring Map For TimeAlarms +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +alarmRepeat KEYWORD2 +alarmOnce KEYWORD2 +timerRepeat KEYWORD2 +timerOnce KEYWORD2 +delay KEYWORD2 +####################################### +# Instances (KEYWORD2) +####################################### +Alarm KEYWORD2 +####################################### +# Constants (LITERAL1) +####################################### +dtINVALID_ALARM_ID LITERAL1 +dtINVALID_TIME LITERAL1 \ No newline at end of file diff --git a/TimeAlarms/readme.txt b/TimeAlarms/readme.txt new file mode 100644 index 0000000..e158c8d --- /dev/null +++ b/TimeAlarms/readme.txt @@ -0,0 +1,220 @@ +Alarms + +The Alarm library is a companion to the Time library that makes it easy to +perform tasks at specific times or after specific intervals. + +Tasks scheduled at a particular time of day are called Alarms, +tasks scheduled after an interval of time has elapsed are called Timers. +These tasks can be created to continuously repeat or to occur once only. + +Here is how you create an alarm to trigger a task repeatedly at a particular time of day: + Alarm.alarmRepeat(8,30,0, MorningAlarm); +This would call the function MorningAlarm() at 8:30 am 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 a MorningAlarm() function in a sketch once only (when the time is next 8:30am) + +Alarms can be specified to trigger a task repeatedly at a particular day of week and time of day: + Alarm.alarmRepeat(dowMonday, 9,15,0, MondayMorningAlarm); +This would call the function WeeklyAlarm() at 9:15am every Monday. + +If you want the alarm to trigger once only on a particular day and time you can do this: + Alarm.alarmOnce(dowMonday, 9,15,0, MondayMorningAlarm); +This would call the function MondayMorning() Alarm on the next Monday at 9:15am. + +Timers trigger tasks that occur after a specified interval of time has passed. +The timer interval can be specified in 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, you can 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. + +If you want to trigger once at a specified date and time you can use the trigger Once() method: + Alarm. triggerOnce(time_t value, explicitAlarm); // value specifies a date and time +(See the makeTime() method in the Time library to convert dates and times into time_t) + +Your sketch should call the Alarm.delay() function instead of the Arduino delay() function when +using the Alarms library. The timeliness of triggers depends on sketch delays using this function. + Alarm.delay( period); // Similar to Arduino delay - pauses the program for the period (in milliseconds). + + + +Here is an example sketch: + +This sketch triggers daily alarms at 8:30 am and 17:45 pm. +A Timer is triggered every 15 seconds, another timer triggers once only after 10 seconds. +A weekly alarm is triggered every Sunday at 8:30:30 + +#include +#include + +void setup() +{ + Serial.begin(9600); + setTime(8,29,0,1,1,11); // set time to Saturday 8:29:00am Jan 1 2011 + // create the alarms + Alarm.alarmRepeat(8,30,0, MorningAlarm); // 8:30am every day + Alarm.alarmRepeat(17,45,0,EveningAlarm); // 5:45pm every day + Alarm.alarmRepeat(dowSaturday,8,30,30,WeeklyAlarm); // 8:30:30 every Saturday + + + Alarm.timerRepeat(15, Repeats); // timer for every 15 seconds + Alarm.timerOnce(10, OnceOnly); // called once after 10 seconds +} + +void loop(){ + digitalClockDisplay(); + Alarm.delay(1000); // wait one second between clock display +} + +// functions to be called when an alarm triggers: +void MorningAlarm(){ + Serial.println("Alarm: - turn lights off"); +} + +void EveningAlarm(){ + Serial.println("Alarm: - turn lights on"); +} + +void WeeklyAlarm(){ + Serial.println("Alarm: - its Monday Morning"); +} + +void ExplicitAlarm(){ + Serial.println("Alarm: - this triggers only at the given date and time"); +} + +void Repeats(){ + Serial.println("15 second timer"); +} + +void OnceOnly(){ + Serial.println("This timer only triggers once"); +} + +void digitalClockDisplay() +{ + // digital clock display of the time + Serial.print(hour()); + printDigits(minute()); + printDigits(second()); + Serial.println(); +} + +void printDigits(int digits) +{ + Serial.print(":"); + if(digits < 10) + Serial.print('0'); + Serial.print(digits); +} +Note that the loop code calls Alarm.delay(1000) - Alarm.delay must be used +instead of the usual arduino delay function because the alarms are serviced in the Alarm.delay method. +Failing to regularly call Alarm.delay will result in the alarms not being triggered +so always use Alarm.delay instead of delay in sketches that use the Alarms library. + +Functional reference: + +// functions to create alarms and timers + +Alarm.triggerOnce(value, AlarmFunction); + Description: Call user provided AlarmFunction once at the date and time of the given value + See the Ttime library for more on time_t values + +Alarm.alarmRepeat(Hour, Minute, Second, AlarmFunction); + Description: Calls user provided AlarmFunction every day at the given Hour, Minute and Second. + +Alarm.alarmRepeat(value, AlarmFunction); + Description: Calls user provided AlarmFunction every day at the time indicated by the given value + +Alarm.alarmRepeat(DayOfWeek, Hour, Minute, Second, AlarmFunction); + Description: Calls user provided AlarmFunction every week on the given DayOfWeek, Hour, Minute and Second. + +Alarm.alarmOnce(Hour, Minute, Second, AlarmFunction); + Description: Calls user provided AlarmFunction once when the Arduino time next reaches the given Hour, Minute and Second. + +Alarm.alarmOnce(value, AlarmFunction); + Description: Calls user provided AlarmFunction once at the next time indicated by the given value + +Alarm.alarmOnce(DayOfWeek, Hour, Minute, Second, AlarmFunction); + Description: Calls user provided AlarmFunction once only on the next DayOfWeek, Hour, Minute and Second. + +Alarm.timerRepeat(Period, TimerFunction); + Description: Continuously calls user provided TimerFunction after the given period in seconds has elapsed. + +Alarm.timerRepeat(Hour, Minute, Second, TimerFunction); + Description: As timerRepeat above, but period is the number of seconds in the given Hour, Minute and Second parameters + +Alarm.timerOnce(Period, TimerFunction); + Description: Calls user provided TimerFunction once only after the given period in seconds has elapsed. + +Alarm.timerOnce(Hour, Minute, Second, TimerFunction); + Description: As timerOnce above, but period is the number of seconds in the given Hour, Minute and Second parameters + +Alarm.delay( period) + Description: Similar to Arduino delay - pauses the program for the period (in miliseconds) specified. + Call this function rather than the Arduino delay function when using the Alarms library. + The timeliness of the triggers depends on sketch delays using this function. + +Low level functions not usually required for typical applications: + disable( ID); - prevent the alarm associated with the given ID from triggering + enable(ID); - enable the alarm + write(ID, value); - write the value (and enable) the alarm for the given ID + read(ID); - return the value for the given ID + readType(ID); - return the alarm type for the given alarm ID + getTriggeredAlarmId(); - returns the currently triggered alarm id, only valid in an alarm callback + +FAQ + +Q: What hardware and software is needed to use this library? +A: This library requires the Time library. No internal or external hardware is used by the Alarm library. + +Q: Why must I use Alarm.delay() instead of delay()? +A: Task scheduling is handled in the Alarm.delay function. +Tasks are monitored and triggered from within the Alarm.delay call so Alarm.delay should be called +whenever a delay is required in your sketch. +If your sketch waits on an external event (for example, a sensor change), +make sure you repeatedly call Alarm.delay while checking the sensor. +You can call Alarm.delay(0) if you need to service the scheduler without a delay. + +Q: Are there any restrictions on the code in a task handler function? +A: No. The scheduler does not use interrupts so your task handling function is no +different from other functions you create in your sketch. + +Q: What are the shortest and longest intervals that can be scheduled? +A: Time intervals can range from 1 second to years. +(If you need timer intervals shorter than 1 second then the TimedAction library +by Alexander Brevig may be more suitable, see: http://www.arduino.cc/playground/Code/TimedAction) + +Q: How are scheduled tasks affected if the system time is changed? +A: Tasks are scheduled for specific times designated by the system clock. +If the system time is reset to a later time (for example one hour ahead) then all +alarms and timers will occur one hour later. +If the system time is set backwards (for example one hour back) then the alarms and timers will occur an hour earlier. +If the time is reset before the time a task was scheduled, then the task will be triggered on the next service (the next call to Alarm.delay). +This is the expected behaviour for Alarms – tasks scheduled for a specific time of day will trigger at that time, but the affect on timers may not be intuitive. If a timer is scheduled to trigger in 5 minutes time and the clock is set ahead by one hour, that timer will not trigger until one hour and 5 minutes has elapsed. + +Q: What is the valid range of times supported by these libraries? +A: The time library is intended to handle times from Jan 1 1970 through Jan 19 2038. + The Alarms library expects dates to be on or after Jan1 1971 so clocks should no be set earlier than this if using Alarms. +(The functions to create alarms will return an error if an earlier date is given). + +Q: How many alarms can be created? +A: Up to six alarms can be scheduled. +The number of alarms can be changed in the TimeAlarms header file (set by the constant dtNBR_ALARMS, +note that the RAM used equals dtNBR_ALARMS * 11) + +onceOnly Alarms and Timers are freed when they are triggered so another onceOnly alarm can be set to trigger again. +There is no limit to the number of times a onceOnly alarm can be reset. + +The following fragment 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 +} + -- cgit v1.2.3