Setup NuttX RTOS with ESP32 Dev Board

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.

ESP32 BT WDT disable

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.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
Bootloader TutorialsRaspberry PI Pico Tutorials

Reference(s):

5 1 vote
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x