Wiznet makers

Grace_Koo

Published May 13, 2026 ©

71 UCC

25 WCC

11 VAR

0 Contests

0 Followers

0 Following

Original Link

SabertoothMicroROSBridge

micro-ROS bridge on RP2040 + WIZnet W6100 forwarding ROS2 cmd_vel to a Sabertooth 2x60 motor driver, with Ethernet UDP transport planned.

COMPONENTS
PROJECT DESCRIPTION

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)
        └──────────────────────────
ComponentDetails
MCURP2040 (Cortex-M0+ dual-core, 264KB SRAM)
EthernetWIZnet W6100 (SPI0, GPIO16-21)
Motor driver commsUART1 GP4, 9600 baud, Sabertooth Packet Serial
E-STOPGP2 → Sabertooth S2 (Active-LOW STO)
ROS2 interfacemicro-ROS, /cmd_vel Twist topic
Development environmentArduino 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 BusAssignmentGPIOStatus
SPI0W6100 Ethernet16-21Hard-wired on board
SPI1CAN (MCP2515, future)10-13Reserved
UART1Sabertooth Packet SerialGP4 (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.

LayerMechanismTrigger
1. Sabertooth HW timeoutBuilt into motor driverNo packet for 500ms → auto stop
2. cmd_vel SW timeoutFirmware timerNo cmd_vel for 300ms → stop()
3. Agent reconnect SM4-state state machineAgent disconnect → immediate stop
4. RP2040 HW watchdogMCU internal timerNo 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

ComponentDetails
MCURP2040 (W6100-EVB-Pico)
EthernetWIZnet W6100 (Hardwired TCP/IP, SPI0)
Firmware languageC++ (Arduino IDE / Mbed OS)
ROS2 connectivitymicro-ROS (USB Serial → UDP transition planned)
Motor driver commsSabertooth Packet Serial, 9600 baud, UART1
Safety system5-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

Documents
Comments Write