You are on page 1of 11

 MENU

DIGITAL EDITION SUBSCRIPTION


EVERYTHING FOR ELECTRONICS

Learn Electronics Projects Reviews High Voltage Science Microcontrollers

Vintage Electronics Radio/Wireless Ideas/Tips/Inspiration Most Popular History of Electronics

Test Equipment

A REAL-TIME OPERATING
SYSTEM FOR THE
ARDUINO
By
Joe Jaworski
  
View In Digital Edition   LATEST
NEWSLETTERS
» Skip to the Extras MARCH 12, 2021

What if you had multiple loop( ) sections in your Arduino sketch, all Wirespondence, Serial
running at the same time? With an RTOS, this and more is possible. Bluetooth with a Micro,
Analog Front End, AM/FM
Receiver Restoration
WHAT IS AN RTOS?
Wirespondence!
An RTOS (Real-Time Operating System) is a software component that lets you rapidly
switch between different running sections of your code. Think of it as having several Using Serial Bluetooth
loop() functions in an Arduino sketch where they all run at the same time.
With a Microcontroller
Everyone has experienced a multi-tasking operating system. On your smartphone or PC,
you can be playing a game while you get an incoming call or text message. At the same
Why You Need an
time, you might be downloading a software update. All these tasks appear to be running Analog Front End and
at the same time but, in fact, there is only one CPU that is switching back and forth How to Set It Up
rapidly to give the illusion of multi-tasking.
Restoring a Vintage
There is one important thing about these operating systems running on your phone or PC Zenith Table Top
in that they are not “real time” operating systems. There’s no way to predict when the text
application will alert you after you receive a text or that the game you’re playing will
AM/FM Receiver from
redraw the screen. In this environment, it doesn’t matter much. You might see a single the ‘60s
app slow down on your smartphone from time to time as other running tasks hog the
processor’s power.
When it comes to your sketch though, you need things to happen in real time. For MARCH 05, 2021
example, your sketch may be blinking an LED at a specific interval that indicates some
sort of status, and you wouldn’t want it to blink erratically as other tasks are getting Analog Waves from Digital
executed. If you have used a Raspberry Pi, you may have noticed how difficult it is to get Signals, DIY Electric Scooter,
an LED to blink consistently. This is a shortfall of using a non-real time OS. The magic of The Solar Alternative, Driving
an RTOS is that your LED will blink exactly at the time interval you programmed, even LEDs with a Microcontroller
though it’s running other tasks at the same time.

Generating Analog
GETTING STARTED Waves From Digital
Signals
There are many RTOS’s available. The one we’ll be using is called FreeRTOS. It is open-
source software that is free and doesn’t require a license to use it. It has already been Build a DIY Electric
“ported” (see sidebar) to the Arduino’s Atmel processor which makes it very easy to
Scooter
integrate into your sketches. It’s also part of the Arduino libraries, so it’s very easy to
install.
The Solar Alternative
To install FreeRTOS, open the Arduino IDE (integrated development environment). From
Driving LEDs with a
the TOOLS menu, select the “Manage Libraries...” item. You can scroll through the list or
type “FreeRTOS” into the search bar. You’ll see the window shown in Figure 1. Select the
Microcontroller
item “FreeRTOS by Richard Barry” and select the latest version. Click Install. After a few
moments, the installation will be complete.
VIEW ALL >

FROM THE
Q&A
Selected questions from
past Q&A columns.

Attic Fan Controller

Stepper Motor
Overview

FIGURE 1. Installing the FreeRTOS library. Winding Single-Layer


Air Coils

The first thing we need to do is create a task. A task is a thread or a function that runs Wide-Range Current
independently of your other code. You create tasks in the setup() function of your sketch. Regulator
You create a task by calling the xTaskCreate() function which takes several parameters:
Sawtooth Generator
xTaskCreate (

  MyTask,                    // task function name

  (const portCHAR *)”task1”, // human readable name

  128,                       // stack size

  NULL,                      // creation parameters


POPULAR
  1,                         // Priority
STORIES
  NULL                       // returned task handle

);
Wirespondence!
MyTask – This is the name of a function in your sketch that holds the task code. You
RADAR And Electronic
probably want to use a name that describes the function like “blinkLED” or “readSensor.”
You then add a new function into your sketch that looks like this:
Warfare Fundamentals

