Skip to content

Power Supply

PSU control (Siglent SPD and generic SCPI-99)

Main PowerSupply class for controlling SCPI power supplies.

Supports generic SCPI-99 compliant power supplies and Siglent SPD series models.

Installation

pip install "SCPI-Instrument-Control"

Or with power supply examples:

pip install "SCPI-Instrument-Control[power-supply-beta]"

Features
  • Multiple connection types: Ethernet/LAN, USB, GPIB, Serial
  • Full control of voltage, current, and output state
  • Model-specific capability detection
  • Data logging and automation support
  • Context manager support for automatic connection management

PowerSupply

PowerSupply(host: str, port: int = 5025, timeout: float = 5.0, connection: Optional[BaseConnection] = None)

Main class for controlling SCPI power supplies.

This class provides a high-level interface for controlling power supply functions including voltage/current settings, output control, and measurements.

Supports both generic SCPI-99 power supplies and Siglent SPD series models with automatic model detection and capability-based feature availability.

Example

psu = PowerSupply('192.168.1.200') psu.connect() print(psu.identify()) print(f"Model: {psu.model_capability.model_name}") print(f"Outputs: {psu.model_capability.num_outputs}") psu.output1.voltage = 5.0 psu.output1.current = 1.0 psu.output1.enabled = True print(f"Actual voltage: {psu.output1.measure_voltage()}V") psu.disconnect()

Or using context manager:

with PowerSupply('192.168.1.200') as psu: ... psu.output1.voltage = 12.0 ... psu.output1.enable()

Initialize power supply connection.

Parameters:

Name Type Description Default
host str

IP address or hostname of the power supply

required
port int

TCP port for SCPI communication (default: 5025, the Siglent raw SCPI socket; 5024 is the telnet-style port with prompts and is not recommended)

5025
timeout float

Command timeout in seconds (default: 5.0)

5.0
connection Optional[BaseConnection]

Optional custom connection object (uses SocketConnection if None)

None
Note

Outputs are created dynamically after connection based on model capabilities. Call connect() to establish connection and initialize outputs.

Source code in scpi_control/power_supply.py
def __init__(
    self,
    host: str,
    port: int = 5025,
    timeout: float = 5.0,
    connection: Optional[BaseConnection] = None,
):
    """Initialize power supply connection.

    Args:
        host: IP address or hostname of the power supply
        port: TCP port for SCPI communication (default: 5025, the Siglent raw SCPI socket; 5024 is the telnet-style port with prompts and is not recommended)
        timeout: Command timeout in seconds (default: 5.0)
        connection: Optional custom connection object (uses SocketConnection if None)

    Note:
        Outputs are created dynamically after connection based on model capabilities.
        Call connect() to establish connection and initialize outputs.
    """
    self.host = host
    self.port = port
    self.timeout = timeout

    # Create connection
    if connection is not None:
        self._connection = connection
    else:
        self._connection = SocketConnection(host, port, timeout)

    # Model capability and SCPI commands (populated after connection)
    self.model_capability: Optional[PSUCapability] = None
    self._scpi_commands: Optional[PSUSCPICommandSet] = None

    # Device information (populated after connection)
    self._device_info: Optional[Dict[str, str]] = None

is_connected property

is_connected: bool

Check if connected to power supply.

Returns:

Type Description
bool

True if connected, False otherwise

tracking_mode property writable

tracking_mode: str

Get tracking mode for multi-output PSUs.

Returns:

Type Description
str

Tracking mode: 'INDEPENDENT', 'SERIES', or 'PARALLEL'

Raises:

Type Description
NotImplementedError

If tracking is not supported by this model

device_info property

device_info: Optional[Dict[str, str]]

Get parsed device information.

Returns:

Type Description
Optional[Dict[str, str]]

Dictionary with keys: manufacturer, model, serial, firmware

Optional[Dict[str, str]]

None if not connected

supported_outputs property

supported_outputs: List[int]

