Open Micro-Manipulator: a delta stage that moves in 50 nm steps over USB serial
A sub-micrometer 3D motion control plattform.
At a glance
- What is it?
- 0x23's MicroManipulatorStepper is an open source XYZ micro-manipulator built from off-the-shelf stepper motors, a delta linkage and a custom RP2040 board, driven by G-Code over a USB serial port. The mechanical resolution is 50 nm and the last push landed on 2026-10-11, but absolute accuracy is far worse than the step size, and the bill of materials is still marked as out of date.
- Who is it for?
- Adopt this stage if you need a repeatable, open, self-built XYZ axis for optical alignment, probing or microscopy and you are willing to machine parts, edit hw_config.h and calibrate each joint. Do not adopt it if your experiment depends on stated absolute accuracy, on a published bill of materials, or on vendor support; the README itself flags both gaps.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the parallel linkage buys and what it costs
The device is a delta arrangement rather than a serial XYZ gantry, and that choice drives everything else in the repository. The README credits the parallel kinematic structure and miniature ball joints with mechanical stiffness and a large range of motion, which is the usual argument for deltas in 3D printing. Off-the-shelf stepper motors drive the moving platform through linkage rods.
The resolution story is more unusual. The motors are driven by a 30 kHz closed loop controller and a very precise PWM signal, but the encoders are cheap magnetic rotary encoders. The design recovers resolution with a magnetic gearing approach that multiplies encoder resolution by a factor of 30, which is how the stage reaches steps down to 50 nm. The README is explicit that this is resolution and not accuracy, and that absolute accuracy is significantly worse. Anyone quoting the 50 nm figure as a positioning guarantee is misreading the design.
The target users are people doing optical alignment, probing electronic components and microscopy work. Those tasks tolerate a repeatable relative move far more than they tolerate a calibrated absolute coordinate, which is why the resolution over accuracy split is a reasonable bargain here.
G-Code over a USB serial port, not a USB HID joystick
Control is plain G-Code over a USB serial interface. The firmware carries a full motion planning stack with look-ahead, so paths are smoothed and cornering is planned rather than stepped abruptly. That choice matters for integration: the stage is not driven by a bespoke GUI or a vendor SDK, so anything that can open a serial port and speak g-code can drive it.
The serial link runs at 921600 baud by default in the Python API. Firmware 1.0.4 added a tool output on the two GPIO pins of J5, integrated into the planner so it fires with correct timing during g-code execution, and driven by the PIO feature rather than software PWM so extra channels do not steal CPU:
M3 T0 S0.7
G4 S0.001The first line sets tool 0 output to 70 percent PWM, the second is a dwell that gives the planner time to apply it. That combination is the documented way to drive something like a laser alongside motion.
Copying one Python file into your own project
The Python API lives in a single file, software/PythonAPI/open_micro_stage_api.py, and the README says to copy it into your project and install the dependencies in requirements.txt. It handles all serial communication and provides debug message printing, so there is no package to install from an index. The documented first run connects, homes, moves to a few positions in millimetres and waits:
from open_micro_stage_api import OpenMicroStageInterface
oms = OpenMicroStageInterface(show_communication=True, show_log_messages=True)
oms.connect('/dev/ttyACM0')
oms.home()
oms.move_to(0.0, 0.0, 0.0, f=10)
oms.move_to(3.1, 4.1, 5.9, f=26)
oms.move_to(0.0001, 0.0, 0.0, f=10)
oms.wait_for_stop()Positions are in millimetres and f is the feed rate. The last move, 0.0001 mm in X, is one micrometre, and it is there to show that the API accepts fractional coordinates the motor can actually resolve. The commented calibration loop near the top runs joint calibration once per joint with save_result=True.
Beyond motion the interface exposes home, calibrate_joint, set_workspace_transform and get_workspace_transform for mapping the mechanical frame to a microscope stage, set_tool_output, set_pose, dwell, enable_motors, wait_for_stop, set_max_acceleration with separate linear and angular limits, and set_servo_parameter taking position and velocity gains plus a velocity filter time constant. That last one matters, because the same gains do not transfer between print quality and hardware revision.
For interactive work the separate Open Micro-Manipulator GUI repository adds jog controls, a live camera feed from a microscope camera, a g-code runner and realtime mouse control aimed at biology work.
Firmware, hardware config and the v3 to v4 migration
Building the device is a hardware job more than a software one. The README lists the order: read the document and videos, get the parts from documentation/bom/bom.md, build the mechanics and electronics, change the hardware configuration for your build in firmware/MotionControllerRP/src/hw_config.h, upload firmware with VSCode and the PlatformIO plugin, then calibrate the axes. A separate documentation/setup_guide/setup_guide.md exists for new devices.
Hardware version 4.0 fixed a specific list of complaints about the earlier design: self collision during homing, a ball joint block held only by screws that could rotate, no reliable way to keep linkage rod length consistent, unnecessary ball joint friction, ball position that depended heavily on print quality and corner rounding, and rubber bands that could not be replaced once glued. The FreeCAD model was restructured to include all pins and fasteners as a reference.
Firmware 1.0.4 presets the kinematic parameters for HW v4.0. Owners of a v3.0 build must edit firmware/MotionControllerRP/src/kinematic_model_delta3d.cpp and flip the #define at the top of that file back to the older parameters. Skipping that step on a v3.0 unit means the inverse and forward kinematics are being fed the wrong link geometry, and the motion will be wrong in a way that looks like a mechanical fault.
Firmware 1.0.1 earlier work landed parallel homing support, higher repeatability, a more accurate geometric reference and refined joint calibration.
Where this stage is the wrong tool
Three limits are worth stating plainly. First, absolute accuracy is significantly worse than the 50 nm step, so the stage is unsuitable for any measurement that assumes a commanded coordinate equals a physical one. Second, the README carries a warning that the BOM is not updated yet for hardware version 4.0, so a parts list cannot be trusted without cross-checking the FreeCAD model, which the author says now covers all required mechanical parts. Third, accuracy of the ball position heavily depends on print quality and corner rounding, so a printer with sloppy corners produces a stage that is quieter but less true, and the README has no calibration procedure that compensates for a poor print.
There is also no GitHub release for the firmware. Versions 1.0.1 and 1.0.4 exist as README sections only, so there is no tagged artifact or changelog to pin against. The build itself is not documented as a package install; it assumes PlatformIO inside VSCode and a hand-built board.
How this differs from a commercial probe station
The alternative approach is a closed piezo or motorised probe station from a microscope supplier. Those units ship with a calibration certificate, a stated repeatability and a warranty, and they arrive as a working instrument with the mechanics, controller and software already validated together. The open design trades all of that for parts you can buy anywhere, a mechanism you can inspect and modify, and a control interface you can script from any language that can open a serial port.
The delta layout is the other difference. A serial stage stacks axis error, while a parallel one keeps the actuators away from the tip, which suits the alignment and probing tasks the README lists. The cost of that choice is the ball joints, and the 4.0 revision exists because of them.
Licence and what you inherit
The repository declares its licence as Other, a custom licence that GitHub cannot classify, with the terms in the LICENSE file at the root. That is worth reading before you publish a derived mechanical design or redistribute firmware, since a non-standard licence can carry conditions that a permissive standard package would not. No legal judgement is offered here.
The repository is not archived and the last push was on 2026-10-11, so the mechanical revisions and the firmware work are moving together. There are no GitHub releases, so version tracking happens through README sections and the commit history.
Editorial conclusion
Adopt this stage if you need a repeatable, open, self-built XYZ axis for optical alignment, probing or microscopy and you are willing to machine parts, edit hw_config.h and calibrate each joint. Do not adopt it if your experiment depends on stated absolute accuracy, on a published bill of materials, or on vendor support; the README itself flags both gaps. Before ordering anything, check documentation/bom/bom.md against what your build actually needs, because the repository warns that the BOM is not updated yet for hardware version 4.0, and confirm your printer can hold the ball joint tolerances the author singles out.
Frequently asked questions
How do I install and control the Open Micro-Manipulator?
The firmware is uploaded with VSCode and the PlatformIO plugin after you set the hardware configuration in firmware/MotionControllerRP/src/hw_config.h. For control, copy software/PythonAPI/open_micro_stage_api.py into your project, install the dependencies in requirements.txt, then connect to the serial port, which is /dev/ttyACM0 in the README example.
Is the 50 nm step size the same as the stage accuracy?
No. The README explicitly asks readers to mind the difference between resolution and accuracy, and states that the absolute accuracy is significantly worse than the 50 nm step produced by the magnetic gearing encoder approach.
What do I need to change if I built hardware version 3.0?
Firmware 1.0.4 presets the kinematic parameters for hardware version 4.0. A v3.0 owner has to open firmware/MotionControllerRP/src/kinematic_models/kinematic_model_delta3d.cpp and switch the #define at the top back to the old parameters.
Can I use the Open Micro-Manipulator from something other than the bundled GUI?
Yes. The device speaks G-Code over a USB serial interface, and the Python API is a single file you copy into your own project. A separate GUI repository adds a live camera feed, a g-code runner and realtime mouse control.
What drives the tools and laser output?
Firmware 1.0.4 uses the two GPIO pins of J5 on the pcb, with PWM handled by the PIO feature rather than software PWM. Tools are integrated into the planner so they run with correct timing during g-code execution.
Official sources
Where developers are discussing it
Posts on Hacker News, dev.to and Lobsters that link to this repository, found by our scan of those communities. 1 in the last 30 days; newest first.
- Open MicroManipulator – A sub-micrometer 3D motion control plattformHacker News · Oct 18, 2026 · 220 points · 18 comments
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/0x23-micromanipulatorstepper)Was this useful?