TimeAlarms scheduler

Has anyone experimented with porting Arduino’s TimeAlarms (source on GitHub)?

This library is a cron-like time-based scheduler, with a typical usage of something like Alarm.alarmRepeat(time, callback). Alarms can either be continuous or one-off.

@peekay123’s Spark-Interval-Timer is close, but our use case requires more than three scheduled tasks (~5-6 at any given time) and longer intervals (Spark-Interval-Timer is limited to a max of ~30 seconds; our intervals can be anywhere between a day to a year).

Any ideas on how best to approach this?

I just took a quick 30 seconds look at the code and it seems OK for porting.

Give it a shot or give me some coffee? :smiley:

EDIT:

So i gave it a shot and used the SparkTime library by @bko for the Time.h. However, some #define are not there and i have no idea why. Maybe @bko should take a look at this porting.

There’s not much compilation errors to be honest. Just some missing declaration of macros and some missing types.

Someone more experienced might be able to get it working faster than me for sure :smile:

I am looking at this now. Thanks for the ping!

Folks, here is a port of the arduino Timer library that might fit the bill :slight_smile: It uses intervals instead of actual time.

OK So I looked at the TimeAlarms library. Most of the issues were easy to correct, like the missing #define's but some of the problems are more structural.

The alarm code is built to interface to the Arduino time library which is function and not class based. There are lots of places where the alarms code calls time functions. In C++ this would be better done with a friend class. The option today for my library would be give the alarm constructor a handle to SparkTIme so it could do rtc->now() instead of just calling now().

So I think it can and should be ported but I think waiting just a bit for the Spark team to finish their time library which is about 7/8’s done now and should be done in the current Sprint. Then it will be easy to change the TimeAlarms library to match the new Spark library.

3 Likes

Just a quick update on this–I have this working but only when the Spark cloud time zone is set to 0 (i.e. GMT). I am going to add a method to the alarms library to set the time zone and get it out soon.

I still think Spark needs an internet controlled version of this and I am working on that in parallel.

Hey Everybody in the TimeAlarms thread!

I put the ported library on github and added it to the webIDE Library feature! Everyone should now have access to it. I included the example program as well.

The ported TimeAlarms library works with the new built-in real-time clock on the Spark core, Time.now().

4 Likes

@bko, this is a really important library to have so NICE JOB! :ok_hand:

2 Likes

Brilliant, thanks bko! Using it now, works perfectly so far

1 Like

@bko would this library still require an occasional time sync as suggested in the docs? http://docs.spark.io/firmware/#time-spark-synctime

Hi @scottsweb,

Yes, I recommend calling Spark.syncTime() at least once a day. Otherwise the local clock drifts up to about +/- 10 seconds per day on my core. I don’t think calling it more that once an hour is beneficial. If you need more exact time, you can check out my older NTP-based Spark Time library, but it consumes more resources and doesn’t current work with TimeAlarms (but that would be easy to fix).

It would probably be bad for the Spark cloud if we all called syncTime() at the same time at midnight GMT or something, so you probably want to call it based on relative time from when the core powered up.

1 Like

@bko Excellent. Thanks for the clarification and for your work on the library. I can see myself making use of this in many projects.

Greetings All!

Receiving compile errors with TimeAlarms:

In file included from ../inc/spark_wiring.h:30:0, from ../inc/application.h:31, from /SparkTime.h:4, from /SparkTime.cpp:31: ../../core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning "Defaulting to Release Build" [-Wcpp] In file included from ../inc/spark_wiring.h:30:0, from ../inc/application.h:31, from /TimeAlarms.h:6, from /TimeAlarms.cpp:27: ../../core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning "Defaulting to Release Build" [-Wcpp] /TimeAlarms.cpp: In member function 'void AlarmClass::updateNextTrigger()': /TimeAlarms.cpp:62:48: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] /TimeAlarms.cpp:73:48: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] /TimeAlarms.cpp: In member function 'AlarmID_t TimeAlarmsClass::alarmOnce(long int, OnTick_t)': /TimeAlarms.cpp:120:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] /TimeAlarms.cpp: In member function 'AlarmID_t TimeAlarmsClass::alarmRepeat(long int, OnTick_t)': /TimeAlarms.cpp:136:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] /TimeAlarms.cpp: In member function 'long int TimeAlarmsClass::getNextTrigger()': /TimeAlarms.cpp:326:31: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] /TimeAlarms.cpp: In member function 'AlarmID_t TimeAlarmsClass::create(long int, OnTick_t, uint8_t, dtAlarmPeriod_t, uint8_t)': /TimeAlarms.cpp:332:46: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] In file included from ../inc/spark_wiring.h:30:0, from ../inc/application.h:31, from /nis_0.1.cpp:2: ../../core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning "Defaulting to Release Build" [-Wcpp] In file included from /nis_0.1.cpp:5:0: /SparkTime.h:48:12: error: expected ')' before '.' token /SparkTime.h:56:11: error: expected ')' before 'tnow' /SparkTime.h:56:11: error: expected ')' before 'tnow' /SparkTime.h:56:11: error: expected ')' before 'tnow' make: *** [/nis_0.1.o] Error 1

SparkTime compiled ok. As you would expect, if#include "TimeAlarms.h" is commented out in the .ino file the compile errors go away.

