Low Power with Event based software architecture brainstorm

Status
Not open for further replies.

neilh20

Well-known member
Hi I'm curious if anybody else is working extending the lower power paradigm with an event driven model.

I've got the Teensy31 and kudos to Paul for putting together a low cost and low power simple board with the powerful Kinetis MK20DX
I've done a product in the past with a custom Mega2560 (not an Arduino Mega) and used the TinyOs.net paradigm of simple cooperative tasks, event timers and low power paradigm - ideal for wireless sensor networks.
TinyOs uses nesC (network embedded systems C), an extension of 'C' but with simple tasking and split-phase event processing that is elegant. However the tools aren't progressing (suck),has an academic focus, and though powerful for small ram ~2-8K systems - thankgoodness for the MK20's 100Kbytes.

The product I designed is a solar powered water depth gauge for streams - ADC12bits or RS485 of water depth readings, and north wards into the internet Digi XBEE or Cellular GSM SOMs.
I'm looking to transfer it over to the Teensy31/MK20DX, and first doing the wireless GSM SOM that has an XBEE footprint - and fits with a Teensy3+XBee board.

There have been some nice Teensy3 posts on low power.
The other part of low power is the software architecture.
It seems to me this needs to be an event driven model with sleep 99.9% of the time in-between "tasks".
Most of the Arduino code - and its fantastic to see the ideas - is evolved to poll for events.
In contrast for low power design the asynchronous event driven model lets the software be stimulated by events.
In the poll model 99.999% of time is spent polling for an event, which uses (wastes) power.

IMHO, for most software architectures, co-operative tasking is quite adequate - providing the tasks are designed to be reasonably short (in the time domain) - and provides clear benefit for simple design and system stability, compared to the potential issues from pre-emptive multi-tasking.
Pre-emptive multitasking is required when "Hard real-time deadlines" are needed - which are specific design cases - and require greater attention to system stability and debugging.

So I'm wondering if anybody has some pointers on using a C++ class with embedded threads and software timers.?
A thread being a procedure that appears to be executed independently - ie invoked from a scheduler on some criteria - post from another procedure or timer, and has low ram/constructor overheads.

What I've done so far:
The teensy3core/IntervalTimer is good start for software timers, and prototyping the concept of input ISR event to post to a thread, with processing on an array of software timers, and then with expired timers posting to other classes threads. I've prototyped it however getting stuck on the posting to other Class threads.

Tasking references so far, mostly are in 'C' and don't translate to a concept of a C++ class with an embedded thread very well

http://www.codeproject.com/Articles/21114/Creating-a-C-Thread-Class - seems like a reasonable starting point
https://github.com/benhoyt/protothreads-cpp - more tools for a simple design

https://code.google.com/p/arduino-scoop-cooperative-scheduler-arm-avr/ - possible starting point
- complex, not structured to able to separate the different classes, not event driven, and partly therefore not clear the level of stability

http://corsi.dei.polimi.it/distsys/2012-2013/pub/17-wsn.pdf -Pg78 is an overview of TinyOS simple threads
https://github.com/tinyos/tinyos-main/blob/master/tos/system/SchedulerBasicP.nc - simple robust fast scheduling algorithm in 'nesC'
http://bharr.is/2013/10/11/toolchain-kinetis-KL25/ Comparision Nuttx, mbed and Contiki - all 'C', selects Nuttx for ease
Nuttx/Contiki/RTOS - complex and benefit if required to use always on TCP/IP, but not for low power system


So throwing it out there, and hoping the feedback can be focused on minimal processing power usage and design.
 
one way to keep it simple... single stack. main() calls a list of state machines. Each state machine's state variable is registered in such as way that interrupt handlers can look at the state and elect to change it.
Each state machine is simply a switch statement.
This is a form of cooperative scheduling.
Preemption is where things get needlessly complex for simple embedded problems.
FreeRTOS with preemption option disabled is easy to use, well documented.
Opinion: contiki is mess abandoned by grad students some years ago. Same, for tinyos.

So one of the forms of cooperative schedulers is what I suggest. FreeRTOS is one, ChibiOS is another (both have Teensyduino ports here).
A multi-state machine scheduler with ISR influence is not many lines of your own code and thus easy to understand.

There is a too-crude scheduler in the Arduino libraries.

The single stack schedulers need each task to (at init) malloc() memory for a local structure for persistent variables. Else declare locals as static but then the function (task) is not reentrant and there can be no 2nd instances or dynamic instances.
 
