TimeAlarms library

Hi,

How to use the TimeAlarms library?

The code below is a spin off from the Time Check example. The idea behind it is to set an alarm once a day. The date and time is read from the linino and saved as integers.

My code (which doesn’t work – seems to be related with time.h) is consuming too many resources. Is there a way that the linino can push to the board the date and time every so often as integers?

TIA

/*
  Time Check

  Gets the time from Linux via Bridge then parses out hours,
  minutes and seconds for the Arduino using an Arduino Yún.

  created  27 May 2013
  modified 21 June 2013
  By Tom Igoe

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/TimeCheck

*/


#include <Process.h>
#include <Time.h>
#include <TimeAlarms.h>

Process date;                 // process used to get the datetime
int days, months, years, hours, minutes, seconds;  // for the results
int lastSecond = -1;          // need an impossible value for comparison

void readDateTime() {
  if (!date.running()) {
    date.begin("date");
    date.addParameter("+%d/%m/%Y-%T");
    date.run();
  }
}

void setDateTime() {
  //if there's a result from the date process, parse it:
  while (date.available() > 0) {

    // get the result of the date process (should be hh:mm:ss):
    String timeString = date.readString();
    timeString.trim();

    // find the slashes /
    int firstSlash = timeString.indexOf("/");
    int secondSlash = timeString.lastIndexOf("/");
    int firstDash = timeString.indexOf("-");

    // find the colons:
    int firstColon = timeString.indexOf(":");
    int secondColon = timeString.lastIndexOf(":");

    // get the substrings for date and time
    String dayString = timeString.substring(0, firstSlash);
    String monthString = timeString.substring(firstSlash + 1, secondSlash);
    String yearString = timeString.substring(secondSlash + 1, firstDash);
    String hourString = timeString.substring(firstDash + 1, firstColon);
    String minString = timeString.substring(firstColon + 1, secondColon);
    String secString = timeString.substring(secondColon + 1);

    // convert to ints,saving the previous seconds
    days = dayString.toInt();
    months = monthString.toInt();
    years = yearString.toInt();
    hours = hourString.toInt();
    minutes = minString.toInt();
    lastSecond = seconds;          // save to do a time comparison
    seconds = secString.toInt();
  }
}

void setup() {
  Bridge.begin();        // initialize Bridge
  Console.begin();    // initialize Console

  while (!Console);              // wait for Console Monitor to open

  // run an initial date process. Should return:
  // m/d/yyyy - hh:mm:ss European format
  readDateTime();
  setDateTime();
  delay(250);

}

void loop() {

  setTime(hours, minutes, seconds, days, months, years);

  displayDateTime();

  Alarm.alarmRepeat(20, 22 , 0, sayHello); // hours, minutes, seconds, function
}

void displayDateTime() {
  // display date and time
  if (lastSecond != seconds) { // if a second has passed
    // print the datetime:
    Console.print("The actual date & time is: ");
    Console.print(days);
    Console.print("/");
    Console.print(months);
    Console.print("/");
    Console.print(years);
    Console.print("-");

    if (hours <= 9) {
      Console.print("0");  // adjust for 0-9
    }
    Console.print(hours);
    Console.print(":");
    if (minutes <= 9) {
      Console.print("0");  // adjust for 0-9
    }
    Console.print(minutes);
    Console.print(":");
    if (seconds <= 9) {
      Console.print("0");  // adjust for 0-9
    }
    Console.println(seconds);

    // restart the date process:
    readDateTime();
  }

  setDateTime();
}

void sayHello() {
  Console.println("Hello World");
}

Wow, that's a lot of code just to print out a message once per day.

You are using the Linux side of the Yun as a dumb RTC. Why not put it to use and greatly simplify things?

You can do almost all of the processing in a Python script using much less code. Python supports a datetime object that can store dates and times, get the current date and time, and do comparisons between them. No reading the system time, no parsing the result into individual fields, and no field-by-field comparisons.

I would start by writing a Python script to get the current date, and set up a datetime object that represents when you want to the alarm to trigger: combine today's date and the desired time. Now, enter an infinite loop where it gets the current date and time, and checks to see if it is greater than or equal to the alarm date and time. if not, sleep for a few seconds and repeat. If it is at or after the alarm time, print out a message indicating that the alarm triggered, add one day (a timedelta object of 24 hours) to the alarm time, and repeat. The net result is that you will have a Python script that runs forever, and prints out an alarm message once per day. I don't think this will be more than about a dozen lines of code. You could easily handle multiple alarms by making an array of alarm datetime objects, and looping through it -- in this case, the message indicating an alarm going off could simply be the array index of the alarm object. It is also easy to repeat at other intervals, just add a timedelta of the appropriate amount. For example, adding timedelta(hours=6) will cause the alarm to repeat every 6 hours.