MyTask(void *)

Wire Wrap Is Alive And


{
Well!
    // code goes here

}
Turing Machines
 

const portCHAR *) “task1” – This is a name for the task. It’s not used at all by the RTOS. 1920s Radio Applause
You can’t use NULL here. It must be a string of characters less than eight. Cards

128 – This is the number of bytes to allocate for the stack. If you’re not familiar with a
CPU stack, it’s a portion of memory set aside to save return addresses and local function
variables when you call a function. If you call a function that calls another function, that
then calls another function, etc., you’ll need more memory for the stack.
LEARNING
ELECTRONICS
Here, we allocate 128 bytes, which is adequate for most sketches. If the stack size is too
low in any of your tasks, the RTOS will blink the LED on the Arduino at four second Need to brush up on your
intervals and stop your program. electronics principles? These
multi-part series may be just
NULL – This is a pointer to any predefined data or structure (defined by you) that you what you need!
would like to pass to the task function when it’s created. To keep things simple, we’re not
using this feature so it’s set to NULL.
Understanding Digital
1 – This parameter sets the task’s priority. It can be from 0 (lowest priority) to 2 (highest Logic ICs
priority). When you have multiple tasks, you can alter the order and priority in which tasks
run. This is an advanced feature. For now, set all your tasks to the same priority. Bipolar Transistor
Cookbook
NULL – This is a pointer to a place to store the newly created task handle. We don’t need
this for a simple sketch. Many RTOS functions will require the task handle when using
Op-Amp Cookbook
more advanced API calls.
FET Principles And
Once the setup() function ends, your tasks are created and they start to run. Figure 2
shows the process.
Circiuits

Triac Principles And


Circuits

Understanding Digital
Buffer, Gate And Logic
IC Circuits

Checking Inductors

Small Logic Gates —


The building blocks of
versatile digital circuits.

Security Electronics
Systems And Circuits

Using Seven-Segment
Displays

FIGURE 2. Flowchart of FreeRTOS tasks. Signal Generators

Think of Loop(), Task-1, and Task-2 all running at the same time. You can put whatever
code you want in each. For example, Task-1 could be displaying characters on an LCD ARCHIVES
display, while Loop() is blinking an LED, while Task-2 is reading a temperature sensor.
April 2017
FIRST SKETCH USING RTOS March 2017

Here’s a simple first sketch using FreeRTOS. We’re going to be blinking the built-in LED on January 2017
the Arduino board while at the same time sending a string of text to the serial monitor.
We’ll be creating two tasks, and just for fun, we won’t have any code in the loop() October 2016
function. You can type in this sketch in the Arduino IDE or download it as RTOS_1.ino as
part of the article.

June 2016
1.    // RTOS_1 1st Example of using FreeRTOS

2.    // by Joe Jaworski View complete


archives list
3.    #include <Arduino_FreeRTOS.h>

4.    const uint8_t LEDPin = 13;  // Built-in LED I/O Pin


RECENT
5.    #define rtDelay(v) {vTaskDelay(v/15);} // delay in mS POSTS
6.    // ***************

April 13 - A Night at the


7.    // setup() function

8.    //

Opera?
9.    void setup()

10.    {
April 13 - 3D Printable
Tractor Beam
11.    // Setup our LED Blinky Task

12.    xTaskCreate
March 07 - Video
13.    (
Games Turn 50
TaskBlink,                      // function name

(const portCHAR *)”blink”,      // task name


March 07 - Make Your
128,                            // stack size
Raspberries Touchable
NULL,                           // No creation parms

2,                              // Priority
January 27 - World’s
NULL                            // returned task handle

Smallest Transistor —
14.    );
Sort of

15.    // Setup our Serial Comm Task

16.    xTaskCreate

17.    (
RECENT
TaskComm,                       // function name

(const portCHAR *)”comm”,       // task name


COMMENTS
128,                            // stack size

NULL,                           // No creation parms

Brad Hines
2,                              // Priority

NULL                            // returned task handle


Hi Ira, I was
18.    ); experiencing similar
problems. See my post
from today. If your
19.    } // End Setup problem is the same,
you'll need to re-flash
the ROM and then turn
20.    // *****************
on device protection to
prevent it from
21.    // loop() function

