Hardware & firmware
An Arduino Uno fitted to the SRM-20's SPI remote header lets SRM-CAM read where the head is, jog it, feel the bit touch the copper, probe the bed and zero Z. This page is what is fitted, how it talks, and how a touch is measured. The drawings are live.
Everything else — traces, drilling, cut-out, panels, double-sided registration, rework, the checks and every export — runs on a stock SRM-20 through VPanel with nothing fitted. The link itself runs on Windows only: on Linux the machine bar says so instead of showing controls. A height map is a file, so you can probe on the CNC PC, the CSV, and it anywhere to export levelled toolpaths.
What is fitted, and how it talks
The Arduino is not listening in on the machine: it is the SPI master on the SRM-20's own remote header, speaking Roland's remote protocol through Roland's SRM20SPIRemote library (vendored in hardware/, with three small marked patches). It sits on an SPI shield plugged into the header, with slave-select on D9 and the ready line on D6.
Two USB cables go to the PC. The black one is the Arduino: SRM-CAM sends it one short text line at a time (115200 baud, distances in microns) and the firmware turns each line into SPI calls. The white one is the mill itself, for VPanel, which keeps playing the files. The two coexist, and both can go to your own laptop when the mill's PC is not free (What you need).
The probe: one wire
There is one wire and one clip. Clip the red alligator clip to the copper: it is the Arduino's input D7, which the chip holds high through its internal pull-up. The bit needs no clip. It is on the machine's ground, which is the Arduino's ground, so when the bit touches the copper the circuit closes and D7 goes low. The spoilboard is wood and conducts nothing, so nothing needs to go under the board.
- Spindle offAlways, while the clip is on.
- Clip on the copperThe red alligator clip, anywhere on the sheet's copper.
- Test itTouch the clip to the bit: SRM-CAM's Z readout on the machine bar turns red and says Touch. Let go and it goes back to Z.
The probe bracket
The printed corner bracket replaces the clip: a sprung tongue presses the bare wire onto the sheet as it slides in. Files: hardware/probe_bracket.
- Print
probe_bracket_print.stlin PLA: 0.15 mm layers, 100% infill, no supports. Check the slit beside the tongue is open. - TongueStrip 15 mm. Bare end down through a tongue hole, along the groove, up the other hole; knot or tape it on top.
- ArmInsulated wire down one arm hole, along the channel underneath, up the other. It takes any pull.
- MountCountersunk M4 in the back-right holes. The wire stays connected while cutting, so keep it off the copper.
- TestSlide in a sheet and lower the bit onto it by hand: Z must turn red and say Touch.
The clamps
Print clamp_print.stl from the same folder in PLA, one for each clamp: 0.15 mm layers, 100% infill, no supports. Each one takes an M4 thumbscrew in any spoilboard hole. Its flat lip presses the copper down, and pushing it round moves the sheet into the corner.
How a touch is measured
- Debounced: three consecutive low reads, 1 ms apart.
- Found twice: a coarse descent at 25 µm a step to contact, a 150 µm lift, then a fine re-descent at 10 µm, the machine's own step. The two contacts must agree within 60 µm. If they don't, the fine touch becomes the reference and it tries once more; a second disagreement reports the point unstable instead of putting it in the map.
- Fast above known copper: once copper has been found, later points rapid to 1 mm above the highest copper seen, step 150 µm (no copper can be up there), and switch to 25 µm from 100 µm above it, so the touch itself is always gentle.
Safety limits (in firmware)
| Guard | Value | Prevents |
|---|---|---|
| Drop floor | 6 mm below safe Z | Endless descent on dead wiring or a missing clip |
| Runaway guard | 1.2 mm past known copper | Driving into the bed when a point misses the copper |
| Fine-touch floor | 0.5 mm past the coarse touch | A re-descent that lost the surface |
| Hole-test cap | 0.2 mm past the surface | The fiducial finder plunging into a hole |
| Status guard | pause, lid open, fatal | Queueing moves into a paused or opened machine (v3 reads the machine's own bits) |
| Spindle deadman | 10 s | A spindle started over the link running on after the app has gone quiet |
| Abort everywhere | — | STOP is honoured between every Z step, and mid-move |
STOP drops the move and stops the spindle. Firmware v4 and later fix the retract after an abort, which used to cancel itself, but SRM-CAM does not count on it: the bit may still be down. Raise it with Page Up before the next move. See Pause and STOP.
Install (one-time, for whoever maintains the mill)
- Fit the ArduinoAn Arduino Uno on the SPI shield, plugged into the SRM-20's SPI remote header on the back. The red clip's wire goes to D7. Full story:
docs/archive/2026-06-25-srm20-spi-and-bed-leveling.md. - Flash
python scripts/flash_firmware.pysyncs the bundled library, compiles, uploads and reads the version back to prove the board came back as v4. It uses thearduino-clibundled with the Arduino IDE.--port COM4pins the port;--compile-onlyjust checks it builds. - Close the Serial MonitorOnly one program can hold the port; SRM-CAM cannot connect while the IDE has it.
- Check it in SRM-CAM, or
python scripts/srm20_bench.py --port COM5 --features, the read-only "is it flashed and talking?" check.
The sketch includes the library with angle brackets, so the IDE builds against the copy in Documents\Arduino\libraries, not the one in this repo. A stale copy builds cleanly and quietly lacks commands. flash_firmware.py syncs first, every time.
Serial protocol (bench reference)
115200 baud, newline-terminated, distances in microns. SRM-CAM speaks it for you; the host side is gerber2rml/engine/spi_probe.py, the board side hardware/srm20_spi_probe/srm20_spi_probe.ino.
| Command | Meaning |
|---|---|
D / P id x y / B | Set the datum and safe Z here / probe a point relative to it / re-touch the datum (drift check) |
T / W | Touch off from here and stop on the surface / touch off and write the work-origin Z there |
H x y | Hole test at absolute (x, y): copper or hole |
J x y / L / Z z | Jog (lifts about 5 mm first) / lift to safe Z / set safe Z |
U z | Fast straight down to machine Z, halting at the first touch (v5). The motors take a moment to stop, so treat it as a safety net: a bit can still hit the copper at speed. |
O x y z | Set the work origin absolutely, no motion (v4). It reaches the User origin only, which NC jobs ignore. |
Q / G / V / I | Position and touch / work origin / firmware version and features / the machine's remote-protocol version |
X / S rpm | Status words and spindle rpm / spindle on or off (the machine ignores the rpm: VPanel's slider sets the speed) |
~ ^ % K | Pause / resume / drop the move in flight / cancel the job |
Y / N dx dy dz s | Move to the View position / timed relative move, no lift (the jog pad and arrow keys) |
C / M x y z s | Begin a streaming session / stream one move (experimental) |
A 0|1 / F us | Status guard off or on / SPI framing delay (bench) |
! | Abort: stop the spindle and the move in flight, then retract |