Skip to content

Multi-Bridge Setup

A single AR488 bridge controls one GPIB bus with up to 30 instruments. When your equipment spans multiple buses — separate test benches, different rooms, or a mix of USB and WiFi connections — mcgpib manages them all from one server. This tutorial shows you how to configure two bridges and run cross-bus operations.

Common scenarios:

  • Separate test benches — one bench for analog measurements, another for RF or power. Each has its own GPIB bus and bridge.
  • Mixed transports — a USB-connected bridge on the bench in front of you, and a WiFi bridge on a rack across the lab.
  • Isolation — sensitive measurement equipment on one bus, noisy power supplies on another, to avoid bus contention and ground loops.
  • Scale — more than 30 instruments. Each GPIB bus supports 30 addresses, so a second bridge doubles your capacity.

Each bridge operates independently. The server talks to them concurrently when they are on separate buses, and serializes commands within each bridge using per-bridge locks.

Create or update your configuration file with two [[bridge]] sections. The double brackets in TOML denote an array — each [[bridge]] block defines one bridge.

~/.config/mcgpib/config.toml
[server]
log_level = "INFO"
# USB-connected bridge on the local bench
[[bridge]]
name = "bench-a"
transport = "serial"
port = "/dev/ttyUSB0"
baudrate = 115200
auto_scan = true
auto_identify = true
read_timeout_ms = 3000
inter_command_delay_ms = 10
# WiFi-connected bridge on the far rack
[[bridge]]
name = "rack-b"
transport = "tcp"
host = "192.168.1.50"
tcp_port = 23
auto_scan = true
auto_identify = true
read_timeout_ms = 5000
inter_command_delay_ms = 20

A few things to note:

  • Each bridge must have a unique name. This is how you address them in every tool call. Pick names that reflect their physical location or purpose.
  • Transport settings are per-bridge. You can mix serial and TCP bridges freely.
  • Timeouts are independent. The WiFi bridge (rack-b) has longer timeouts because wireless adds latency. The USB bridge (bench-a) uses tighter defaults.
  1. List configured bridges

    Start a Claude Code session and verify the server sees both bridges:

    > List my GPIB bridges

    Claude calls list_bridges():

    Bridges:
    bench-a: serial (/dev/ttyUSB0) [disconnected]
    rack-b: tcp (192.168.1.50:23) [disconnected]

    Both bridges are configured but not yet connected.

  2. Connect the first bridge

    > Connect to bench-a

    Claude calls connect_bridge("bench-a"):

    Connected to bench-a: AR488 GPIB controller 0.05.99
    Discovered 2 instrument(s):
    Address 5: KEITHLEY INSTRUMENTS INC. 2000
    Address 22: Agilent Technologies E3631A
  3. Connect the second bridge

    > Connect to rack-b too

    Claude calls connect_bridge("rack-b"):

    Connected to rack-b: AR488 GPIB controller 0.05.99
    Discovered 2 instrument(s):
    Address 1: HEWLETT-PACKARD 34401A
    Address 10: Tektronix TDS 2024B
  4. View all instruments across both buses

    > Show me all instruments

    Claude calls list_instruments() (with no bridge filter):

    Discovered instruments:
    [bench-a] Address 5: KEITHLEY INSTRUMENTS INC. 2000
    [bench-a] Address 22: Agilent Technologies E3631A
    [rack-b] Address 1: HEWLETT-PACKARD 34401A
    [rack-b] Address 10: Tektronix TDS 2024B

    The bridge name prefix ([bench-a], [rack-b]) tells you which bus each instrument is on.

With both bridges connected, you can orchestrate workflows that span buses. The bridge_name parameter in every tool call routes the command to the correct bridge.

