Problems with long-delay Ticker / suggestions for firing at certain time

06 Jan 2012

Hi,

I'm new to mbed and its been a very long time since I wrote any c/c++ too. I'm having some issues with understanding the Ticker.

I am trying to make something which reads a sensor every hour and post it over HTTP. But...I can't make the Ticker fire every hour. I removed all the sensor code and the HTTPClient code, but it still doesn't work. Instead what is happening is that the handler is called first one hour after startup, and thereafter every 25mins, 15seconds. Can anyone tell me why?

As a slightly different note, eventually I need to be able to make a callback happen at a certain time, no matter what the startup time, for example if I wanted to turn on some lights at 9pm every day and off again at 12pm. I read in another post that Tickers don't work for more than 1hr 11min so I realise that I could calculate the time offset between now and 9pm, but I still can't attach that delay to a ticker by the sound of it.

TIA, Paul

#define SENSOR_RATE 3600.0
#define GUID_LEN 36

#include "mbed.h"
#include "EthernetNetIf.h"
#include "NTPClient.h"

EthernetNetIf eth;
NTPClient ntp;

Ticker sensorTicker; // ticker to poll sensors
bool pollSensor = 1; // initially 1 so we get one entry immediately

void pollSensors_cb()
{
    pollSensor = 1;
}

/**
 * Function to poll the temperature and post to HTTP server (GAE)
 */
void pollSensors_handler()
{
    
    char * timeStr;
    time_t ctTime;
    
    // get current time
    ctTime = time(NULL);
    timeStr = ctime(&ctTime);
    
    // is this needed?
    int i = 0;
    while (timeStr[i] != '\0') {
        if (timeStr[i] == '\n') {
            timeStr[i] = ' ';
        }
        i++;
    }
    printf("\r\npollSensors_handle called at (UTC): %s\r\n", timeStr);
    
    wait(20);
}

int main()
{
    time_t ctTime;
    ctTime = time(NULL);  
    printf("\r\nStartup time (UTC): %s\r\n", ctime(&ctTime));
    
    
    
    // start networking via DHCP
    eth.setup();
    
    // set up time via NTP   
    Host server(IpAddr(), 123, "0.uk.pool.ntp.org");
    ntp.setTime(server);
    ctTime = time(NULL);
    printf("NTP time (UTC): %s\r\n", ctime(&ctTime));  
    
    // set up ticker for publish sensor data
    sensorTicker.attach(&pollSensors_cb, SENSOR_RATE);
    
    ctTime = time(NULL);
    printf("Starting super-while at (UTC): %s", ctime(&ctTime));
    
    while(1)
    {
        // test if pollSensor flag has been triggered
        if (pollSensor)
        {
            pollSensor = 0;
            pollSensors_handler();
        }
    }
}
06 Jan 2012

Hi Paul,

Yes, you are right, the timers/tickers are only really designed for us-second durations; they all use a shared running 32-bit modulo counter running at 1us/tick, so whilst you can use as many timers as you want at any time to time/trigger things, the delay for a particular tick/timeout can't be greater than approximately 30 minutes (2^31/1x10^6 seconds) .

For minutes/hours, the real time clock/time functionality is a good bet, as it is designed to work on seconds and foreverer(!).

Note, the real-time clock can't trigger an interrupt using the mbed API unfortunately. This reminds me that it was discussed and some ideas for an API but it never got added to the todo list; would be interesting if it would achieve what you want, as it probably should be:

In the mean time to get something working, there are probably a couple of solutions depending on desires of quickest vs. neatest:

  1. Set a ticker to tick every minute, and in the handler, check the realtime clock using time()
  2. Write to the underlying RTC peripheral's match registers to setup an interrupt to be triggered (i.e. dive in to the LPC17xx User Manual/Low Level)

Hope that is helpful, and would be interested in your feedback,

Simon

06 Jan 2012

I think the API suggested in the other post would work perfectly for what I'm trying to achieve. For convenience I suppose it would be nice to have an auto-reschedule option:

rtc.attach(ptr func, time_t time, float delay);

or even just

rtc.attach(ptr func, float delay);

where triggering the alarm would automatically reschedule it to time(NULL) + delay. I guess this would make it analogous to the Ticker.

In the meantime, your first suggestion seems to work nicely. As I'm basically a beginner at c++, and still exploring the various APIs, I'm doing the 9am alarm as follows. Can't really imagine a way to add this to an API without making a general DateTime class such as those that exist in C#/Java.

// set an alarm for 9am
time_t alarmTime;
struct tm * timeinfo;
timeinfo = localtime(&ctTime);
//if 9am is past...
if (timeinfo->tm_hour > 9)
{
    // schedule for tomorrow
    ctTime = ctTime + 60*60*24;
    timeinfo = localtime(&ctTime);
}
// now we are on the right day
timeinfo->tm_hour = 9;
timeinfo->tm_min = 0;
timeinfo->tm_sec = 0;

// convert it back to something we can test
alarmTime = mktime(timeinfo);    
        
printf("Setting 9am alarm for %s", ctime(&alarmTime9));