AR488 Protocol
The AR488 firmware on the ESP32 acts as a backend between a serial/TCP text interface and the GPIB bus. All communication between mcpyvisa (through the @ar488 pyvisa backend) and the adapter uses a simple line-oriented ASCII protocol where commands to the adapter are prefixed with ++ and everything else is sent directly to the GPIB bus.
Prologix heritage
Section titled “Prologix heritage”The AR488 command set is based on the Prologix GPIB-USB adapter, a commercial product that established the ++ prefix convention for GPIB controller commands. Prologix adapters have been widely used since the mid-2000s, and many GPIB software tools support the protocol.
AR488 implements the full Prologix command set and extends it with additional features. This means software written for Prologix adapters generally works with AR488, and vice versa.
Command vs data mode
Section titled “Command vs data mode”The protocol has a simple rule: lines starting with ++ are commands to the adapter itself. Everything else is data that gets sent to the currently addressed instrument on the GPIB bus.
++addr 5 <- Command: set current GPIB address to 5MEAS:VOLT:DC? <- Data: sent to instrument at address 5++read eoi <- Command: read from instrument until EOIThere is no escape mechanism. If you need to send data to an instrument that literally starts with ++, you are out of luck (though no known instrument uses such a protocol).
How mcpyvisa uses the protocol
Section titled “How mcpyvisa uses the protocol”When using the @ar488 backend, mcpyvisa’s pyvisa-ar488 package translates standard VISA operations into AR488 ++ command sequences. Here is what happens when the LLM calls instrument_query("dmm", "MEAS:VOLT:DC?") where dmm resolves to GPIB0::22::INSTR on an AR488 backend:
sequenceDiagram
participant M as mcpyvisa
participant PV as pyvisa-ar488
participant AR as AR488 Adapter
participant BUS as GPIB Bus
participant I as Instrument<br/>(addr 22)
M->>PV: resource.query("MEAS:VOLT:DC?")
PV->>AR: ++addr 22
Note over PV,AR: inter-command delay
PV->>AR: MEAS:VOLT:DC?
AR->>BUS: ATN + address 22
AR->>BUS: Send query string
Note over PV,AR: inter-command delay
PV->>AR: ++read eoi
AR->>BUS: Request data (NRFD/NDAC handshake)
I-->>BUS: +4.23451E+00 [EOI]
AR-->>PV: +4.23451E+00
PV-->>M: "+4.23451E+00"
The pyvisa-ar488 backend handles the ++addr, data send, and ++read eoi sequence transparently. From mcpyvisa’s perspective, it is just calling resource.query() on a standard pyvisa resource.
For instrument_write (no response expected), the ++read step is omitted — the backend sends the command but does not request a response.
The init sequence explained
Section titled “The init sequence explained”When the @ar488 backend connects to an adapter, the pyvisa-ar488 library sends a carefully ordered series of ++ commands to put the adapter in a known state. Each command addresses a specific requirement:
graph TD
START(["Connect transport"])
DRAIN1["Drain buffer<br/>(clear macro 0 output)"]
VERBOSE["++verbose 0"]
DRAIN2["Drain buffer<br/>(clear verbose response)"]
PROMPT["++prompt 0"]
AUTO["++auto 0"]
MODE["++mode 1"]
EOI["++eoi 1"]
EOS["++eos 0"]
TMO["++read_tmo_ms N"]
VER["++ver"]
CHECK{"Valid firmware<br/>version?"}
OK(["Adapter ready"])
FAIL(["InitError"])
START --> DRAIN1
DRAIN1 --> VERBOSE
VERBOSE --> DRAIN2
DRAIN2 --> PROMPT
PROMPT --> AUTO
AUTO --> MODE
MODE --> EOI
EOI --> EOS
EOS --> TMO
TMO --> VER
VER --> CHECK
CHECK -- "Yes" --> OK
CHECK -- "Timeout" --> FAIL
style START fill:#78350f,stroke:#d97706,color:#fde68a
style OK fill:#166534,stroke:#22c55e,color:#dcfce7
style FAIL fill:#7f1d1d,stroke:#ef4444,color:#fecaca
style CHECK fill:#78350f,stroke:#d97706,color:#fde68a
style DRAIN1 fill:#334155,stroke:#94a3b8,color:#e2e8f0
style VERBOSE fill:#334155,stroke:#94a3b8,color:#e2e8f0
style DRAIN2 fill:#334155,stroke:#94a3b8,color:#e2e8f0
style PROMPT fill:#334155,stroke:#94a3b8,color:#e2e8f0
style AUTO fill:#334155,stroke:#94a3b8,color:#e2e8f0
style MODE fill:#334155,stroke:#94a3b8,color:#e2e8f0
style EOI fill:#334155,stroke:#94a3b8,color:#e2e8f0
style EOS fill:#334155,stroke:#94a3b8,color:#e2e8f0
style TMO fill:#334155,stroke:#94a3b8,color:#e2e8f0
style VER fill:#334155,stroke:#94a3b8,color:#e2e8f0
++verbose 0
Section titled “++verbose 0”Purpose: Machine-parseable output.
With verbose mode on, the adapter returns human-readable strings like Current address: 5. With verbose off, it returns just 5. pyvisa-ar488 needs to parse responses programmatically, so verbose must be off.
After sending this command, the backend drains the buffer because the ++verbose command itself might produce output in the old (verbose) format.
++prompt 0
Section titled “++prompt 0”Purpose: Clean response lines.
When the prompt is enabled, the adapter prepends a prompt character to its output, which would corrupt parsed responses. Disabling it ensures responses contain only data.
++auto 0
Section titled “++auto 0”Purpose: Explicit read control.
This is the most important setting. Auto mode controls whether the adapter automatically reads from the GPIB bus after sending data:
| Mode | Behavior |
|---|---|
auto 0 | Manual — read only when ++read is sent |
auto 1 | Prologix compatible — read after every data send |
auto 2 | On query — read after commands ending with ? |
auto 3 | Continuous — read repeatedly |
pyvisa-ar488 uses auto 0 because:
- Write commands produce no response. In auto mode, the adapter would wait for a response that never comes, causing a timeout on every write operation.
- Timing control. The backend needs to decide exactly when to read, especially when inserting inter-command delays or handling slow instruments.
- Multi-step operations. Sequences like “address, send, delay, read” require fine-grained control that auto mode does not provide.
++mode 1
Section titled “++mode 1”Purpose: Controller mode.
Mode 1 sets the AR488 as a GPIB controller (as opposed to mode 0, which makes it act as a GPIB device). As controller, the AR488 can address instruments, initiate data transfers, and assert bus management signals like IFC and REN.
++eoi 1
Section titled “++eoi 1”Purpose: Proper message termination.
EOI (End Or Identify) is asserted on the last byte of every GPIB message. This tells the receiving device “this is the end of my message.” Without EOI, the receiver has to guess when a message ends based on a termination character — which is unreliable for binary data and can cause problems with instruments that embed newlines in their responses.
++eos 0
Section titled “++eos 0”Purpose: Consistent line endings.
EOS (End Of Send) controls what termination characters the adapter appends when sending data to the GPIB bus. Setting 0 appends CR+LF, which is the most commonly expected termination across instruments.
| Value | Termination |
|---|---|
| 0 | CR+LF |
| 1 | CR only |
| 2 | LF only |
| 3 | None (EOI only) |
++read_tmo_ms N
Section titled “++read_tmo_ms N”Purpose: Read timeout.
Sets how long the adapter waits for data when ++read is issued. If the instrument does not respond within this time, the adapter returns nothing and pyvisa-ar488 raises a timeout error. The value comes from the backend’s configuration.
Purpose: Communication verification.
After all settings are applied, the backend sends ++ver and reads the response. A valid firmware version string confirms that the adapter is communicating and processing commands correctly. If this step times out, the init fails — something is wrong with the connection.
Read modes in detail
Section titled “Read modes in detail”The ++read command has several forms:
| Syntax | Behavior |
|---|---|
++read eoi | Read until EOI is asserted (most reliable) |
++read 10 | Read until character 10 (LF) is received |
++read | Read until timeout |
pyvisa-ar488 always uses ++read eoi because EOI provides an unambiguous end-of-message signal. Relying on character matching can fail when instrument data contains the termination character.
Key gotchas
Section titled “Key gotchas”Brownout detector
Section titled “Brownout detector”The ESP32’s brownout detector is disabled in the AR488 firmware (RTC_CNTL_BROWN_OUT_REG is cleared in setup()). GPIB bus activity causes current spikes through the SN75160/SN75161 transceiver ICs, which can dip the ESP32’s supply voltage below the brownout threshold. With the detector enabled, these dips would cause spontaneous resets.
This means the ESP32 will not self-protect against severe undervoltage. If powered from a weak USB source, it may silently corrupt data or lock up instead of cleanly resetting. Use a reliable power source.
Macro 0 auto-execution
Section titled “Macro 0 auto-execution”AR488 supports up to 10 macros (0—9) stored in NVS flash. Macro 0 executes automatically at startup. This is useful for instrument initialization (e.g., setting up a multimeter for a default measurement mode), but it can cause problems:
- If macro 0 sends output, it fills the receive buffer before the backend connects. The init sequence drains this buffer, but unexpected data can still cause timing issues.
- If macro 0 contains commands that change the adapter’s mode or settings, the init sequence may conflict with them.
- If macro 0 sends commands that hang (e.g.,
++readwith no talker), the adapter becomes unresponsive.
To clear macro 0: ++macro 0 clear (or ++macro 0 del).
++savecfg persistence
Section titled “++savecfg persistence”The ++savecfg command writes the current adapter configuration (mode, auto, eoi, eos, address, timeout, etc.) to the ESP32’s NVS flash. This survives power cycles.
The risk: if the init sequence changes a setting (which it always does), and someone sends ++savecfg, the adapter’s power-on defaults will reflect mcpyvisa’s settings — which may not be what you want for manual use with a serial terminal.
mcpyvisa never sends ++savecfg automatically. If you use the ar488_command tool to send it, be aware that you are changing the adapter’s power-on behavior.
Verbose mode changes output format
Section titled “Verbose mode changes output format”When ++verbose 1 is set, commands like ++addr return Current address: 5 instead of 5. If verbose mode is accidentally enabled (e.g., by macro 0 or a previous session), the response parser will fail because it expects numeric values.
The init sequence sets ++verbose 0 first, then drains the buffer, specifically to handle this case.
AR488 extensions beyond Prologix
Section titled “AR488 extensions beyond Prologix”AR488 adds several commands that the original Prologix adapter does not support:
| Command | Purpose |
|---|---|
++findlstn | Find all listeners on the bus (returns space-separated addresses) |
++findrqs addr [addr ...] | Find which device is requesting service |
++allspoll addr [addr ...] | Serial poll multiple addresses in one operation |
++dcl | Send Universal Device Clear to all devices |
++ppoll | Conduct a parallel poll |
++xdiag mode byte | Bus diagnostics — write a byte directly to data or control bus |
++macro N [set|del] | Manage stored macros (0—9, 128 bytes each) |
++default | Reset to factory default configuration |
++id name [S] | Set/get the interface name |
++id serial [N] | Set/get the interface serial number |
++id verstr [S] | Set/get a custom version string |
++prompt [0|1] | Show/hide the command prompt |
++ren [0|1] | Assert/deassert Remote Enable |
mcpyvisa uses several of these extensions through the AR488 tools:
++findlstnfordiscover_instruments— much more efficient than polling each address individually++allspollfor polling all instruments in a single command++findrqsfor identifying the SRQ source++dclfor universal device clear++ppollfor parallel poll operations++xdiagfor hardware-level bus diagnostics viaar488_diagnostic