Arduino has a great software platform, lots of libraries and so on. But if you need to program something more complex than a single routine, it could bring some sincere headache synchronizing all the bits of code you might need.
Then I found ArdOS, here's a quote from their website:
ArdOS is a powerful multitasking operating system for the Arduino series of microcontroller boards centered around the Atmel ATmega 168, 328, 1280 and 2560 microcontrollers.
ArdOS lets you define tasks for which get executed in multi-tasking fashion, providing a true sleep
function which lets
the currently running task to be suspended and resumed after a given interval, much different from the delay
provided
by the standard Arduino library.
Getting started with ArdOS
First install the ArdOS library in the usual Arduino library location. Restart the IDE.
My first try at ArdOS didnt' go very far, as I was using and Arduino Leonardo see section below, luckily I have a few Arduino's around so I quickly moved to my trusted old Duemilanove board, where I started coding a basic sketch:
#include "kernel.h"
#define NUM_TASKS 2 // we must know in advance how many
/* simple sleep forever task */
void task1(void* p){
while (1){
OSSleep(500);
}
}
/* simple sleep forever task */
void task2(void* p){
while(1){
OSSleep(50);
}
}
/* regular Arduino setup */
void setup()
{
OSInit(NUM_TASKS);
// Create the tasks
OSCreateTask(0, &task1, NULL);
OSCreateTask(1, &task2, NULL);
// Start the scheduler
OSRun();
}
/* mandatory Arduino loop */
void loop()
{
// Nothing
}
The sketch doesn't do much, but you can immediately notice few things:
- both
task()
functions loop forever, one sleeping every 50ms, the other every 500ms - the
loop()
body is empty, but required for compiling - creating multi tasking apps is damn easy
Now let's say we want to blink a led connected at LED_PIN on a fixed rate, while echoing the serial. So let's add a definition for it and change the task1, task2 and setup functions:
#define LED_PIN=6
void task1(void* interval){
int pause = (int) *interval;
while(1){
digitalWrite(LED_PIN, HIGH);
OSSleep(pause);
digitalWrite(LED_PIN, LOW)
OSSleep(pause);
}
}
void task2(void* p){
while(1){
while(Serial.available()){
Serial.write(Serial.read());
}
OSSleep(100);
}
}
void setup()
{
OSInit(NUM_TASKS);
Serial.begin(57600);
pinMode(LED_PIN,OUTPUT);
// Create the tasks
OSCreateTask(0, &task1, (void*) 100);
OSCreateTask(1, &task2, NULL);
// Start the scheduler
OSRun();
}
As you can see we can deal with as many concurrent tasks we need, having them share independetly the MCU. The immediate benefit is that code is much cleaner this way.
Furthermore, the first parameter of OSCreateTask
is the priority of each task. In our example that would mean that Task 2
would be awaken from sleep only if Task 1 has given up execution using OSSleep itself, thus guaranteeing a more precise timing.
If you have many tasks this also gives you the possibility to choose which one are more important and which could be delayed a bit without any problem.
But the library offers much more functionality:
- Semaphores: both binary and counter, allow checking when some fixed resource is over.
- Queues: allow to transfer data between tasks in a synchronized and respecting priority.
- Mutexes: allow to grant exclusive access to a resource to one task at a time.
I plan to discuss in more detail all of these components and provide more advanced examples in further posts.
Note: Doesn't work on Arduino Leonardo
As mentioned in the project description, the OS is designed around Atmel ATmega 168, 328, 1280 and 2560, so if you have an Arduino Leonardo, using ATmega 32u4 you cannot use it.
Here's what you get compiling the sources:
/Users/fiore/Documents/Arduino/libraries/ArdOS/kernel.cpp: In function 'void configureTimer()':
/Users/fiore/Documents/Arduino/libraries/ArdOS/kernel.cpp:471: error: 'TCCR2A' was not declared in this scope
/Users/fiore/Documents/Arduino/libraries/ArdOS/kernel.cpp:472: error: 'TCNT2' was not declared in this scope
/Users/fiore/Documents/Arduino/libraries/ArdOS/kernel.cpp: In function 'void startTimer()':
/Users/fiore/Documents/Arduino/libraries/ArdOS/kernel.cpp:482: error: 'TCCR2B' was not declared in this scope
I found out that some timer registers have changed and currently investigating a possible solution to port it. For example this post tells to rename the registers in order to make use of timer no. 4 instead of no. 2.
Comments !