Last edited:
one way to keep it simple... single stack. main() calls a list of state machines. Each state machine's state variable is registered in such as way that interrupt handlers can look at the state and elect to change it.
You know that may keep it really simple and deterministic to implement the decoding of running the tasks from a switch and each case deals with 'C' or 'C++', and then taskIds can be posted through a simple interface - and if its at interrupt it is very fast.
The switch automatically builds a table to be fast and minimal code.
I can use the scheduler of my choice - in this case the TinyOs SchedulerBasic to guarantee the tasks run if posted and add a priority mechanism for a limited set of tasks to always run first in a priority order if posted.
When all tasks complete then sleep.
Very simple solution compared to trying to make it work for a general case.
I've got most of it prototyped. Thanks for the thoughts. :)
 
Look to at the port of Scoop for Teensy.
I can provide a scheduler I wrote years back called OPEX. It is a cooperative run to completion scheduler. Dynamic task creation. Scheduler keeps track of each task's name, status and a pointer used to point to a C struct in which a task instance can store persistent variables. These run to completion approaches have a problem in that any function called from the main task function must not do an OS sleep or wait for semaphore or wait for I/O; only the main can do that, in a single stack scheme.

I always circle back to FreeRTOS - small and simple and for me, well understood.
 
In this message, I'm going to talk about only 1 piece of this puzzle: calling C++ objects from C-only interrupts.

Just yesterday I wrote a tiny library which needed to do this. Here's the code:

https://github.com/PaulStoffregen/PulsePosition

In this case, ftm0_isr() actually services 9 different interrupts. There's an interrupt when the timer overflows. Each of the 8 channels (corresponding to a specific pin controlled by that timer) can generate an interrupt too. In this particular library, I needed each of the 8 channel interrupts to call different C++ objects, which needed to run as true C++ object code.

The main challenge is interrupts are pure C code, not C++ objects. The solution is to store a static pointer (or reference) to the C++ object. In this code, I used a static array of 8 pointers declared as a private static variable, and the interrupt function as a "friend" so it's allowed to access it. The static array could also have been just a static variable only in that .cpp file (in hindsight, that would have been simpler...)

To be more specific, the static variables were:

Code:
        static PulsePositionInput *list[8];
        static uint8_t channelmask;

Here "list" is just an array of 8 pointers. When the object initializes itself, one of the pointers is assigned a value:

Code:
        list[channel] = this;
        channelmask |= (1<<channel);

C++ keyword "this" is used to get a pointer to the current object. That's the key bit of magic needed to be able to later call the C++ object from a pure C interrupt. The "this" pointer is stored to a static pointer, which the pure C code can use to call the C++ object.

Then inside the interrupt, the code is:

Code:
        uint8_t maskin = PulsePositionInput::channelmask;
        if ((maskin & 0x01) && (FTM0_C0SC & 0x80)) PulsePositionInput::list[0]->isr();
        if ((maskin & 0x02) && (FTM0_C1SC & 0x80)) PulsePositionInput::list[1]->isr();
        if ((maskin & 0x04) && (FTM0_C2SC & 0x80)) PulsePositionInput::list[2]->isr();
        if ((maskin & 0x08) && (FTM0_C3SC & 0x80)) PulsePositionInput::list[3]->isr();

Since this is C-only code (but being compiled with the C++ compiler), to access static stuff from objects, the variable name needs to be prepended with the object name, "PulsePositionInput::". But only static variables in the object can be accessed from the C-only code.

Remember, this single interrupt function actually services 9 different hardware interrupts. Those if conditions check if an object has requested service from a particular interrupt, and if that interrupt has actually occurred (the hardware sets a bit in each channel's register to indicate that channel triggered the interrupt). If both are true, the pointer is used to call the object.

Calling the C++ object through it's "this" pointer means the C++ code runs as an actual C++ object with access to all that specific object's non-static member variables. Several different objects may be created if the user wishes to have this library run multiple simultaneous inputs, so calling through the "this" pointer allows the C-only interrupt to execute the specific C++ object functions, which then handle the actual I/O stuff using that specific object.
 
Last edited:
Thanks... I'll take a look. Looks good at a glance: list[n]

In prior projects, I've had the run time instance of an object pass or store into an ISR's linked-list, each address upon creation of each instance. The goal was to avoid statics and allow for run time additions of callbacks' addresses.
 
This intrigues me, where does the Low Power paradigm come in to this discussion? As far as I know, no RTOS that are ported for the teensy have any hooks for low power operation, correct me if I'm wrong? The next generation ChibiOS kernel (3.0) will support a Tickless Kernel but thats a way's off and the Teensy ports seems not be that active anyway. I think until there is some "supported" RTOS or some type of scheduler you just have one off port that is not supported very well. That being said look at the arm "Sleep-On-Exit" feature when entering low power mode, this would be an event driven model. Then what events (irq's) should be considered since the K20 have a plethora of options.

One thing that would be nice for the Teensy arm platform is figuring out how to correctly handle the USB when using the low power features. I have hit my head against the wall many times trying to get the Teensy to play nice with the USB using low power. I just don't know enough about how the USB protocol works to figure it out nor do I have time.

As far as TinyOS, I did a lot of work with NesC with the Crossbow Mica2 and found it was not supported at all. So if you are going to port its scheduler over, all i have to say is good luck!
 
I have hit my head against the wall many times trying to get the Teensy to play nice with the USB using low power. I just don't know enough about how the USB protocol works to figure it out nor do I have time.

Eventually I'll have the time to look into this. Maybe not until later this year or even early 2015, but sooner or later I can...

But reproducible test cases are elusive, especially for Windows. If you could document a good test case, with the exact steps, it would really help. Please keep in mind I don't use Windows and I'll test starting from clean install, so all the specific setting needed and steps needed to cause the problem are essential.

In the past, I've heard reports of problems on Windows, but when I put a fresh install on a laptop for testing, it doesn't happen. So many troubles seem to occur on "well used" machines, but not a clean install. Sometimes the troubles only happen it some setting changes, which was probably set up long ago on the troublesome machine, but isn't when I start from fresh.
 
If one has a USB connection for data/power, why does one care about low power operation (e.g., sleeping the Teensy CPU) ? I'm thinking in terms of a battery powered Teensy - where there wouldn't be a USB data connection.
 
Please keep in mind I don't use Windows and I'll test starting from clean install, so all the specific setting needed and steps needed to cause the problem are essential.

I don't use windows either, you would think with all the money they made windows wouldn't be so buggy.
 
If one has a USB connection for data/power, why does one care about low power operation (e.g., sleeping the Teensy CPU) ? I'm thinking in terms of a battery powered Teensy - where there wouldn't be a USB data connection.

for one, any debugging you might do, you might have jedi mastery and not need to debug but us common folks need a little help sometimes;)
 
Low Power is typically a design requirement IF running off batteries or solar powered.
I'm suggesting if low power usage is a design requirement then the software needs to be an event based design methodology. Design program flow to only process the events. When there is no queue of events, put the processor to sleep. When an event happens - usually a timer, but could be other async real world events - wake the processor up, process the event, then go back to sleep.
I was impressed with some of the low power usage that other people have reported getting with the Teensy3, and the hardware is a low power design - so it needs the right software processing to decide when to power down - and not use power.
My experience with low power is about managing power usage over time.
Calculate the largest power consumption, and then aim for the rest of the usage to be 10-30% of that power usage.
If there is a cellphone that takes 1A for 1minute @4.2V to deliver the power - then the peak power usage becomes how often does that cellphone need to communicate - if every 15minutes (avgUsage=1/15Ahrs), 1hr(1/60Ahrs), 4hrs (1/240Ahrs~4mAHr) ?
So if the average power consumption using a cellphone is ~ 4mAHrs, being able to get the rest of the power consumption to less than 0.4mAhrs is probably pretty adequate.
If the rechargeable battery is somewhere about 2200mA hrs, (ignoring other inefficiencies) there could be about 400Hrs /2weeks operation from a standard LiIon 2.2 battery.

USB Master mode is inherently a very busy bus and required to delivery up to 0.5A when on.
If the USB is powered off most of the time, and only turned on when needed - effectively simulating a plugin - data accessed fast and then power to the USB turned off - and if the off time is considerable, say 5-15minutes- it could be very low average power.

I've used the analog 4-20mA 2 wire interface. That is supply power from a 12V(boosted) to the 4-20mA two wire, it takes at least 4mA but may go up to 20mA - so long as the sensor stabilizes quickly, say within a second, a reading can be made - and then the 12V power turned off.

So low power is all about managing different parts of the power usage cycle.

I'm porting a design I did from the TinyOs framework to Teensy3 and plan to post it on github. The overall feature is an input sample (multiple ADCs etc ) on a variable/provisionable time (say 15minutes) and variable CellPhone/Xbee output, with an integrated debugging/tracing framework.
The core scheduler features are simple scheduler with guaranteed run when a task is posted. The scheduler is managed from a software timer framework, and operates on a variable timer tick. When all tasks complete it will go into low power. The "tasks" or "threads" are simply procedures/fn() invoked round robbin in the order posted at a timer tick, with a selection of tasks able to operate on a higher precedence.
 
Last edited:
Status
Not open for further replies.
Back
Top