Metadata-Version: 2.5
Name: plesty-k2410-source-meter
Version: 0.1.0
Summary: Plesty Keithley 2410 SourceMeter (SMU) device.
Author-email: Yunshuang Yuan <yuanyunshuang@163.com>
Maintainer-email: Plesty Development Team <plesty.dev@example.com>
License-Expression: LGPL-3.0-or-later
License-File: COPYING
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: plesty-lib>=0.5.0
Requires-Dist: pyvisa>=1.14
Provides-Extra: gui
Requires-Dist: plesty-lib[gui]>=0.5.0; extra == 'gui'
Description-Content-Type: text/markdown

# PLESTY Keithley 2410 SourceMeter

A PLESTY device module for the Keithley 2410 SourceMeter, a high-voltage source-measure unit
(SMU). An SMU sources a voltage or a current and measures both at the same time. The 2410
sources up to 1100 V or 1.05 A and talks SCPI (the standard text command language of
laboratory instruments) over VISA (the standard software layer for GPIB, serial and USB
instruments) on GPIB or RS-232.

The source can put hazardous voltage on its terminals, so this module refuses before it acts.
Read [Safety rules](#safety-rules) before switching anything on.

This module replaces an older PLEXY driver for the same instrument. It is not
backwards-compatible with it.

## Supported hardware

| Instrument | Link | Notes |
|---|---|---|
| Keithley 2410 SourceMeter | GPIB or RS-232 | The preflight check requires `MODEL 2410` and refuses other models |

## Installation

```bash
git clone https://gitlab.com/plesty/hub/devices/keithley/plesty-k2410-source-meter.git
cd plesty-k2410-source-meter
uv sync
```

A VISA backend must be available to talk to a real instrument (for example NI-VISA, plus a
GPIB adapter for a GPIB link). The `mock` address needs none.

## Configuration

The address of the instrument is configuration, not code. Copy `.env.example` to `.env` and
edit it:

| Variable | Default | Meaning |
|---|---|---|
| `K2410_ADDRESS` | `GPIB0::24::INSTR` | VISA resource name of the instrument |
| `K2410_TCP_PORT` | `5555` | ZMQ port the device server binds for remote clients |

- **GPIB**: `GPIB0::<n>::INSTR`. The factory GPIB address is 24; the front panel shows the
  current one under MENU > COMMUNICATION > GPIB.
- **RS-232**: `ASRL<n>::INSTR`. Set the instrument to 9600 baud, 8N1, CR terminator.
- **mock**: the word `mock` runs an in-memory simulator of the instrument, with no hardware.

Each value resolves as CLI argument, then environment variable.

## Quick start

Run the device as a TCP server:

```bash
cp .env.example .env   # set K2410_ADDRESS
uv run python -m plesty.k2410_source_meter
uv run python -m plesty.k2410_source_meter --address ASRL3::INSTR --tcp-port 5555
uv run python -m plesty.k2410_source_meter --address mock   # simulator
```

Use it from Python (local, on the simulator):

```python
from plesty.k2410_source_meter import Device

dev = Device(address="mock")  # a VISA address such as "GPIB0::24::INSTR" for the instrument
dev.init()
dev.connect()
reading = dev.source_and_measure(
    source_function="VOLT", level=5.0, compliance=1e-3, source_range=20.0
)
print(reading["voltage"], reading["current"])
dev.disconnect()
```

## Preflight

Before the server binds its port, the module opens the configured address, reads the
identification string (`*IDN?`) and requires `MODEL 2410`. When the configured address does
not answer as a 2410 but another GPIB address does, the message names that address and tells
you to set `K2410_ADDRESS` to it; the address is never changed for you. `--skip-preflight`
serves anyway, for protocol work.

## Safety rules

The 2410 answers a bad setting with an error-queue entry only after it has acted on the
neighbouring ones, which is too late for a source that may carry 1100 V. So every rule below
is checked in software before the first command is sent; a violation raises a refusal and
sends nothing.

- **Explicit compliance.** The compliance is the limit on the quantity that is not sourced
  (amperes while sourcing voltage, volts while sourcing current). `output_on` is refused until
  `configure_source` set one explicitly since connect. There is no default compliance.
- **`voltage_limit` defaults to 20 V.** This is the instrument's hardware over-voltage
  protection. It is applied as 20 V on connect, so any higher voltage needs a deliberate write
  of `voltage_limit` first, while the output is off. A level above it is refused.
- **`output_on` ramps from 0 V.** It switches the output on at 0 and steps up to the
  configured level (steps of at most 10 V, a tenth of the range, with a short wait between
  steps). A warning is logged when `voltage_limit` is above 30 V.
- **Operating boundary.** Above 21 V the current compliance must be 21 mA or less; above 21 mA
  the voltage compliance must be 21 V or less.
- **Compliance floor.** A compliance may not be below 0.1 % of the measure range in force.
- **Sourced quantity on the source range.** The 2410 measures the quantity it sources on the
  source range and rejects a measure range for it (error 823). So `voltage_measure_range` and
  its `_auto` flag are refused while sourcing voltage, and the current ones while sourcing
  current; set `voltage_range` or `current_range` instead.
- **No reading with the output off.** `measure` never switches the output on.
- **Stepped level changes.** With the output on, `set_level` walks to the new level in steps.
- **Refusals when unsafe.** `output_on` is also refused while the instrument's error queue is
  not empty or, when `interlock` is on, while the interlock line is open.
- **Shutdown on failure.** `safe_shutdown` walks the level to zero in steps and then switches
  the output off. Every failure with the output on, and `disconnect`, do the same. A refusal
  changes nothing, so it does not shut down.

## Parameters

Read with `dev.query(name)` and write with `dev.write(name, value)`; `dev.state` returns all
of them at once. Values are in volts, amperes and seconds.

| Parameter | Type | Range / options | Default | Meaning |
|---|---|---|---|---|
| `source_function` | str | `VOLT`, `CURR` | `VOLT` | What is sourced |
| `voltage_level` | float, V | -1100 to 1100 | 0 | Level when sourcing voltage |
| `current_level` | float, A | -1.05 to 1.05 | 0 | Level when sourcing current |
| `voltage_range` | float, V | 0.2 to 1100 | 20 | Voltage source range |
| `voltage_range_auto` | bool | | off | Instrument picks the range |
| `current_range` | float, A | 1e-6 to 1.05 | 1e-4 | Current source range |
| `current_range_auto` | bool | | off | Instrument picks the range |
| `current_compliance` | float, A | 1e-9 to 1.05 | 1e-4 | Current limit while sourcing voltage |
| `voltage_compliance` | float, V | 2e-4 to 1100 | 1 | Voltage limit while sourcing current |
| `voltage_limit` | float, V | 20, 40, 100, 200, 300, 400, 500, 1100 | 20 | Over-voltage protection (1100 is none) |
| `sense_function` | str | `VOLT_CURR`, `VOLT`, `CURR`, `RES` | `VOLT_CURR` | What `measure` returns |
| `current_measure_range` | float, A | 1e-6 to 1.05 | 1e-4 | Current measure range |
| `current_measure_range_auto` | bool | | on | Instrument picks the range |
| `voltage_measure_range` | float, V | 0.2 to 1100 | 20 | Voltage measure range |
| `voltage_measure_range_auto` | bool | | on | Instrument picks the range |
| `nplc` | float, PLC | 0.01 to 10 | 1 | Integration time of one reading, in power line cycles |
| `source_delay` | float, s | 0 to 9999.999 | 0 | Settling time before each measurement |
| `remote_sense` | bool | | off | 4-wire measurement at the load |
| `terminals` | str | `FRON`, `REAR` | `FRON` | Which connector carries the output |
| `output_off_mode` | str | `ZERO`, `NORM`, `GUAR`, `HIMP` | `ZERO` | Terminal behaviour while off |
| `interlock` | bool | | off | Hardware interlock of the rear Digital I/O connector |
| `output_state` | bool | read only | off | Whether the output is on |

The compliance written by `configure_source` is what allows `output_on`; the value applied on
connect is a conservative baseline, not a choice. Change `source_function`, `remote_sense`,
`terminals` and `voltage_limit` only while the output is off.

## Functions

| Function | Arguments | Returns |
|---|---|---|
| `get_identity()` | | manufacturer, model, serial_number, firmware |
| `configure_source(source_function, level, compliance, source_range=None)` | compliance is required | values read back; output stays off |
| `output_on()` | | output state |
| `output_off()` | | output state; immediate, no step down |
| `safe_shutdown(step=None, delay=None)` | | output_state, level |
| `set_level(level, step=None, delay=None)` | | level read back |
| `measure()` | | voltage, current, resistance, timestamp, status, compliance, overrange, ovp_tripped |
| `sweep(source_function, start, stop, points, compliance, spacing="LIN", delay=None, return_to_zero=True)` | 2 to 2500 points | iv_curve, points, compliance_hit |
| `read_errors()` | | errors, count (reads and clears the queue) |

A `compliance` flag of `True` on a reading means the source did not reach its setpoint, so the
reading is a limit and not a measurement.

### Composite operations

Two methods of `Device` chain the functions above into a complete measurement. Both step the
source down and switch the output off afterwards, also on failure.

- `source_and_measure(source_function, level, compliance, source_range=None, keep_output_on=False)`
  configures, switches on, takes one reading and switches off.
- `run_iv_sweep(source_function, start, stop, points, compliance, spacing="LIN", delay=None, source_range=None)`
  configures at the first level, switches on, sweeps and switches off.

The full generated reference (types, ranges, per-function arguments) is in the documentation
under `docs/`; build it locally with `CI= uv run plesty check`.

## Open items for the field test

Verified on a real 2410 over GPIB (2026-10-20, open terminals, at most 5 V):

- The instrument accepts `:SYST:ERR:ALL?` (read the whole error queue at once).
- `:OUTP:INT:TRIP?` answers 0 while the interlock is open and 1 while it is closed. The
  manual contradicts itself here; its SCPI reference table is right. With the interlock
  enabled and open, `:OUTP ON` is blocked with error 802.
- The five-element `:READ?` reading and its status bits; `output_on` stepping up from 0,
  `set_level`, `safe_shutdown` and a 6-point sweep; GPIB terminators.

Still to confirm, marked in the code:

- Whether `NONE` is the right setting for the 1100 V over-voltage protection.
- Whether `:READ?` behaves as expected with the output off (the driver refuses it anyway).
- The ramp step size (10 V) and wait (30 ms) on a real load, and compliance on a load.
- How the integration time is shared between the voltage, current and resistance functions.
- RS-232 line terminators.

## License

LGPL-3.0-or-later. See `LICENSE` and `COPYING`.
