Skip to content

About

RollingMill a cross platform USB controller for Roland MDX-series desktop mills

Resources

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

 

History

89 Commits

Folders and files

Repository files navigation

RollingMill

RollingMill logo

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 (.nc files) to the mill
  • Runs on Linux and macOS

demo-small.mp4

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
$ pip install "rollingmill[maze]"                        # adds maze generator (needs cairo headers on Linux)
$ 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
n command mode: RML-1 / NC Code / automatic, plus the NC Code preferences (tool-diameter offset paths Type A / Type B, decimal-point method, optional block); Space selects, Enter applies
b Z0 sensor probe dialog (sets the Z origin of User, EXOFS or G54–G59 using probe)
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.
  • 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, G00 etc)
  • Dont forget to turn the spindle on (Sxxxx M03) otherwise the machine will fault on the first G01 operation
  • Ensure all XYZ values have a decimal point, and no more than 3 digits of precision
  • Feed rates need a decimal point too
  • Machine coordinate moves G53 must be always given in G90 absolute programming. Specifying G53 in G91 incremental results in an error.
  • No embedded parenthesis within a G-code comment
  • Decimal G-codes (e.g. G38.2, G90.1) are not supported
  • Comments must be on their own line: no inline comments after code, e.g. G01 X1.0 (note)

rollingmill.check lints an NC file, and prints an estimate of the milling time. rollingmill --file refuses to stream a file that fails.

$ python -m rollingmill.check sample-nc/hole47.gcode
sample-nc/hole47.gcode: OK (140 blocks)
Estimated time: 2m 57s (cutting 2m 44s, rapid 12s, dwell 0s, commands 0s; 97 moves, 1017 mm path)

The rules are:

Rule Checks
1 no embedded parenthesis within a comment
2 a comment that opens with ( must close with )
3 G-codes must have two digits (G01, not G1)
4 no G01/G02/G03 cutting move before the spindle is started with M03
5 no inline comments: a ( comment must be on a line of its own
6 no decimal G-codes (G38.2, G90.1)
7 only supported G-codes (e.g. G64 is rejected)
8 only supported M-codes (e.g. M04 and M08 are rejected)

The supported G- and M-code lists for rules 7 and 8 come from section 2-7 "List of Words" (printed pages 63–64) of Roland's NC Code Reference Manual.

If the file breaks a rule, each problem is shown gcc-style with the offending line and a caret, and the exit code is 1 (0 is clean, 2 is an I/O error):

Input file failed validation:
broken.gcode:1:4: [rule 1] embedded parenthesis in comment
(a (b)
   ^
broken.gcode: 1 warning(s)

The estimate assumes the tool starts at the workspace origin and moves at the programmed feed, with no acceleration model, so files made of many tiny moves will run slower than predicted. Every move or dwell is counted as taking at least 250 ms to cover communications overhead. The rapid speeds are assumptions rather than measured values, so set them for your machine:

Option Default Meaning
--rapid-xy MM_MIN 3000 XY rapid speed (G00)
--rapid-z MM_MIN 3000 Z rapid speed (G00)
--feed-override PCT 100 cutting feed override, as set on the TUI with - / +
--min-op SEC 0.25 minimum time for any move or dwell
--no-estimate lint only

For example, with the feed override at 80% the same file takes longer:

$ python -m rollingmill.check --feed-override 80 sample-nc/hole47.gcode
sample-nc/hole47.gcode: OK (140 blocks)
Estimated time: 3m 38s (cutting 3m 25s, rapid 12s, dwell 0s, commands 0s; 97 moves, 1017 mm path)

You can visualise the MDX toolpaths with my online G-code viewer.

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 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.

Engraving text

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.

Drawing spirals

Drawing mazes

rollingmill.maze carves a random rectangular maze using the erbsland-maze library, extracts the wall centerlines (one stroke per wall, not the outline of a thick wall), and emits a single-pass pen-down/pen-up G-code program. Start- and end-of-path rooms are flagged with small marker squares.

python -m rollingmill.maze \
    --out /tmp/maze.gcode \
    --width 100 --height 100 \
    --cell 4 --wall 1.7 \
    --seed 42 \
    --x0 100 --y0 100 \
    --feed 200 \
    --workspace G54

--width and --height set the overall canvas in mm; --cell is the grid pitch and --wall is the wall thickness used when laying out the cells. --seed makes the maze reproducible (a random seed is chosen and echoed into the header if omitted). --endpoints {none,outline,filled} selects how the two path-end rooms are marked (default outline).

Then load on the machine: rollingmill --file /tmp/maze.gcode.

Drawing mazes

See also

About

RollingMill a cross platform USB controller for Roland MDX-series desktop mills

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages