Zephyr Emulator

In this tutorial, you’ll learn the use of micro-ROS with a Zephyr RTOS emulator (also known as Native POSIX) by testing a Ping Pong application.

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 zephyr host

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 Zephyr, these are located at firmware/zephyr_apps/apps. Each app is represented by a folder containing the following files:

  • src/main.c: This file contains the logic of the application.
  • app-colcon.meta: This file contains the micro-ROS app specific colcon configuration. Detailed info on how to configure the RMW via this file can be found here.
  • CMakeLists.txt: This is the CMake file containing the script to compile the application.
  • <transport>.conf: This is a Zephyr specific and transport-dependent app configuration file. <transport> can be serial, serial-usb and host-udp.

For the user to create its custom application, a folder <my_app> will need to be registered in this location, containing the four files just described.

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]

The options available for this configuration step are:

  • --transport or -t: udp, serial or any hardware-specific transport label
  • --dev or -d: agent string descriptor in a serial-like transport
  • --ip or -i: agent IP in a network-like transport
  • --port or -p: agent port in a network-like transport

In this tutorial, we will use a UDP transport that looks for the agent on the port UDP/8888 at localhost, and focus on the out-of-the-box ping_pong application located at firmware/zephyr_apps/apps/ping_pong. To execute this application with the chosen transport, run the configuration command above by specifying the [APP] and [OPTIONS] parameters as below:

# Configure step
ros2 run micro_ros_setup configure_firmware.sh ping_pong --transport udp --ip 127.0.0.1 --port 8888

You can check the complete content of the ping_pong 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 Zephyr app specific files can be found here: main.c, app-colcon.meta, CMakeLists.txt and host-udp.conf. 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

Now you have a Zephyr + micro-ROS app ready to run on your own computer. Notice that in this case, the steps of flashing the firmware and running the micro-ROS app go together.

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

Running the micro-ROS app

At this point, you have both the client and the agent correctly installed in your host machine.

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 udp4 --port 8888

Flashing the firmware

Finally, in order to run the micro-ROS node inside of the Zephyr RTOS emulator, open a new command shell and execute the flash step by means of the flashing command:

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

# Flash/run step
ros2 run micro_ros_setup flash_firmware.sh

Testing the micro-ROS app

Now, we 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 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 micro-ROS 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 node will answer with a pong:

user@user:~$ ros2 run micro_ros_demos_rcl ping_pong
Ping send seq 1706097268_1085377743
Ping send seq 181171802_1085377743
Ping send seq 1385567526_1085377743
Ping send seq 926583793_1085377743
Ping send seq 1831510138_1085377743
Ping received with seq fake_ping. Answering.
Ping send seq 1508705084_1085377743
Ping send seq 1702133625_1085377743
Ping send seq 176104820_1085377743

As a consequence, in the pong subscriber console, we should see the micro-ROS app 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

One of the advantages of having an emulator is that you don’t need to buy a bunch of hardware in order to test some multi-node micro-ROS apps. So, with the same micro-ROS agent of the last section, let’s open four different command lines and run the following on each:

cd microros_ws

# This is an alternative way of executing the Zephyr emulator
./firmware/build/zephyr/zephyr.exe

As soon as all micro-ROS node are up and connected to the micro-ROS agent you will see them interacting:

user@user:~$ ./firmware/build/zephyr/zephyr.exe
*** Booting Zephyr OS build zephyr-v2.2.0-492-gc73cb85b4ae9  ***
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.

TIP: use the help flag to discover some Zephyr emulation features ./firmware/build/zephyr/zephyr.exe -h