CC Blog Design Solutions Research & Design Hub

The NuttX Engineer

Figure 1 Espressif ESP32-DevKitC
Written by Pedro Bertoleti

An Introduction to Apache Nuttx

Apache NuttX is a real-time operating system (RTOS) for microcontrollers (MCUs). In this article, I discuss what the system consists of, its major features, and when it should be considered for embedded system software development. I also discuss an example in which I use NuttX RTOS and an ESP32 to read a BMP180 barometric pressure sensor.

  • What are the features of Apache NuttX RTOS?
  • When should I use Apache NuttX RTOS for embedded software development?
  • How do I deploy Apache NuttX on an ESP32?
  • Apache NuttX RTOS
  • ESP32 microcontroller
  • BMP180 barometric pressure sensor

Embedded systems and software needs to be reliable. This means embedded software should be designed to work under highly variable conditions; it must be well-developed, well-tested and extensively validated. One way to ensure good embedded software design and performance is by using a reliable operating system.

The major advantage of using an operating system in embedded system software is to delegate critical features—such as memory management, CPU usage, network management, and I/O management—to the operating system itself, so developers can focus their efforts on developing applications for the embedded system software. In embedded systems, it’s common to use a real-time operating system (RTOS), due to its small footprint (compared to a general purpose operating system) and real-time features—a requirement for some embedded software.

In this article, you’ll be introduced to the Apache NuttX RTOS, and you’ll learn how to make embedded software to read a Bosch BMP180 barometric pressure sensor, using an Espressif ESP32 microcontroller/System-on-Chip (MCU/SoC) with NuttX.

Figure 1
Espressif ESP32-DevKitC
Figure 1
Espressif ESP32-DevKitC

To reproduce the project described here, you’ll need the following electronic components:

  • ESP32-DevKitC board (Espressif) (Figure 1)
  • Micro-USB cable (for programming and powering DevKitC and sensors)
  • 830-point breadboard
  • Male-male jumper wires.
  • Bosch BMP180 breakout board (Figure 2) with four male pin-headers soldered
Figure 2
Bosch BMP180 breakout board
Figure 2
Bosch BMP180 breakout board

NuttX is a free, open-source RTOS designed to work in small footprint MCUs [1]. It is scalable from 8-bit to 32-bit MCU environments, and supports many MCU architectures [2].

NuttX RTOS relies on a significant number of developers around the world to support it (myself included). Global big tech companies currently use NuttX in their solutions, and contribute to its development. These companies include Sony, Xiaomi, Fitbit, WEG (one of the largest companies in industrial equipment development in Latin America), and Engie (one of the largest companies in energy solutions development in Latin America).

The features of NuttX RTOS are listed on the Apache NuttX website [3]. Without a doubt, its major feature is compliance with Posix and ANSI standards. This means you can develop embedded software to run on NuttX in a similar way to what you’d do for embedded Linux, as Posix standards and APIs are common to both operating systems. A strong advantage that NuttX offers, however, is that it can run on much cheaper and more readily available MCUs/SoCs than those required by Linux OS. NuttX source-code is available from its official OS and applications repositories [4].


Advertise Here


NuttX compliance with Posix and ANSI standards leads to an interesting benefit for those who already have embedded software solutions with embedded Linux, or those who are skilled in developing solutions using embedded Linux: Most embedded Linux applications can be ported for NuttX and run on a great variety of small-footprint MCUs officially supported by NuttX RTOS.

This means it’s possible to port an existing embedded Linux solution (which usually demands an expensive microprocessor/SoC to run) to NuttX, running on a much less expensive hardware set. As a consequence, it can significantly contribute to reducing final solution costs—a major highlight considering how semiconductor prices are rising due to electronic components shortages resulting from the COVID pandemic.

The ESP32 is an affordable (less than $5) MCU, considering its built-in resources. It has plenty of the resources most required in a typical embedded system/IoT solution, including rich peripheral communication interfaces (three UARTs, two I²C, four SPI), a dual-core CPU working at 240MHz, good flash memory size (4MB to 32MB), Wi-Fi (2.4GHz), and Bluetooth connectivity (support for both Bluetooth Low Energy (BLE) and Bluetooth Classic). Most of the ESP32 resources are already supported by NuttX RTOS, and the following ESP32 SoC versions were supported by NuttX at the time of this writing: ESP32 (WROOM and WROVER), ESP32-S2, and ESP32-S3.

