micro-ROS Embedded Guide 2026
Run ROS 2 on microcontrollers — ESP32, STM32, and Raspberry Pi Pico 2W. Full guide from agent setup to working publisher/subscriber nodes on 520KB RAM.
Supported Boards — 2026 Comparison
ESP32-S3
Espressif · $4–$15
Flash
8MB
RAM
512KB SRAM + 8MB PSRAM
Transport
Serial / WiFi UDP
RTOS
FreeRTOS
✓Dual-core 240MHz Xtensa
✓Built-in WiFi/BT for wireless transport
✓Arduino + ESP-IDF frameworks supported
STM32H743
ST Microelectronics · $8–$40 (Nucleo)
Flash
2MB
RAM
1MB
Transport
Serial / USB CDC / Ethernet
RTOS
FreeRTOS / ThreadX / Zephyr
✓480MHz Cortex-M7 with FPU + DSP
✓1MB RAM — largest in the class
✓Ethernet + USB HS built-in on some variants
Raspberry Pi Pico 2 W
Raspberry Pi Ltd · $7
Flash
4MB
RAM
520KB
Transport
Serial / WiFi UDP
RTOS
FreeRTOS / bare-metal
✓RP2350 dual Cortex-M33 150MHz
✓Built-in WiFi on Pico 2W
✓Excellent SDK documentation
Arduino Portenta H7
Arduino · $103
Flash
16MB QSPI
RAM
8MB SDRAM
Transport
Serial / Ethernet / WiFi
RTOS
Mbed OS
✓STM32H747 dual core (M7 + M4)
✓8MB SDRAM — runs complex ROS 2 graphs
✓Official micro-ROS Arduino library
1. micro-ROS Agent Setup
The Agent runs on the host (PC, Jetson, Raspberry Pi) and bridges the microcontroller to ROS 2. Install it first.
Install the micro-ROS Agent
The micro-ROS Agent bridges between the microcontroller and ROS 2. Run it on any Linux machine (PC, Jetson, Raspberry Pi) connected to the MCU.
# Method 1: Docker (easiest — no build required) docker run -it --net=host microros/micro-ros-agent:jazzy udp4 --port 8888 # For serial: docker run -it --device=/dev/ttyUSB0 microros/micro-ros-agent:jazzy serial --dev /dev/ttyUSB0 # Method 2: Build from source (ROS 2 Jazzy) sudo apt install ros-jazzy-micro-ros-agent -y ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888 # Method 3: snap sudo snap install micro-ros-agent micro-ros-agent udp4 --port 8888
💡 UDP transport (port 8888) is recommended for WiFi-connected MCUs. Serial /dev/ttyUSB0 is best for wired ESP32/STM32. The agent auto-discovers entities — no config needed.
2. ESP32 — PlatformIO + Arduino
Fastest path: ESP32 + Arduino framework + micro_ros_arduino library.
ESP32 micro-ROS with PlatformIO
Create a PlatformIO project that publishes an Int32 topic from ESP32 over serial.
; platformio.ini [env:esp32dev] platform = espressif32 board = esp32dev framework = arduino lib_deps = https://github.com/micro-ROS/micro_ros_arduino
ESP32 publisher — publishes /counter every 1s
Minimal micro-ROS publisher that sends an incrementing counter to a ROS 2 topic.
#include <micro_ros_arduino.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/int32.h>
rcl_publisher_t publisher;
std_msgs__msg__Int32 msg;
rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;
void timer_callback(rcl_timer_t * timer, int64_t last_call_time) {
(void) last_call_time;
if (timer != NULL) {
rcl_publish(&publisher, &msg, NULL);
msg.data++;
}
}
void setup() {
// Transport: Serial (for wired) or WiFi UDP
set_microros_serial_transports(Serial);
// For WiFi: set_microros_wifi_transports("SSID", "PASS", "192.168.1.100", 8888);
delay(2000);
allocator = rcl_get_default_allocator();
rclc_support_init(&support, 0, NULL, &allocator);
rclc_node_init_default(&node, "esp32_counter", "", &support);
rclc_publisher_init_default(&publisher, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
"counter");
rclc_timer_init_default(&timer, &support, RCL_MS_TO_NS(1000), timer_callback);
rclc_executor_init(&executor, &support.context, 1, &allocator);
rclc_executor_add_timer(&executor, &timer);
msg.data = 0;
}
void loop() {
delay(100);
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}💡 Flash this to ESP32 via PlatformIO (`pio run -t upload`). Then start the agent: `ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0 -b 115200`. After 2s you should see `/counter` in `ros2 topic list`.
3. Raspberry Pi Pico 2 W — CMake SDK
Official micro-ROS Pico SDK using CMake. Supports both RP2040 and RP2350.
Raspberry Pi Pico 2 — micro-ROS CMake setup
Use the official micro-ROS SDK for Pico. Clone the example repo and build with CMake.
# Clone micro-ROS Pico SDK git clone -b jazzy https://github.com/micro-ROS/micro_ros_raspberrypi_pico_sdk cd micro_ros_raspberrypi_pico_sdk # Set up Pico SDK environment export PICO_SDK_PATH=/path/to/pico-sdk # Build mkdir build && cd build cmake .. -DPICO_BOARD=pico_w # or pico2_w for RP2350 make -j$(nproc) # Flash the generated .uf2 file to the Pico # (hold BOOTSEL and plug in USB → drag pico_micro_ros_example.uf2 to RPI-RP2 drive)
Pico subscriber — receive commands from ROS 2
Subscribe to /cmd from ROS 2 and toggle the onboard LED based on the value.
#include <stdio.h>
#include "pico/stdlib.h"
#include <rcl/rcl.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/bool.h>
#include "pico_uart_transports.h"
// LED pin on Pico (GP25 on original Pico, GP32 on Pico W)
#define LED_PIN 25
rcl_subscription_t subscriber;
std_msgs__msg__Bool msg;
rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
void subscription_callback(const void * msgin) {
const std_msgs__msg__Bool * msg = (const std_msgs__msg__Bool *) msgin;
gpio_put(LED_PIN, msg->data ? 1 : 0);
}
int main() {
stdio_init_all();
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
rmw_uros_set_custom_transport(true, NULL,
pico_serial_transport_open,
pico_serial_transport_close,
pico_serial_transport_write,
pico_serial_transport_read);
allocator = rcl_get_default_allocator();
rclc_support_init(&support, 0, NULL, &allocator);
rclc_node_init_default(&node, "pico_led", "", &support);
rclc_subscription_init_default(&subscriber, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Bool), "cmd");
rclc_executor_init(&executor, &support.context, 1, &allocator);
rclc_executor_add_subscription(&executor, &subscriber, &msg,
&subscription_callback, ON_NEW_DATA);
while (true) {
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(10));
}
return 0;
}💡 After flashing, send from ROS 2: `ros2 topic pub /cmd std_msgs/msg/Bool '{data: true}'` — the Pico LED lights up within 10ms.
4. STM32 — Zephyr RTOS
Production-grade micro-ROS on STM32 using Zephyr RTOS. Best for industrial embedded systems.
STM32 with Zephyr RTOS (recommended for production)
Zephyr provides the most robust RTOS foundation for micro-ROS on STM32. Works with STM32H743, STM32F4, and 100+ other boards.
# Install Zephyr SDK pip install west west init ~/zephyrproject cd ~/zephyrproject west update west zephyr-export pip install -r zephyr/scripts/requirements.txt # Download Zephyr SDK toolchain wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.0/zephyr-sdk-0.17.0_linux-x86_64.tar.xz tar -xf zephyr-sdk-0.17.0_linux-x86_64.tar.xz cd zephyr-sdk-0.17.0 ./setup.sh # Build micro-ROS Zephyr example for STM32H743-NUCLEO cd ~/zephyrproject/zephyr west build -p auto -b nucleo_h743zi \ samples/subsys/bindesc -- \ -DEXTRA_CONF_FILE="overlay-microros.conf" # Flash west flash
💡 Zephyr's native USB CDC-ACM transport is the most reliable for STM32. No USB-to-serial adapter needed — plug directly into PC.