Open-source USB control stack for Roland DG MDX-series desktop mills
Project description
RollingMill
Open-source USB control stack for Roland DG MDX-series desktop mills, tested on the MDX-40A.
RollingMill communicates directly with the machine over libusb, replacing both the proprietary Windows kernel driver and the VPanel control application. It provides a terminal application that:
- Shows live machine state read-out
- Jogs four axes at variable speed, with cancel
- Moves to stored origins, view position, or arbitrary coordinates
- Controls spindle speed and rotary-axis drilling
- Sets workspace origins and tool diameter offsets
- Streams saved G-code (
.ncfiles) to the mill - Runs on Linux and macOS
https://github.com/user-attachments/assets/d28e8a8f-c5a4-4877-8cd2-d2d479c63437
Reverse Engineering
The MDX USB protocol was reverse engineered from the official Windows driver stack using Ghidra (and the MCP bridge) and documented here in this respository. It should be stated that the Roland DG corporation neither authorised or approved of this work.
The official Windows stack consists of:
- RD25D — kernel-mode USB and printer filter driver
- VPanel — user-space control application
RollingMill implements both layers in Python, and works great on macOS.
Getting started
This library is available from PyPi as rollingmill.
$ pip install rollingmill
$ rollingmill # launch TUI on real hardware
$ rollingmill --file sample-nc/example-doc-part.gcode # stream an NC file
The interactive TUI exposes the following keybindings:
Jogging
| Key | Action |
|---|---|
| ← / → | X axis −/+ |
| ↑ / ↓ | Y axis −/+ |
| a / z | Z axis +/− (a raises, z lowers) |
| [ / ] | A axis −/+ |
| Esc | cancel an in-flight jog |
| f | cycle jog speed: vslow / slow / medium / fast |
| 1…5 | step size: XYZ 0.01 / 0.1 / 1.0 / 10.0 / 50.0 mm — A 0.01 / 0.1 / 1.0 / 10.0 / 90.0° |
Spindle
| Key | Action |
|---|---|
| s | spindle on / off |
| d | A-axis rotary drilling on / off |
| < / > | spindle target RPM −500 / +500 |
| - / + | spindle & feed override % −10 / +10 |
Dialogs
| Key | Action |
|---|---|
| c | coordinate systems (activate / move-to / overwrite) |
| m | Move-To picker (presets + User Specify numeric entry) |
| t | tool diameter offsets |
| q | quit |
Cut panel (only when --file is given)
| Key | Action |
|---|---|
| r | run — stream the file continuously to the end |
| x | step — send one block; or, while running, stop after the current block |
Gotchas/Notes using the mill
- If your NC-code 'crashes' the Z position above absolute machine zero (i.e. at the top of the machine), then the machine's interpreter starts running all future G codes as rapid moves flattened against Z=0 (all feeds become rapid moves, descending is inhibited) until the end of the program. This is quite terrifying should it happen, but is a machine feature.
- If you send NC-code (i.e. G-code) in RML-1 mode, it will hang. Recovery for now is a power-cycle.
- Single-stepping nc-code partially works, but the MDX complains about being starved of NC code and lights the View-LED when you reach a cutting operation.
- The progress display during a cutting job is that of filling the machine buffer, rather than the actual block being executed, which usually runs a few blocks behind. It's a shame there are not two counters so that this could be made 100% accurate.
- There might be an issue with storing A-axis values in workspace offsets and recalling them, as sometimes I get an extra +360° rotation on recall.
- I need a Z-origin sensor to test the sensor-based Z-origin setting, and decode the thickness settings — NYI.
- The geometry of the mill means that without tooling, it is fairly difficult (but not impossible) to crash. The rotary axis makes this much easier as the firmware allow the collet to hit the vice area.
- Be warned that bad, terrible things could happen driving your hardware with this experimental software, due to known or unknown bugs or defects, for which you entirely agree to take all risks in operating and hold the contributors blameless.
MDX GCode Intepreter Notes
- G-codes must be two digits (
G01,G00etc) - Dont forget to turn the spindle on (Sxxxx M03) otherwise the machine will fault on the first
G01operation - Ensure all
XYZvalues have a decimal point, and no more than 3 digits of precision - Feed rates need a decimal point too
- Machine coordinate moves
G53must be always given inG90absolute programming. Specifying G53 in G91 incremental results in an error.
Check your code using this online G-code viewer, customised for the MDX G-code varient.
ZCL-40A 4th Axis
When this unit is installed, the X-, Y-, and Z-axis travel of the MDX-40A is reduced:
| Axis | MDX-40A | MDX-40A + ZCL-40A |
|---|---|---|
| X | 0 → 305 mm | 34 → 305 mm |
| Y | 0 → 305 mm | 0 → 305 mm |
| Z | 0 → −105 mm | 0 → −68 mm |
| A | — | 0 → 360° (continuous) |
These limits are enforced in firmware, including from the physical jog controls on the machine itself. The X-restriction clears the rotary axis body, but not the rotary vice or tailstock, so care is needed.
Engraving text
rollingmill.text_to_gcode renders a text string to a single-line G-code file using a Hershey stroke font from the pyhershey library. Glyphs carry per-character advance-width metrics, so spacing is naturally kerned. The output is plain RS-274 intended to be streamed via the TUI's --file flow.
python -m rollingmill.text_to_gcode \
--text "Parcels" \
--out /tmp/label.gcode \
--height 8 \
--z-cut -0.1 --z-safe 2 \
--feed 200 \
--workspace G54
--workspace G54..G59 is optional; when set, the matching WCS-select word is emitted in the header so the cut runs against that work-offset.
Then load on the machine: rollingmill --file /tmp/label.gcode.
The default font is roman_simplex. List all available fonts with --list-fonts; pass any name (e.g. --font gothic_german_triplex) to switch. Use --letter-spacing 1.5 to add (or, with a negative value, remove) extra mm of gap after each glyph's natural advance.
Drawing spirals
rollingmill.spiral renders hypotrochoids, the curve traced when a small toothed wheel rolls inside a fixed toothed ring. You give it the two gear tooth counts and a pen-arm length, and it emits a single closed pen-down/pen-up loop scaled to fit your requested overall radius.
python -m rollingmill.spiral \
--ring 96 --rotor 52 --pen 30 \
--radius 40 --x0 100 --y0 100 \
--out /tmp/spiral.gcode \
--workspace G54
--ring R and --rotor r are tooth counts (R strictly greater than r). --pen d is the pen-arm length measured in the same tooth-count units as the rotor: d < r gives smooth curtate lobes, d = r gives a hypocycloid with cusps, d > r gives prolate loops. --radius is the desired max radial extent of the finished drawing in mm, centred on (--x0, --y0). The curve closes exactly after 2π · r / gcd(R, r) of the rolling parameter.
The output uses G02 / G03 arcs with incremental I/J offsets — each arc is fitted adaptively to the true hypotrochoid, with maximum deviation bounded by --chord-tol (default 0.05 mm).
Then load on the machine: rollingmill --file /tmp/spiral.gcode.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rollingmill-0.1.0.tar.gz.
File metadata
- Download URL: rollingmill-0.1.0.tar.gz
- Upload date:
- Size: 217.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2cc7dc4be53029a692a467b29ca3693c22abdd7b28a7602cf28abc0ebac5d528
|
|
| MD5 |
b1b33af61ce1005bd903bcc9804974fe
|
|
| BLAKE2b-256 |
e4722579360b47a6165fc4c7154ca5162be651adb9cb0000e75efd76a9c3d03e
|
Provenance
The following attestation bundles were made for rollingmill-0.1.0.tar.gz:
Publisher:
publish.yml on TeaEngineering/rollingmill
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rollingmill-0.1.0.tar.gz -
Subject digest:
2cc7dc4be53029a692a467b29ca3693c22abdd7b28a7602cf28abc0ebac5d528 - Sigstore transparency entry: 1771936358
- Sigstore integration time:
-
Permalink:
TeaEngineering/rollingmill@61c4d684f7055131f8be4ae7b7b317a6cb4103c8 -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/TeaEngineering
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@61c4d684f7055131f8be4ae7b7b317a6cb4103c8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rollingmill-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rollingmill-0.1.0-py3-none-any.whl
- Upload date:
- Size: 63.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f25105c09b3f47bfcd92a67dfdd43f52388a1d5786219985b29318a2c1d6a4dd
|
|
| MD5 |
e4da13b988ab2aaf38f30d49e15004cd
|
|
| BLAKE2b-256 |
a352d12672916534916ffa39806a43e7738b867a042fce91c209f3c4645f9fc5
|
Provenance
The following attestation bundles were made for rollingmill-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on TeaEngineering/rollingmill
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rollingmill-0.1.0-py3-none-any.whl -
Subject digest:
f25105c09b3f47bfcd92a67dfdd43f52388a1d5786219985b29318a2c1d6a4dd - Sigstore transparency entry: 1771936787
- Sigstore integration time:
-
Permalink:
TeaEngineering/rollingmill@61c4d684f7055131f8be4ae7b7b317a6cb4103c8 -
Branch / Tag:
refs/tags/0.1.0 - Owner: https://github.com/TeaEngineering
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@61c4d684f7055131f8be4ae7b7b317a6cb4103c8 -
Trigger Event:
release
-
Statement type: