As I mentioned on my previous post, I got an Arduino kit to start experimenting with the platform and eventually interconnect it with the Snap Circuits so that my grandkids and I can have fun building some cool projects. The kit includes an Arduino Mega2560 board and a bunch of components that can be used to build a variety of projects.
It is pretty easy to get started with the Arduino platform. You install the Arduino IDE, plug in the board to your computer using the USB cable, and boom! you are up and running in minutes. I am using the new version 2 of the IDE, and so far it has been very stable.
To warm up, I decided to do a silly project that combines several capabilities all at once. Even though the Arduino platform is very easy to use, if you need to do multiple things at the same time, it can get out of hand very quickly trying to keep track of the required timings and switching between functions. A giant
loop() function with a bunch of
delay() calls will definitely not cut it, except for really small projects. You really need to have a good methodology to keep the various functionalities nicely organized, making sure the processor is fairly shared across functions, and minimizing side effects between them.
“FreeRTOS is a real-time kernel (or real-time scheduler) on top of which embedded applications can be built to meet their hard real-time requirements. It allows applications to be organized as a collection of independent threads of execution,” quoted from “Mastering the FreeRTOS Real Time Kernel” by Richard Barry, the original developer.
But, oops! the first thing I noticed was that FreeRTOS is a pure C framework, whereas the Arduino platform is all C++. Of course the two can play together, but without the object orientation of C++, mixing C and C++ is going to start to look pretty ugly in no time. A way to handle this is to create C++ wrappers for FreeRTOS. But that is a little bit of a pain and not a great way to get my project started. But, hey! it did not take a lot of googling to find someone who actually developed and published the wrappers: “frt - Flössie’s ready (FreeRTOS) threading”, a header file with a pretty good set of class declarations on top of FreeRTOS. Woo hoo! What a relief!
There are a lot of tutorials out there that introduce FreeRTOS concepts. Here is one, for example. One caveat is that none of the tutorials available show how to use FreeRTOS with the frt wrappers. However, it is super straightforward to use and there are some really good examples in the project repo.
The project does the following:
We start with the platform in power savings mode
Pressing a push button
- Wakes up from power saving mode
- Emits a buzzing sound
- Starts blinking an LED
- Measures temperature and humidity every 4 seconds
- Displays the data on a 16x2 LCD screen.
Pressing the button again
- Sounds the buzzer with a different tone
- Stops blinking the LED
- Stops measuring temperature and humidity
- Turns off the LCD screen
- Goes back to power savings mode.
Regardless in what mode the platform is, detecting a loud sound turns on an induction coil that powers a bunch of wireless LEDs. And, if the platform is awake, also move a servo motor to put some of the LEDs above the coil so that they are turned on.
Clapping again turns off the induction coil (and the wireless LEDs). If the platform is awake, the servo moves the LEDs away from the coil, thus turning them off.
Here is the list of components used in this project.
Arduino Mega2560 Board
The diagram below lays out the hardware component connections for the project.
The table below lists each Arduino Mega2560 Board pin, which components are attached, the type of interface, and a brief discussion of each item.
|PWM 2||Push button||Input||The push button Pin is configured as INPUT_PULLUP, thus saving us a resistor. When pushed, an interrupt is generated on LOW.|
|PWM 3||LCD display contrast control.||Pulse Width Modulation (PWM) wave output||LCD off: 0% duty cycle. LCD on: 80% duty cycle. To eliminate a buzzing sound, we include a low pass filter (1KΩ, 100μF).|
|PWM 4||Induction LEDs||Digital output||Since the induction LED coil draws over 80mA of current, which is too much for an Arduino output pin, we need to use a transistor switch.|
|PWM 5||Temperature & humidity sensor||Digital output/input||Access to the sensor is handled by the DHTNew library. This library appears to behave well under FreeRTOS.|
|PWM 6||LCD Display Register Select (RS)||Digital output||Access to the LCD display is handled by the standard Arduino LiquidCrystal library. This library appears to behave well under FreeRTOS.|
|PWM 7||LCD Display Enable (EN)||Digital output||Same as above.|
|PWM 8||Active Buzzer||PWM output||Access to the active buzzer is handled by the standard Arduino tone() library. This library appears to behave well under FreeRTOS. However, I am only generating two short tones. I have not tested longer music-like tone sequences.|
|PWM 9, 10, 11, 12||LCD Display data pins 7, 6, 5, 4||Digital output||Same as PWM 6 & 7.|
|PWM 13||LED light||Digital output||HIGH turns on the LED, LOW turns off the LED. Pin 13 is also connected to the internal Mega2560 LED.|
|RX 19||Sound Sensor Digital Output (DO)||Digital input||When sound is detected, the DO pin is raised and an interrupt is generated on HIGH.|
|PM 45||Servo Motor Pulse Input||PWM output||A pulse of 0.5-2.5ms duty cycle over a period of 20ms will move the servo to 0 to 180°.|
The diagram below depicts how the project is divided in FreeRTOS tasks, shown in blue. It also shows inter-task communication via semaphores and a queue, shown in yellow. And in addition, it shows the interrupt service routines need to handle some external events (e.g., button press), shown in orange. Finally, the
isAwake global flag is shown in green.
All the code used on this project is available here.
Below we describe each one of the software components depicted above.
|Init||Arduino setup() function||Starts FreeRTOS tasks and enables interrupts.|
|Button ISR||Interrupt service routine||Handles push button interrupts. Posts to button_semaphore.|
|Sound ISR||Interrupt service routine||Handles “clapping” sound interrupts. Posts to clap_semaphore.|
|Timer0 Compare ISR||Interrupt service routine||Handles timer interrupts. It is invoked every millisecond. Provides timing for the SoftServo component (20ms period).|
|button_semaphore||FreeRTOS Semaphore||A button-press interrupt posts to this semaphore. The Button Task waits on it.|
|wakeup_semaphore||FreeRTOS Semaphore||The Button Task post to this semaphore. The Blink Task waits on it.|
|clap_semaphore||FreeRTOS Semaphore||A sound interrupt is used to post to this semaphore. The Clap Task waits on it.|
|servo_semaphore||FreeRTOS Semaphore||The Clap Task posts to this semaphore. The Servo Task waits on it.|
|LCDQueue||FreeRTOS Queue||The Measure Task writes data to this queue, which is read by the DisplayLCD Task.|
|isAwake||Volatile Global||This flag is only toggled by the Button Task. Some of the other tasks just read its value.|
|Button Task||FreeRTOS Task||Waits for button_semaphore, toggles
Note that since this is the only task that updates the
Also note that this is the highest priority task, it is very short lived and we need to make sure it is always responsive.
|Blink Task||FreeRTOS Task||This task blinks a LED if
|Measure Task||FreeRTOS Task||This task measures temperature and humidity if
|DisplayLCD Task||FreeRTOS Task||Reads the LCDQueue and displays the data on an LCD screen.|
|Clap Task||FreeRTOS Task||Waits for the clap_semaphore, toggles the induction LEDs on/off and posts to the servo_semaphore.|
|Servo Task||FreeRTOS Task||Waits on the servo_semaphore and if
While operating, it is necessary to disable the sound interrupt to prevent the noise of the moving servo from triggering the sound sensor.
|SoftServo||Instance of Adafruit_SoftServo||This object is used to control the servo. Generates 0.5-2.5 ms duty cycle pulses, depending on desired servo positioning (0-180°).
I had trouble finding a servo library that would work with FreeRTOS. Apparently, the standard libraries have some timer/timing conflicts with FreeRTOS. Fortunately, I found Adafruit_SoftServo, an extremely simple library that only requires a 20ms refresh generated by tapping the Arduino Timer0 1ms interrupt.
|Idle Task||FreeRTOS Task, Arduino loop() function||The idle task is used to enter power savings mode when
And here are some pictures of the project implementation.
Full view of the project (without servo)
Adding the servo
Below are the power consumption measurements I took under the various scenarios.
|Induction leds on|
|Induction leds on and servo active|
The low power mode is not particularly impressive. I don’t know if FreeRTOS is not letting the platform enter power saving mode as much as it could.
I was very pleased with FreeRTOS and the functionality it provides. It is great to be able to split up a project into separate parts (tasks) that can be developed almost independently. Communication between tasks can be easily achieved using semaphores, mutexes and/or queues. Perhaps for many situations FreeRTOS may seem overkill, but if the project combines more than a handful of sensors/actuators, with somewhat complex logic, FreeRTOS can help keep things under control.
Nevertheless, there are a couple of drawbacks to consider when using FreeRTOS on the Arduino platform:
Due to timing issues or possible conflicts in the use of timers, some standard Arduino libraries may not be compatible with FreeRTOS. For example, the standard Servo library doesn’t work with FreeRTOS. I had to search for an alternative or be ready to write my own interface.
The FreeRTOS timer granularity of 15ms (default) may not work for all projects. For example, in the case of the servo interface, we need to be able to generate 0.5ms to 2.5ms pulses every 20ms. I had to use a timer interrupt to refresh the servo interface, which operates completely outside of a FreeRTOS task. Not a huge deal, but something to keep in mind.
My next experiment is to re-implement this project using one of the alternatives I mentioned at the beginning. I am particularly intrigued by the AceRoutine cooperative multitasking framework, which is based on the concept of coroutines. I expect that it would be a lot more compatible with mainstream Arduino libraries, and perhaps a happy medium between bare Arduino and a full blown RTOS.
Until then, auf wiedersehen!
PS: Oh, by the way, do you want to start playing with Arduino, FreeRTOS and other stuff without having to deal with hardware, cables, or installing any software whatsoever? Try Wokwi. It is a full blown simulator of the Arduino platform that runs in your browser!