A Klipper extension for controlling Feetech ST3215 serial bus servo motors. Perfect for grippers, tool changers, camera pan/tilt, and other positioning applications on 3D printers.
This extension was completely coded by LLM tools, and then verified by hand. Please verify this for your use case carefully before use.
- ✅ Multi-Servo Support - Control multiple servos on shared or separate buses
- ✅ Full Position Control - Precise 12-bit positioning (0-4095)
- ✅ Safety Features - Position limits, temperature monitoring, error recovery
- ✅ Status Monitoring - Real-time position, temperature, current, and voltage
- ✅ G-code Integration - Native Klipper commands and macro support
- ✅ Moonraker Compatible - Status accessible via web interface
- ✅ Easy Installation - Automated installation script
- ✅ Version Control - Git-based updates
- Grippers - Pick and place objects during prints
- Tool Changers - Automatic tool changing systems
- Camera Control - Pan/tilt camera positioning
- Part Ejectors - Automated part removal
- Bed Probes - Deployable probe mechanisms
- Material Feeders - Automated filament/material handling
- Feetech ST3215 serial bus servo motor(s)
- USB-to-serial adapter (FTDI, CP2102, CH340, etc.)
- Klipper-compatible controller board (Manta M4P, etc.)
- Power supply appropriate for ST3215 (11.1-14.8V)
- Klipper firmware (already installed)
- Python 3.7+
- Git (for installation and updates)
cd ~
git clone https://github.com/freefoote/klipper-st3215.git
cd klipper-st3215
./install.shThe installation script will:
- ✓ Check for Klipper installation
- ✓ Install the st3215 Python library if needed
- ✓ Create a symlink in Klipper's extras directory
- ✓ Verify the installation
Add to your printer.cfg:
[st3215 gripper]
serial: /dev/ttyUSB0
servo_id: 1
position_min: 500
position_max: 3500
initial_position: 2048sudo systemctl restart klipperSTSERVO_ENABLE SERVO=gripper
STSERVO_MOVE SERVO=gripper POSITION=3000
STSERVO_STATUS SERVO=gripper
STSERVO_DISABLE SERVO=gripper| Parameter | Type | Description |
|---|---|---|
serial |
string | Serial port device (e.g., /dev/ttyUSB0 - we recommend the full path though - eg /dev/serial/by-id/usb-1a86_USB_Single_Serial_5AB0183212-if00 so as to distinguish it from the klipper MCU). |
servo_id |
int (0-253) | Unique servo ID on the bus |
| Parameter | Type | Default | Description |
|---|---|---|---|
baudrate |
int | 1000000 | Serial communication baud rate |
position_min |
int (0-4095) | 0 | Minimum allowed position |
position_max |
int (0-4095) | 4095 | Maximum allowed position |
initial_position |
int (0-4095) | None | Position to move to on startup |
max_speed |
int (0-3400) | 3400 | Maximum movement speed |
max_acceleration |
int (0-254) | 254 | Maximum acceleration |
status_update_interval |
float | 1.0 | Status polling interval (seconds) |
temperature_warning |
int (0-100) | 70 | Temperature warning threshold (°C) |
temperature_critical |
int (0-100) | 85 | Temperature shutdown threshold (°C) |
# First servo on bus
[st3215 gripper]
serial: /dev/ttyUSB0
servo_id: 1
position_min: 500
position_max: 3500
# Second servo on same bus
[st3215 tool_changer]
serial: /dev/ttyUSB0
servo_id: 2
position_min: 0
position_max: 4095
# Servo on different bus
[st3215 camera_pan]
serial: /dev/ttyUSB1
servo_id: 1Note: Command names containing a letter sequence followed immediately by digits (for example ST3215_*) can be parsed incorrectly by Klipper's gcode parser. This extension registers new command names without embedded digits. If you have existing workflows that use the older ST3215_* names, see the "Legacy command aliases" section below.
Move servo to absolute position.
STSERVO_MOVE SERVO=<name> POSITION=<value> [SPEED=<value>] [ACCEL=<value>] [WAIT=<seconds>]With WAIT, the command will block (in a Klipper friendly way) until the position has been reached. This is done via polling; use status_update_interval to tune how often this polls (default 1.0 seconds).
Examples:
STSERVO_MOVE SERVO=gripper POSITION=3000
STSERVO_MOVE SERVO=gripper POSITION=3000 SPEED=2000
STSERVO_MOVE SERVO=gripper POSITION=3000 SPEED=2000 ACCEL=200Stop servo immediately.
STSERVO_STOP SERVO=<name>Enable servo motor (allows movement).
STSERVO_ENABLE SERVO=<name>Disable servo motor (free to move manually).
STSERVO_DISABLE SERVO=<name>Set current position without moving (for homing/zeroing).
STSERVO_SET_POSITION SERVO=<name> POSITION=<value>Query servo status (position, temperature, current, voltage).
STSERVO_STATUS SERVO=<name>List servos detected on the bus associated with the configured servo instance.
STSERVO_LIST SERVO=<name>Note: STSERVO_LIST performs a bus scan which can be slow (typically 10–15 seconds depending on the adapter and bus). When you run the command the extension will immediately emit an informational message such as:
Listing servos on /dev/ttyUSB0 — this may take up to 10-15s...
This message informs you the scan is starting; the command will then perform the scan and return either a concise list (e.g. Servos on /dev/ttyUSB0: 1, 2, 3) or No servos found on /dev/ttyUSB0.
If you prefer to keep the original ST3215_* command names in your macros or scripts, add the following macros to your printer.cfg. They map the legacy names to the new STSERVO_* commands:
[gcode_macro ST3215_STATUS]
gcode:
STSERVO_STATUS SERVO={params.SERVO}
[gcode_macro ST3215_LIST]
gcode:
STSERVO_LIST SERVO={params.SERVO}
[gcode_macro ST3215_MOVE]
gcode:
STSERVO_MOVE SERVO={params.SERVO} POSITION={params.POSITION} {% if params.SPEED %}SPEED={params.SPEED}{% endif %} {% if params.ACCEL %}ACCEL={params.ACCEL}{% endif %}
[gcode_macro ST3215_ENABLE]
gcode:
STSERVO_ENABLE SERVO={params.SERVO}
[gcode_macro ST3215_DISABLE]
gcode:
STSERVO_DISABLE SERVO={params.SERVO}
[gcode_macro ST3215_STOP]
gcode:
STSERVO_STOP SERVO={params.SERVO}
[gcode_macro ST3215_SET_POSITION]
gcode:
STSERVO_SET_POSITION SERVO={params.SERVO} POSITION={params.POSITION}These macros are lightweight and preserve backward compatibility with scripts or example configs that reference the older names.
[gcode_macro GRIPPER_OPEN]
gcode:
ST3215_MOVE SERVO=gripper POSITION=500 SPEED=2000
[gcode_macro GRIPPER_CLOSE]
gcode:
ST3215_MOVE SERVO=gripper POSITION=3500 SPEED=2000
[gcode_macro GRIPPER_GRAB]
gcode:
GRIPPER_OPEN
G4 P500 # Wait 500ms
GRIPPER_CLOSE[gcode_macro CAMERA_CENTER]
gcode:
ST3215_MOVE SERVO=camera_pan POSITION=2048
ST3215_MOVE SERVO=camera_tilt POSITION=2048
[gcode_macro CAMERA_LOOK_AT_BED]
gcode:
ST3215_MOVE SERVO=camera_pan POSITION=2048
ST3215_MOVE SERVO=camera_tilt POSITION=3000See examples/printer.cfg.example for more examples.
STSERVO_STATUS SERVO=gripperOutput:
st3215 gripper Status:
Position: 2048
Target: 2048
Moving: False
Temperature: 35.2°C
Current: 125.5mA
Voltage: 12.1V
Enabled: True
Query servo status programmatically:
curl http://mainsail.local/printer/objects/query?st3215%20gripperResponse:
{
"result": {
"status": {
"st3215 gripper": {
"position": 2048,
"target_position": 2048,
"is_moving": false,
"temperature": 35.2,
"current": 125.5,
"voltage": 12.1,
"enabled": true,
"last_error": null
}
}
}
}Problem: Servo ID X not found on /dev/ttyUSB0
Solutions:
- Check servo is powered on
- Verify servo_id is correct (default is usually 1)
- Check serial cable connections
- Try scanning: Run
STSERVO_STATUSto trigger detection
Problem: Permission denied: '/dev/ttyUSB0'
Solution:
sudo usermod -a -G dialout $USER
# Log out and back in, or rebootProblem: /dev/ttyUSB0 doesn't exist
Solutions:
# List available serial ports
ls -l /dev/ttyUSB* /dev/ttyACM*
# Use persistent device names
ls -l /dev/serial/by-id/
# Update config with correct portProblem: Klipper fails to start after adding servo config
Solutions:
- Check Klipper logs:
tail -f ~/printer_data/logs/klippy.log - Verify configuration syntax
- Ensure servo_id is valid (0-253)
- Check position_min < position_max
Problem: Temperature warnings in logs
Solutions:
- Reduce duty cycle (don't move continuously)
- Lower max_speed and max_acceleration
- Add cooling (fan)
- Adjust temperature thresholds if appropriate
Problem: Servo moves unpredictably
Solutions:
- Check power supply voltage (should be 11.1-14.8V)
- Ensure adequate current capacity
- Reduce speed/acceleration
- Check for loose connections
- Verify no electrical interference
Keep your installation up to date:
cd ~/klipper-st3215
./update.sh
sudo systemctl restart klipperTo remove the extension:
cd ~/klipper-st3215
./uninstall.sh
# Then remove [st3215 ...] sections from printer.cfg
sudo systemctl restart klipper- ST3215 uses 12-bit positioning: 0-4095
- Position 2048 ≈ center (180°)
- Full range ≈ 360° (model dependent)
- 0-3400 units
- Higher = faster movement
- Adjust based on load and application
- Protocol: Feetech SCS serial protocol
- Default baud rate: 1,000,000 bps
- Half-duplex serial communication
- Internal locking for multi-servo bus sharing
- Reactor-safe callbacks for Klipper integration
- Automatic retry on communication failures
klipper-st3215/
├── st3215_servo/ # Main module
│ ├── __init__.py # Module entry point
│ ├── st3215_bus.py # Bus manager
│ └── st3215_servo.py # Servo controller
├── examples/ # Example configurations
│ └── printer.cfg.example
├── docs/ # Documentation
├── install.sh # Installation script
├── uninstall.sh # Uninstallation script
├── update.sh # Update script
├── README.md # This file
└── LICENSE # GPL v3 license
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Klipper firmware team for the excellent 3D printer firmware
- Feetech for ST3215 servo motors
- st3215 Python library developers
Made with ❤️ for the Klipper community