Skip to content
Thomas Chou edited this page Dec 13, 2024 · 71 revisions

micro_ros_arduino_examples_platformio

This is a port of examples from micro_ros_arduino to PlatfromIO.

Recommended MCU for beginners : pico2 pico

Pico2/pico are cheap and have more resources for robotic than most other MCUs. They have 4M/2M flash, 512K/256K RAM, 24/16 PWM for motors and 12/8 PIO state-machines for wheel encoders. The USB CDC /dev/ttyACM0 on pico2/pico works better than USB serial /dev/ttyUSB0 on esp32. Pico2 and pico share the same pinout. Because their prices are very close, you should choose pico2.

Esp32 supports micro-ROS wifi transport and can be used if you do not have a robot computer such as RPI4/5. It has 4M flash, 512K RAM, 16 PWM for motors and 8 pulse counters for wheel encoders.

Teensy41 is fast but more expensive. It has 8M flash, 1M RAM, 31 PWM pins. It uses interrupt-driven encoders.

Most Arduino boards have very limited flash and RAM for micro-ROS. Some have only 32K RAM. They should be avoided.

Install the software tools

The latest ROS2 LTS release is Jazzy and the latest Ubuntu LTS release is 24.04. This combination is recommended for beginners and new designs. The micro_ros_platformio is based on Arduino framework. Most Arduino drivers should work. The following guide assumes you are building pico micro-ROS firmware for ROS2 Jazzy on Ubuntu 24.04.

If you develop on Windows

If you develop on Windows, you may install vscode with platformio extension and use docker to run ROS2.

Install Ubuntu 24.04 and low latency kernel

Install Ubuntu 24.04 Desktop. Or, if you use Rpi4/5 as robot computer, you may use vscode/ssh for remote development on your Windows or Linux desktop.

Install low latency kernel and reboot to use new kernel. Default linux kernel is optimized for computation. Switch to low latency kernel will improve realtime response for robot control.

sudo apt update
sudo apt upgrade -y
sudo apt-get install -y --install-recommends linux-lowlatency
sudo reboot

Prepare

Install essential build tools. Remove brltty package which interferes with CH340 USB serial bridge on some esp32 boards.

sudo apt remove -y brltty
sudo apt install -y python3-venv build-essential cmake git curl

Install PlatfromIO and udev rules

curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
python3 get-platformio.py
rm get-platformio.py
echo "PATH=\"\$PATH:\$HOME/.platformio/penv/bin\"" >> $HOME/.bashrc
source ~/.bashrc
curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules
sudo service udev restart
sudo usermod -a -G dialout $USER
sudo usermod -a -G plugdev $USER

Skip the udev step or ignore the udev error if you are running on docker.

Blink

Blink is the "hello world" for micro-controller. It should be run when we get a new board as the very first test. Clone the example source into your working directory. Change the env "pico" to the board you use, eg "esp32" or "teensy41" etc. Build and upload. The on-board LED should blink every two seconds.

git clone https://github.com/hippo5329/Blink-platformio.git
cd Blink-platformio
pio run -e pico -t upload

You may skip the following ROS2 and micro-ROS agent installations

Platformio does not require ROS2 installation to build micro-ROS firmware. If you want to build the micro-ROS firmware without ROS2 installed, you may set the ROS_DISTRO env to yours, eg, jazzy or humble. Then proceed to build with platformio.

export ROS_DISTRO=jazzy

Or, set it in platformio/platformio.ini

board_microros_distro = jazzy

Install ROS2 Jazzy

(PS. If you still want to use Humble and Ubuntu 22.04, you will need to replace all commands here from "jazzy" to "humble".)

export ROS_DISTRO=jazzy
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
sudo apt update
sudo apt install -y ros-$ROS_DISTRO-desktop ros-dev-tools python3-colcon-common-extensions python3-pip
sudo rosdep init
rosdep update
echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc
source ~/.bashrc

Build micro-ROS agent