Clearly, using NuttX RTOS with ESP32 can be a powerful combination for either developing embedded systems from scratch or porting an existing embedded Linux project to NuttX. Also, active communities support both ESP32 and NuttX, so you can easily get help if you’re stuck in some part of your project, or when learning how to use a specific feature. In addition, you don’t need to pay for any licenses when using NuttX in a commercial product.

With all these features, using NuttX in an ESP32 gives you the best of both worlds—an affordable MCU with plenty of useful resources, and a well-designed, thoroughly tested, and well-developed RTOS that is Posix-compliant.


Despite resemblances to embedded Linux, a full compilation—of NuttX RTOS and applications—differs from Linux in certain respects. As happens in most of RTOSs available today, the result of a full compilation of NuttX is a single binary file that contains NuttX RTOS and the applications you selected to be part of your NuttX image. Also, in the particular case of ESP32, the NuttX final image needs to be flashed along with two other images: bootloader and partition table images. These are pre-compiled images provided by Espressif. We’ll see how to download and flash these to the ESP32 later in this article. For now, here’s a brief summary of these images:

  • Pre-compiled bootloader image, which contains the bootloader for ESP32 and NuttX
  • Pre-compiled partition table image, which contains all the information for ESP32 to compose its partition table to run NuttX
  • NuttX RTOS image, which contains NuttX RTOS itself and all applications you want to be part of the NuttX image

While it’s convenient to only need to handle a single image that contains NuttX and the selected applications, this has a downside: updating an individual application is not straightforward, and in some cases is not even possible. You’ll have to keep this in mind when planning your firmware upgrade over-the-air (FUOTA) process for your embedded device, as it will probably always require a full upgrade (that is, upgrading the single binary containing NuttX RTOS and applications).

An alternative to updating an individual application is to write the applications in scripts for interpreted languages supported by NuttX (so far, NuttX supports Lua), and just upload applications’ scripts into an SPI flash partition in the ESP32. However, I would not recommend you take this approach. Interpreted languages offer significantly lower performance than compiled programs. Also, the libraries and community support for these interpreted languages to run in interpreters embedded in NuttX RTOS may be not as readily available as using C/C++ to code your applications. Therefore, this can become a dead-end for your application if a critical problem is found, more performance is needed, or a library or peripheral support isn’t available.


Advertise Here


For your NuttX development environment, it’s best to use a Linux distro on your PC. A Linux Virtual Machine will work, too. For Windows 10 or 11 users, you can also set up your NuttX development environment with WSL2. However, it can be tricky to correctly configure WSL2 to build NuttX and flash MCUs using USB/UART interfaces, so I strongly recommend a Linux distro, instead.

The NuttX environment also works great in Ubuntu distros. Ubuntu is a popular Linux distro with community support, and it’s user-friendly—you don’t need to be experienced with Linux. So if you don’t have a Linux distro already installed in your PC, and you don’t know how to choose which Linux distro to use, please consider Ubuntu.

Once you have a Linux distro installed and running on your PC, the next step is to install the dependencies that NuttX requires for development and compilation. To do this, update and upgrade your distro’s installed packages, using the following commands:

sudo apt update

sudo apt upgrade

sudo apt-get install automake bison build-essential flex gperf git libncurses5-dev libtool libusb-dev libusb-1.0.0-dev pkg-config kconfig-frontends git

Once the dependencies are successfully installed, it’s time to get your own copies of NuttX repositories. NuttX is composed of two different repositories [4]:

  • nuttx: This is the main NuttX repository. It contains the operating system, drivers, architecture-specific codes and NuttX libraries. If you want to see how NuttX RTOS works under the hood, or want to contribute to NuttX RTOS development, this is the place to go.
  • nuttx-apps: This is the applications repository for NuttX. It contains all the applications that NuttX uses, from examples to fundamental applications. Go here if you’re looking for a specific application, or want to contribute to NuttX with your own application.

To manage these two repositories correctly, you need to create a folder to use as the NuttX workspace. In this article, this workspace will be called “nuttxspace.” Create and access this workspace by using the following commands:

mkdir ~/nuttxspace

cd ~/nuttxspace

Once your NuttX workspace is set, clone the NuttX repositories to your NuttX workspace; put the nuttx repository in the nuttx folder, and the nuttx-apps repository in the apps folder. This can be done using the following commands:

git clone nuttx

git clone apps