22.    //
happening...
23.    void loop()

24.    {
Build a Pocket-Sized
Altair Computer · 8
25.    /* Nothing to do here! */
hours ago
26.    } // End loop()
Brad Hines
27.    // **********************

28.    // TaskBlink() function


To be clear, the
29.    //
machine works
30.    void TaskBlink(void*)
flawlessly, until I run a
31.    {
BASIC program that
32.       pinMode(LEDPin, OUTPUT);  // Set LED pin to OUTPUT tries to input a
character from the
serial port.
33.       for(;;)
This used to work but
34.       {
now it hangs the
   rtDelay(500);
machine hard.
   digitalWrite(LEDPin, HIGH);   // Turn on Led

   rtDelay(500);
Build a Pocket-Sized
   digitalWrite(LEDPin, LOW);    // Turn off Led Altair Computer · 1 day
ago
35.       }

36.    } // End TaskBlink Brad Hines

37.    // **********************


Further update. I have
38.    // TaskComm() function
tried: * Replacing the
39.    //
8085 processor *
40.    void TaskComm(void*)
Switching to a really
41.    { short FTDI cable (using
a discrete FTDI
      Serial.begin(9600);     // Open Serial Monitor FT232RL mini-circuit
board) The latter of
42.       for(;;)
these was based on a...
43.       {

   Serial.println(“The LED is blinking now”);


Build a Pocket-Sized
   rtDelay(500); Altair Computer · 1 day
ago
44.       }

David Goodsell
45.    } // End TaskComm

Good point about using


Let’s go through this sketch line by line.
uuF and cps for vintage
tube projects. I'll
Line 3 has an #include <Arduino_FreeRTOS.h> which must be included in every sketch
consider that in the
using FreeRTOS.
future. Glad you
enjoyed my "almost
Line 4 defines a constant which is the I/O pin number of the built-in LED. Note that we’re
useful" project.
using the C99 style declaration of uint8_t which is an unsigned eight-bit value, same as a
byte. It would be good to learn and use these declarations in your sketches because it
The Retro-Shield:
becomes easy to use your code on another CPU. It also shows the size of the variable (in
Where the Past Meets
this case, eight bits) which will help prevent coding errors.
the Present · 1 day ago

On an eight-bit processor like the Atmel, your sketches will run much faster if you use
uint8_t instead of int providing, of course, your data will fit into eight bits. Paul Ramasco

VBA still exists in all

PORTING AN RTOS microsoft office


products. Make sure
you're enabling
An RTOS needs to be “ported” to a particular microprocessor. This is a daunting task
developer mode on the
that requires intimate knowledge of the microprocessor. Porting requires writing code
ribbon in the options in
(usually in assembly language) that integrates with the higher level functions or API of
Excel. I'm using it on all
the RTOS. In this way, the functions remain the same no matter what processor is
my win10/11 systems.
used.

DFT Basics · 1 day ago


The Atmel port uses interrupts and preserves most all of the Arduino’s functions and
libraries without interference. This port uses the AVR watchdog timer for task
switching interrupts. This means if your sketch is also using the watchdog timer, it will
not work properly. All other interrupts should be fine to use. Many sketches that use
FreeRTOS usually don’t need to use interrupts anymore as you can replace ISRs with
tasks.

Line 5 is a bit confusing. It defines a macro called rtDelay(v), where v specifies the
number of milliseconds to delay (see “Why have a special delay function?”). You should
use this instead of the Arduino delay() function within tasks.
WHY HAVE A SPECIAL DELAY
FUNCTION?
The job of an RTOS is to switch back and forth so that all your tasks appear to be
running at the same time. The default run time of any given task (assuming equal
priority) is 15 ms. This is called the tick count. Each task gets a “time slice” of 15 ms
before it’s interrupted by the next task.

