First micro-ROS Application on NuttX

In this tutorial, you’ll learn the use of micro-ROS with NuttX by testing a Ping Pong application. The target hardware for this tutorial is the Olimex STM32-E407 evaluation board.

The following hardware will be used:

Installing ROS 2 and the micro-ROS build system

First of all, install ROS 2 Humble Hawksbill on your Ubuntu 22.04 LTS computer. To do so from binaries, via Debian packages, follow the instructions detailed here.

TIP: Alternatively, you can use a docker container with a fresh ROS 2 Humble installation. The one that serves the purpose is the container run by the command:

docker run -it --net=host -v /dev:/dev --privileged ros:humble

Once you have a ROS 2 installation in the computer, follow these steps to install the micro-ROS build system:

# Source the ROS 2 installation
source /opt/ros/$ROS_DISTRO/setup.bash

# Create a workspace and download the micro-ROS tools
mkdir microros_ws
cd microros_ws
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro_ros_setup.git src/micro_ros_setup

# Update dependencies using rosdep
sudo apt update && rosdep update
rosdep install --from-paths src --ignore-src -y

# Install pip
sudo apt-get install python3-pip

# Build micro-ROS tools and source them
colcon build
source install/local_setup.bash

These instructions will setup a workspace with a ready-to-use micro-ROS build system. This build system is in charge of downloading the required cross-compilation tools and building the apps for the required platforms.

The build system’s workflow is a four-step procedure:

  • Create step: This step is in charge of downloading all the required code repositories and cross-compilation toolchains for the specific hardware platform. Among these repositories, it will also download a collection of ready to use micro-ROS apps.
  • Configure step: In this step, the user can select which app is going to be cross-compiled by the toolchain. Some other options, such as transport, agent’s IP address/port (for UDP transport) or device ID (for serial connections) will be also selected in this step.
  • Build step: Here is where the cross-compilation takes place and the platform-specific binaries are generated.
  • Flash step: The binaries generated in the previous step are flashed onto the hardware platform memory, in order to allow the execution of the micro-ROS app. Further information about micro-ROS build system can be found here.

Creating a new firmware workspace

Once the build system is installed, let’s create a firmware workspace that targets all the required code and tools:

# Create step
ros2 run micro_ros_setup create_firmware_ws.sh nuttx olimex-stm32-e407

Once the command is executed, a folder named firmware must be present in your workspace.

This step is in charge, among other things, of downloading a set of micro-ROS apps for the specific platform you are addressing. In the case of NuttX, these are located here. Each app is represented by a folder containing the following files:

  • app.c: This file contains the logic of the application.
  • Kconfig: This file contains the NuttX Kconfig configuration.
  • Make.defs: This file contains the NuttX build system definitions.
  • Makefile: This file contains the NuttX specific app build script.

Configuring the firmware

The configuration step will set up the main micro-ROS options and select the desired application. It can be executed with the following command:

# Configure step
ros2 run micro_ros_setup configure_firmware.sh [APP] [OPTIONS]

In this tutorial, we will use a Serial transport and focus on the out-of-the-box uros_pingpong application located here. To execute this application with the chosen transport, run the configuration command above by specifying the [APP] parameter as below:

# Configure step with ping_pong app and serial-usb transport
ros2 run micro_ros_setup configure_firmware.sh pingpong

and with no [OPTIONS] parameter.

A pre-configured ethernet example is also available:

# Configure step with ping_pong app and serial-usb transport
ros2 run micro_ros_setup configure_firmware.sh pingpong-eth

To proceed with the configuration, clone the following NuttX tools repo:

# Download the tools necessary to work with NuttX
git clone https://bitbucket.org/nuttx/tools.git firmware/tools

and then install the required kconfig-frontends:

pushd firmware/tools/kconfig-frontends
./configure
make

# if the make command fails, type: autoreconf -f -i , and then rerun the make command.

sudo make install
sudo ldconfig
popd

