Time Library added to Playground

A library called Time that provides timekeeping functionality for Arduino has been added to the playground.

This library is derived from the playground DateTime code but has a different API that is intended to be more flexible and easier to use.

This is the first full release of the Library. The API and examples have changed slightly from prototype code posted at the end of December and anyone using the prototype code is encouraged to download the playground version.

See the [u]Playground article[/u] for more on using the Time library.

This looks very good!

Clean and readable code.

Now, I have only one (okay maybe two...) request(s)/concern(s):

I think you should (continue to) prefix your defines with dt or t.
All your macros listed under /* Useful Macros for getting elapsed time */ Time.h line 53+ are potential user defined function names.

Such as elapsedDays, this might be a function name in a sketch that wats to use you Time library, but it's functionality might differ from your macro (so a userdefined function is necessary).

On a more pickyer (is that even a word?) note, I'd also like your tmElements_t to be renamed to a more Arduinoesque TimeElements and that all of its member fields start lowercase.
This could sure be of use for the users of your library.

Good work!

AlhpaBeta, thanks for the (as always) thoughtful suggestions.

Prefixing the macros does reduce the likelihood of name collision but it makes them less Arduino like.

I could change them to inline functions so the compiler will warn if the user provides a function with a similar name. Lets see if there other opinions on how best to address this.

The name tmElements_t was chosen because this structure was really intended for programmers writing time provider code rather than use in a sketch (I wanted to keep the tm prefix to remind programmers that its derived from the C tm structure). But I can certainly change the name if it looks like TimeElements could be useful in a sketch.

Prefixing the macros does reduce the likelihood of name collision but it makes them less Arduino like.

I could change them to inline functions so the compiler will warn if the user provides a function with a similar name. Lets see if there other opinions on how best to address this.

I did not think that the macros might be useful for the library user, then I think your inline function suggestion is the way to go :slight_smile:

The name tmElements_t was chosen because this structure was really intended for programmers writing time provider code rather than use in a sketch (I wanted to keep the tm prefix to remind programmers that its derived from the C tm structure). But I can certainly change the name if it looks like TimeElements could be useful in a sketch.

Well, all ideas I can think of (beyond the helloworldlike clock use-case) would've used such a TimeElements structure.

Maybe a timelapse photo that follows a predetermined 'most traffic of the day' time. Then a day of week, and a time of day datarepresentation would be useful. Your tmElements_t would be a perfect storage container.

Or a simple alarm/eventgenerator with alarms/events triggering each year, each month, each day of week, each hour etc etc..

I can think of many more use-cases, but now. Enough from me, I'll let someone else speak their mind :slight_smile:

Hi mem,

Thanks for this new library! One small problem (at least on Ubuntu 9.04), the

#include <time.h>

in Time.cpp should have a capital T e.g.

#include <Time.h>

otherwise the examples won't compile as they complain about a missing time.h file.

Regards

Ver

Thanks Veronica,

I have fixed this in the download. Please try it and let me know if you still have a problem.

Hi Guys ,
I just got second place in the Arduino contents on instructables - yet I am such a noob (you could all win easy) .
In any case I just did two small projects and want to make a general purpose logger . It will need to run on a bat so it will need to be low power (my second challenge) yet I want it to log time correctly .
I DL the library and got it running but cant get it to sync with my pc (I user the serial port version to sync) .
Do I need to compile some C file and run it so it takes the time from the PC and sends it to Arduino .
I sent it "T111111111" and it did get something what is the acts format ? T [day][month][year]etc' ?
Thanks guys - I hope to contribute too soon (when I get good enough).

Hi Moriszen,

The example sketch expects the time in raw format – it's the number of seconds since Jan 1 1970. This format may seem odd but it is very widely used in computer software (its sometimes called Unix time). There are many web sites that will generate the a value you can use to set the time, for example: http://www.epochconverter.com/

If you past the value into the Serial monitor preceeded by the letter T you will set Arduino time to this value.

For example, pasting the following will set the clock to the time a wrote this reply.
T1262972083

There is also a Processing application in the download that you can run on your computer that will read your computers time and send this to the Arduino example sketch

