Multitasking and real time systems at Arduino
to make your sketch more professional
Real time operating system
A real-time operating system (RTOS) is an operating system (OS) intended to serve real-time application requests. It must be able to process data as it comes in, typically without buffering delays. Processing time requirements (including any OS delay) are measured in tenths of seconds or shorter.
It's not a secret that the Arduino has small system resources. Is it possible to run RTOS at Arduino? Fortunately, yes! There is some RTOS which can run at Arduino. We have used some of them and our choice is ChibiOS.
ChibiOS/RT is designed for deeply embedded real time applications where execution efficiency and compact code are important requirements. This RTOS is characterized by its high portability, compact size and, mainly, by its architecture optimized for extremely efficient context switching.
What plus we get when use RTOS
All examples which you see when study Arduino or KSduino is a Linear program. The basic part of program are placed to Loop function and run from its begin to the end. There is a delay at the end of loop when our Arduino does nothing. This kind of program for similar tasks, for example: we read some sensors one time per second. What should we do if we need read light sensor every ten milliseconds and read temperature sensor one time per five seconds. We can write this program in standard loop using millis function and much if which will perform this task. This program will work but the loops code will very long with much conditions. Programs like this is not readable, heavily modified and may contain difficult to debug errors.
When we use RTOS we can create separate processes (threads). Each of the processes looks like separate loop function. So we can create a program with much loops each of they will execute its separate task. In our example the first process will read light sensor with quick delay the second process will read temperature sensors with long delay. Both of this process will send information to KSduino.
Setup RTOS and create sketches
How the RTOS for Arduino looks and what we will use to create programs? The ChibiOS is a simple library which should be copied to your Arduinos library folder. And the program is simple Arduino sketch created in Arduino IDE.
OK, Let us download ChibiOS and create your first ChibiOS Arduino sketch!
We get the ChibiOS from http://code.google.com/p/rtoslibs/ page. This page contain information about other RTOS and link for official ChibiOS website. There is direct download link, we use version ChibiOS20130208: http://ksdu.in/o/45 You can download also this archive from the KSduino website: http://ksduino.org/downloads/ChibiOS20130208.zip
The ChibiOS and KSduino example
We modify example from previous article: Pseudo multitasking. Using timer in Arduino sketch In this example we create process:
Thread 1 for sending parameters to KSduino
Thread 2 for calculate DS18b20 temperature
Thread 3 for read KSduino answers
To synchronize KSduino tasks we use semafor so in one time moment only one KSduino task can run.
// Includes #include "SPI.h" #include "EthernetUdp.h" #include "OneWire.h" // one wire library to connect ds18b20 #include "ksDS18B20.h" // subclass to read ds18b20 values #include "KSduino.h" // KSduino Library #include "ChibiOS_AVR.h" // ChibiOS library // DS18B20 address uint8_t devAddr1[] = { 0x28, 0x16, 0x05, 0xB2, 0x03, 0x00, 0x00, 0x00 }; // Define OneWire, DS18B20 & Timer classes OneWire ds(7); // OneWire on pin 7 ksDS18B20 ds18b20_1 (&ds, devAddr1); // KSduino User definition ----------------------------------------- // This Arduino ID and Password // Change this values to yours unsigned int deviceID = 0000; // set your Device ID unsigned int devicePwd = 1111; // set your Device Password // This Arduino MAC, IP,DNS, Gateway and port // Change this values to yours byte mac[] = { 0x74,0x69,0x69,0x2D,0x00,0x00 }; byte ip[] = { 192, 168, 1, 222 }; //byte dns[] = { 192, 168, 1, 1 }; //byte gateway [] = { 192, 168, 1, 1 }; //byte subnet [] = { 255, 255, 255, 0 }; unsigned int port = 40000 + deviceID; // KSduino Server definition ---------------------------------------- // Server address & port byte serverIp[] = { 178,63,53,233 }; // Global IP address unsigned int serverPort = 9930; // Port number // ---------------------------------------------------------------- // Define KSduino class KSduino ksd (deviceID, devicePwd, serverIp, serverPort); // Define some KSduino parameters with KSduinoParameter class KSduinoParameter d1 (&ksd, "d1", 0.5, 15*60); KSduinoParameter f1 (&ksd, "f1", 0.5, 15*60); // Semaphore to trigger context switch Semaphore sem; // Idle time counter volatile uint32_t count = 0; //------------------------------------------------------------------ void setup() { ksd.begin(mac, ip, port); // start KSduino // ksd.begin(mac, ip, dns, gateway, port); // ksd.begin(mac, ip, dns, gateway, subnet, port); // Setup ChibiOS chBegin(mainThread); } //------------------------------------------------------------------------------ // thread 1 - this task send KSduino parameters // 160 byte stack for KSduino task, beyond task switch and interrupt needs static WORKING_AREA (waThread1, 160); static msg_t Thread1(void *arg) { #define WAIT_FOR_ANSWER 2 for (;;) { // Create D1 test value int d1_generator = random(1,1000); byte d1_value = d1_generator < 10 ? 1 : 0; // will high ones per second // Send values to KSduino if something changed if (d1.check (d1_value) || ds18b20_1.getTemp () && f1.check(ds18b20_1.celsius)) { chSemWait(&sem); // only one KSduino task should run at this moment ksd.beginPacket (); d1.addParameter (d1_value); f1.addParameter( ds18b20_1.celsius); ksd.endPacket (WAIT_FOR_ANSWER); chSemSignal(&sem); } chThdSleepMilliseconds(10); } return 0; } //------------------------------------------------------------------------------ // thread 2 - calqulate DS18b20 thermometer every second // 64 byte stack beyond task switch and interrupt needs static WORKING_AREA (waThread2, 64); static msg_t Thread2(void *arg) { for (;;) { ds18b20_1.readTermometer(); chThdSleepMilliseconds(1000); } return 0; } //------------------------------------------------------------------------------ // thread 3 - KSduino update // 160 byte stack for KSduino task, beyond task switch and interrupt needs static WORKING_AREA(waThread3, 160); static msg_t Thread3 (void *arg) { for (;;) { chSemWait(&sem); // only one KSduino task should run at this moment ksd.update (); chSemSignal(&sem); chThdSleepMilliseconds(5); } return 0; } //------------------------------------------------------------------------------ void mainThread() { // initialize semaphore chSemInit(&sem, 1); // start blink thread chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO + 1, Thread1, NULL); // start print thread chThdCreateStatic(waThread2, sizeof(waThread2), NORMALPRIO + 1, Thread2, NULL); // start ksduino send thread chThdCreateStatic(waThread3, sizeof(waThread3), NORMALPRIO + 1, Thread3, NULL); // Main thread (iadle thread) loop for (;;) { // must insure increment is atomic // in case of context switch for print noInterrupts(); count++; interrupts(); } } //------------------------------------------------------------------ void loop() { // not used }
To use this example you need one DS18b20 sensor: https://ksdu.in/o/3h This sketch is using ksDS18B20 class. You can download this example with ksDS18B20 class at: http://ksdu.in/o/46 Best regards, Kirill Scherba The founder and main developer of KSduino project. Sea also: How to skip repeated parameter values? Pseudo multitasking. Using timer in Arduino sketch











