SabertoothMicroROSBridge
micro-ROS bridge on RP2040 + WIZnet W6100 forwarding ROS2 cmd_vel to a Sabertooth 2x60 motor driver, with Ethernet UDP transport planned.
Bridging ROS2 and Sabertooth with W6100-EVB-Pico — SabertoothMicroROSBridge
Summary
"A micro-ROS bridge on RP2040 + WIZnet W6100 that forwards ROS2 cmd_vel commands to a Sabertooth motor driver — currently running over USB Serial, with W6100 Ethernet UDP transport planned as the next step."
This project appears to be the work of Nowlab, a rapid prototyping studio based in Budapest, Hungary. The GitHub profile (nowlabstudio) lists Nowlab as the affiliated organization, and the other repositories follow the same theme of industrial automation and robot control. Whether this is an official company account or a personal one is not explicitly stated.
The simplest way to connect a robot to ROS2 is USB Serial — easy to set up, and the micro-ROS agent recognizes it immediately. SabertoothMicroROSBridge starts from that point and is actively preparing the transition to W6100 Ethernet transport.
Key Concepts
What is micro-ROS? A framework that brings ROS2 to microcontrollers. It connects to a ROS2 agent running on a host PC and exposes standard ROS2 interfaces — topic publish/subscribe, service calls — directly on embedded hardware.
What is the Sabertooth 2x60? A high-current DC motor driver rated at 60A per channel. Controlled via UART Packet Serial protocol, widely used in robots, AGVs, and drive systems.
System Architecture
[ROS2 Host PC]
cmd_vel (geometry_msgs/Twist)
│
│ USB CDC Serial (current) / Ethernet UDP (planned)
▼
[W6100-EVB-Pico]
RP2040 + WIZnet W6100
micro-ROS bridge firmware
│
│ UART1 GP4 → S1, 9600 baud, Packet Serial
▼
[Sabertooth 2x60]
M1 (left motor), M2 (right motor)
│
│ GP2 → S2 (E-STOP/STO)
└──────────────────────────
| Component | Details |
|---|---|
| MCU | RP2040 (Cortex-M0+ dual-core, 264KB SRAM) |
| Ethernet | WIZnet W6100 (SPI0, GPIO16-21) |
| Motor driver comms | UART1 GP4, 9600 baud, Sabertooth Packet Serial |
| E-STOP | GP2 → Sabertooth S2 (Active-LOW STO) |
| ROS2 interface | micro-ROS, /cmd_vel Twist topic |
| Development environment | Arduino IDE + Mbed OS RP2040 |
From USB to Ethernet — A Planned Transition
The micro-ROS transport currently runs over USB CDC Serial. The W6100 is already hard-wired to the board and dedicated to SPI0 (GPIO16-21), but the Ethernet transport has not been implemented yet. For now, only a placeholder comment exists in config.h:
// Ethernet transport placeholder — to be activated later
// #define TRANSPORT_ETHERNET // W6100 UDP via SPI0 (GPIO16-21)
USB Serial-based micro-ROS requires a physical USB cable connected to the host at all times. Switching to W6100 UDP transport would allow the agent to run anywhere on a wired network, enabling cable-free operation. The implementation path is to write the four micro-ROS custom transport callbacks (open/close/read/write) and switch the agent launch command from micro-ros-agent serial to micro-ros-agent udp4 -p 8888.
What Is Already Working
The W6100 Ethernet code is not yet implemented, but the USB-based core logic is fully functional. The codebase includes placeholders for the Ethernet (and CAN) transitions.
Ethernet placeholder in config.h:
// #define TRANSPORT_ETHERNET // W6100 UDP via SPI0 (GPIO16-21)
// #define TRANSPORT_CAN // MCP2515 CAN via SPI1 (GPIO10-13)
Hardware resource allocation:
| SPI Bus | Assignment | GPIO | Status |
|---|---|---|---|
| SPI0 | W6100 Ethernet | 16-21 | Hard-wired on board |
| SPI1 | CAN (MCP2515, future) | 10-13 | Reserved |
| UART1 | Sabertooth Packet Serial | GP4 (TX) | Active |
The W6100 is already wired to SPI0 exclusively. SPI1 is left open for future CAN. Since the two buses operate independently, Ethernet and CAN can run simultaneously once both are implemented.
5-Layer Safety System
The safety design is one of the more notable aspects of this project. Five independent layers ensure the motors stop whenever they should.
| Layer | Mechanism | Trigger |
|---|---|---|
| 1. Sabertooth HW timeout | Built into motor driver | No packet for 500ms → auto stop |
| 2. cmd_vel SW timeout | Firmware timer | No cmd_vel for 300ms → stop() |
| 3. Agent reconnect SM | 4-state state machine | Agent disconnect → immediate stop |
| 4. RP2040 HW watchdog | MCU internal timer | No kick within 2s → MCU reset |
| 5. Hardware E-STOP (STO) | GP2 → S2 (Active-LOW) | Immediate physical motor cutoff |
Layer 5 is particularly important. GP2 is wired to the Sabertooth S2 pin, implementing a hardware-level Safe Torque Off. Even if the firmware is completely frozen, pulling this pin LOW cuts the motors immediately — a last line of defense independent of software.
// Agent disconnected → immediate E-STOP
case AGENT_DISCONNECTED:
estopActivate(); // GP2 = LOW → immediate motor cutoff
sabertooth.stop(); // also send stop via Packet Serial
destroyEntities(); // clean up micro-ROS entities
state = WAITING_AGENT; // transition to reconnect wait
micro-ROS State Machine
Connection state with the agent is managed across four stages. Safety actions are taken at each transition.
WAITING_AGENT
│ ping success
▼
AGENT_AVAILABLE
│ entities created successfully
│ E-STOP released
▼
AGENT_CONNECTED ──── (ping every 5s)
│ │
│ cmd_vel received │ ping failed
▼ ▼
differential drive AGENT_DISCONNECTED
motor(1), motor(2) │ E-STOP activated
│ stop()
▼
WAITING_AGENT
Differential Drive Mixer
linear.x and angular.z from cmd_vel are converted into two independent motor commands. Instead of simple clipping, proportional scaling is used to preserve the turning ratio during cornering.
int left = (linear - angular) * MAX_MOTOR_POWER;
int right = (linear + angular) * MAX_MOTOR_POWER;
// If either side exceeds the limit, scale both down proportionally
int peak = max(abs(left), abs(right));
if (peak > MAX_MOTOR_POWER) {
left = left * MAX_MOTOR_POWER / peak;
right = right * MAX_MOTOR_POWER / peak;
}
sabertooth.motor(1, left); // Packet Serial cmd 0-5
sabertooth.motor(2, right);
Direct motor() commands (cmd 0-5) are used instead of mixed mode (cmd 8-11). Mixed mode requires drive and turn to be sent together as a pair; motor() operates independently, making the implementation simpler and more reliable.
Tech Stack
| Component | Details |
|---|---|
| MCU | RP2040 (W6100-EVB-Pico) |
| Ethernet | WIZnet W6100 (Hardwired TCP/IP, SPI0) |
| Firmware language | C++ (Arduino IDE / Mbed OS) |
| ROS2 connectivity | micro-ROS (USB Serial → UDP transition planned) |
| Motor driver comms | Sabertooth Packet Serial, 9600 baud, UART1 |
| Safety system | 5-layer (HW timeout / SW timeout / SM / Watchdog / E-STOP) |
FAQ
Q. The W6100 is already on the board — why is it still using USB? A. The micro-ROS Arduino library only provides USB Serial transport out of the box. Using W6100 as a micro-ROS transport requires implementing a custom UDP transport with four callbacks (open/close/read/write). The hardware is ready, the placeholder is in the code — the next step is writing that custom transport.
Q. Sabertooth Packet Serial is one-way. How do you monitor motor state? A. The Sabertooth 2x60 protocol provides no feedback. This project publishes cmd_vel receive status, agent connection state, and E-STOP status via the /sabertooth/status topic for monitoring from the ROS2 side. For motor current or encoder feedback, a separate sensor would need to be connected to the Pico ADC.
Q. Can SPI0 (W6100) and SPI1 (CAN) run at the same time? A. Yes, in hardware. The RP2040 runs SPI0 and SPI1 independently. W6100 is dedicated to GPIO16-21 (SPI0), and a CAN controller (MCP2515) can be connected to GPIO10-13 (SPI1) without conflict.
Project Links
- GitHub: https://github.com/nowlabstudio/SabertoothMicroROSBridge
- Author: nowlabstudio (Nowlab, Budapest — likely a rapid prototyping studio)
- Hardware: W6100-EVB-Pico (WIZnet W6100 + RP2040)
- Motor driver: Sabertooth 2x60 (Dimension Engineering)
