Watlow heaters¶
Audience: operators and config authors using Watlow EZ-Zone controllers as the rig's heater control loop.
Scope: capa's watlowlib adapter — [devices.params] fields, channel binding kind, command surface, discovery, and the rig-specific quirks the source docstrings already document.
At a glance¶
| Adapter id | capa.devices.watlow |
| Sibling library | watlowlib |
| Real adapter | capa.devices.watlow |
| Sim adapter | capa.devices.sim.watlow_sim |
| Resource scheme | serial:<port> — one worker per COM port |
| Channel binding | watlow_parameter |
| Emission shape | long_row — one row per (parameter, instance) per tick |
| Default poll rate | 1 Hz (Std Bus) — see rate_hz |
Supported hardware¶
Watlow EZ-Zone PM-series controllers over Standard Bus or Modbus RTU, addressed 1–16 (Std Bus) or 1–247 (Modbus). The full protocol scope — which parameters are readable, which are writable, the registry of parameter IDs — is owned by watlowlib; capa's adapter is a thin shim over it.
Configuration¶
Every key under [devices.params] is parsed by
WatlowAdapterParams
(Pydantic, extra="forbid" — typos are rejected, not silently dropped).
[[devices]]
name = "heater"
adapter = "capa.devices.watlow"
[devices.params]
port = "COM6"
address = 1
protocol = "stdbus" # "stdbus" | "modbus_rtu" | "auto"
parameters = ["process_value", "setpoint"]
instances = [1]
rate_hz = 1.0
auto_reconnect = true
snapshot_period_s = 30.0
identify_on_open = true
wire_temperature_unit = "F" # see Quirks
| Key | Default | Notes |
|---|---|---|
port |
required | COM6, /dev/ttyUSB0, or fake://... for tests against a FakeTransport. |
address |
1 |
Std Bus accepts 1–16; Modbus RTU accepts 1–247. |
protocol |
"stdbus" |
"auto" runs watlowlib's conservative detector; pin one in production. |
baudrate / parity / stopbits / bytesize |
38400 / "none" / 1 / 8 |
EZ-Zone factory defaults. |
parameters |
("process_value", "setpoint") |
Polled each tick. Names must resolve in watlowlib.PARAMETERS. |
instances |
(1,) |
1-indexed loop selectors. Single-loop devices stay at (1,). |
rate_hz |
1.0 (gt 0, le 100) |
Production sits at 1–5 Hz. The 100 Hz cap exists to catch typos and to give unit tests a fast path; real Std Bus round-trips ~50 ms per parameter, so even Modbus tops out near 10 Hz for two-parameter polls. |
auto_reconnect |
true |
Transient WatlowConnectionErrors are logged and retried on the next tick instead of ending the stream. |
snapshot_period_s |
30.0 |
Cadence of DeviceSnapshot health pings. |
identify_on_open |
true |
Run Controller.identify after open to populate the cached DeviceInfo. |
io_timeout_s |
None (inherits watlowlib 1.0 s) |
Per-call timeout for set_setpoint / write_parameter / read_pv / identify. Bump to 2.0–3.0 s for slow USB-RS485 bridges. Streaming reads ignore this knob. |
wire_temperature_unit |
"F" |
Operator-asserted wire scale. See Quirks — this is the most common bring-up footgun. |
Channels and emission shape¶
Watlow is the long-row family:
watlowlib.streaming.Sample
emits one row per (parameter, instance) per tick. capa preserves that
shape verbatim into device_records/watlow.parquet via
watlowlib.sinks.sample_to_row.
The only supported binding source is watlow_parameter:
[[channels]]
name = "heater.pv"
kind = "process_var"
unit = "degF"
derived_unit = "degC"
plot_group = "temperatures"
[channels.source]
source = "watlow_parameter"
device = "heater"
parameter = "process_value"
instance = 1
[channels.calibration]
kind = "linear_two_point"
input_unit = "degF"
output_unit = "degC"
ref_low_raw = 32.0
ref_low_value = 0.0
ref_high_raw = 212.0
ref_high_value = 100.0
The channel's unit is the wire scale; derived_unit is what
operators see in plots and what the manual setpoint widget accepts.
Setpoint commands flow the calibration backwards — the operator types a
Celsius value, the adapter writes the Fahrenheit wire equivalent.
See channel bindings for the
selector contract and devices overview § emission
shapes for how long_row differs from the
other shapes.
Capability flags¶
The Watlow descriptor declares:
| Flag | Used by |
|---|---|
HAS_SETPOINT |
Manual-control setpoint widget. |
HAS_RAMP |
Method-step ramp UI. |
READS_PROCESS_VAR |
Plot binding for PV channels. |
HAS_PARAMETER_CONFIG |
Diagnostic dock's parameter-read panel. |
It does not declare SUPPORTS_AUTO_RECONNECT even though
auto_reconnect=True is the default — the flag is reserved for adapters
where the operator can rely on reconnect without losing samples.
Watlow's reconnect silently drops the in-flight tick.
Commands¶
The adapter exposes typed wrappers for IDE help and refactor safety;
the generic command() form exists so plugins that don't import
WatlowAdapter can still drive it.
| Typed call | DeviceCommand.kind |
Payload |
|---|---|---|
set_setpoint(value, instance=1) |
"set_setpoint" |
{"value": float, "instance": int} |
write_parameter(name, value, instance=1) |
"write_parameter" / "set_parameter" |
{"name": str, "value": float \| int, "instance": int} |
set_display_units(unit) |
"set_display_units" |
{"unit": str} |
read_state_snapshot() |
n/a | Used by the manual-control card; not gated. |
Every write goes through the authorization
gate — issued_by is required, plus
either authorization_id (run-arm cover) or confirmed_by (manual
confirmation). Procedures should issue commands through
ctx.dispatcher, not directly on the adapter, so the per-resource
worker serializes them on shared serial buses.
set_setpoint inverts the bound channel's calibration before writing —
operators set "600 °C", the adapter writes "1112 °F" if the wire is
Fahrenheit. This is a no-op for identity calibrations and for raw
commands issued outside any bound channel.
Discovery and handshake¶
Both hooks are read-only — they open + identify + close without
touching any control surface.
discover(ports=..., addresses=..., baudrates=..., timeout_s=0.5) is a
thin wrapper over
watlowlib.find_devices
that iterates the cartesian product of
ports × baudrates × protocols × addresses. Defaults are
addresses=(1,) and baudrates=(38400, 19200, 9600) — CAPA rigs
universally leave Watlows at address 1, so the address sweep is
single-shot. Pass addresses=range(1, 248) for a slow exhaustive scan.
Results are deduped on (port, address), first-hit-wins, so a bus that
answers at multiple bauds or protocols produces one row per physical
controller (the most-likely production config, since find_devices
iterates baud 38400 → 19200 → 9600 and protocol STDBUS → MODBUS_RTU
outermost-first).
handshake(params) is the per-device form capa validate --strict
runs. It returns a one-line identity summary or raises AdapterError
so the operator sees wiring problems before arming a run.
See Discovery for the cross-cutting UX.
Quirks¶
Wire-unit assertion (wire_temperature_unit)¶
This is the most common bring-up footgun and the only Watlow setting that needs operator-level validation.
watlowlib 0.4+ requires the caller to assert the temperature scale on the wire. The rig's PM3R1CA (fw=1) was empirically verified to emit Fahrenheit on the wire regardless of panel-display configuration: with the panel set to °C, the panel reads PV=18.7 and comms reports 65.65 — 65.65 °F = 18.69 °C. Parameter 3005 controls the panel display only; parameter 17050 is label-only on at least one firmware revision. The wire scale appears to be hardware-pinned on this SKU.
If you bring up a different PM3 (or a different SKU/firmware), verify
empirically with watlow-diag probe-unit (compare a panel reading to
the comms readback) before pinning. Set wire_temperature_unit = "C"
or "F" based on the result. Setting it to None suppresses the
assertion entirely; Sample.unit will be None for temperature
parameters and the per-channel drift check becomes a no-op.
Poll-rate ceiling¶
rate_hz accepts up to 100 but production sits at 1–5 Hz. Std Bus
round-trips ~50 ms per parameter on the PM family, so even Modbus tops
out near 10 Hz for a two-parameter poll. The 100 Hz upper bound exists
to catch typos (rate_hz=1000) and to give the unit tests a fast path
against an in-process stub controller.
Auto-reconnect drops the tick¶
auto_reconnect=True (the default) means transient
WatlowConnectionErrors do not terminate the stream. The recorder
retries on the next tick — but the tick that hit the error is gone,
not buffered. For a typical 1 Hz heater this is invisible; for higher
rates or a flaky bus, watch the saturation pill and the events.sqlite
device-event entries.
Sim equivalent¶
capa.devices.sim.watlow_sim
mirrors the same long_row shape and the same capability set
minus HAS_PARAMETER_CONFIG. Signal keys follow the
"<parameter>/<instance>" schema ("process_value/1", "setpoint/1");
bare "process_value" defaults to instance 1. The sim's
parameter_units knob coerces watlowlib aliases ("C", "degC",
"%") into the right enum value; unknown strings pass through
verbatim as a cross-vendor escape hatch. See Simulators
for the full signal schema.
See also¶
- Devices overview — adapter contract, capability enum, resource grouping.
- Hardware TOML — how the
[[devices]]block is parsed. - Channel bindings — the
watlow_parametersource schema. - Authorization gates — the contract for any device write.
- Discovery — cross-cutting Setup-tab and CLI behavior.
- watlowlib docs — parameter registry, PID tuning, protocol-level reference.