Now you need to get the ESP32 toolchain (also called the cross-compiler). This toolchain makes your PC able to compile software to run in a target with CPU architecture that’s different from your PC’s—in this case, an ESP32 MCU, which uses Xtensa architecture. NuttX uses it to compile the NuttX RTOS image/binary for the ESP32 MCU.

To get this toolchain, you have two options. The first is to download and install the full ESP-IDF on your computer. Instructions for this are on the Espressif website [5]. The second, since the main goal is to use NuttX in ESP32, is to only download and install the toolchain, referring it in your ~/.bashrc file. To do this, go to a temporary folder (for example, ~/Downloads) and download and extract the toolchain by using the following command:


Advertise Here

curl | tar -xz

In Ubuntu and other Linux distros, the /opt folder is used for storing third-party software. Therefore, it’s a good idea to store the ESP32 toolchain in a sub-folder within /opt. This can be done with these commands:

sudo mkdir /opt/xtensa

sudo mv xtensa-esp32-elf/ /opt/xtensa/

Also, you’ll need to include the ESP32 toolchain path in your PATH environment variable in Linux. To automatically do this every time you start a Linux terminal session, follow these steps.

1) Open your .bashrc file by using sudo nano ~/.bashrc command.

2) At the end of the file, add a line and insert there the following content:

# Add and its dependencies directory

PATH=$PATH:/home/<user>/.local/bin # Add the cross compiler path for ESP32


3) To finish your NuttX development environment setup, you need to get the bootloader and partition table pre-compiled images, provided by Espressif. These will be flashed into the ESP32 along with the NuttX image. Go to your nuttxspace folder, create an esp_bins sub-folder to store these pre-compiled binaries, and download them, with the following commands:

cd ~/nuttxspace

mkdir esp-bins

cd esp-bins

curl -L “” -o ../esp-bins/bootloader-esp32.bin

curl -L “” -o ../esp-bins/partition-table-esp32.bin

All set! Now you have a NuttX development environment ready for use.


NuttX RTOS is highly customizable. It’s possible to change almost every resource and configuration that will compose your final NuttX image—from very specific CPU architecture configurations to applications that your NuttX RTOS final image will contain—by tweaking configurations in menuconfig. In fact, those who are familiar with the Linux Kernel customization and compilation process will find this process to be similar, including the use of menuconfig (kconfig-frontends).

Once the NuttX development environment is set up, you need all the basic configurations to work on an ESP32 MCU. To do this, NuttX requires some pre-defined configuration files (much like defconfigs used in the Linux Kernel compilation), with multiple variations and pre-configured resources. For example, you can configure NuttX to use ESP32 Wi-Fi. It sets up almost everything that is needed for it, only requiring you to insert Wi-Fi credentials in menuconfig. You do this by configuring the NuttX build system with a pre-defined config file—so you don’t have to know every configuration needed to make Wi-Fi work properly. It helps a lot, since these files automatically load a great part of the required (and sometimes tricky) configurations.

For the project I describe later in this article, you only need basic resources to read some sensors and run NuttX RTOS on an ESP32. Fortunately, these configurations are available in the predefined configuration files sets, called esp32-devkitc:nsh. Thus, NuttX will be configured to be used in ESP32-DevKitC board and only the NuttX Shell (NSH) will be pre-loaded. It can be done using the following commands:

cd ~/nuttxspace/nuttx/tools

./ esp32-devkitc:nsh

After these commands run, your NuttX development environment is ready to build an ESP32 NuttX image. To customize NuttX RTOS, run the following commands:

cd ~/nuttxspace/nuttx

make menuconfig

The menuconfig window (Figure 3) is an interface that allows you to customize and tweak almost everything related to NuttX RTOS. Feel free to explore all the available options, and get familiar with the navigation.

Figure 3 NuttX menuconfig interface
Figure 3 NuttX menuconfig interface

Figure 4 is the wiring for this project. Note that the Bosch BMP180 barometric pressure sensor uses an I²C communication interface. The sensor’s I²C signals are SCL and SDA; SCL is wired to GPIO 22 and SDA is wired to GPIO 23.

Figure 4
The project's wiring
Figure 4
The project’s wiring

Let’s go a little deeper and explore how sensors are integrated into NuttX RTOS. Every peripheral (sensors included) is integrated in the operating system. Each supported peripheral has a driver, which can be found in the official repository’s drivers/sensors folder [6].

