Skip to content

Quickstart — async

The async core is open_device, Controller, parameter read / write, and identify. The example below uses FakeTransport so it runs without hardware. Replace open_controller(transport, ...) with open_device("/dev/ttyUSB0", protocol=ProtocolKind.STDBUS, address=1) to talk to a live PM3.

Read PV via FakeTransport

import anyio

from watlowlib import (
    FakeTransport,
    ProtocolKind,
    SerialSettings,
    open_controller,
)

# Captured PM3 round-trip: read PV (parameter 4001) at MAC 0x10
REQ_READ_PV = bytes.fromhex("55 FF 05 10 00 00 06 E8 01 03 01 04 01 01 E3 99")
RSP_READ_PV = bytes.fromhex(
    "55 FF 06 00 10 00 0B 88 02 03 01 04 01 01 08 45 1E 3C D4 A7 28"
)


async def main() -> None:
    transport = FakeTransport({REQ_READ_PV: RSP_READ_PV})
    controller = await open_controller(
        transport,
        protocol=ProtocolKind.STDBUS,
        address=1,
        serial_settings=SerialSettings(port="fake://demo"),
    )
    async with controller as ctl:
        reading = await ctl.read_pv()
        print(reading)
        # → Reading(value=2531.78..., unit=None, ..., protocol=ProtocolKind.STDBUS)


anyio.run(main)

Live serial — replace one line

from watlowlib import ProtocolKind, open_device

controller = await open_device(
    "/dev/ttyUSB0",
    protocol=ProtocolKind.STDBUS,
    address=1,
)
async with controller as ctl:
    info = await ctl.identify()
    pv = await ctl.read_pv()
    await ctl.set_setpoint(75.0, confirm=True)

Persistent writes need confirm=True

SafetyTier.PERSISTENT parameters (RW / RWE / RWES) require an explicit confirm=True at the facade. The session raises WatlowConfirmationRequiredError before any I/O if the gate is missing — so a forgotten confirm never reaches the controller.

await ctl.set_setpoint(75.0)              # WatlowConfirmationRequiredError
await ctl.set_setpoint(75.0, confirm=True)  # writes; returns Reading echo

Modbus RTU

The same read_parameter("setpoint") works against a controller switched to Modbus front-panel mode with no user-facing code changes — just pass protocol=ProtocolKind.MODBUS_RTU at open time.