Hopefully something simple.

Hi @xPonic

TimeAlarms is ported to work with the built-in real-time clock that syncs from the cloud connection ( Time.now() ).

I could port it to work with my SparkTime NTP based library but the built in real-time clock uses fewer resources.

Thank you @bko! No port necessary.

1 Like

Thank you for the port @bko!

1 Like

@bko and anyone else. I’m getting a compile error with this library as automatically imported in the web IDE. If I comment out the include line, it compiles perfectly. I haven’t even tried to set up any alarms yet, just include and hit compile. The following is the tail of the error log. Please let me know if you need more:

^
In file included from doorbell.cpp:2:0:
doorbell.cpp: In function 'void setup()':
TimeAlarms/TimeAlarms.h:14:15: error: expected unqualified-id before '(' token
#define now() (Time.now()+time_zone_cache)
^
doorbell.cpp:55:38: note: in expansion of macro 'now'
digitalWrite(doorbellPin, LOW);
^
doorbell.cpp: In function 'void processSchedule()':
doorbell.cpp:143:52: warning: NULL used in arithmetic [-Wpointer-arith]
}
^
In file included from doorbell.cpp:2:0:
TimeAlarms/TimeAlarms.h:14:15: error: expected unqualified-id before '(' token
#define now() (Time.now()+time_zone_cache)
^
doorbell.cpp:147:60: note: in expansion of macro 'now'
void processSchedule() {
^
doorbell.cpp:145:13: warning: unused variable 'workingTime' [-Wunused-variable]

^
make: *** [doorbell.o] Error 1

Error: Could not compile. Please review your code.

I would like to use this, as apparently writing a recurring scheduler from scratch is hard indeed, and I can probably use this without a problem. Thanks for taking a look.

EDIT: If I comment out all calls to Time.now() this compiles fine. Which Time library must I use, if not the one provided in the core of spark?

EDIT 2: (Workaround) If I rename all instances of Time.now() to just now() it seems to compile fine, flash fine, and the code seems to work. This is nice and all, but it breaks the convention of using Time.now() doesn’t it?

EDIT 3: So I may have figured out the problem with now() vs Time.now(). I don’t know how to solve it, unfortunately, but this can help someone that does know how:

This exists in TimeAlarms.h:

extern time_t time_zone_cache;  // from spark_wiring_time.cpp
#define now() (Time.now()+time_zone_cache)

So in my ino file, when I type:

Time.now()

it is getting replaced in the compiler with:

Time.(Time.now()+time_zone_cache)

Because Time class has no (Time.now()+time_zone_cache) method (even if it did, that syntax is invalid), it gives a compiler error and refuses to compile.

By replacing all references of Time.now() with just now() I get a compiling program, but the times are all whacky. For instance, when I call Time.zone(-5) to set my timezone to central time, the spark reports the time as 20:41 when in fact it is 01:41.

Anyone have an idea? Am I doing this wrong, or is there a bug in the library?

Hi @damccull

OK, definitely a bug but no one has run into it before since you call Alarm.delay(N); in loop to make the alarms update. I put in this macro to avoid editing the library in a million call sites for now() but it is hurting you in your code.

Try adding this after the #include for the library in your code:

#undef now()

If that works I will put it in the library and republish it.

Sorry!

Hi @bko. Thanks for checking on this for me. Here’s my results:

When adding Time.now() back in to replace instances of now() I get the correct time, calibrated for my set timezone again, and it compiles and runs properly.

I do have another issue I’d like your advice on. No matter which way I do it (with or without the #undef now()) I still can’t get the alarms to fire. I might be doing it wrong.

I have a global struct array:

struct ScheduledTime schedule[112];

Then in setup() I have this loop for testing:

for(int i = 0; i < 112;i++) {
    schedule[i].tstamp = Time.now() + (i * 2);
    schedule[i].active = (i%2 ? true : false);
    if(schedule[i].active) {
        AlarmId aid = Alarm.alarmRepeat(schedule[i].tstamp,EnableDoorbell);
        Spark.publish("Alarm set", "Alarm ID: " + String(aid) + " -- " + "Alarm Time: " + Time.timeStr(schedule[i].tstamp));
    } else {
        Alarm.alarmRepeat(schedule[i].tstamp,disableDoorbell);
    }
}

When I monitor the events for this device, all the alarms set to EnableDoorbell report their ID as 255, and none of the alarms (regardless of their task) actually go off. The beginning of my loop() function is:

Alarm.delay(0);

if(doorbellEnabledSchedule) {
    digitalWrite(D7, HIGH);
} else {
    digitalWrite(D7, LOW);
}

At the bottom of my file I have these two callback functions:

void EnableDoorbell() {
    doorbellEnabledSchedule = true;
    Spark.publish("doorbellState","enabled:" + String(schedule[scheduleCurrentEntry].tstamp));
}

void disableDoorbell() {
    doorbellEnabledSchedule = false;
    Spark.publish("doorbellState","disabled:" + String(schedule[scheduleCurrentEntry].tstamp));
}

With this code in appropriate places, it never executes the callbacks. I never get events published, nor a blinking LED.

Hi @damccull

I don’t the library works properly with Alarm.delay(0);

It has a while loop that will never service the alarms if the value is zero. Try a small positive number and see if that works.