In this two-part tutorial, we will get started with Mbed OS (a powerful and lightweight RTOS from ARM) on recent Arduino boards such as those from the MKR and Nano families. Having Mbed OS integration has many advantages such as first class support for connectivity, security and more. This blog is the second of a two-part series, and shows example programs (including multithreading) running on the Arduino Nano 33 BLE and BLE Sense using Mbed OS. The previous part builds up a simple understanding of an RTOS and shows how to set up the Mbed Core on the Arduino IDE, click here to go to the first part.
Simple Blinky Example
The Blinky program could be considered the “Hello World” of Arduino Programming. In this section, we will rewrite the Blinky example for the Arduino using Mbed OS functions and classes. For reference, the regular Blinky is shown below.
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // set LED_BUILTIN as an OUTPUT pin
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on
delay(1000); // Wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off
delay(1000); // wait for a second
}
Start by creating a new sketch, selecting the “Arduino Nano 33 BLE” under Tools>Boards, and including the mbed.h
header file in the program. Additionally, bring all members of the mbed
, rtos
and std::chrono_literals
namespaces into the current namespace through the using
directive. This is required to be able to use all the Mbed OS functions and classes without having to repeatedly prefix them with mbed::
, rtos::
and std::chrono_literals::
. The code should look as follows –
#include <mbed.h>
using namespace mbed;
using namespace rtos;
using namespace std::chrono_literals;
Unlike in Arduino which uses functions to control the mode and state of the pins, Mbed OS encapsulates this behavior in classes. The following code can be used to create a global object of the DigitalOut
class that controls the state of the pin LED
1. I have named the object led
, but any valid identifier name can be used. LED1, in this case, refers to pin 13 on the Arduino (which contains the built-in LED). A list of all pin numbers and their corresponding Mbed pin names is given at the end of this blog for your reference. The code should now look as follows –
#include <mbed.h>
using namespace mbed;
using namespace rtos;
using namespace std::chrono_literals;
DigitalOut led(LED1);
The above code implicitly sets the mode of pin 13, so the setup function can be left empty. Enter the following code in the loop, which switches on the pin, sleeps for a second, switches it off and waits for a second again. To set the state of the pin, the write
method of the DigitalOut
class has been used. To add delays within the program, the sleep_for
function has been used from the ThisThread
namespace. The function puts the current thread to sleep for the specified duration, during which other threads (or the OS) can execute.
Unlike the delay
function used in regular Arduino programs, the function accepts the time as a chrono literal rather than an integer. The literal consists of a number with a suffix describing the units. For example, to create a delay of 500 milliseconds, the value 500ms should be used. For the current program, we use the value 1s to cause a delay of 1 second. A list of allowed suffixes for chrono literals are given at the end of this guide for your reference.
void loop() {
led.write(1); // Switch the LED on
ThisThread::sleep_for(1s); // Wait for a second
led.write(0); // Switch the LED off
ThisThread::sleep_for(1s); // Wait for a second
}
The complete code should look as follows –
#include <mbed.h>
using namespace mbed;
using namespace rtos;
using namespace std::chrono_literals;
DigitalOut led(LED1);
void setup() {
}
void loop() {
led.write(1); // Switch the LED on
ThisThread::sleep_for(1000); // Wait for a second
led.write(0); // Switch the LED off
ThisThread::sleep_for(1000); // Wait for a second
}
This concludes the Blinky program. You may verify and upload the program as with a regular Arduino program to see the LED blink.
Multitasking by spawning Threads
Now that we have finished blinking an LED, let’s see how to spawn two threads to perform multiple tasks concurrently, therefore giving the effect of multi-tasking.
Once again, start by including the mbed.h
header file and bringing members of the mbed
and rtos
namespaces into the current namespace. Also include all members from the std::chrono
namespaces. This will be explained in a bit. Additionally, create two threads called t1
and t2
(you may use any valid identifier name, just make sure to substitute it in the remaining code) as shown below –
#include <mbed.h>
using namespace mbed;
using namespace rtos;
using namespace std::chrono_literals;
Thread t1;
Thread t2;
The threads will need functions to execute. Create two functions called func1 and func2. These functions should not return any value or accept any arguments. These functions will print the characters “A” and “B” to the Serial Monitor every 2 and 3 seconds respectively. The functions are also required to never return, so the code within them has been placed within infinite for(;;)
loop blocks. The function
void func1() {
for(;;) { // Repeat forever
Serial.println("A"); // Print A to the Serial port
ThisThread::sleep_for(2s); // Wait for 2 seconds
}
}
void func2() {
for(;;) { // Repeat forever
Serial.println("B"); // Print B to the Serial port
ThisThread::sleep_for(3s); // Wait for 3 seconds
}
}
Finally, place the following code in the setup
function to being Serial communication and start executing the threads using the provided functions. The loop
function can be left empty.
void setup() {
Serial.begin(9600); // Start Serial communication
t1.start(func1); // Pass func1 to t1 to start executing it as a independant thread
t2.start(func2); // Pass func2 to t2 to start executing it as an independant thread
}
The complete code should look as follows –
#include <mbed.h>
using namespace mbed;
using namespace rtos;
using namespace std::chrono_literals;
Thread t1;
Thread t2;
void func1() {
for(;;) { // Repeat forever
Serial.println("A"); // Print A to the Serial port
ThisThread::sleep_for(2s); // Wait for 2 seconds
}
}
void func2() {
for(;;) { // Repeat forever
Serial.println("B"); // Print B to the Serial port
ThisThread::sleep_for(3s); // Wait for 3 seconds
}
}
void setup() {
Serial.begin(9600); // Start Serial communication
t1.start(func1); // Pass func1 to t1 to start executing it as a independant thread
t2.start(func2); // Pass func2 to t2 to start executing it as an independant thread
}
void loop() { // The loop function is executed in its own thread (left empty in this case)
}
This concludes the multi-tasking example. You may verify and upload the program as with a regular Arduino Program and upload the Serial Monitor to see the output.

