Skip to content

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.

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.

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 5
MEAS:VOLT:DC? <- Data: sent to instrument at address 5
++read eoi <- Command: read from instrument until EOI

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

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.

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

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.

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.

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:

ModeBehavior
auto 0Manual — read only when ++read is sent
auto 1Prologix compatible — read after every data send
auto 2On query — read after commands ending with ?
auto 3Continuous — 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.

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.

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.

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.

ValueTermination
0CR+LF
1CR only
2LF only
3None (EOI only)

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.

The ++read command has several forms:

SyntaxBehavior
++read eoiRead until EOI is asserted (most reliable)
++read 10Read until character 10 (LF) is received
++readRead 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.

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.

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., ++read with no talker), the adapter becomes unresponsive.

To clear macro 0: ++macro 0 clear (or ++macro 0 del).

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.

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 adds several commands that the original Prologix adapter does not support:

CommandPurpose
++findlstnFind 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
++dclSend Universal Device Clear to all devices
++ppollConduct a parallel poll
++xdiag mode byteBus diagnostics — write a byte directly to data or control bus
++macro N [set|del]Manage stored macros (0—9, 128 bytes each)
++defaultReset 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:

  • ++findlstn for discover_instruments — much more efficient than polling each address individually
  • ++allspoll for polling all instruments in a single command
  • ++findrqs for identifying the SRQ source
  • ++dcl for universal device clear
  • ++ppoll for parallel poll operations
  • ++xdiag for hardware-level bus diagnostics via ar488_diagnostic