Now we have two options to configure our micro-ROS transport:

  • Interactive NuttX menu config
    • Launch the configuration menu:

      cd firmware/NuttX
      make menuconfig
      
    • You can check that the application has been selected under Application Configuration ---> Examples ---> micro-ROS Ping Pong.
    • The transport is also pre-configured under the Application Configuration ---> micro-ROS ---> Transport option.
    • To configure the transport, use the IP address of the agent and Port number of the agent options for UDP and Serial port to use for the serial example.
    • To save the changes, navigate to the bottom menu with the left and right arrows, and click on the Save button.
    • You will be asked if you want to save your new .config configuration, and you need to click Ok, and then Exit.
    • Push three times the Esc key to close the menu and go back to microros_ws with:

        cd ../..
      
  • kconfig-tweak console commands:
    • Go to Nuttx configuration path:

      cd firmware/NuttX
      
    • UDP transport configuration:
      kconfig-tweak --set-val CONFIG_UROS_AGENT_IP "127.0.0.1"
      kconfig-tweak --set-val CONFIG_UROS_AGENT_PORT 8888
      
    • Serial transport configuration:
      kconfig-tweak --set-val CONFIG_UROS_SERIAL_PORT "/dev/ttyS0"
      

You can check the complete content of the uros_pingpong app here.

This example showcases a micro-ROS node with two publisher-subscriber pairs associated with a ping and a pong topics, respectively. The node sends a ping package with a unique identifier, using a ping publisher. If the ping subscriber receives a ping from an external node, the pong publisher responds to the incoming ping with a pong. To test that this logic is correctly functioning, we implement communication with a ROS 2 node that:

  • Listens to the topics published by the ping subscriber.
  • Publishes a fake_ping package, that is received by the micro-ROS ping subscriber. As a consequence, the pong publisher on the micro-ROS application will publish a pong, to signal that it received the fake_ping correctly.

The diagram below clarifies the communication flow between these entities:

pingpong

The contents of the FreeRTOS app specific files can be found here: app.c, Kconfig, Make.defs and Makefile. A thorough review of these files is illustrative of how to create a micro-ROS app in this RTOS.

Building the firmware

When the configuring step ends, just build the firmware:

# Build step
ros2 run micro_ros_setup build_firmware.sh

Flashing the firmware

Flashing the firmware into the platform varies across hardware platforms. Regarding this tutorial’s target platform (Olimex STM32-E407), the JTAG interface is going to be used to flash the firmware.

Connect the Olimex ARM-USB-TINY-H to the board:

Make sure that the board power supply jumper (PWR_SEL) is in the 3-4 position in order to power the board from the JTAG connector:

Once you have your computer connected to the Olimex board through the JTAG adapter, run the flash step:

# Flash step
ros2 run micro_ros_setup flash_firmware.sh

Creating the micro-ROS agent

The micro-ROS app is now ready to be connected to a micro-ROS agent to start talking with the rest of the ROS 2 world. To do that, let’s first of all create a micro-ROS agent:

# Download micro-ROS-Agent packages
ros2 run micro_ros_setup create_agent_ws.sh

Now, let’s build the agent packages and, when this is done, source the installation:

# Build step
ros2 run micro_ros_setup build_agent.sh
source install/local_setup.bash

Then, depending on the selected transport and RTOS, the board connection to the agent may differ. In this tutorial, we’re using the Olimex STM32-E407 Serial connection, for which the Olimex development board is connected to the computer using the usb to serial cable.

Additionally, you’ll need to connect a USB-to-mini-USB cable to the USB OTG 1 connector (the miniUSB connector that is closer to the Ethernet port).

TIP: Color codes are applicable to this cable. Make sure to match Olimex Rx with Cable Tx and vice-versa. Remember GND!

Running the micro-ROS app

At this point, you have both the client and the agent correctly installed.

To give micro-ROS access to the ROS 2 dataspace, run the agent:

# Run a micro-ROS agent
ros2 run micro_ros_agent micro_ros_agent serial --dev [device]

