Send SCPI and G-code From Your Dashboard

How SCPI and G-code work on the wire, why sending commands is harder than reading data, and how dashboard controls format outbound messages safely.

A slider widget passing through a transmit template that formats a SCPI voltage command for a bench power supply

Reading data from a device is passive. Whatever arrives, you parse it, plot it, and nothing bad happens if you get it slightly wrong. Sending a command is a transaction: it has to be formatted exactly the way the device expects, terminated correctly, and acknowledged. That asymmetry is why so many home-grown dashboards are read-only. This post looks at the two command languages you are most likely to meet, SCPI on the bench and G-code on a machine, and at what it takes to wire a slider or a switch to them.

SCPI, the language of bench instruments

SCPI (Standard Commands for Programmable Instruments) is an ASCII command language layered on IEEE 488.2, and nearly every modern bench instrument speaks it: Keysight, Rigol, Siglent, Rohde & Schwarz, Tektronix. Commands form a tree with colon-separated keywords, and each keyword has a long and a short form, so these two lines are the same command:

MEASURE:VOLTAGE:DC?
MEAS:VOLT:DC?

A trailing ? makes a command a query that returns data. Commands starting with * are IEEE 488.2 common commands every compliant instrument must implement: *IDN? returns the identification string, *RST resets, *OPC? blocks until pending operations finish. A real session with a Rigol DP832 power supply looks like this:

*IDN?           → RIGOL TECHNOLOGIES,DP832,...
:INST CH1         select channel 1
:VOLT 3.3         set 3.3 V
:CURR 1           set the current limit
:OUTP CH1,ON      enable the output
:MEAS:VOLT? CH1   read back what actually happened

The transport varies (USB-TMC, a raw TCP socket on port 5025 for LAN instruments, plain serial on older gear) but the framing convention is stable: one command per line, terminated with \n. Two habits keep sessions healthy. Set limits before you enable an output, and read values back instead of trusting the setpoint you sent.

G-code, the language of motion

CNC controllers and 3D printers speak G-code, and the most common open firmware dialect is Grbl (and its actively developed successor, grblHAL). The protocol is strict and simple: you send one ASCII line, the controller answers every line with exactly ok or error:N, and you do not send the next line until you hear back. A handful of single-character realtime commands bypass the line buffer entirely and get no reply: ? requests a status report, ! is feed hold, ~ resumes, and Ctrl-X soft-resets.

For a dashboard, the most useful part of Grbl 1.1 is the jogging interface:

$J=G91 X10.00 F500

That jogs the X axis 10 mm at 500 mm/min. Jog commands are checked against soft limits before they execute, they do not disturb the G-code parser's modal state, and an in-flight jog can be cancelled instantly. Those properties exist precisely so that a hold-a-button UI can be safe.

Why the write path is harder than the read path

Three things make control messier than monitoring. First, formatting: VOLT 3.3 and $J=G91 X3.30 F500 encode the same user intent for different devices, and the device rejects anything else. Second, interleaving: command replies arrive on the same line as streaming telemetry, so the reader has to tell ok apart from data. Grbl helps by making status reports syntactically distinct (<Idle|MPos:...>); SCPI does not, which is why an unread query reply desynchronizes every read that follows. Third, state: the UI's belief and the device's truth drift apart, so a good dashboard displays what the device reports, not what the user last asked for.

Wiring a slider to the wire

Serial Studio's approach is to keep the widget generic and put the device-specific part in a small script. The output widgets (buttons, toggles, sliders, knobs, text fields) each carry a transmit(value) function that turns the widget's value into the exact bytes to send. The bundled SCPI template is short enough to read in full:

function transmit(value) {
  if (typeof value === "string")
    return value + "\n";
  if (value === 0 || value === 1)
    return "OUTP1 " + (value ? "ON" : "OFF") + "\n";
  return "SOUR1:VOLT " + Number(value).toFixed(3) + "\n";
}

A toggle sends OUTP1 ON, a slider sends SOUR1:VOLT 3.300, and a text field passes raw SCPI through. The G-code and Grbl templates have the same shape: a switch maps to M3/M5 for the spindle, a slider maps to a $J= jog, a text field sends $H or $$ verbatim. Because the formatting lives in an editable script rather than in application code, adapting it to your instrument means changing one line, and a transmit test dialog shows the exact bytes a value will produce before anything reaches the port. Output widgets are part of Serial Studio Pro.

Keep the safety rails physical

One convention matters more than all the others: an emergency stop must not depend on software. A dashboard button is a convenience, not a safety device, because it sits behind an operating system, a serial driver, and a cable. Real machine safety lives in hardware power cutoff, in controller-side soft limits, and in instrument-side protection like over-voltage and over-current clamps that fire no matter what the host sends. Configure those first, then enjoy the slider.

If the vocabulary of downlink and telecommand is new, What Is Telemetry? covers the read path this post writes back against, and Serial Studio Isn't Just for Serial Ports tours the transports these commands travel over.

Comments

Copied to clipboard!