Get list of supported output numbers for this model.

Returns:

Type Description
List[int]

List of output numbers (e.g., [1, 2, 3] for 3-output model)

List[int]

Empty list if not connected

Example

psu.connect() print(psu.supported_outputs) [1, 2, 3]

connect

connect() -> None

Establish connection to the power supply.

This method connects to the power supply, detects the model, and initializes model-specific capabilities and outputs.

Raises:

Type Description
SiglentConnectionError

If connection fails

SiglentTimeoutError

If connection times out

Source code in scpi_control/power_supply.py
def connect(self) -> None:
    """Establish connection to the power supply.

    This method connects to the power supply, detects the model, and initializes
    model-specific capabilities and outputs.

    Raises:
        SiglentConnectionError: If connection fails
        SiglentTimeoutError: If connection times out
    """
    logger.info(f"Connecting to power supply at {self.host}:{self.port}")
    self._connection.connect()
    logger.info("Connected successfully")

    # Verify connection by getting device identification
    try:
        idn_string = self.identify()
        self._device_info = self._parse_idn(idn_string)
        logger.info(f"Connected to: {self._device_info.get('manufacturer', 'Unknown')} " f"{self._device_info.get('model', 'Unknown')}")

        # Detect model capability
        self.model_capability = detect_psu_from_idn(idn_string)
        logger.info(f"Model capability: {self.model_capability}")

        # Initialize SCPI command set for this model
        self._scpi_commands = PSUSCPICommandSet(self.model_capability.scpi_variant)
        logger.info(f"Using SCPI variant: {self.model_capability.scpi_variant}")

        # Create outputs dynamically based on model capability
        self._create_outputs()

        # Update device info with capability information
        self._device_info["manufacturer"] = self.model_capability.manufacturer
        self._device_info["num_outputs"] = str(self.model_capability.num_outputs)

    except Exception as e:
        logger.error(f"Failed to identify device or initialize: {e}")
        self.disconnect()
        raise exceptions.SiglentConnectionError(f"Connected but failed to identify device: {e}")

disconnect

disconnect() -> None

Close connection to the power supply.

Source code in scpi_control/power_supply.py
def disconnect(self) -> None:
    """Close connection to the power supply."""
    logger.info("Disconnecting from power supply")
    self._connection.disconnect()
    self._device_info = None
    self.model_capability = None
    self._scpi_commands = None

    # Remove dynamically created outputs
    for i in range(1, 4):  # Check all possible outputs
        output_attr = f"output{i}"
        if hasattr(self, output_attr):
            delattr(self, output_attr)

write

write(command: str) -> None

Send a SCPI command to the power supply.

Parameters:

Name Type Description Default
command str

SCPI command string

required

Raises:

Type Description
SiglentConnectionError

If not connected

CommandError

If command contains invalid characters

Source code in scpi_control/power_supply.py
def write(self, command: str) -> None:
    """Send a SCPI command to the power supply.

    Args:
        command: SCPI command string

    Raises:
        SiglentConnectionError: If not connected
        CommandError: If command contains invalid characters
    """
    logger.debug(f"Write: {command}")
    self._connection.write(command)

query

query(command: str) -> str

Send a SCPI query and get the response.

Parameters:

Name Type Description Default
command str

SCPI query command

required

Returns:

Type Description
str

Response string from power supply

Raises:

Type Description
SiglentConnectionError

If not connected

SiglentTimeoutError

If query times out

CommandError

If command contains invalid characters

Source code in scpi_control/power_supply.py
def query(self, command: str) -> str:
    """Send a SCPI query and get the response.

    Args:
        command: SCPI query command

    Returns:
        Response string from power supply

    Raises:
        SiglentConnectionError: If not connected
        SiglentTimeoutError: If query times out
        CommandError: If command contains invalid characters
    """
    logger.debug(f"Query: {command}")
    response = self._connection.query(command)
    logger.debug(f"Response: {response}")
    return response

identify

identify() -> str