Just like most sketches, most tasks need to introduce delays in their code. Using a
special form of delay tells the RTOS that “my task has nothing else to do for the next x
milliseconds, so go run other tasks.” This makes the RTOS aware of the differences
between your code and the dummy loops that make up delays. The RTOS becomes
faster and more efficient.

There is one caveat to be aware of. Task switching occurs at a 15 ms rate, so the
smallest delay is 15 ms, and all other delays are truncated to the nearest 15 ms
increment. So, if you specify rtDelay(100), the delay will actually be 100 / 15 or 90 ms. If
you need precise delays in a task, you could use the delay() function to make up the
difference.

Lines 9-19 are the setup() function. Here, we create two RTOS tasks: one to blink the LED
and the other to write text to the serial monitor.

Lines 23–26 are the loop() function. In this sketch, it’s empty! All the work is being done
in tasks. However, you certainly could put whatever code you needed here without any
restrictions.

Lines 30-36 are the TaskBlink() function. We set the I/O pin connected to the LED to an
output, then blink the LED every 500 ms. Notice that we don’t call the delay() function and
instead call the rtDelay()macro. This informs the RTOS that we need to be idle during the
specified time. It’s best not to use the Arduino delay() function in a task; use rtDelay()
instead.

Notice that the TaskBlink() function is wrapped in a never-ending loop via the for(;;)
statement. This is very important! Tasks never return (where would they return to?) and
will crash if you don’t use an infinite loop around the repeating code.

Lines 40-46 are the TaskComm() function. Here, we initialize the serial port to 9600
baud, then have an infinite loop that displays a line of text every 500 ms as shown in
Figure 3.

FIGURE 3. RTOS_1 sketch serial monitor output.


I recommend that you download or enter the code and run it on either an Arduino Uno or
Nano. It’s quite remarkable to see this sketch in action and still have an empty loop()
function, ready to do anything you need.

EXCHANGING DATA BETWEEN


TASKS
Our second example will modify this sketch so that the serial monitor will accept
commands to set the LED to full on, full off, or blink. We’ll use a global variable that holds
the state of the LED accessed by both tasks.

Using a global variable requires special attention in an RTOS. The problem is we don’t
know exactly when the TaskComm() function or the TaskBlink() function is running at any
given time. (It’s switching back and forth about 66 times a second.) With both functions
having access to the same global variable, one function may be reading/writing to it
while another function is doing the same, resulting in corrupt data. This is not unlike the
problem faced when modifying global variables within an interrupt. We must somehow
tell a task not to use the global variable until another task is done changing it.

The mechanism to do this is called a semaphore. It’s a way to alert all tasks that it must
wait for the semaphore to be available before it can proceed. A semaphore is just a
signal to the RTOS. It doesn’t do anything by itself. Your code defines what a semaphore
does.

Semaphores have give and take functions. When you need to prevent other tasks from
accessing a shared variable, you “take” the semaphore, which blocks all other tasks from
using the variable until you’re done. When you’re finished, you “give” the semaphore back,
which frees others to use it. Between the give and take functions is where you put your
code. You can use a semaphore to protect global variables, sensors, hardware, or
anything else that is accessed by more than one task.

With semaphores in hand, we’ll now modify the first example so that both tasks can use
the global variable. At the top of the file, we add:

#include <semphr.h>

enum uint_8 {

  LED_OFF,                                    // LED ON

  LED_ON,                                     // LED Off

  LED_Blink                                   // Blinking LED

};

volatile SemaphoreHandle_t SemaStatus;        // LED status semaphore

volatile uint8_t g_LEDStatus = LED_Blink;     // Current LED status

The #include <semphr.h> specifies the header file that allows us to link with the
semaphore library. Below that, we create the three states of the LED using the C enum
keyword. We define a variable to hold the semaphore handle, followed by the global
variable g_LEDStatus which is initialized to the LED blink state. Notice that both these
statements use the volatile keyword. This prevents the compiler from optimizing out the
variable, as it does not know that the variable will be modified within the RTOS task
(which, in essence, is an interrupt).