In NuttX, each board must have an initialization code for each peripheral device (sensors, actuators, buttons, and so on) supported by NuttX. These initialization codes link NuttX sensor drivers to the MCU’s particular communication interfaces (such as I²C, SPI, and UART). They assign a specific communication interface to a peripheral, and register the peripheral into this communication interface. The initialization codes for the ESP32 can be found in the boards/xtensa/esp32/common/src/ folder [7].

Subsequently, each initialization code must be called in the “board bring-up code,” which initializes the board, loading all peripherals and interfaces configured for the current NuttX build. The initialization code for ESP32-DevKitC can be found in the file boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c [8]. It’s important to know that, according to ESP32 bring-up code, the BMP180 sensor must be wired to the ESP32’s I²C 0 communication interface only, as shown in Listing 1. The BMP180 initialization code for ESP32 can be found in the file boards/xtensa/esp32/common/src/esp32_bmp180.c [9].

In this article, we’ll use the BMP180 NuttX app example [10] as reference. This example is straightforward. It opens BMP180 sensor communication (registered in the /dev/press0 path in NuttX RTOS), and reads the sensor every 500ms, as shown in Listing 2.


Code to wire the BMP180 barometric pressure sensor to the ESP32's I2C 0 communication interface

  /* Try to register BMP180 device in I2C0 */

  ret = board_bmp180_initialize(0, 0);

  if (ret < 0)
      syslog(LOG_ERR, “Failed to initialize BMP180 driver: %d\n”, ret);

This code opens BMP180 sensor communication (registered in the /dev/press0 path in NuttX RTOS), and reads the sensor every 500ms.

int main(int argc, FAR char *argv[])
  int fd;
  int ret;
  uint32_t sample;

  fd = open(“/dev/press0”, O_RDONLY);
  while (1)
      ret = read(fd, &sample, sizeof(uint32_t));
      if (ret != sizeof(sample))
          perror(“Could not read”);

      printf(“Pressure value = %05” PRId32 “\n”, sample);


  return 0;

Now, let’s proceed to the BMP180 configuration in menuconfig. The first thing to do is to enable I²C 0 in System Type → ESP32 Peripheral Selection, as shown in Figure 5. Then, I²C 0 GPIOs (SCL and SDA) must be configured in System Type → I2C configuration, setting GPIO 22 as SCL and GPIO 23 as SDA, as shown in Figure 6.

Figure 5
Enable I²C 0 in System Type → ESP32 Peripheral Selection.
Figure 5
Enable I²C 0 in System Type → ESP32 Peripheral Selection.
Figure 6
I²C 0 GPIOs (SCL and SDA) must be configured in System Type → I2C configuration, setting GPIO 22 as SCL and GPIO 23 as SDA.
Figure 6
I²C 0 GPIOs (SCL and SDA) must be configured in System Type → I2C configuration, setting GPIO 22 as SCL and GPIO 23 as SDA.
Figure 7
Enable Bosch BMP180 Barometer Sensor support at Device Drivers → Sensor Device Support,
Figure 7
Enable Bosch BMP180 Barometer Sensor support at Device Drivers → Sensor Device Support,
Figure 8
Enable the BMP180/280 Barometer sensor example application in Application Configuration → Examples.
Figure 8
Enable the BMP180/280 Barometer sensor example application in Application Configuration → Examples.

Next, the BMP180 device driver sensor should be selected. To do this, enable Bosch BMP180 Barometer Sensor support at Device Drivers → Sensor Device Support, as shown in Figure 7.

Finally, enable the BMP180/280 Barometer sensor example application in Application Configuration → Examples, as shown in Figure 8. Then, exit menuconfig and save configurations.


At this point, all the configurations and resources are set up, allowing NuttX RTOS to be compiled for the ESP32 with all the customizations done so far—BMP180 sensor support and its example app. So, the next step is to compile NuttX. Go to the ~/nuttxspace/nuttx folder, and trigger compilation with the make command:


You might find this process a little bit slow, since it can demand significant CPU usage, and this command allocates only one CPU core for it. You can speed up the compilation process by using more CPU cores. To do this, first you need to know how many CPU cores are available in your PC processor. You can get this information by executing the nproc –all command in the Linux terminal. Let’s suppose you get “8” as the command output, meaning your PC has an eight-core processor. You can speed up compilation process with the command:

make -j8

