
Single sh proto
andrewsharmon/rp2350-stepper-controller
Low-cost 10-axis sine-microstepping controller for small steppers on the RP2350 (PIO + DMA). WIP.

Low-cost 10-axis sine-microstepping controller for small steppers on the RP2350 (PIO + DMA). WIP.
Controller is an open-source printed circuit board by andrewsharmon, from the andrewsharmon/rp2350-stepper-controller repository on GitHub, published under the MIT licence. It is a 4-layer board of 30 × 30 mm with 63 parts on 94 nets, routed with 1,082 tracks and 199 vias.
It is built around the RP2354B, SN74AHCT1G125DBVR(UMW) and ME6211C33M5G-N, with the TPD2E2U06DRLR-TP, 0402CG150J500NT and DF40C-40DP-0.4V(51) among its other parts. Its parts include 24 capacitors, 14 resistors, 5 connectors, 4 ICs and 3 switches. It belongs with the robots and motor control boards and the RP2040 and RP2350 boards of this library.
Work in progress. Firmware runs on a Raspberry Pi Pico 2 bench setup. The custom board designs are an unreviewed feasibility first pass: schematics only, no PCB layout, not checked against datasheets or built. Don't fabricate them as-is.
A very low-cost, compact controller for up to 10 small stepper motors (8 mm micro steppers, 28BYJ-48 converted to bipolar, about 0.2 A per coil or less). It's aimed at kinetic art first, then lab and robotics use.
There are no stepper-driver ICs. The RP2350 generates sine-PWM microstepping itself (voltage mode), using PIO state machines fed by DMA, into cheap DRV8833-class dual H-bridges (AT8833).
| # | Milestone | State |
|---|---|---|
| 1 | Hardware spec and KiCad schematics for all three boards | first pass, not reviewed (open part picks in hardware/README.md) |
| 2 | One motor: sine PWM via PIO + DMA | done, verified on hardware |
| 3 | 10-axis PWM, ADC monitor, e-stop/button ladder, WS2812 | done on Pico 2; ladder buttons not yet bench-wired |
| 4 | Trajectory generators and profiles, jog modes, telemetry | done (text telemetry; binary framing comes with milestone 5) |
| 5 | Coordinated groups and planner, USB protocol, host tool | done |
| 6 | I²C target, cam/LED shows, flash config, standalone mode | done; I²C target written for the RP2354B board but untested (no free pins or I²C master on the bench) |
| 7 | PCB layout, fab, bring-up of the board stack |
What works today (measured on a Pico 2 + DRV8833 + 8 mm stepper):
save also stores the loaded cam tables in flash; they are restored at boot.trap, scurve (the default: jerk-limited by a moving average, Tj = 30 ms), smooth (two moving averages), cosine and quintic. Every profile lands exactly on its target within the speed and acceleration limits, and moves can be redirected mid-move without jumps.How it works, from trajectory planning to PIO pin toggling: docs/THEORY.md.
tools/stepperctl/ host library + command line (binary protocol)
tools/shows/ example shows (JSON; format in tools/stepperctl/show.py)
tools/cams/ example cam tables (JSON: {"cyclic": true, "points": [[x, y], ...]})
tools/pvt_demo.py host demo: streams a PVT wave over USB serial
firmware/ Pico SDK (C) firmware
src/ motor PWM (PIO/DMA), microstepping, ADC monitor, ladder, LEDs
boards/ board header for the custom RP2354B controller
test/test_host.c host-side tests (PWM encoder, microstep math, ladder, motion)
tools/ sine table generator
hardware/ spec, KiCad schematics and BOMs (generated by hardware/gen)Requirements:
PICO_SDK_PATH);arm-none-eabi GCC toolchain;# Custom RP2354B controller (default board)
cmake -S firmware -B firmware/build -G Ninja
cmake --build firmware/build
# single_sh_proto prototype board
cmake -S firmware -B firmware/build-proto -G Ninja -DPICO_BOARD=single_sh_proto
cmake --build firmware/build-proto
# Raspberry Pi Pico 2 bench setup
cmake -S firmware -B firmware/build-pico2 -G Ninja -DPICO_BOARD=pico2
cmake --build firmware/build-pico2Options: -DNUM_MOTORS=1..10 (default 10), -DLADDER_BYPASS=ON (bench without
a ladder wired; never use with motors you can't stop by hand), -DBOOT_TRACE=ON.
Flash by copying stepper.uf2 to the board in BOOTSEL mode, or with picotool:
picotool load -x firmware/build-pico2/stepper.uf2Host tests (no hardware needed):
cd firmware/test
cc -std=c11 -O1 -Wall -Wextra -DCONFIG_HOST_TEST -I../src test_host.c ../src/hbridge_encode.c \
../src/microstep.c ../src/sine_lut.c ../src/ladder.c ../src/motion.c ../src/group.c \
../src/frame.c ../src/config.c ../src/show.c ../src/standalone.c ../src/cam.c ../src/cam_store.c \
-lm -o test_host && ./test_host| Pico 2 | Connects to |
|---|---|
| GP0-GP3 | motor 1: DRV8833 module AIN1, AIN2, BIN1, BIN2 (motors 2-5 on GP4-GP19) |
| GP20 | WS2812 chain data in (pixel 0 status, pixels 1-10 axes) |
| GP26 | ladder: 10k to 3V3, 20k to GND (idle); see hardware/README.md |
| GP28 | board ID (optional) |
| VBUS / GND | DRV8833 VM and GND; pull the module's sleep pin (EEP) high |
Motors 6-10 have no pins on the Pico 2, but their state machines and DMA still run, so the full 10-axis load is exercised.
The USB serial console takes line commands (type help). Some examples:
m 1 500 move axis 1 to 500 full steps
mr * -100 move every axis back 100 steps
j 3 400 jog axis 3 at 400 steps/s (stops unless repeated within 300 ms)
lim 1 1200 4000 axis 1 limits: 1200 steps/s, 4000 steps/s^2
prof * quintic minimum-jerk moves on every axis (axes must be at rest)
cam 1 cyclic 0,0 100,40 200,50 cam table 1 (leader x, follower y); +50 per 200-step cycle
vl 1 lim 150 150 virtual leader 1 limits; ce 3 1 v1 makes axis 3 follow cam 1 behind it
vl 1 vel 100 run the virtual leader (negative reverses; followers retrace)
g 1 1,2 800 group 1 = axes 1 and 2, path speed 800 steps/s
gl 1 400 400 group line to (400, 400); ga 1 -300 0 360: full circle around (-300, 0)
p 2 150 300 20 PVT point: axis 2 reaches 150 steps at 300 steps/s, 20 ms after the last
pgo 1,2 start axes 1 and 2 following their queued points on the same tick
t 100 100 telemetry lines/s (header line names the columns)
tf pos,vel,mode per-axis telemetry fields; ta 1,2 picks axes, ts adds system fields
te on event lines: E <tick> done <ax> <pos>, underrun, stop, clear
echo off for scripts: no echo or prompt
drive 1 40 60 25 axis 1 amplitude: 40% at low speed, 60% at high speed, 25% holding
dir 2 rev reverse axis 2 (coils 2 ba swaps its coils)
cfg / save show the configuration / write it to flash (axes at rest)
? status, loads and per-axis tableThe same USB port also speaks a framed binary protocol (COBS + CRC-16; the
message spec is firmware/src/protocol.h). Frames
and the text console coexist: binary frames are wrapped in 0x00 bytes, which
the console never sends. tools/stepperctl is a standard-library Python
client and command line:
python3 tools/stepperctl ping
python3 tools/stepperctl move 1,2 250 --wait
python3 tools/stepperctl group 1 create 1,2 --feed 800
python3 tools/stepperctl group 1 arc -200 0 360
python3 tools/stepperctl telem --hz 200 --axes 1,2 --seconds 5 > log.csv
python3 tools/stepperctl show upload tools/shows/wave.json --slot 2
python3 tools/stepperctl show run 2 # show list / show stop
python3 tools/stepperctl boot 2 && python3 tools/stepperctl save # run show 2 at power-up
python3 tools/stepperctl cam load 1 tools/cams/rise_dwell.json
python3 tools/stepperctl cam engage 3 1 v1 && python3 tools/stepperctl vl 1 vel 100from stepperctl import Client # run from tools/, or put tools/ on sys.path
with Client() as c:
seq = c.move([1], 500)
c.wait_settled([1], after=seq)Command-line tool tests (no hardware needed):
python3 -m unittest discover -s tools/stepperctl -p 'test_*.py' -t tools
On the RP2354B board the Qwiic port (I2C1, GPIO 46/47) is an I²C target at
address 0x42 carrying the same requests as the binary protocol, without COBS
framing: write [type][payload…], then read [n][type][reply payload…]
(n = 0 means not ready yet, read again). See
firmware/src/i2c_target.h. It is compiled out on
the Pico 2 bench build and has not been run on hardware yet.
tools/pvt_demo.py streams a travelling wave across every axis as PVT points
(standard-library Python, macOS/Linux):
tools/pvt_demo.py --amp 200 --hz 0.5 --seconds 15sinf returned wrong values near multiples
of π/2 on the RP2350, which produced reverse pulses at each coil peak. The
sine table is generated offline (firmware/tools/gen_sine_lut.py), and the
motion code uses its own polynomials (firmware/src/trig.h).TRANS_COUNT mode 0xF)
still needs a non-zero count, or the channel completes immediately.MIT, covering both firmware and hardware design files.
| Qty | Value | Footprint | References |
|---|---|---|---|
| 16 | 100n | Capacitor_SMD:C_0402_1005Metric | C1, C2, C3, C4, C5, C6, C7, C8, C9, C11, C12, C13, C20, C22, C23, C24 |
| 3 | 10k 1% | Resistor_SMD:R_0402_1005Metric | R10, R11, R14 |
| 3 | 4.7u | Capacitor_SMD:C_0402_1005Metric | C10, C14, C15 |
| 2 | 15p | Capacitor_SMD:C_0402_1005Metric | C16, C17 |
| 2 | 1k | Resistor_SMD:R_0402_1005Metric | R2, R13 |
| 2 | 1u | Capacitor_SMD:C_0402_1005Metric | C19, C21 |
| 2 | 27 | Resistor_SMD:R_0402_1005Metric | R3, R4 |
| 2 | 4.7k | Resistor_SMD:R_0402_1005Metric | R8, R9 |
| 2 | 5.1k | Resistor_SMD:R_0402_1005Metric | R5, R6 |
| 2 | BTB 40P header | stack:Hirose_DF40C-40DP_Header | J10, J11 |
| 2 | Qwiic | Connector_JST:JST_SH_BM04B-SRSS-TB_1x04-1MP_P1.00mm_Vertical | J2, J3 |
| 1 | +3V3 | TestPoint:TestPoint_Pad_D1.0mm | TP5 |
| 1 | 1.5A hold | Fuse:Fuse_1812_4532Metric | F1 |
| 1 | 100k | Resistor_SMD:R_0402_1005Metric | R7 |
| 1 | 10u | Capacitor_SMD:C_0805_2012Metric | C18 |
| 1 | 12MHz | Crystal:Crystal_SMD_3225-4Pin_3.2x2.5mm | Y1 |
| 1 | 3.3k 1% | Resistor_SMD:R_0402_1005Metric | R12 |
| 1 | 3.3u | Inductor_SMD:L_Cenker_CKCS201610 | L1 |
| 1 | 33 | Resistor_SMD:R_0402_1005Metric | R1 |
| 1 | 74AHCT1G125 | Package_TO_SOT_SMD:SOT-23-5 | U4 |
| 1 | AO3401A | Package_TO_SOT_SMD:SOT-23 | Q1 |
| 1 | BOOTSEL | Button_Switch_SMD:SW_Push_1P1T_XKB_TS-1187A | SW3 |
| 1 | BTN1 | Button_Switch_SMD:SW_SPST_TS-1088-xR020 | SW1 |
| 1 | BTN2 | Button_Switch_SMD:SW_SPST_TS-1088-xR020 | SW2 |
| 1 | GND | TestPoint:TestPoint_Pad_D1.0mm | TP4 |
| 1 | I2C_PU | Jumper:SolderJumper-2_P1.3mm_Bridged_RoundedPad1.0x1.5mm | JP2 |
| 1 | ME6211C33M5G | Package_TO_SOT_SMD:SOT-23-5 | U3 |
| 1 | QWIIC_SUPPLY | Jumper:SolderJumper-2_P1.3mm_Open_RoundedPad1.0x1.5mm | JP1 |
| 1 | RP2354B | Package_DFN_QFN:QFN-80-1EP_10x10mm_P0.4mm_EP3.4x3.4mm | U1 |
| 1 | RUN | TestPoint:TestPoint_Pad_D1.0mm | TP3 |
| 1 | SS54 | Diode_SMD:D_SMA | D1 |
| 1 | SWCLK | TestPoint:TestPoint_Pad_D1.0mm | TP1 |
| 1 | SWDIO | TestPoint:TestPoint_Pad_D1.0mm | TP2 |
| 1 | TPD2E2U06 | Package_TO_SOT_SMD:SOT-553 | U2 |
| 1 | USB-C | Connector_USB:USB_C_Receptacle_HRO_TYPE-C-31-M-12 | J1 |
| 1 | XL-2020RGBC-WS2812B | LED_SMD:LED_WS2812B-2020_PLCC4_2.0x2.0mm | D2 |
It measures 30 × 30 mm and has 4 copper layers, 1 mm thick.
63 parts, built around the RP2354B, SN74AHCT1G125DBVR(UMW), ME6211C33M5G-N and TPD2E2U06DRLR-TP. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the andrewsharmon/rp2350-stepper-controller repository on GitHub, at the commit this page was made from: the board is hardware/controller/controller.kicad_pcb and its schematic hardware/controller/controller.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Its repository publishes it under the MIT licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

andrewsharmon/rp2350-stepper-controller
Low-cost 10-axis sine-microstepping controller for small steppers on the RP2350 (PIO + DMA). WIP.

andrewsharmon/rp2350-stepper-controller
Low-cost 10-axis sine-microstepping controller for small steppers on the RP2350 (PIO + DMA). WIP.

frindaddy/digichiver
Archival hardware/firmware for the MM54104 Digitalker chip

sedlak477/owl-hardware
Hardware design files and build instructions for the OWL platform

adamhrnciar/hyperion-controller
Open-source controller for 150 g antweight combat robots: RP2354A, 2x DRV8231A, 2-4S, ELRS/CRSF/SBUS/iBUS/PPM input, IMU. KiCad 10, CERN-OHL-S-2.0.


ratmuds/spark
The PCB files for this project. The gerber and other PCBA files are in the production folder.
Jotrorox/minicontrol
RP2354A + 868 MHz LoRa (Ai-Thinker Ra-01SH) USB-C controller board, designed in KiCad 10.


sequoia-hope/ultraroute
PCB autorouting experiments around copperroute: route jobs, leaderboards, board morphing