With that working, in the script you get rid of the Time and TimeAlarms libraries. Keep your date Process object, but in setup() have it run your new script with the runAsynchronously() method. Then, in loop, check date.available(), and if you get any data it will be the alarm message printed from the script - read and parse the value and act accordingly. This will also be a very short sketch.

Doing something like that will be less code overall, and will leave lots of free resources for your sketch. The sketch and the Python code together will be shorter than what you have now.

The TimeAlarms library was written for a garden variety Arduino. With the Linux side being there on the Yun, you've got a much more powerful system that can do the same thing in a much simpler manner. Make use of the Linux processor's power.

Thanks ShapeShifter, will look into it.

I'm just starting a new project myself, a lighting controller that looks up sunrise/sunset times and controls a light output. It's going to be all in Python, with the sketch just being a dumb I/O controller. I have a basic idea of what I'm going to do (using datetime and timedelta objects like I just mentioned.) As I work out the details and get things working, I'll post here (unless you beat me to it.) My project sounds very similar to yours, except that instead of a single repeating alarm, I have separate on and off times.

What I end up doing should map rather closely to what you need.

I spoke too soon... it is apparent that my project is quickly going too grow to big/complicated to be an effective sample. So I tried something very simple...

alarm.py:

#!/usr/bin/python -u

import time
import datetime
from datetime import datetime, timedelta

# Set the period of the alarm
interval = timedelta(seconds = 10)

# Set the alarm to the current date and time, plus the alarm period
alarm = datetime.now() + interval

# Repeat forever...
while (True):

   # Check whether the alarm time has expired
   if (datetime.now() >= alarm):
      # The alarm time has arrived, print out a message
      print "A"

      # Now, set up for the next alarm time
      alarm = alarm + interval

   # Sleep a little so we don't use up all of the CPU time
   time.sleep(1)

As this is currently written, it will print out the letter "A" every 10 seconds. (A short interval like that is much easier to test than waiting 24 hours for each iteration.) One important subtlety in the code: note that it starts with the shebang "#!/usr/bin/python -u" which tells it to run Python when this script is run. The important part is the "-u" which is a Python command line parameter which tells Python to run in ubuffered mode. This will allow the messages that are printed to show up right away. Normally, Python is buffered, and it won't actually show any output until the script finishes, or the buffer fills up (which will take a very long time when data is sent so infrequently.)

Sketch:

#include <Process.h>

#define LED 13

Process alarm;

void setup()
{
  // Control the built in LED
  pinMode(LED, OUTPUT);

  // Turn on the LED while setting things up
  digitalWrite(LED, HIGH);
  Bridge.begin();

  // Start the Python process to run forever
  alarm.begin("/mnt/sda1/alarm.py");
  alarm.runAsynchronously();

  // All done setting up, turn off the LED
  digitalWrite(LED, LOW);
}

void loop()
{
  // Did we get any data from the alarm process?
  while (alarm.available())
  {
    // Did we get the alarm message from the Python script?
    if (alarm.read() == 'A')
    {
      // Pulse the LED
      digitalWrite(LED, HIGH);
      delay(1000);
      digitalWrite(LED, LOW);
    }
  }
}

Setup starts the bridge, and then runs the Python script asynchronously. In loop, it simply checks whether there is any data from the script, and if there is, and it is the 'A' command sent by the script, then blip the LED on for a second.

Running this sketch with the above script, the LED comes on for a few seconds while things are set up, and then it blinks on every 10 seconds. (If you just applied power, the initial LED on time will be about a minute while it waits for the Linux processor to boot up.)

Now, if you want the alarm to repeat every day, then set the interval to one day or 24 hours. Either of the following will work:

interval = timedelta(hours = 24)
interval = timedelta(days = 1)

And if you want the alarm to trigger at a specific time of day, rather than being dependent on the time the script started, then explicitly set the alarm time, doing something like this:

now = datetime.now()
alarm = datetime(now.year, now.month, now.day, 18, 30, 0)

That will set the first alarm to trigger at 6:30:00 pm today.