I hope you found this two-part series interesting and learnt something new. If you have any thoughts or queries, drop them in the comments below. I’d love to know them! Good Luck!
Reference
Allowed suffixes/units for chrono literals –
Suffix | Unit of resulting Literal | Examples |
h | integral/floating point hours | 1h , 1.5h |
min | integral/floating point minutes | 10min , 20.5min |
s | integral/floating-point seconds | 1s , 2.5s |
ms | integral/floating-point milliseconds | 500ms , 50.5ms |
us | integral/floating-point microseconds | 750us , 1.5us |
ns | integral/floating-point nanoseconds | 1ns , 10.5ns |
Note that while all of the above literals are syntactically valid, the sleep_for
function only accepts integral hours, minutes, seconds and milliseconds. Providing floating-point versions of these literals, or literals with smaller units causes an error during compilation.
Pin mappings from Arduino to Mbed –
Arduino Pin Number | Mbed Pin Number | Microcontroller Pin Name |
D0 | P1_3 | Port 1 Pin 3 |
D1 | P1_10 | Port 1 Pin 10 |
D2 | P1_11 | Port 1 Pin 11 |
D3 | P1_12 | Port 1 Pin 12 |
D4 | P1_15 | Port 1 Pin 15 |
D5 | P1_13 | Port 1 Pin 13 |
D6 | P1_14 | Port 1 Pin 14 |
D7 | P1_23 | Port 1 Pin 23 |
D8 | P1_21 | Port 1 Pin 21 |
D9 | P1_27 | Port 1 Pin 27 |
D10 | P1_2 | Port 1 Pin 2 |
D11 | P1_1 | Port 1 Pin 1 |
D12 | P1_8 | Port 1 Pin 8 |
D13 | P0_13 | Port 0 Pin 13 |
A0 | P0_4 | Port 0 Pin 4 |
A1 | P0_5 | Port 0 Pin 5 |
A2 | P0_30 | Port 0 Pin 30 |
A3 | P0_29 | Port 0 Pin 29 |
A4 | P0_31 | Port 0 Pin 31 |
A5 | P0_2 | Port 0 Pin 2 |
A6 | P0_28 | Port 0 Pin 28 |
A7 | P0_3 | Port 0 Pin 3 |
Power LED | P1_9 | Port 1 Pin 9 |
Pingback: Using Mbed with Arduino ARM Boards – DumbleBots
For whatever reason wait() and wait_ms() do not work for me. I have resorted to wait_ns() to get your examples working here. Not sure what might have changed. Using arduino 1.8.13 on linux.
Which version of the Mbed core are you using? You can check this in the Boards manager next to the Mbed core installation.
I too am having issues with the “wait(n)” command. I’m also using IDE 1.8.13 and I’m using the latest board version (1.3.0). I noticed in your previous “getting started” page, you are using an much older version, 1.1.4. I’ve tried reverting to this older version and it did work.
The function wait is deprecated in favor of explicit sleep functions. To sleep, replace wait with ThisThread::sleep_for (C++) or thread_sleep_for (C). To wait (without sleeping), call wait_us. wait_us is safe to call from ISR context.
Hi Mario,
Thank you for your comment. I have updated the programs to use the latest APIs and removed the deprecated one.
Thank you for you blog, it help me to start using my Arduino Portenta H7. Just a small comment on the Threads example, “using namespace rtos” instead of “usint namespace rtos”
Hey Nguyen,
Glad you liked the blog! Thanks for pointing out the typo, I’ve made the appropriate changes.
Do you tell me how to turn on Bluetooth of nano 33 ble using mbed os