Get device identification string.

Returns:

Type Description
str

Device identification string (manufacturer, model, serial, firmware)

Example

'Siglent Technologies,SPD3303X,SPD3XXXXXXXXXXX,V1.01'

Source code in scpi_control/power_supply.py
def identify(self) -> str:
    """Get device identification string.

    Returns:
        Device identification string (manufacturer, model, serial, firmware)

    Example:
        'Siglent Technologies,SPD3303X,SPD3XXXXXXXXXXX,V1.01'
    """
    return self.query("*IDN?")

reset

reset() -> None

Reset power supply to default settings.

Note: This may take several seconds to complete and will turn off all outputs.

Source code in scpi_control/power_supply.py
def reset(self) -> None:
    """Reset power supply to default settings.

    Note: This may take several seconds to complete and will turn off all outputs.
    """
    logger.info("Resetting power supply to defaults")
    self.write("*RST")

clear_status

clear_status() -> None

Clear status registers.

Source code in scpi_control/power_supply.py
def clear_status(self) -> None:
    """Clear status registers."""
    self.write("*CLS")

get_error

get_error() -> str

Get the last error from the error queue.

Returns:

Type Description
str

Error string (format: "code,description")

Source code in scpi_control/power_supply.py
def get_error(self) -> str:
    """Get the last error from the error queue.

    Returns:
        Error string (format: "code,description")
    """
    return self.query("SYST:ERR?")

all_outputs_off

all_outputs_off() -> None

Disable all outputs (safety feature).

This is a safety method to quickly turn off all power supply outputs.

Source code in scpi_control/power_supply.py
def all_outputs_off(self) -> None:
    """Disable all outputs (safety feature).

    This is a safety method to quickly turn off all power supply outputs.
    """
    if self.model_capability is None:
        logger.warning("Cannot disable outputs - not connected")
        return

    logger.info("Disabling all outputs (safety)")
    for i in range(1, self.model_capability.num_outputs + 1):
        output = getattr(self, f"output{i}", None)
        if output:
            try:
                output.disable()
            except Exception as e:
                logger.error(f"Failed to disable output {i}: {e}")

set_independent_mode

set_independent_mode() -> None

Set outputs to independent mode (default).

Source code in scpi_control/power_supply.py
def set_independent_mode(self) -> None:
    """Set outputs to independent mode (default)."""
    self.tracking_mode = "INDEPENDENT"

set_series_mode

set_series_mode() -> None

Set outputs to series tracking mode.

In series mode, output voltages add up while current remains the same.

Source code in scpi_control/power_supply.py
def set_series_mode(self) -> None:
    """Set outputs to series tracking mode.

    In series mode, output voltages add up while current remains the same.
    """
    self.tracking_mode = "SERIES"

set_parallel_mode

set_parallel_mode() -> None

Set outputs to parallel tracking mode.

In parallel mode, output currents add up while voltage remains the same.

Source code in scpi_control/power_supply.py
def set_parallel_mode(self) -> None:
    """Set outputs to parallel tracking mode.

    In parallel mode, output currents add up while voltage remains the same.
    """
    self.tracking_mode = "PARALLEL"

get_output

get_output(output_num: int) -> Optional[PowerSupplyOutput]

Get output object by number.

Parameters:

Name Type Description Default
output_num int

Output number (1-based)

required

Returns:

Type Description
Optional[PowerSupplyOutput]

PowerSupplyOutput object or None if output doesn't exist

Example

psu.connect() out1 = psu.get_output(1)

Source code in scpi_control/power_supply.py
def get_output(self, output_num: int) -> Optional[PowerSupplyOutput]:
    """Get output object by number.

    Args:
        output_num: Output number (1-based)

    Returns:
        PowerSupplyOutput object or None if output doesn't exist

    Example:
        >>> psu.connect()
        >>> out1 = psu.get_output(1)
    """
    output_attr = f"output{output_num}"
    return getattr(self, output_attr, None)