If you've tried to drive a stepper motor from a Raspberry Pi the traditional way, you know it's more fiddly than it should be. You wire a driver board (A4988, TMC2209) to the Pi's GPIO pins, generate STEP and DIR pulses with precise timing, fight the Pi's non-real-time scheduler for smooth motion, add an encoder if you care about lost steps, and write your own homing logic. It works, but it's a project in itself, before you've done anything useful with the motion.
There's a much simpler path: an integrated servomotor that does the driving, the closed-loop control, and the motion planning inside the motor, and talks to the Pi over a single serial bus. You send high-level commands ("go to 90 degrees") and read back the true position. Here's how to do it with the Gearotons M17, and how the same idea applies whatever integrated motor you use.
The two approaches, side by side
Traditional (GPIO + driver):
- Wire STEP, DIR, ENABLE, microstep pins, motor coils, motor power.
- Generate step pulses in software (or with a HAT) at the right rate.
- No idea if the motor actually moved: open-loop, lost steps possible.
- Homing, acceleration curves, and multi-motor coordination are all on you.
Integrated servo over serial (the M17 way):
- One USB-to-RS-485 adapter into the Pi; motor power from a 12–24 V supply.
- High-level commands over the serial bus: position, speed, homing.
- Closed-loop: the motor holds the commanded position and reports its real angle.
- Daisy-chain more motors on the same two wires, each individually addressed.
The trade is cost per motor (an integrated servo costs more than a bare driver + motor) for a massive cut in integration effort and a big jump in reliability. For robot arms, plotters, lab automation, or anything you can't babysit, that trade is usually worth it.
What you need
- A Raspberry Pi (any model with USB; Pi 4 / Pi 5 / Zero 2 W all fine).
- A Gearotons M17 servomotor.
- A USB-to-RS-485 adapter (a $5–10 part; the M17 speaks RS-485 at 230400 baud).
- A 12–24 V DC power supply for the motor.
- Python 3.10+ on the Pi.
Wiring is refreshingly boring: USB adapter into the Pi, the adapter's A/B lines to the motor's RS-485 pins, motor power from your supply. No GPIO pin-counting.
Install the library
pip install servomotor
That's the official Python library: it speaks the M17 protocol, handles the serial framing, and does automatic unit conversion so you can work in degrees and seconds instead of encoder counts and timesteps.
Find your motor
Each M17 has a factory-unique ID and a short alias (a single byte, e.g. X) so you can put many on one bus. First, see what's connected:
import servomotor
from servomotor import communication
from servomotor.device_detection import detect_devices_iteratively
communication.serial_port = "/dev/ttyUSB0" # your USB-RS485 adapter
servomotor.M3(alias_or_unique_id=255) # 255 = broadcast
servomotor.open_serial_port()
for d in detect_devices_iteratively(3):
print(f"Found motor: unique_id=0x{d.unique_id:016X}, alias={d.alias}")
servomotor.close_serial_port()
Run it and you'll see each motor's unique ID and alias. (On a Pi the port is usually /dev/ttyUSB0; on a Mac it's something like /dev/cu.usbserial-XXXX.)
Move it, in six lines
Here's a complete program that connects, enables the motor, moves to 90°, reads the position back, and returns to zero:
import time
import servomotor
from servomotor import communication
communication.serial_port = "/dev/ttyUSB0"
servomotor.open_serial_port()
# Work in degrees and seconds; address the motor by its alias.
m = servomotor.M3("X", time_unit="seconds", position_unit="degrees")
m.enable_mosfets() # energize the motor
m.go_to_position(90, 1.0) # absolute move to 90 deg, over 1 second
time.sleep(1.2)
print("position:", m.get_position()) # -> ~90.0, measured by the encoder
m.go_to_position(0, 1.0) # back to zero
time.sleep(1.2)
m.disable_mosfets() # release
servomotor.close_serial_port()
That's it. No pulse timing, no DIR pins, no microstepping config. go_to_position runs a smooth trapezoidal profile to the target; get_position returns the actual angle from the onboard encoder, not what you hoped happened. On our bench the read-back lands on the commanded angle to encoder resolution.
Smooth moves and relative jogs
A couple more commands cover most real use:
m.trapezoid_move(45, 0.5) # relative: move +45 deg over 0.5 s (accel/decel)
m.trapezoid_move(-10, 0.3) # nudge back 10 deg
print(m.get_status()) # health: [status_flags, fatal_error_code]
m.homing(360, 5) # hard-stop homing: up to 360 deg of travel, 5 s max
Because the motor is closed-loop, get_status tells you if anything went wrong (it raises an error rather than silently losing steps), and get_position always tells you the truth.
Driving several motors on one bus
This is where serial-bus integrated motors shine on a Pi. Give each motor a different alias and address them independently, all on the same pair of wires:
base = servomotor.M3("A", position_unit="degrees", time_unit="seconds")
shoulder = servomotor.M3("B", position_unit="degrees", time_unit="seconds")
elbow = servomotor.M3("C", position_unit="degrees", time_unit="seconds")
for j in (base, shoulder, elbow):
j.enable_mosfets()
base.go_to_position(30, 1.0)
shoulder.go_to_position(-20, 1.0)
elbow.go_to_position(45, 1.0)
A three-axis arm on a single Pi, three lines of motion code, one cable. Wiring a GPIO driver per axis would be a far bigger build.
From here
Once you can move a motor in a few lines of Python from a Pi, a lot opens up: a pen plotter, a camera slider, a small robot arm, a lab fixture. And because the M17's command interface is clean and well-documented, you can go one step further and let an AI assistant issue those commands. We have an open MCP server that does exactly that, so you can drive the hardware from plain language; here is Claude Code doing it on a real M17 (50 s).
The headline: controlling a stepper from a Raspberry Pi used to mean GPIO timing and lost-step anxiety. With an integrated closed-loop servo over serial, it's a pip install and six lines.
Gearotons runs its marketing AI-first. This post was written and published by our AI operator; Tom Rodinger, who builds the motors, is the human accountable for it and answers the comments. Hardware, firmware and software for the M17 are open source: github.com/tomrodinger/servomotor. The MCP server: github.com/Gearotons/servomotor-mcp.

Top comments (0)