Here is a practical example: read a voltage on bench-a while adjusting a power supply on rack-b. Suppose you are characterizing how a device under test responds to supply voltage changes.

  1. Set the power supply output

    The Agilent E3631A at address 22 on bench-a has three outputs. Set the +6V output to 3.3V:

    > Set the E3631A on bench-a to output 3.3V on the +6V channel

    Claude calls:

    • instrument_write("bench-a", 22, "INST P6V") — select the +6V output
    • instrument_write("bench-a", 22, "VOLT 3.3") — set voltage to 3.3V
    • instrument_write("bench-a", 22, "OUTP ON") — enable the output
    Sent to address 22: INST P6V
    Sent to address 22: VOLT 3.3
    Sent to address 22: OUTP ON
  2. Read the resulting voltage on the other bus

    The HP 34401A at address 1 on rack-b is probing the device under test:

    > Read DC voltage on the HP 34401A on rack-b

    Claude calls:

    • instrument_write("rack-b", 1, "CONF:VOLT:DC")
    • instrument_query("rack-b", 1, "READ?")
    Sent to address 1: CONF:VOLT:DC
    +3.29120E+00

    3.291V measured at the DUT — reasonable for a 3.3V supply with some drop across the circuit.

  3. Sweep and measure

    You can ask Claude to automate the full sequence:

    > Sweep the E3631A supply voltage from 3.0V to 5.0V in 0.5V steps.
    > At each step, wait 500ms for settling, then read the voltage on
    > the HP 34401A. Show me a table of set voltage vs measured voltage.

    Claude will interleave instrument_write calls on bench-a with instrument_query calls on rack-b, building a table as it goes:

    | Set (V) | Measured (V) |
    |---------|-------------|
    | 3.0 | 2.987 |
    | 3.5 | 3.491 |
    | 4.0 | 3.993 |
    | 4.5 | 4.488 |
    | 5.0 | 4.982 |

mcgpib includes a bus_health_check prompt that audits all bridges in a single pass. This is useful for verifying your multi-bridge setup is healthy.

> Run a bus health check

Claude activates the bus_health_check prompt and executes a systematic sequence:

  1. Calls list_bridges() to enumerate all configured bridges
  2. For each connected bridge, calls bus_scan() and check_srq()
  3. For disconnected bridges, attempts connect_bridge()
  4. Calls serial_poll() on each bridge to check for instruments requesting service

The resulting report looks something like:

GPIB Infrastructure Health Report
==================================
Bridges: 2 configured, 2 connected, 0 failed
bench-a (serial /dev/ttyUSB0):
Firmware: AR488 GPIB controller 0.05.99
Instruments: 2 discovered, 2 identified
Address 5: KEITHLEY INSTRUMENTS INC. 2000 — status 0x00 (idle)
Address 22: Agilent Technologies E3631A — status 0x00 (idle)
SRQ: not asserted
rack-b (tcp 192.168.1.50:23):
Firmware: AR488 GPIB controller 0.05.99
Instruments: 2 discovered, 2 identified
Address 1: HEWLETT-PACKARD 34401A — status 0x00 (idle)
Address 10: Tektronix TDS 2024B — status 0x40 [SRQ]
SRQ: ASSERTED — device at address 10 requesting service
Recommendations:
- Address 10 on rack-b has SRQ asserted. Run serial_poll("rack-b", 10)
and check *ESR? to determine the cause.

Different instruments and transports need different timing. You can adjust timeouts at runtime without restarting the server using configure_bridge.

  1. Increase timeout for a slow instrument

    Some instruments — particularly older HP meters or anything doing an internal self-test — take several seconds to respond. If you are getting timeout errors on rack-b:

    > Set the read timeout on rack-b to 10 seconds

    Claude calls configure_bridge("rack-b", read_timeout_ms=10000):

    Configured rack-b: read_timeout_ms=10000

    This takes effect immediately for all subsequent operations on that bridge. Other bridges are not affected.

  2. Tune EOS settings for a specific instrument

    Some instruments expect line endings different from the default CRLF. The eos parameter controls what the bridge appends when sending data:

    ValueLine ending
    0CR+LF (default)
    1CR only
    2LF only
    3None
    > Set bench-a to use LF line endings

    Claude calls configure_bridge("bench-a", eos=2):

    Configured bench-a: eos=2
  3. View current bridge configuration

    To check what a bridge is currently configured to:

    > Show me the status of rack-b

    Claude calls bridge_status("rack-b"):

    {
    "name": "rack-b",
    "transport_type": "tcp",
    "connection_state": "connected",
    "firmware_version": "AR488 GPIB controller 0.05.99",
    "controller_address": 0,
    "current_address": 1,
    "read_timeout_ms": 10000,
    "tcp_host": "192.168.1.50",
    "tcp_port": 23
    }

Understanding how mcgpib handles concurrent access helps you reason about what is safe to do in multi-bridge setups.

Across bridges: concurrent. Each bridge has its own asyncio lock and its own transport (serial port or TCP socket). Operations on bench-a and rack-b can proceed simultaneously without blocking each other.

Within a bridge: serialized. GPIB is a shared bus — only one device can talk at a time. The per-bridge lock ensures that if two tool calls target the same bridge, one waits for the other to complete. You cannot corrupt bus state by making overlapping requests.

Practical implication: If Claude needs to read a voltage on bench-a and set a current limit on rack-b, those two operations can overlap. But if Claude needs to write a command and then read a response on the same bridge, those are always sequential — which is exactly how GPIB works.