mkdir ~/uros_ws/src -p
cd ~/uros_ws/src
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro-ROS-Agent.git
git clone -b $ROS_DISTRO https://github.com/micro-ROS/micro_ros_msgs.git
cd ..
rosdep install --from-paths src --ignore-src -r -y
colcon build
cd ~
echo "source \$HOME/uros_ws/install/setup.bash" >> ~/.bashrc
source ~/.bashrc

Clone examples repository

Clone the examples source into your working directory.

git clone https://github.com/hippo5329/micro_ros_arduino_examples_platformio.git

All the examples use a shared configuration file platformio/platformioio.ini.

micro-ROS transports

Micro-ROS supports serial, mutli-serial, udp/wifi and custom transports.

  1. If you have a robot computer such as rpi4/5, jetson, laptop or mini PC on your robot. You will connect your micro-ROS controller with USB serial port and use serial transport. This is the most popular use case.

  2. If you have several micro-ROS controllers connected to USB ports, you will use multi-serial transport.

  3. If you do not have a robot computer on your robot, you can use wifi transport to connect the micro-controller to ROS2 on a remote PC or server.

Serial transport

Take micro-ros_reconnection_example as an example, build and upload to pico.

cd micro_ros_arduino_examples_platformio/micro-ros_reconnection_example
pio run -e pico -t upload

Then connect to micro-ROS serial agent.

ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0 --baudrate 921600

Screen output after connect.

[1718994292.702493] info     | TermiosAgentLinux.cpp | init                     | running...             | fd: 14
[1718994292.702712] info     | Root.cpp           | set_verbose_level        | logger setup           | verbose_level: 4
[1718994293.013253] info     | Root.cpp           | create_client            | create                 | client_key: 0x64E60CB5, session_id: 0x81
[1718994293.013293] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x64E60CB5, address: 0
[1718994293.033488] info     | ProxyClient.cpp    | create_participant       | participant created    | client_key: 0x64E60CB5, participant_id: 0x000(1)
[1718994293.049832] info     | ProxyClient.cpp    | create_topic             | topic created          | client_key: 0x64E60CB5, topic_id: 0x000(2), participant_id: 0x000(1)
[1718994293.060696] info     | ProxyClient.cpp    | create_publisher         | publisher created      | client_key: 0x64E60CB5, publisher_id: 0x000(3), participant_id: 0x000(1)
[1718994293.072949] info     | ProxyClient.cpp    | create_datawriter        | datawriter created     | client_key: 0x64E60CB5, datawriter_id: 0x000(5), publisher_id: 0x000(3)

You may use Ctrl-C to kill the agent. Then run the agent again. It will reconnect to the agent.

micro-ROS serial transport uses serial port exclusively

Both the pico programmer and micro-ROS agent use the same serial port. They can not run at the same time. You will need to kill the micro-ROS agent to program pico. And you cannot use serial port to print messages when micro-ROS serial transport is running.

Micro-ROS wifi transport does have such limitation.

use pico2

pio run -e pico2 -t upload
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0

use esp32

pio run -e esp32 -t upload
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0

use teensy41

pio run -e teensy41 -t upload
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0

Check topic

Open another terminal to check the publisher node and topic.

ubuntu@better-snipe:~$ ros2 node list
/int32_publisher_rclc

ubuntu@better-snipe:~$ ros2 node info /int32_publisher_rclc
/int32_publisher_rclc
  Subscribers:

  Publishers:
    /std_msgs_msg_Int32: std_msgs/msg/Int32
  Service Servers:

  Service Clients:

  Action Servers:

  Action Clients:

ubuntu@better-snipe:~$ ros2 topic list -t
/std_msgs_msg_Int32 [std_msgs/msg/Int32]
/parameter_events [rcl_interfaces/msg/ParameterEvent]
/rosout [rcl_interfaces/msg/Log]

ubuntu@better-snipe:~$ ros2 topic echo /std_msgs_msg_Int32
data: 162
---
data: 163
---
data: 164
---