Thanks for the reply.
I couldn't use the example you added (can't compile in VB.net or C#).
I wrote a simple program in Vb.Net that does it and also tracks the time drift and enables manual sync .
I will try to post it here .. not sure how to ...
Moris

too bad I cant add an image or the exe for the program so others can use it ...

when I press "add image icon" I only get /img ... I guess I am to dumb to use it ...

Here is the source for the VB.Net for it

Add two buttons and two listboxes and this code and you should get what you need:

Public Class Form1
    Public RecString As String
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Me.AddListBoxItem(DateDiff("s", "1/1/1970 0:00 PM", Now))
        'Second(Now) + Minute(Now) * 60 & Hour(Now) * 3600 & 
        SerialPort1.WriteLine("T" & DateDiff("s", "1/1/1970 0:00 AM", Now))
    End Sub

   

    Private Sub Form1_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Leave
        SerialPort1.Close()
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' Show all available COM ports.
        For Each sp As String In My.Computer.Ports.SerialPortNames
            ListBox2.Items.Add(sp)
        Next
    End Sub

    Private Delegate Sub AddListBoxItemInvoker(ByVal item As Object)

    Private Sub SerialPort1_DataReceived(ByVal sender As Object, _
                                         ByVal e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        RecString = Me.SerialPort1.ReadLine()
        Me.AddListBoxItem(RecString)
    End Sub

    Private Sub AddListBoxItem(ByVal item As Object)
        If Me.ListBox1.InvokeRequired Then
            'We are on a secondary thread so delegation is required.
            Me.ListBox1.Invoke(New AddListBoxItemInvoker(AddressOf AddListBoxItem), _
                               item)
        Else
            'We are on the primary thread so add the item.
            'System.DateTimeKind.Utc()
            Me.ListBox1.Items.Clear()
            Me.ListBox1.Items.Add("Arduino:" & vbTab & item)
            Me.ListBox1.Items.Add("PC:" & vbTab & Now.ToString)
            If IsDate(RecString) Then
                Me.ListBox1.Items.Add(DateDiff("s", Now, RecString) & " Secons different")
            End If
        End If
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        If Not (ListBox2.SelectedItem = "") Then
            SerialPort1.Close()
            SerialPort1.PortName = ListBox2.SelectedItem
            SerialPort1.Open()
        Else
            MsgBox("Select a com port to use")
        End If
    End Sub
End Class

This forum only allows links to images and code, you need to upload the item in the link to some other public web site. If you do that I will put a link to your VB app in the playground.

A suggestion for your VB app, if you read a character at time from the serial port you can check for the character 7 which is what Arduino sends when it wants the time. This will set the time without you having to press a button if you send the Time string whenever you received the character 7

Also, you can strip off the Carriage Return / Line Feed at the end of the clock display so you don't get that funny character at the end of the line in the list box.

Your earlier post inspired be to create a new example sketch that allows setting of explicit times from the serial port.

The time can also be set as follows:
S[hh:mm:ss dd mm yy]

Values must be given in order starting from hh
For example: S[14] sets the hour to 2pm, S[14:10] sets 2:10 pm
S[14:10:00 09 01 10] sets the time to 14:10:00 9th Jan 2010

Values must be two digits with preceeding 0 if necessary (eg 05 to set the value 5)
Year must be two digits, 2010 is entered as 10

Opening and closing square brackest are required, colon and spaces are optional.

Here is the new sketch:

/* 
 * NewTimeSerial.pde
 * example code illustrating Time library date strings
 *
 * Time can be set using an 11 character message consisiting of the letter 'T' 
 * followed by the ten digit number of seconds since Jan 1 1970 (Unix time)
 *
 * The time can also be set as follows:
 * S[hh:mm:ss dd mm yy]
 * Values must be given in order starting from hh
 * S[14] sets the hour to 2pm, S[14:10] sets 2:10 pm 
 * values must be two digits with preceeding 0 if necessary (eg 05 to set the value 5)
 * year must be two digits, 2010 is entered as 10
 * opening and closing square brackest are required, colon and spaces are optional
 *  
 * 
 */

#include <Time.h>  
#include <ctype.h>

#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_SET_TAG 'S'    // Indicates start of time set string
#define TIME_SET_StrLen 15  // the maximum length of a time set string [hhmmssddmmyyyy]
#define TIME_REQUEST  7     // ASCII bell character requests a time sync message 

TimeElements te;            // holds the time elements for setting the time 

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() ) 
  {
    char tag = Serial.read();
    if(tag == TIME_HEADER)
      processSyncMessage();
    else if(tag == TIME_SET_TAG)
      processSetTime();   
  }
  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 processSetTime(){
  //[hh:mm:ss dd mm yyyy]
  char setString[TIME_SET_StrLen];
  int index = 0;
  char c = Serial.read();
  if( c != '[')
     return;  // first character must be opening square brackets
  do 
  {
     c = Serial.read();
     if( isdigit(c))  // non numeric characters are discarded
       setString[index++] = c -'0'; // convert from ascii    
  }
  while (c != ']'); // wait for trailing square brackets

  breakTime(now(), te);   // put the time now into the TimeElements structure  
  
  int count = index;
  int element = 0;
  for( index = 0; index < count; index += 2)  // step through each pair of digits
  {
      int val = setString[index] * 10 + setString[index+1] ; // get the numeric value of the next pair of numbers
      switch( element++){
        case  0 :  te.Hour = val; break; 
        case  1 :  te.Minute = val; break; 
        case  2 :  te.Second = val; break; 
        case  3 :  te.Day = val; break; 
        case  4 :  te.Month= val; break; 
        case  5 :  te.Year = val + 30; break; // year 0 is 1970        
      }    
  }
  setTime( makeTime(te));
}