It will use all 8 CPU cores for compilation, significantly reducing the compilation time. As a consequence, you’ll notice a much higher CPU usage. If your PC is running other heavy-CPU-usage tasks while you compile NuttX, it’s a good idea not to use all CPU cores for compilation.

At the end of the compilation process, the NuttX RTOS binary is available in the ~/nuttxspace/nuttx folder, as a file called nuttx. This is the file you’ll use to flash NuttX RTOS to the ESP32.

To flash the ESP32 with the NuttX RTOS image, pre-compiled bootloader binary and pre-compiled partition table binary, use the command below. Note: If the ESP32 is being mapped into a different /dev/ttyUSB* path, replace “/dev/ttyUSB0” with the correct one in the command.

make download ESPTOOL_PORT=/dev/ttyUSB0 ESPTOOL_BAUD=115200 ESPTOOL_BINDIR=../esp-bins


Once NuttX has been flashed into the ESP32-DevKitC board, you’re ready to interact with NuttX. To do this, use the picocom terminal. Picocom is a simple (but efficient) and lightweight serial terminal program for Linux [11]. To install it on your Linux machine (assuming you’re using an Ubuntu distro), execute the following command:

sudo apt-get install picocom

With the ESP32DevKitC board connected to your Linux machine by USB cable, you can use the following command to launch picocom to communicate with it and interact with NuttX. Note: As I mentioned, if your board is enumerated in a different interface than /dev/ttyUSB0, replace it with the correct one in the command.

sudo picocom -b 115200 /dev/ttyUSB0

Picocom terminal will run, and you can start interacting with NuttX. Press the “Enter” key twice, and the NuttX Shell prompt will be shown to you as nsh>. You’re now in direct communication with NuttX flashed into the ESP32.

To check all programs and commands available in NuttX flashed into the ESP32, type “? and press “Enter.” The list of commands and applications will be given, as shown in Figure 9, with the BMP180 application outlined in red.

Finally, let’s run the BMP180 sensor example apps, and watch the sensor being read. To do this, type “bmp180 and press “Enter.” The example application will run, and barometric pressure readings (in Pascal units) will be printed in the picocom terminal (Figure 10).

Figure 9
This is the list of commands and applications. The BMP180 application is outlined in red.
Figure 9
This is the list of commands and applications. The BMP180 application is outlined in red.
Figure 10
These are BMP 180 sensor barometric pressure readings, printed in Pascal units in the picocom terminal.
Figure 10
These are BMP 180 sensor barometric pressure readings, printed in Pascal units in the picocom terminal.

In this article, you’ve been introduced to NuttX RTOS, and now you know its main features, how peripherals are integrated into it, and how to compile and run it in an ESP32 MCU. So you have all the information you need to start exploring NuttX RTOS more deeply. If you like NuttX RTOS and want to know more about it, here are some suggested topics for you to study and practice.

First, try to integrate and use other peripherals (sensors, displays, LEDs, buttons, and so on) into NuttX RTOS. By doing this, you’ll accrue knowledge on how to configure the ESP32 peripherals and its communication interfaces, which is fundamental to developing real-world NuttX projects.

Second, learn how to init applications automatically after NuttX boot, especially regarding NuttShell start-up scripts. Because most real-world electronic devices must automatically init their applications (or the main application) just after boot completes, this resource is a “must-know” for those who want to develop solutions using NuttX RTOS.

Third, if you’re an open-source-software enthusiast, I encourage you to contribute to the NuttX project (RTOS itself and/or applications). It will require you to read, learn, and get hands-on experience with how NuttX RTOS works under the hood. The hands-on experience and understanding you’ll get from this is priceless, and will greatly increase your ability to develop solutions using NuttX. 

[1] NuttX official website:
[2] NuttX RTOS repository:
[3] NuttX RTOS features:
[4] NuttX RTOS applications and OS repositories:
[5] Instructions for downloading and installing full ESP-IDF on your computer:
[6] The official repository’s drivers/sensors folder for peripherals:
[7] Initialization codes for the ESP32 MCU:
[8] Initialization code for ESP32-DevKitC:
[9] BMP180 initialization code for ESP32:
[10] BMP180 NuttX app example:
[11] Picocom official website:,devices%20that%20provide%20serial%20consoles



Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.

Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

Sponsor this Article

Supporting Companies

Upcoming Events

Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

The NuttX Engineer

by Pedro Bertoleti time to read: 16 min