Wifi transport - esp32

Update micro_ros_arduino_examples_platformio/platformio/platformio.ini with agent ip (ie. your computer's ip) and wifi keys.

[env]
...
build_flags =
    ...
    '-D AGENT_IP={ 192, 168, 1, 100 }'
    '-D AGENT_PORT=8888'
    '-D WIFI_SSID="wifi_ssid"'
    '-D WIFI_PASSWORD="wifi_password"'

Take micro-ros_reconnection_example as an example, build and upload to esp32.

cd micro_ros_arduino_examples_platformio/micro-ros_reconnection_example
pio run -e esp32_wifi -t upload

Run micro-ROS wifi agent.

ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888

Screen output of agent after connected.

[1719004509.846308] info     | UDPv4AgentLinux.cpp | init                     | running...             | port: 8888
[1719004509.846534] info     | Root.cpp           | set_verbose_level        | logger setup           | verbose_level: 4
[1719004511.136599] info     | Root.cpp           | create_client            | create                 | client_key: 0x6CFC0C00, session_id: 0x81
[1719004511.136763] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x6CFC0C00, address: 192.168.1.101:47138
[1719004511.348916] info     | ProxyClient.cpp    | create_participant       | participant created    | client_key: 0x6CFC0C00, participant_id: 0x000(1)
[1719004511.399397] info     | ProxyClient.cpp    | create_topic             | topic created          | client_key: 0x6CFC0C00, topic_id: 0x000(2), participant_id: 0x000(1)
[1719004511.416526] info     | ProxyClient.cpp    | create_publisher         | publisher created      | client_key: 0x6CFC0C00, publisher_id: 0x000(3), participant_id: 0x000(1)
[1719004511.461559] info     | ProxyClient.cpp    | create_datawriter        | datawriter created     | client_key: 0x6CFC0C00, datawriter_id: 0x000(5), publisher_id: 0x000(3)

Open another terminal to check the publisher node and topic as it was in serial transport.

NativeEthernet transport - teensy41 ethernet kit

Follow the ethernet kit guide to build the hardware. Test it with Arduino examples - native ethernet - web server. You should make sure the hardware works before proceeding.

Update micro_ros_arduino_examples_platformio/platformio/platformio.ini with your network settings.

[env]
...
build_flags =
    '-D LOCAL_MAC={ 0xAA, 0xBB, 0xCC, 0xEE, 0xDD, 0xFF }'
    '-D LOCAL_IP={ 192, 168, 1, 101 }'
    '-D AGENT_IP={ 192, 168, 1, 100 }'
    '-D AGENT_PORT=8888'

Take micro-ros_reconnection_example as an example, build and upload to teensy41.

cd micro_ros_arduino_examples_platformio/micro-ros_reconnection_example
pio run -e teensy41_eth -t upload

The native ethernet transport uses the same udp4 protocol as wifi transport. Follow the wifi transport guide to bring up the agent and check topic.

Supported ROS and Linux distributions

  • rolling -- ubuntu 24.04
  • jazzy -- ubuntu 24.04 -- recommended
  • iron -- ubuntu 22.04
  • humble -- ubuntu 22.04
  • (foxy EOL) -- (ubuntu 20.04 EOL)

Supported boards

portenta_h7_m7, teensy41, teensy40, teensy36, teensy35, teensy31, due, zero, olimex_e407, esp32, esp32s3, esp32c3, nanorp2040connect, pico, pico2.

micro_ros_arduino examples

Supported examples:

  • micro-ros_subscriber
  • micro-ros_subscriber_twist
  • micro-ros_addtwoints_service
  • micro-ros_time_sync
  • micro-ros_publisher
  • micro-ros_types_handling
  • micro-ros_reconnection_example

Unsupported examples due to specific boards/sensors requirement:

  • micro-ros_IMU_ML
  • micro-ros_tf_publisher
  • micro-ros_decibels

Removed examples:

  • micro-ros_publisher_ethernet = micro-ros_publisher
  • micro-ros_publisher_wifi = micro-ros_publisher

Development on Windows or Linux desktop and use VScode remote development SSH to Rpi4/5

If you run Windows or Linux desktop and want to develop on Rpi4/5, you can use remote development SSH feature of VSCode. Though the setup may take some time, it will save a lot of time in the future.

Overview

  1. Install VSCode on Windows or Linux desktop
  2. Install Remote development extension on VSCode
  3. Generate SSH key pair on Windows or Linux desktop
  4. Upload SSH public key to your github account
  5. Import SSH key from github to Rpi
  6. Connect to Rpi from VSCode on Windows or Linux desktop
  7. Edit and build your application on Rpi through VSCode on Windows or Linux desktop

In details

Install VSCode on Windows or Linux desktop

Open browser on Windows. Download and install VSCode. https://code.visualstudio.com/download

Install Remote development extension on VSCode

Run VSCode and add the "Remote Development" extension from microsoft.com.

Generate SSH key pair on Windows or Linux desktop

Run PowerShell on Windows or open a terminal on Linux desktop. Run ssh-keygen and press Enter to the questions.

ssh-keygen -t ed25519
cat .ssh/id_ed25519.pub
XXXXXX

The long string XXXXXX is your SSH public key. Copy it to clipboard.

Upload SSH public key to your github account

Open browser. Sign-in to your github. In Profile - Settings - SSH and GPG keys - New SSH Key, paste the key from your clipboard and save.

Import SSH key from github to Rpi

You may create your Rpi SD card using RPI Imager, select "Ubuntu 24.04 LTS Server" to run ROS2 Jazzy. Then setup your WIFI keys, username, password and ssh key XXXXXX. Or, after you login the Rpi on screen or ssh, import the ssk key from github.

sudo apt install -y ssh-import-id
ssh-import-id-gh <your github account name>

It should install and list your public keys on github.

Connect to Rpi from VSCode on Windows or Linux desktop

From VSCode, press F1, search SSH, Remote SSH: Add New SSH Host, enter the command below, change ubuntu to your username on Rpi, change 192.168.1.100 to your Rpi IP address.

ssh ubuntu@192.168.1.100

Save it to .ssh/config on your Windows account. Press F1 again, search SSH, Remote SSH: Connect to Host.. or Connect current Windows to Host..

Edit and build your application on Rpi through VSCode on Windows or Linux desktop

Now the VSCode is connected to the remote Rpi. Open a terminal in vscode to run command on Rpi. Run "ssh-keygen -t ed25519" for Rpi and add the public key to your github.

You will install VSCode extensions, such as PlatfromIO, GitLens etc, to your Rpi. And clone repository from github, edit, build and develop to Rpi from your Windows or Linux desktop. The code will be built on the Rpi. This way, you develop remotely on your VSCode on Windows or Linux desktop. You do not need a monitor connected to your Rpi.

You may start with the Blink example. It is the "hello world" for micro-controller. It should be run when we get a new board as the first test.

https://github.com/hippo5329/Blink-platformio

You may fork the Blink example, use vscode on Windows to clone build and upload on Rpi4/5 to pico. You will use use "SSH" url to clone your forks instead of "HTTPS" when you clone the other people's repos.

VSCode on Windows --> Rpi4/5 --> pico

VSCode on Windows or Linux desktop is your command center. You can open terminals in VSCode on Windows to run and launch ros2 on Rpi4. A browser (mostly to github.com) and VSCode will server your development work. Once you get familiar with this github/vscode/platformio workflow, you will work better then ever. enjoy.

Tip: enable trailing whitespace trimming.

String Array Multiarray

If you use variable size message, such as string, array or multiarray, you have to handle the allocation of the message.

https://micro.ros.org/docs/tutorials/advanced/handling_type_memory/

Service Action Parameters

If you use service, action or parameters, you will need to increase RMW allocation with a user meta file. An action requires 3 services and 2 topics. A parameters server requires 5 services.

platformio.ini board_microros_user_meta = action.meta

https://micro.ros.org/docs/tutorials/advanced/microxrcedds_rmw_configuration/

Additional tutorials

micro-ROS tutorials includes important usage information.

Learn VSCode

Learn VSCode SSH

Git guide on github

Getting started on VSCode

VSCode SSH Pi

PlatfromIO IDE for VSCode - a bit outdated

ROS2 and VSCode - 2024 very new

micro-ROS demos on PlatfromIO

linorobot2_hardware firmware for mobile robots

VNC server Install VNC server on Rpi, and view RVIZ etc on Windows with VNC viewer.

Docker

An Updated Guide to Docker and ROS 2

Setup ROS 2 with VSCode and Docker

Develop on a remote Docker host

Connect to remote Docker over SSH

Docker nodes must be on the same host. Only the platformio programmer and micro-ros agent dockers need " -v /dev:/dev --privileged" to use the USB serial port.

# Open terminal, run docker to install platformio, build and upload the micro-ros firmware to pico

$ docker run -it -v /dev:/dev --privileged ros:humble
...

# In another terminal, run micro-ros agent

$ docker run -it -v /dev:/dev --privileged microros/micro-ros-agent:humble serial --dev /dev/ttyACM0 --baudrate 921600
[1730936457.655211] info     | TermiosAgentLinux.cpp | init                     | running...             | fd: 3
[1730936457.656602] info     | Root.cpp           | set_verbose_level        | logger setup           | verbose_level: 4
[1730936461.765455] info     | Root.cpp           | create_client            | create                 | client_key: 0x7426150A, session_id: 0x81
[1730936461.765664] info     | SessionManager.hpp | establish_session        | session established    | client_key: 0x7426150A, address: 0
[1730936461.836409] info     | ProxyClient.cpp    | create_participant       | participant created    | client_key: 0x7426150A, participant_id: 0x000(1)
[1730936461.886272] info     | ProxyClient.cpp    | create_topic             | topic created          | client_key: 0x7426150A, topic_id: 0x000(2), participant_id: 0x000(1)
[1730936461.931648] info     | ProxyClient.cpp    | create_publisher         | publisher created      | client_key: 0x7426150A, publisher_id: 0x000(3), participant_id: 0x000(1)
[1730936461.977454] info     | ProxyClient.cpp    | create_datawriter        | datawriter created     | client_key: 0x7426150A, datawriter_id: 0x000(5), publisher_id: 0x000(3)

In another terminal, check the published messages

$ docker run -it ros:humble ros2 topic echo /micro_ros_arduino_node_publisher
data: 44
---
data: 45
---
data: 46
---

Add two int service example

cd micro_ros_arduino_examples_platformio/micro-ros_addtwoints_service

Build, upload and connect to micro-ROS agent as before. Open another terminal to call the service.

ros2 service call <service_name> <service_type> <arguments>
ros2 interface show <type_name>

ros2 service call /addtwoints example_interfaces/srv/AddTwoInts "{ a: 12, b: 13 }"

ubuntu@better-snipe:~$ ros2 node list
/add_twoints_client_rclc

ubuntu@better-snipe:~$ ros2 node info /add_twoints_client_rclc
/add_twoints_client_rclc
Subscribers:
Publishers:
Service Servers:
    /addtwoints: example_interfaces/srv/AddTwoInts
Service Clients:
Action Servers:
Action Clients:

ubuntu@better-snipe:~$ ros2 service list -t
/addtwoints [example_interfaces/srv/AddTwoInts]

ubuntu@better-snipe:~$ ros2 service call /addtwoints example_interfaces/srv/AddTwoInts "{ a: 12, b: 23 }"
requester: making request: example_interfaces.srv.AddTwoInts_Request(a=12, b=23)

response:
example_interfaces.srv.AddTwoInts_Response(sum=35)