void processSyncMessage() {
  // wait for ten digts, convert to time and update systemTime
  // todo - timeout after say 5 seconds    
  time_t pctime = 0;
  while(Serial.available() < TIME_MSG_LEN-1 )  // time message consists of a header and ten ascii digits      
    ; 
  for(int i=0; i < TIME_MSG_LEN -1; i++)
  {   
    char 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
}

mem - Thanks for the reply and new time set function.
Is there a common external site you guys use here to link to ?
I am thinking of building some king of general purpose logger which I want to run on batteries (1 li-ion bat) but I am afraid if I reduce the clock speed I will loose the functionality of the RTC .
AN example would be loging my body temperature along the day and having the Atmega wake up every min or so and sample the temperature place in flash with current time and go back to sleep . Other examples would be to wake up on external events (open door etc').
B.T.W this is a link to the project I posted on INSTRUCTABLES http://www.instructables.com/id/Arduino-True-Battery-Capacity-Tester-Li-IonNiMH/

Hey mem.

I tried to use your code (from your last post) and the error I get is:
error: 'TimeElements' does not name a type In function 'void processSetTime()':

Any suggestions?
Thank you in advance

skipper,

you may be using an early version of the library. You can either change the following in the sketch:
TimeElements te;
to
tmElements_t te;

or try downloading the latest version of the library.

Thanks mem! library works great with my arduino clock.

Things that helped me: Simplicity of the new library. The adjust time function (rather than using macro). And the proccessing sketch. I'm sure I'll uncover all the benefits of the library eventually.

This seems simple but.. how does adjustment value work? And how would one derive this number, T1262347200 , to equal Jan 1st 2010?

EDIT: Nevermind. It's Unix time. So i just enter the new Unix time into the adjustment function.

Thanks! ;D :smiley:

mem, thank you. It works now.

Cheers

Skipper, good to hear you have it working.

Catcher, adjustment works by offsetting the time by the given number of seconds. For example, adjustTime(-3600) ; sets the clock back 1 hour.

mem ,
The Sw i tried using didnt work for me :frowning: ( I sent you PM with the problem)
But I manged to get the uFAT runing and reading from my SD!!!
cool
But write doesn't work .. tried two SDs - they are not write protected .. but I cant write to them (only read the stuff I wrote in the file on windows) :frowning:
any help ? :-[(no errors...)

moriszen, It sounds like the problem relates to the interface to the SD card rather than something to do with the Time Library. I would like to keep this thread focused on issues relating to the Time library.

Perhaps you can start another thread for a discussion on getting your SD card going.

Hey mem,

Just wanted to thank you for writing this library. I was just sitting down to solve this problem and your library pretty much nailed everything I needed.

It works great.

J

Is there an example somewhere of this library running an alarmed event?