Building a complex robot—like a quadruped or an autonomous rover—requires two different types of computing. You need high-level "Brain" computing (for computer vision and path planning, usually done in Python on a Raspberry Pi) and low-level "Spine" computing (for real-time motor control and sensor reading, usually done in C++ on an ESP32).
Academic research in distributed robotics solves this communication problem using the Robot Operating System (ROS). In this article, we will look at the theory of Publisher/Subscriber networks and apply it by bridging an ESP32 to a ROS 2 network using micro-ROS.
The Academic Theory: The Publisher/Subscriber Model
Unlike traditional HTTP or serial communication where one device explicitly asks another for data, ROS utilizes a decentralized Publisher/Subscriber (Pub/Sub) architecture running over Data Distribution Service (DDS).
In this theory, nodes (individual programs) do not need to know about each other. An ESP32 simply acts as a Publisher, shouting sensor data into a designated "Topic" (e.g., /robot/imu). Any other node on the network—like a Python script acting as a Subscriber—can listen to that topic and process the data. This decoupled architecture is what allows modern robotics to scale without bottlenecks.
The Engineering Application: micro-ROS on the ESP32
Historically, ROS was too heavy to run on microcontrollers. However, the development of micro-ROS allows embedded devices to act as native ROS 2 nodes over Wi-Fi or Serial.
Here is how an embedded developer writes C++ code to turn an ESP32 into a micro-ROS publisher that broadcasts data to the wider robotics network.
The C++ Code
#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>
// ROS 2 core components
rcl_publisher_t publisher;
std_msgs__msg__Int32 msg;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;
rclc_executor_t executor;
// Timer callback: This runs repeatedly to publish data
void timer_callback(rcl_timer_t * timer, int64_t last_call_time) {
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
// Populate the message (e.g., reading a sensor value)
msg.data++;
// Publish the message to the ROS 2 network
rcl_publish(&publisher, &msg, NULL);
}
}
void setup() {
// Connect to the micro-ROS agent over Wi-Fi
set_microros_wifi_transports("YOUR_SSID", "YOUR_PASSWORD", "192.168.1.100", 8888);
delay(2000);
allocator = rcl_get_default_allocator();
// Initialize micro-ROS support
rclc_support_init(&support, 0, NULL, &allocator);
// Create a ROS 2 Node named "esp32_sensor_node"
rclc_node_init_default(&node, "esp32_sensor_node", "", &support);
// Create a Publisher on the topic "sensor_data"
rclc_publisher_init_default(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
"sensor_data");
// Create a timer that fires every 1000ms (1 Hz)
rclc_timer_init_default(
&timer,
&support,
RCL_MS_TO_NS(1000),
timer_callback);
// Create an executor to manage the timer
rclc_executor_init(&executor, &support.context, 1, &allocator);
rclc_executor_add_timer(&executor, &timer);
msg.data = 0;
}
void loop() {
// Spin the executor to handle ROS 2 communication
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}
Practical Considerations
Setting up micro-ROS requires a host machine (like a laptop or Raspberry Pi) running a micro-ROS-agent. The ESP32 connects to this agent, which bridges the microcontroller's lightweight protocol into the full ROS 2 DDS ecosystem. This architecture is the industry standard for bridging the gap between hardware limitations and advanced AI planning.
by Malik Hassan
.jpg)