TIP: you can use this command to find your serial device name: ls /dev/serial/by-id/*

Then, in order to launch the micro-ROS application, you need to install and open Minicom, a text-based serial port communications program. Open a new shell, and type:

sudo minicom -D [device] -b 115200

TIP: you can use this command to find your serial device name: ls /dev/serial/by-id/*. Select the one that starts with usb-NuttX.

From inside the Minicom application, press three times the Enter key until Nuttx Shell (NSH) appears. Once you enter the NSH command line, type:

uros_pingpong

Testing the micro-ROS app

At this point, the micro-ROS app is built and flashed and the board is connected to a micro-ROS agent. We now want to check that everything is working.

Open a new command line. We are going to listen to the ping topic with ROS 2 to check whether the micro-ROS Ping Pong node is correctly publishing the expected pings:

source /opt/ros/$ROS_DISTRO/setup.bash

# Subscribe to micro-ROS ping topic
ros2 topic echo /microROS/ping

You should see the topic messages published by the Ping Pong node every 5 seconds:

user@user:~$ ros2 topic echo /microROS/ping
stamp:
  sec: 20
  nanosec: 867000000
frame_id: '1344887256_1085377743'
---
stamp:
  sec: 25
  nanosec: 942000000
frame_id: '730417256_1085377743'
---

At this point, we know that our micro-ROS app is publishing pings. Let’s check if it also answers to someone else’s pings. If this works, it’ll publish a pong.

So, first of all, let’s subscribe with ROS 2 to the pong topic from a new shell (notice that initially we don’t expect to receive any pong, since none has been sent yet):

source /opt/ros/$ROS_DISTRO/setup.bash

# Subscribe to micro-ROS pong topic
ros2 topic echo /microROS/pong

And now, let’s publish a fake_ping with ROS 2 from yet another command line:

source /opt/ros/$ROS_DISTRO/setup.bash

# Send a fake ping
ros2 topic pub --once /microROS/ping std_msgs/msg/Header '{frame_id: "fake_ping"}'

Now, we should see this fake_ping in the ping subscriber console, along with the board’s pings:

user@user:~$ ros2 topic echo /microROS/ping
stamp:
  sec: 0
  nanosec: 0
frame_id: fake_ping
---
stamp:
  sec: 305
  nanosec: 973000000
frame_id: '451230256_1085377743'
---
stamp:
  sec: 310
  nanosec: 957000000
frame_id: '2084670932_1085377743'
---

Also, we expect that, because of having received the fake_ping, the micro-ROS pong publisher will answer with a pong. As a consequence, in the pong subscriber console, we should see the board’s answer to our fake_ping:

user@user:~$ ros2 topic echo /microROS/pong
stamp:
  sec: 0
  nanosec: 0
frame_id: fake_ping
---

Multiple Ping Pong nodes

If you have multiple boards, by connecting them to the same ROS 2 space it is possible to see them interacting. In the case you only have one board, it is possible to see your micro-ROS ping pong app running on hardware interacting with the ping pong app from the Linux tutorial. When multiple ping pong nodes coexists, it is possible to see their output like this micro-ROS for Linux app:

Ping send seq 1711620172_1742614911                      <---- This micro-ROS node sends a ping with ping ID "1711620172" and node ID "1742614911"
Pong for seq 1711620172_1742614911 (1)                   <---- The first mate pongs my ping
Pong for seq 1711620172_1742614911 (2)                   <---- The second mate pongs my ping
Pong for seq 1711620172_1742614911 (3)                   <---- The third mate pongs my ping
Ping received with seq 1845948271_546591567. Answering.  <---- A ping is received from a mate identified as "546591567", let's pong it.
Ping received with seq 232977719_1681483056. Answering.  <---- A ping is received from a mate identified as "1681483056", let's pong it.
Ping received with seq 1134264528_1107823050. Answering. <---- A ping is received from a mate identified as "1107823050", let's pong it.
Ping send seq 324239260_1742614911
Pong for seq 324239260_1742614911 (1)
Pong for seq 324239260_1742614911 (2)
Pong for seq 324239260_1742614911 (3)
Ping received with seq 1435780593_546591567. Answering.
Ping received with seq 2034268578_1681483056. Answering.

This completes the First micro-ROS Application on NuttX tutorial. Do you want to go back and try a different RTOS, i.e. FreeRTOS or Zephyr?