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.

Optional, and Windows-only

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, Level the bed › Save… the CSV, and Load… it anywhere to export levelled toolpaths.

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

Follow one exchangeEach button sends a real command from SRM-CAM down the black cable, the SPI call it becomes, and the answer coming back. VPanel's file takes the white cable and never passes the Arduino.

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.

  1. Spindle offAlways, while the clip is on.
  2. Clip on the copperThe red alligator clip, anywhere on the sheet's copper.
  3. 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.

  1. Printprobe_bracket_print.stl in PLA: 0.15 mm layers, 100% infill, no supports. Check the slit beside the tongue is open.
  2. TongueStrip 15 mm. Bare end down through a tongue hole, along the groove, up the other hole; knot or tape it on top.
  3. ArmInsulated wire down one arm hole, along the channel underneath, up the other. It takes any pull.
  4. MountCountersunk M4 in the back-right holes. The wire stays connected while cutting, so keep it off the copper.
  5. 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.

A switch, read three timesLower the bit onto the copper and D7 goes low. The firmware only believes it after three low reads in a row, 1 ms apart, so a spike of stepper noise cannot fake a touch.

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.
Z against timeThe firmware's own step sizes; the surface heights are an example. Switch between the first point, later points and a flaky touch.

Safety limits (in firmware)

GuardValuePrevents
Drop floor6 mm below safe ZEndless descent on dead wiring or a missing clip
Runaway guard1.2 mm past known copperDriving into the bed when a point misses the copper
Fine-touch floor0.5 mm past the coarse touchA re-descent that lost the surface
Hole-test cap0.2 mm past the surfaceThe fiducial finder plunging into a hole
Status guardpause, lid open, fatalQueueing moves into a paused or opened machine (v3 reads the machine's own bits)
Spindle deadman10 sA 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 does not promise to lift the tool

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)

  1. 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.
  2. Flashpython scripts/flash_firmware.py syncs the bundled library, compiles, uploads and reads the version back to prove the board came back as v4. It uses the arduino-cli bundled with the Arduino IDE. --port COM4 pins the port; --compile-only just checks it builds.
  3. Close the Serial MonitorOnly one program can hold the port; SRM-CAM cannot connect while the IDE has it.
  4. Check itMachine › Machine test… in SRM-CAM, or python scripts/srm20_bench.py --port COM5 --features, the read-only "is it flashed and talking?" check.
Do not flash from the Arduino IDE

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.

CommandMeaning
D / P id x y / BSet the datum and safe Z here / probe a point relative to it / re-touch the datum (drift check)
T / WTouch off from here and stop on the surface / touch off and write the work-origin Z there
H x yHole test at absolute (x, y): copper or hole
J x y / L / Z zJog (lifts about 5 mm first) / lift to safe Z / set safe Z
U zFast 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 zSet the work origin absolutely, no motion (v4). It reaches the User origin only, which NC jobs ignore.
Q / G / V / IPosition and touch / work origin / firmware version and features / the machine's remote-protocol version
X / S rpmStatus words and spindle rpm / spindle on or off (the machine ignores the rpm: VPanel's slider sets the speed)
~ ^ % KPause / resume / drop the move in flight / cancel the job
Y / N dx dy dz sMove to the View position / timed relative move, no lift (the jog pad and arrow keys)
C / M x y z sBegin a streaming session / stream one move (experimental)
A 0|1 / F usStatus guard off or on / SPI framing delay (bench)
!Abort: stop the spindle and the move in flight, then retract