ESP32-based micro-ROS servo controller for the Waveshare General Driver for Robots board.
- Real-time servo control via ROS 2 topics
- 10 Hz telemetry publishing (position, velocity, effort)
- Dynamic configuration via topics (position, velocity, max load)
- Overload protection with automatic re-enable
- Best-effort logging via
/rosout_best_effort
- Timer callbacks unreliable: Telemetry published directly in
loop()at 10Hz instead of timer callbacks - Parameter server doesn't work: Service discovery fails over serial transport - use topics for configuration instead
- RCUTILS logging doesn't work: Custom logging via
/rosout_best_efforttopic (rcl_interfaces/msg/Log) - Best-effort QoS required: All publishers/subscribers use best-effort QoS for reliable operation
- Waveshare General Driver for Robots (ESP32-WROOM-32)
- USB connection to development machine
- PlatformIO extensino
- Docker (for micro-ROS agent)
- ROS 2 Jazzy (on host or in container)
use the build and upload items from VSCode platformio menus. PlatformIO is not otherwise installed locally.
The micro-ROS agent must be running before the ESP32 starts up or is reset.
# Start the agent with proper device access
docker run -it --rm --net=host --device=/dev/waveshare-esp32 \
microros/micro-ros-agent:jazzy serial --dev /dev/waveshare-esp32 -b 921600Note: Baud rate is 921600 to match the ESP32 configuration.
Tip: Use -v5 or -v6 flag for verbose logging if debugging:
docker run -it --rm --net=host --device=/dev/waveshare-esp32 \
microros/micro-ros-agent:jazzy serial --dev /dev/waveshare-esp32 -b 921600 -v6- Start the micro-ROS agent (see command above)
- Upload code to ESP32 or press the reset button
- Wait a few seconds for initialization
- Verify connection:
# Source ROS 2
source /opt/ros/jazzy/setup.bash
### Controlling the Servo
```bash
# Set servo position (0-1023)
ros2 topic pub /servo/position_command std_msgs/msg/Float32 "data: 800.0" --once
# Set velocity limit (1-2000)
ros2 topic pub /servo/velocity_limit std_msgs/msg/Float32 "data: 300.0" --once
# Set max load threshold (0-2000, writes to servo EEPROM)
ros2 topic pub /servo/max_load std_msgs/msg/Float32 "data: 800.0" --once# Joint state (position, velocity, effort)
ros2 topic echo /servo/joint_state
# Current goal position
ros2 topic echo /servo/goal_position
# Current load
ros2 topic echo /servo/load
# Debug messages
ros2 topic echo /servo/debug
# Logging messages
ros2 topic echo /rosout_best_effortWhen making code changes:
- Stop the agent (Ctrl+C in the agent terminal)
- Upload new code:
platformio run --target upload --environment esp32dev - Restart the agent (ESP32 auto-resets after upload)
- Test your changes
Note: Both the agent and PlatformIO uploader need exclusive access to the serial port, so the agent must be stopped during uploads.
- Node:
servo_controller - Servo: ID 3, 10-bit position control (0-1023)
/servo/joint_state(sensor_msgs/JointState) - position, velocity, effort at 10Hz/servo/goal_position(std_msgs/Float32) - target position at 10Hz/servo/load(std_msgs/Float32) - current load at 10Hz/servo/debug(std_msgs/String) - debug messages/rosout_best_effort(rcl_interfaces/Log) - custom logging. Note, you the Foxlgove log viewer can be configured to use this topic :)
/servo/position_command(std_msgs/Float32) - set target position (0-1023)/servo/velocity_limit(std_msgs/Float32) - set movement speed (1-2000)/servo/max_load(std_msgs/Float32) - set overload threshold (0-2000, writes to EEPROM)
- Automatically disables servo when load exceeds
max_load_threshold - Re-enables after 5 seconds
- Threshold configurable via
/servo/max_loadtopic