The only change in the setup() function is to create the semaphore with the
xSemaphoreCreateBinary() function and store its handle in the SemaStatus variable:

SemaStatus = xSemaphoreCreateBinary();  // Create semaphore

xSemaphoreGive(SemaStatus);             // must GIVE after creation


We’re creating a “binary semaphore” which can either be given or taken. There are also
“counting semaphores” which is a more advanced feature and not discussed here.

Within the infinite loop of the TaskBlink() function, we add a few lines to manage the
semaphore and get the global variable:

xSemaphoreTake(SemaStatus, portMAX_DELAY);

Status = g_LEDStatus;                      // get a copy

xSemaphoreGive(SemaStatus);

Notice that we “take” it, then make a copy of the g_LEDStatus global variable, then
immediately release it with the xSemaphoreGive(). It’s a good idea not to hold on to a
semaphore for long periods of time, as you force other tasks to stop executing, waiting
for you to release it. Same philosophies as using interrupt routines. You don’t want to
have a lot of code in it and want to return it as soon as possible.

The parameter portMAX_DELAY is passed to the xSemaphoreTake() function. This tells


the RTOS to wait forever until the semaphore is available. You can also specify how long
you want to wait before giving up. In most cases though, your code probably needs the
semaphore data before it can proceed anyway, so using portMAX_DELAY is very
common.

The rest of the source in TaskBlink() uses a switch() statement to determine how to set
the state of the LED.

Changes in the TaskComm() function are mostly to create an interactive environment so


the user can enter data in the displayed menu as shown in Figure 4. We get the new
status for the LED, then “take” the semaphore, write the new value to LEDStatus, then
“give” the semaphore.

FIGURE 4. RTOS-2 sketch serial monitor menu.

If you’re a seasoned Arduino sketch writer, you’re probably thinking you could figure out
how to do all this within the loop() function. Yes, you probably can, but dividing your code
up into separate tasks makes your code very readable so you can easily make changes
(especially six months from now when you forgot what you did).

As your code gets more complicated (especially where you have to have delays but need
to do other things in the meantime), nothing beats an RTOS to manage the situation.

I urge you to visit https://www.freertos.org to learn more about this amazing software
library and all it can do.  NV

For a complete overview of FreeRTOS, go to www.freertos.org.


The detailed FreeRTOS API Reference can be found at www.freertos.org/a00106.html.

DOWNLOADS
download

201906-Jaworski-r1.zip

What’s In The Zip?

Sketches

Like One person likes this. Sign Up to see what your friends like.
Share 11

COMMENTS
11 Comments 
1 Login

Join the discussion…

LOG IN WITH OR SIGN UP WITH DISQUS ?

Name

Share Best Newest Oldest

Chuck Miller − ⚑
⏲ 3 years ago edited
I have down loaded the sketch, but get the following:

error: expected primary-expression before 'const'

2 0 • Reply • Share ›

Chuck Miller > Chuck Miller − ⚑


⏲ 3 years ago edited
OK, here is the expanded error file. Thanks for
you help..

Arduino: 1.8.9 (Windows 10), Board: "Arduino


Nano, ATmega328P (Old Bootloader)"

C:\Program Files (x86)\Arduino\arduino-


builder -dump-prefs -logger=machine -
hardware C:\Program Files
(x86)\Arduino\hardware -hardware
C:\Users\Chuck\AppData\Local\Arduino15\p
ackages -tools C:\Program Files
(x86)\Arduino\tools-builder -tools C:\Program
Files (x86)\Arduino\hardware\tools\avr -tools
C:\Users\Chuck\AppData\Local\Arduino15\p
ackages -libraries
C:\Users\Chuck\Documents\Arduino\libraries
-fqbn=arduino:avr:nano:cpu=atmega328old -
vid-pid=1A86_7523 -ide-version=10809 -build-

see more

1 0 • Reply • Share ›

Chuck Miller > Chuck Miller − ⚑


⏲ 3 years ago edited
Here is the expanded... Thanks for your
expected primary-expression before 'const'

