Timer lib available?

I will try and get the merges done this week, both 4.9 into develop and then the gateway branch into develop, then we can start adding this. There have been some good fixes in the branch so far, so adding the Timer library will probably justify a new release, so we should be able to get to 1.1.49 quickly

Could we wrap it so the same API calls are available and a timer.stop stores the timer settings so when start is called again it gets re-created and started on the fly? Dispose becomes the app_timer_stop without saving the rest.

Since the millis() counter is tied to an app_timer id say we don’t expose any stop_all functionality unless that has a Millis safeguard in it.

Hopefully this isn’t a massive change but I like the idea of being able to start a timer and pass a scheduler handler at the same time. Timers for things we want to happen on a fairly tight interval and if something of value comes of that to be able to pass that into the loop for processing would be awesome. Going back to my “don’t publish if it is the same routine” a sensor could be read, if the value matches we go back to sleep immediately and I have the gateway republish the last dataset. Of course there would need to be a check to make sure Bluz is still connected and online with a protection to publish irrespective of dataset at some set interval (every 5 sensor readings for example). Maybe that’s squeezing things too much for battery life but I want to hermetically seal a Bluz into a watertight casing and that means replacing sensors overtime. Every additional day/hour of battery will matter.

@LukeUSMC, I think that should be left to the user code IMO. I don’t see the need for a stop_all() given its all-or-nothing approach. Keeping things simple and as true to the Softdevice API is the best approach. The same is done with the Software Timer API on Particle staying true to the FreeRTOS timer API.

One thing to note about timers is that the start and stop are scheduled events, not immediate. So “tight” timing will be tricky but not impossible. There will always be a +/- 3 ticks accuracy as outlined in the Nordic docs, plus whatever overhead is imposed by the HAL. All in all, exposing the app_timers and scheduler is a big plus IMO. :smile:

Agreed, stop_all breaks stuff and there is no Particle equivalent anyways, it shouldn’t be exposed via the Timer API. As for squirreling away a timer’s config when timer.stop calls app_timer_stop I was thinking from the perspective of Particle API alignment and code portability. It would allow current code that uses those calls to be moved to Bluz with fewer changes to the user code.

The only way to get tight timing (without jitter and the such) on the nrf51 as far as I can tell would be to start the HFQCLK which is a battery hog and that will take some getting used to by people but I wonder if eventually if that shouldn’t be exposed as well. Not all bluetooth devices are battery powered so power hungry clocks wouldn’t matter for those use cases. Perhaps it is a different mode like so: System.mode(HFQCLK_ENABLE)?

@LukeUSMC, another approach for the Timer API is to keep things true to Softdevice and create a wrapper class library that runs in user space instead of HAL/system space. That gives both portability and flexibility IMO though my opinion is “lite” at best :wink:

I also agree that anything that can affect power/performance should be exposed. I think clock control is something sorely missing on Particle though one member did write up a great topic on doing it. :smile:

So what is the proper location (i.e. file) to wrap app_timer into Timer API? Other than stop and dispose the rest is a pretty easy mapping from one to the other. @eric can provide ultimate direction on which API should be mostly adhered to, I have no strong opinion on it myself. I have a preference for keeping Particle to Bluz same::same as much as possible but the user code changes wouldn’t be that big of a deal.

1 Like

This is sorely needed, as it would seem to be a requirement for the best practices noted on the docs.bluz.io front page, which state that “Any necessary code, from Spark functions to sensor inputs can be handled through interrupts.”

Last night I tried to do:

#include "blynk/blynk.h"

void keepBlynkConnected() { Blynk.run(); }
Timer blynkTimer(2000, keepBlynkConnected);

void setup() {
  Blynk.auth("key")
  blynkTimer.start();
}

void loop() {
  System.sleep(SLEEP_MODE_CPU);
}

This failed miserably due to missing Timer support and I think missing Blynk library support. Consider adding a “low power hello world” example with interrupts to the getting started guide. Blynk support is a separate concern that I’ll take to another thread.

Yes, Timer support is definitely on the list for the next release. We have all the gateway shields out the door now, so we can start working on that and hopefully have it out quickly.

As for Blynk support, that is a but out of our hands, it will fall to the roadmap of their team. We have sent them hardware and so hopefully we can get a timeline on this soon.

1 Like

In the meantime, I tried to include app_timer.h and use app_timer_create, etc. Couldn’t get it to work. Has anyone else done this successfully?

The _create function returns an error code 8 which equates to NRF_ERROR_INVALID_STATE

… which (according to header file code comments) means, “Invalid state, operation disallowed in this state”

… and for which the Nordic API docs state, concerning the app_timer_create function specifically, “Application timer module has not been initialized.”.

But that cannot be true.

I’ve tried it in setup() and as a one-shot in loop().

So, it seems I’m stumped before I even got out the gate.

Did you copy the files to your app code?

The way we dynamically link from the user app to the system firmware, this wouldn’t really work. The way to add it is to add the functionality at the HAL layer in the system firmware, and then implement it in the platform/ section of the firmware. Then the functions get exposed to the user app through a special table.

Once I get the gateway firmware updating done, I will start working on the next release and we can try to prioritize this feature. I will let you know as soon as I start adding it into the repository

1 Like

Thanks again @eric. Just FYI – no need to reply to this.

I was just trying to make it work for a one-off project, without having to go down to the firmware level. But never mind that.

I might mess about with the HAL, just for fun. If I get something working and looking right at the code level, I’ll throw a pull request at you – just in case you haven’t started that TODO by then. :wink:

1 Like

@eric – I’ve written some code and got it all compiling.

It is designed to follow the familiar Particle::Timer class pattern …

