A MicroPython library for controlling STS3215 serial bus servos on Raspberry Pi Pico. Provides both high-level Servo class for ease of use and low-level register access for advanced control.
- 🎯 High-level API - Clean
Servoclass with intuitive methods - ⚡ Synchronised movement - Move multiple servos simultaneously
- 🔧 Fully configurable - Custom UART pins, baudrate, and timing
- 📊 Status monitoring - Temperature, voltage, position, and movement detection
- 🛡️ Safety features - Torque control, angle limits, automatic value clamping
Choose one of these installs:
# In the MicroPython REPL
import mip
mip.install("github:youssefalboraei/sts3215-micropython")# Clone or download this repository
git clone https://github.com/youssefalboraei/sts3215-micropython.git
# Install mpremote (host tool for USB copy)
python3 -m pip install mpremote
# Copy the sts3215 folder to your Pico
mpremote fs cp -r sts3215 :No mpremote? Use Thonny (or another uploader) to copy sts3215/ onto the Pico.
- Hardware: Raspberry Pi Pico (or Pico W)
- Firmware: MicroPython 1.20 or later
- Servos: STS3215 serial bus servos (Feetech/Waveshare compatible)
- Host tools (optional): mpremote for USB copy/launch (
python3 -m pip install mpremote)
No external Python dependencies on the Pico - uses only built-in MicroPython modules (machine, time).
from sts3215 import ServoController, Servo
# Initialize controller with your UART pins
controller = ServoController(tx_pin=0, rx_pin=1)
# Create servo instance
servo = Servo(controller, servo_id=1)
# Check connection
if servo.ping():
print("Servo connected!")
# Enable torque and move
servo.enable()
servo.move_to(2048, time_ms=500) # Move to centre
servo.wait_until_idle()
# Check status
print(servo.status())Low-level communication layer. Configure once and share with multiple Servo instances.
controller = ServoController(
tx_pin=0, # GPIO pin for UART TX
rx_pin=1, # GPIO pin for UART RX
uart_id=0, # UART peripheral (0 or 1)
baudrate=1_000_000, # Communication speed
timeout=50, # UART timeout in ms
read_wait=100, # Wait before reading response
)High-level interface for individual servos.
servo = Servo(controller, servo_id=1)
# Movement
servo.move_to(position, time_ms=400, speed=2000)
servo.center(time_ms=500)
servo.wait_until_idle(timeout_ms=5000)
# Status
servo.get_position() # Returns 0-4095
servo.is_moving() # Returns True/False
servo.get_temperature() # Returns °C
servo.get_voltage() # Returns volts
servo.status() # Returns complete status dict
# Torque
servo.enable() # Enable torque
servo.disable() # Disable torque
servo.set_torque(True) # Explicit control
# Limits
servo.set_limits(cw=1000, ccw=3000)
servo.set_full_range()
servo.get_limits() # Returns (cw, ccw)Move multiple servos together for coordinated motion:
from sts3215 import ServoController, Servo, sync_write_pos
controller = ServoController(tx_pin=0, rx_pin=1)
# Move servos 1, 2, 3 simultaneously
sync_write_pos(controller, [1, 2, 3], [1024, 2048, 3072], time_ms=500)| Pico Pin | Servo Wire | Description |
|---|---|---|
| GPIO 0 | TX (Yellow) | Data out |
| GPIO 1 | RX (White) | Data in |
| VSYS | VCC (Red) | 6-8.4V power |
| GND | GND (Black) | Ground |
⚠️ Note: STS3215 servos use half-duplex serial. TX and RX may be combined on a single data wire in some setups.
from sts3215 import ServoController, Servo, sync_write_pos
controller = ServoController(tx_pin=0, rx_pin=1)
class RobotArm:
def __init__(self):
self.shoulder = Servo(controller, 1)
self.elbow = Servo(controller, 2)
self.gripper = Servo(controller, 3)
def home(self):
"""Move all joints to centre position."""
sync_write_pos(controller, [1, 2, 3], [2048, 2048, 2048], time_ms=1000)
self.shoulder.wait_until_idle()
def enable_all(self):
for servo in [self.shoulder, self.elbow, self.gripper]:
servo.enable()
def disable_all(self):
for servo in [self.shoulder, self.elbow, self.gripper]:
servo.disable()
arm = RobotArm()
arm.enable_all()
arm.home()def monitor_temperature(servos, max_temp=70):
"""Disable overheating servos."""
for servo in servos:
temp = servo.get_temperature()
if temp and temp > max_temp:
print(f"Servo {servo.id} overheating: {temp}°C")
servo.disable()def scan_servos(controller, id_range=range(1, 20)):
"""Find all connected servos."""
found = []
for sid in id_range:
if controller.ping(sid):
found.append(sid)
print(f"Found servo ID {sid}")
return foundThese commands could be used to run your files on Pico from terminal. Replace
/dev/tty.usbmodemXXXX with your Pico port.
# Find the Pico port
mpremote connect list
# or on macOS
ls /dev/tty.usbmodem*
# Copy the library and example to the Pico (persistent)
mpremote connect /dev/tty.usbmodemXXXX fs cp -r sts3215 :
mpremote connect /dev/tty.usbmodemXXXX fs cp examples/robot_finger.py :main.py
# Run (boots and runs main.py)
mpremote connect /dev/tty.usbmodemXXXX reset
# Run without copying (one-off)
mpremote connect /dev/tty.usbmodemXXXX run examples/robot_finger.py
# Live dev without copying (mount local files)
mpremote connect /dev/tty.usbmodemXXXX mount . run examples/robot_finger.py
# Stop a running script
mpremote connect /dev/tty.usbmodemXXXX repl
# press Ctrl-C to interrupt
# Remove auto-run on boot
mpremote connect /dev/tty.usbmodemXXXX fs rm :main.py| Address | Name | Size | Description |
|---|---|---|---|
| 0x2A | Goal | 6B | Position + time + speed |
| 0x38 | Position | 2B | Current position (0-4095) |
| 0x3E | Voltage | 1B | Input voltage (×0.1V) |
| 0x3F | Temperature | 1B | Temperature (°C) |
| 0x34 | Torque | 1B | 1=enabled, 0=disabled |
| 0x06 | CW Limit | 2B | Clockwise angle limit |
| 0x08 | CCW Limit | 2B | Counter-clockwise limit |
For direct register access:
# Read register
value = controller.read_reg_value(servo_id=1, addr=0x38, nbytes=2)
# Write register
controller.write_reg(servo_id=1, addr=0x34, value=1, nbytes=1)- Check wiring connections
- Verify servo ID with
scan_servos() - Ensure adequate power supply (6-8.4V)
- Try
controller.ping(servo_id)
- Check for loose connections
- Add
time.sleep_ms(50)between rapid commands - Reduce speed parameter
- Register addresses may differ between servo models
- Use
controller.read_reg_value()to scan registers - Modify constants in
sts3215/constants.pyif needed
MIT License - see LICENSE for details.
Contributions welcome! Please read CONTRIBUTING.md first.