Arduino: 1.8.9 (Windows 10), Board:


"Arduino/Genuino Mega or Mega 2560,
ATmega2560 (Mega 2560)"

C:\Program Files (x86)\Arduino\arduino-


builder -dump-prefs -logger=machine -
hardware C:\Program Files
(x86)\Arduino\hardware -hardware
C:\Users\Chuck\AppData\Local\Arduino15\p
ackages -tools C:\Program Files
(x86)\Arduino\tools-builder -tools C:\Program
Files (x86)\Arduino\hardware\tools\avr -tools
C:\Users\Chuck\AppData\Local\Arduino15\p
ackages -libraries
C:\Users\Chuck\Documents\Arduino\libraries
fqbn=arduino:avr:mega:cpu=atmega2560
see more

1 0 • Reply • Share ›

ForrestErickson − ⚑
⏲ 2 years ago
20200821 I too got the "error: expected primary-
expression before 'const'"

I guess the root cause was FreeRTOS library changed


since the article was written.

I fixed it by adding a line of code to define "portCHAR" as


type CHAR.

Here is the line: #define portCHAR char


I figured this out with the help of this form thread:
https://forum.arduino.cc/in...

I am now getting another error which may have the same


root cause. The library has changed.

Here is error: RTOS_2Example:20:28: error: expected


initializer before 'SemaStatus'

I expect the cure is to define the type of


"SemaphoreHandle_t " but I am to new to figure out how.

see more

1 0 • Reply • Share ›

Edward Andrews − ⚑
⏲ 2 years ago
Joe - good article on Arduino RTOS; I really enjoyed the
read! Your article reminded me of my EE college days
learning software concepts on a DEC PDP-8. At the time,
my college was 'state of the art' as they had several PDP-
8s, one running as a multiuser Time-Sharing system.
Considering that a PDP-8 with its 4K words of 12 bit
memory is considerably less powerful that a typical
Arduino board, it will be interesting to see what can be
done with an Arduino-RTOS combination. I will definitely
be trying RTOS on my next Arduino project! Thanks again
for your article!

1 0 • Reply • Share ›

nutsvolts − ⚑
⏲ 2 years ago
A revised version of the sketches is now available. Since
the Arduino libraries have recently changed, this makes
everything work with the latest version.

1 0 • Reply • Share ›

Kenneth Ciszewski
nutsvolts
> − ⚑

⏲ 2 years ago
So where are the revised sketches located?

1 0 • Reply • Share ›
ForrestErickson
> Kenneth Ciszewski
− ⚑

⏲ 2 years ago
On 20200821 the down load link in
the article above has sketches that
work with my version 10.3.0-9

1 0 • Reply • Share ›

Chuck Miller > nutsvolts − ⚑


⏲ 2 years ago
Super! Many thanks.

This is a great "tool" to have in our bag of


software.

1 0 • Reply • Share ›

Chuck Miller − ⚑
⏲ 3 years ago edited
OK, I now have it working. I had installed the latest
version of FreeRTOS 10.8. It would't compile. I kept
getting the error I mentioned below. I rolled beck to the
version 10.2.0-2 and it now complies and runs..
1 0 • Reply • Share ›

CONNECT
WITH US
  
  

SUBSCRIBE TO
2022 ISSUE-2 2022 ISSUE-1 2020 ISSUE-6 2020 ISSUE-5
OUR
The Birth of the The Transistor BUILD A ONE Restoration of NEWSLETTER
Integrated Compound INCH SCOPE a Vintage
Circuit Pair Zenith G725 Sign Up Now
Vintage Tek:
AM/FM
Build an Build a Farmer, Current
Receiver
Electronic Tic- Fox, Chicken, Limiters
Tac-Toe Game Corn Puzzle Build a Digital
Clock Family
Finding the Vintage Tek:
Using Nextion
Outside Foil Continuously
Displays
Lead Variable
Autotransformers Beginning to
Build with
Tubes

Copyright © 2023 T & L Publications. All Rights Reserved |


Privacy Policy |
Terms & Conditions |
Copyright Statement

You might also like