This article is a series on NuttX RTOS tutorials using the ESP32 Dev board and carries the discussion on NuttX RTOS and their usage. The aim of this series is to provide easy and practical examples that anyone can understand. In this NuttX RTOS tutorial, we are going to set up the NuttX RTOS with ESP32 Dev Board.
You can also read the FreeRTOS Tutorials, ESP32 Intro, ESP32 IDF Getting Started, ESP32 Serial Communication Tutorials, STM32 RTOS Getting started, Boot sequence in Cortex-M4, and PIC16F877A Getting started.
Prerequisites
I assume you know about the RTOS already. If you don’t know about RTOS and want to refresh the concepts, you can read the below articles before getting started.
Hardware Required
- ESP32 Dev board
- Laptop machine
Introduction
Whenever people talk about RTOS, they used to mostly talk about FreeRTOS, uC OS, Keil RTX, RT Linux, VxWorks, etc. In recent days, we have been hearing about the NuttX RTOS here and there. We just wanted to experiment that NuttX RTOS. So, we thought of sharing the details with everyone.
What is NuttX RTOS?
NuttX is a real-time operating system (RTOS) with an emphasis on standards compliance and a small footprint. Scalable from 8-bit to 32-bit microcontroller environments, the primary governing standards in NuttX are Posix and ANSI standards. Additional standard APIs from Unix and other common RTOS’s (such as VxWorks) are adopted for functionality not available under these standards, or for functionality that is not appropriate for deeply embedded environments (such as fork()).
NuttX is a POSIX RTOS. You don’t need to learn a new API to program it. You can write an application in a POSIX Operating System like Linux or macOS, validate it, and then compile it to run on NuttX. If you don’t want to create an application from scratch, you can grab some small Linux libraries and perform some minor modifications to get them working on NuttX.
Key features of NuttX
-
Standards Compliant.
-
Core Task Management.
-
Modular design.
-
Fully preemptible.
-
Naturally scalable.
-
Highly configurable.
-
Easily extensible to new processor architectures, SoC architecture, or board architectures.
-
FIFO, round-robin, and “sporadic” scheduling.
-
Real-time, deterministic, with support for priority inheritance.
-
Tickless operation.
-
POSIX/ANSI-like task controls, named message queues, counting semaphores, clocks/timers, signals, pthreads, robust mutexes, cancellation points, environment variables, filesystem.
-
Standard default signal actions (optional).
-
VxWorks-like task management and watchdog timers.
-
BSD socket interface.
-
Extensions to manage pre-emption.
-
Optional tasks with address environments (Processes).
-
Symmetric Multi-Processing (SMP)
-
Loadable kernel modules; lightweight, embedded shared libraries.
-
Memory Configurations: (1) Flat embedded build, (2) Protected build with MPU, and (3) Kernel build with MMU.
-
Memory Allocators: (1) standard heap memory allocation, (2) granule allocator, (3) shared memory, and (4) dynamically sized, per-process heaps.
-
Thread Local Storage (TLS)
-
Inheritable “controlling terminals” and I/O redirection. Pseudo-terminals.
-
On-demand paging.
-
System logging
-
May be built either as an open, flat embedded RTOS or as a separately built, secure kernel with a system call gate interface.
-
Built-in, per-thread CPU load measurements.
-
Custom NuttX C library
You can read the complete information about NuttX.
Supported platforms
NuttX supported many Platforms. Visit list.
ESP32 is also NuttX supported. Now we will see how to port the NuttX to the ESP32 device.
In this demonstration, I have installed the VirtualBox in my windows machine which contains Ubuntu 20.04
and Linux kernel 5.8.0-50-generic
.
Setup NuttX RTOS with ESP32 Dev Board
Prepare the ESP32
Update the system
Open up a terminal window and issue the below command.
sudo apt-get update |
Install the esptool
sudo pip install esptool |
Install the tools
Install the required tools by using the below command.
sudo apt-get install git wget libncurses-dev flex bison gperf python3 python3-pip python3-setuptools python3-serial python3-cryptography python3-future python3-pyparsing python3-pyelftools cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0 autoconf build-essential texinfo libtool libncurses5-dev gawk libc6-dev libtool-bin gettext libncursesw5-dev automake pkg-config libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev libexpat-dev gcc-multilib g++-multilib picocom u-boot-tools util-linux
Download the ESP-IDF source code
We have to download the ESP32 toolchain and ESP-IDF. We can download the ESP-IDF and toolchain by using the below steps. If you have the ESP32-IDF in your machine already, then you can skip these steps.
mkdir -p ~/esp cd ~/esp git clone --recursive https://github.com/espressif/esp-idf.git
Set up the tools
We can install the tools by using the below commands.
cd ~/esp/esp-idf export IDF_GITHUB_ASSETS="dl.espressif.com/github_assets" ./install.sh
Set up the environment variables
. $HOME/esp/esp-idf/export.sh
Till now we are done with the ESP32 tools. Let’s configure the NuttX.
Install the NuttX and configure
Create a directory
mkdir ~/NuttX cd ~/NuttX
KConfig
NuttX configuration system uses the KConfig. Let’s install the KConfig.
If you are using Ubuntu 20.04 LTS and later, then you just need to install the kconfig-frontends
by using the below command.
sudo apt install kconfig-frontends
If you are using Ubuntu 18.04 and earlier, then you have to download the source and build that.
git clone https://bitbucket.org/nuttx/tools.git tools cd tools/kconfig-frontends ./configure --enable-mconf --disable-nconf --disable-gconf --disable-qconf make make install
Download NuttX
Let’s download the NuttX source code and build it.
git clone https://github.com/apache/incubator-nuttx.git nuttx git clone https://github.com/apache/incubator-nuttx-apps apps
We keep on adding some example programs in our forked NuttX repository. If you want that examples also, then please clone the below repositories instead of the above original repo.
git clone https://github.com/Embetronicx/incubator-nuttx.git nuttx git clone https://github.com/Embetronicx/incubator-nuttx-apps.git apps
Initialize the hardware with NuttX
After downloading the source code, we need to initialize the hardware. We can see the supported hardware using the below command.
cd nuttx ./tools/configure.sh -L | less
As I am using the ESP32 DevKit, I gonna select that along with the NuttShell (nsh) by using the below command.
./tools/configure.sh -l esp32-devkitc:nsh
NSH is a good starting point since it enables booting into the interactive command-line NuttShell (NSH).
When you run the above command, you should get the output like below.
Copy files Select CONFIG_HOST_LINUX=y Refreshing... make[1]: Entering directory '/home/etx/NuttX/nuttx' make[2]: Entering directory '/home/etx/NuttX/nuttx/boards' make[2]: Leaving directory '/home/etx/NuttX/nuttx/boards' make[2]: Entering directory '/home/etx/NuttX/apps' make[3]: Entering directory '/home/etx/NuttX/apps/platform' make[3]: Leaving directory '/home/etx/NuttX/apps/platform' make[3]: Entering directory '/home/etx/NuttX/apps/builtin' make[3]: Leaving directory '/home/etx/NuttX/apps/builtin' make[2]: Leaving directory '/home/etx/NuttX/apps' make[2]: Entering directory '/home/etx/NuttX/nuttx/graphics' make[3]: Entering directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[3]: Leaving directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[3]: Entering directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[3]: Leaving directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[3]: Entering directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[3]: Leaving directory '/home/etx/NuttX/nuttx/graphics/nxglib' make[2]: Leaving directory '/home/etx/NuttX/nuttx/graphics' make[2]: Entering directory '/home/etx/NuttX/nuttx/arch/xtensa/src' make[2]: Nothing to be done for 'clean_context'. make[2]: Leaving directory '/home/etx/NuttX/nuttx/arch/xtensa/src' make[1]: Leaving directory '/home/etx/NuttX/nuttx' LN: include/arch to arch/xtensa/include LN: include/arch/board to /home/etx/NuttX/nuttx/boards/xtensa/esp32/esp32-devkitc/include LN: include/arch/chip to arch/xtensa/include/esp32 LN: arch/xtensa/src/board to /home/etx/NuttX/nuttx/boards/xtensa/esp32/esp32-devkitc/../common LN: arch/xtensa/src/board/board to /home/etx/NuttX/nuttx/boards/xtensa/esp32/esp32-devkitc/src LN: arch/xtensa/src/chip to arch/xtensa/src/esp32 LN: /home/etx/NuttX/nuttx/drivers/platform to /home/etx/NuttX/nuttx/boards/xtensa/esp32/esp32-devkitc/../drivers make[1]: Entering directory '/home/etx/NuttX/nuttx/libs/libxx' make[1]: Nothing to be done for 'dirlinks'. make[1]: Leaving directory '/home/etx/NuttX/nuttx/libs/libxx' make[1]: Entering directory '/home/etx/NuttX/nuttx/boards' make[1]: Leaving directory '/home/etx/NuttX/nuttx/boards' make[1]: Entering directory '/home/etx/NuttX/nuttx/openamp' make[1]: Nothing to be done for 'dirlinks'. make[1]: Leaving directory '/home/etx/NuttX/nuttx/openamp' make[1]: Entering directory '/home/etx/NuttX/apps' make[2]: Entering directory '/home/etx/NuttX/apps/platform' LN: platform/board to /home/etx/NuttX/apps/platform/dummy make[2]: Leaving directory '/home/etx/NuttX/apps/platform' make[1]: Leaving directory '/home/etx/NuttX/apps' make[1]: Entering directory '/home/etx/NuttX/apps' make[2]: Entering directory '/home/etx/NuttX/apps/examples' mkkconfig in /home/etx/NuttX/apps/examples make[2]: Leaving directory '/home/etx/NuttX/apps/examples' make[2]: Entering directory '/home/etx/NuttX/apps/gpsutils' mkkconfig in /home/etx/NuttX/apps/gpsutils make[2]: Leaving directory '/home/etx/NuttX/apps/gpsutils' make[2]: Entering directory '/home/etx/NuttX/apps/system' make[3]: Entering directory '/home/etx/NuttX/apps/system/libuv' mkkconfig in /home/etx/NuttX/apps/system/libuv make[3]: Leaving directory '/home/etx/NuttX/apps/system/libuv' mkkconfig in /home/etx/NuttX/apps/system make[2]: Leaving directory '/home/etx/NuttX/apps/system' make[2]: Entering directory '/home/etx/NuttX/apps/fsutils' mkkconfig in /home/etx/NuttX/apps/fsutils make[2]: Leaving directory '/home/etx/NuttX/apps/fsutils' make[2]: Entering directory '/home/etx/NuttX/apps/interpreters' mkkconfig in /home/etx/NuttX/apps/interpreters make[2]: Leaving directory '/home/etx/NuttX/apps/interpreters' make[2]: Entering directory '/home/etx/NuttX/apps/testing' mkkconfig in /home/etx/NuttX/apps/testing make[2]: Leaving directory '/home/etx/NuttX/apps/testing' make[2]: Entering directory '/home/etx/NuttX/apps/graphics' mkkconfig in /home/etx/NuttX/apps/graphics make[2]: Leaving directory '/home/etx/NuttX/apps/graphics' make[2]: Entering directory '/home/etx/NuttX/apps/netutils' mkkconfig in /home/etx/NuttX/apps/netutils make[2]: Leaving directory '/home/etx/NuttX/apps/netutils' make[2]: Entering directory '/home/etx/NuttX/apps/canutils' mkkconfig in /home/etx/NuttX/apps/canutils make[2]: Leaving directory '/home/etx/NuttX/apps/canutils' make[2]: Entering directory '/home/etx/NuttX/apps/wireless' make[3]: Entering directory '/home/etx/NuttX/apps/wireless/ieee802154' mkkconfig in /home/etx/NuttX/apps/wireless/ieee802154 make[3]: Leaving directory '/home/etx/NuttX/apps/wireless/ieee802154' make[3]: Entering directory '/home/etx/NuttX/apps/wireless/bluetooth' mkkconfig in /home/etx/NuttX/apps/wireless/bluetooth make[3]: Leaving directory '/home/etx/NuttX/apps/wireless/bluetooth' mkkconfig in /home/etx/NuttX/apps/wireless make[2]: Leaving directory '/home/etx/NuttX/apps/wireless' make[2]: Entering directory '/home/etx/NuttX/apps/industry' mkkconfig in /home/etx/NuttX/apps/industry make[2]: Leaving directory '/home/etx/NuttX/apps/industry' mkkconfig in /home/etx/NuttX/apps make[1]: Leaving directory '/home/etx/NuttX/apps' # # configuration written to .config #
Build the NuttX
The next step is to build the NuttX.
make menuconfig ( You can configure the option thatt you want. But I didn't touch anything) make clean make
You should get a print like the one below.
CP: nuttx.hex CP: nuttx.bin MKIMAGE: ESP32 binary esptool.py -c esp32 elf2image -fs 4MB -fm dio -ff 40m -o nuttx.bin nuttx esptool.py v3.0 Generated: nuttx.bin (ESP32 compatible)
That’s all. We have the nuttx.bin
and need the bootloader.bin
and the partitions. We can get it from the hello world example of ESP-IDF.
Build bootloader and partition
Copy the hello world example to our NuttX directory and compile that using the below commands.
cp -r ~/esp/esp-idf/examples/get-started/hello_world ~/NuttX/nuttx/ cd hello_world make
It will automatically restart the ESP32 board during the boot if Bootloader WDT enabled. This Bootloader WDT has to be disabled if you don’t want to restart it automatically.
To do that, one pop-up will come with a blue screen (menuconfig) when you run the above command make
.
You deselect the Bootloader WDT by just navigate to Bootloader config
—> Use RTC Watchdog in start code
.
Refer to the below image.
After this, save and exit. Then it will build the hello world example and give you the bootloader.bin
and partitions_singleapp.bin
.
We’ve got the bootloader and partitions binary. Let’s copy the bootloader and partitions to our NutteX project.
cp build/bootloader/bootloader.bin ~/NuttX/nuttx/ cp build/partitions_singleapp.bin ~/NuttX/nuttx/partitions.bin
Now we have everything. So, we can flash it to the ESP32 and verify that.
Flash
Let’s flash the NuttX, Bootloader, and partition to the ESP32.
cd ~/NuttX/nuttx/ sudo esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 write_flash 0x1000 bootloader.bin 0x8000 partitions.bin 0x10000 nuttx.bin
Once you enter the above command, the shell will be waiting for the ESP32 to go to BOOT mode. You will get print like below.
esptool.py v3.0 Serial port /dev/ttyUSB0 Connecting........_____....._____..
Then you have to press and hold the BOOT button of the ESP32 board (IO0 pin). Then it will flash the code to ESP32. You will get the below prints.
esptool.py v3.0 Serial port /dev/ttyUSB0 Connecting........_____....._____.. Chip is ESP32-D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: 7c:9e:bd:06:e9:48 Uploading stub... Running stub... Stub running... Changing baud rate to 921600 Changed. Configuring flash size... Compressed 23600 bytes to 14769... Wrote 23600 bytes (14769 compressed) at 0x00001000 in 0.2 seconds (effective 1052.4 kbit/s)... Hash of data verified. Compressed 3072 bytes to 103... Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.0 seconds (effective 3265.4 kbit/s)... Hash of data verified. Compressed 123968 bytes to 51567... Wrote 123968 bytes (51567 compressed) at 0x00010000 in 1.0 seconds (effective 973.9 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin...
Boot ESP32
That’s all. We can check the output using the picocom
. Run the below command and press the RESET button (EN Button).
picocom -b 115200 /dev/ttyUSB0
You should get the output as below.
picocom v3.1 port is : /dev/ttyUSB0 flowcontrol : none baudrate is : 115200 parity is : none databits are : 8 stopbits are : 1 escape is : C-a local echo is : no noinit is : no noreset is : no hangup is : no nolock is : no send_cmd is : sz -vv receive_cmd is : rz -vv -E imap is : omap is : emap is : crcrlf,delbs, logfile is : none initstring : none exit_after is : not set exit is : no Type [C-a] [C-h] to see available commands Terminal ready ets Jun 8 2016 00:22:57 rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 load:0x3fff0030,len:6724 load:0x40078000,len:13140 load:0x40080400,len:3644 entry 0x40080674 I (27) boot: ESP-IDF v4.4-dev-1183-g9d34a1cd4 2nd stage bootloader I (27) boot: compile time 00:57:50 I (27) boot: chip revision: 1 I (31) boot_comm: chip revision: 1, min. bootloader chip revision: 0 I (38) boot.esp32: SPI Speed : 40MHz I (43) boot.esp32: SPI Mode : DIO I (47) boot.esp32: SPI Flash Size : 2MB I (52) boot: Enabling RNG early entropy source... I (57) boot: Partition Table: I (61) boot: ## Label Usage Type ST Offset Length I (68) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (76) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (83) boot: 2 factory factory app 00 00 00010000 00100000 I (91) boot: End of partition table I (95) boot_comm: chip revision: 1, min. application chip revision: 0 I (102) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=01a68h ( 6760) map I (113) esp_image: segment 1: paddr=00011a90 vaddr=3ffb1370 size=0005ch ( 92) load I (119) esp_image: segment 2: paddr=00011af4 vaddr=40080000 size=016b8h ( 5816) load I (130) esp_image: segment 3: paddr=000131b4 vaddr=00000000 size=0ce64h ( 52836) I (155) esp_image: segment 4: paddr=00020020 vaddr=400d0020 size=0e3f0h ( 58352) map I (179) boot: Loaded app from partition at offset 0x10000 I (179) boot: Disabling RNG early entropy source... NuttShell (NSH) NuttX-10.1.0-RC1 nsh>
If you enter the ?
, then you will get the help commands like below.
nsh> ? help usage: help [-v] [<cmd>] . cat dd false ls ps sleep uname [ cd df free mkdir pwd source umount ? cp echo help mkrd rm test unset basename cmp exec hexdump mount rmdir time usleep break dirname exit kill mv set true xd Builtin Apps: sh nsh
Finally, we have completed all the required steps. In our next tutorial, we will see how to Blink the LED on ESP32 using NuttX RTOS.
You can also read the below tutorials.
Reference(s):
- https://nuttx-companion.readthedocs.io/en/latest/user/install.html
- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/#get-started-get-esp-idf
- https://nuttx-companion.readthedocs.io/en/latest/user/install.html
- https://acassis.wordpress.com/2018/01/04/running-nuttx-on-esp32-board/
- https://nuttx.apache.org/docs/latest/quickstart/install.html

Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!