RTCTimer myTimer(2000 /*ticks*/, handlerFunction);
RTCTimer myOtherTimer(2000 /*ticks*/, handlerFunction, true /* oneshot */);
...
void loop() {
    myTimer.start();
    myOtherTimer.start();
...
}

EDIT2: I bricked my bluzDK, by using the s120 softdevice instead of the s110. Duh! Thanks to @eric (again!) for getting me back on track.

So … back to testing/debugging and improving my RTCTimer stuff, as above. Not sure about the class name. Maybe it should simply, Timer, since millis() is using the LF clock now anyways.

A pull request will eventuate. Hold tight. :wink:

1 Like

Well @eric, it doesn’t work – for just exactly the same reason my Web IDE test version didn’t (see above).

The call to the softdevice app_timer_create() always returns error code 8, NRF_ERROR_INVALID_STATE.

This implies that the softdevice’s app timer has not in fact been initialised. And yet we see that it is, in hw_config.c.

So, I’m stumped.

Incidentally, I failed to come up with anything worthy of adding to the HAL for this. The SD’s Application Timer (app_timer.h etc) is already within the HAL layer, far as I can tell. So I’m just calling it from platform land … example …

void RTCTimer::start()
{
  if (!this->timerID) 
    this->errorCode = app_timer_create( &this->timerID, APP_TIMER_MODE_REPEATED, this->callbackFunc);

  if (this->timerID)
    this->errorCode = app_timer_start(this->timerID, this->timerInterval, this->callbackContextPointer);

};

Maybe that has something to do with it though? Try as I have, I failed to find documentation on how the HAL and that, “special table” you mentioned is supposed to work. It bothers me that I’m just calling underlying firmware functions directly from the platform layer – thus bypassing the HAL pattern entirely – but only in as much as this is arguably (in this case) not best practice. It should still work, right?

Of course, I have assumed that the app_timer_init (macro thereof) in hw_config.c was in fact called and that the SD’s app timer is expected to be running by the time we get to setup(). After all, the app timer is what drives other internal BluzDK timing, right? Alas, the error code I get suggests otherwise.

I’m confused and completely stumped, for now. :-/

Is your code posted somewhere? I am a little confused how you added this, perhaps it would help if I take a look.

@eric … Code locations in my forked repo are at …
rtc_timer.h
rtc_timer.cpp (firmware implementation)
applications/rtc_timer/rtc_timer.cpp (test app)

EDIT[zillionth]: I forgot to push my repo earlier. Current version is now on GitHub [2016-05-04 01:18 UTC]

EDIT: I have confirmed that the error code 8 – NRF_ERROR_INVALID_STATE is returned when and only when the app timer has not been initialised …

    if (mp_nodes == NULL)
    {
        return NRF_ERROR_INVALID_STATE;
    }

… where mp_nodes is the array of app timer entities, which is set up during app_timer_init() and which I am sure is being called during system start-up.

So, I really am bamboozled. How could mp_nodes end up back at NULL? There has to be some kind of funky pointer offset thing going on here – something to do with the user-land vs. system-land crossover, as a rougher than rough guess. But I can’t find it.

Oh and @peekay123, I now understand that the app timer is being used to drive the millis counter, flash the LED and so on but not drive the app scheduler – despite the code comment that says it is – not directly, anyway. The app scheduler is however being used. But it’s manually triggered (ticked over) as part of the MCU sleep/standby/power management and system events manager loop. That is to say … the scheduler as implemented in Bluz is by no means time domain accurate and is therefore of no use to us for the task at hand. But I see no reason why the app timer itself cannot get us what we want … if only I understood why it’s not currently showing up in user land. Soon! Soon …

1 Like

@eric, @peekay123 … after two days of pawing over dynalib related code and finally finding modules/bluz/system-part1/module_system_part1_export.ld … I’ve got this timer thing working!! No more error code 8’s. Yay \o/

Now I have to re-write my previous attempt in a similar manner, back in my forked develop branch, so I can throw a pull request at @eric when I’m done.

In the end, we should get 5 useable timers, suitable for low power applications.

Watch this space. :slight_smile:

3 Likes

@eric, @peekay123 … I might have to give up. I haven’t the foggiest idea in the world how to successfully pass a pointer across binary domains (or whatever we call system-part1 vs. user-part) in this “dynalib” system.

I’ve pushed the working, HAL complient code to my GitHub repo …

rtc_timer.h
rtc_timer.cpp (firmware implementation)
applications/rtc_timer/rtc_timer.cpp (test app)

… plus the HAL related files …

apptimer_hal.h
apptimer_hal.c
hal_dynalib_apptimer.h
module_system_part1_export.ld

When I say, “working” I mean that the timer itself is working fine … but the attempt to call the function handed to it …

Timer testTimer(200000, thisCallBackFunctionWillNeverBeCalled);

… goes horribly wrong. I naively just used old fashioned C function pointers. It’s obvious why that doesn’t work. At the very least, the linker needs to know some offset to add, to get to the right place. I could do that manually in a small project of my own … but not in this complex dynalib ecosystem.

OH AND … currently, the "timer interval argument is not milliseconds – hence the 200000 timer interval above, which is about 5 seconds, just guessing. Just some math needed, obviously.

I’ve come a long way in two days with the whole dynalib thing, entirely without any documentation. But I really, really, REALLY need a LOT of help to get past this bump. Probably best that someone else who knows how just does it, so I can hopefully learn.

2 Likes

@gruvin, I don’t think the dynalib setup in hal_dynalib_apptimer.h is correct. I’ll try to explore this weekend. :wink:

@peekay – Oh … that’s interesting. So … maybe we can export some kind of pointer reference in there and then sort of proxy through that, to cross domains. Hmmm. Time to go look more closely at the macros in dynalib.h perhaps.

1 Like