← SCPI Power Supply Control with Raspberry PLC in Python
Fuse test benchRaspberry PLC 19RSCPISerie USBControl
SCPI Power Supply Control with Raspberry PLC in Python — full example
Control a Sorensen XG programmable power supply over SCPI from a Raspberry PLC: serial setup, SOUR:CURR and SOUR:VOLT commands, full Python example.
Complete, runnable program for the Raspberry PLC 19R (sorensen-scpi-power-supply-control.py): wiring header, requirements and integration notes included.
Download the full project pack — freeThis example + the related ones + bill of materials
Read-only preview.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
COMPLETE EXAMPLE — Sorensen XG programmable power supply control over SCPI
Equipment: Raspberry PLC 19R (Industrial Shields)
Based on: fuse test bench project
Wiring:
- Sorensen XG (Ametek) supply, 0-5 V / 0-220 A, connected over USB
to the Raspberry PLC port -> shows up as /dev/ttyUSB0
- Serial link: 9600 baud, 8N1
Requirements:
pip3 install pyserial
Integration with the catalog:
- The current setpoint is fine-tuned by the closed-loop calibration
module (closed-loop-current-calibration.py).
- The actual current is measured with the ADS1015 over I2C
(ads1015-differential-adc-autogain.py).
"""
import time
import serial
class SorensenXG:
"""Minimal SCPI driver for the Sorensen XG supply over a serial port."""
def __init__(self, port="/dev/ttyUSB0", address=1, timeout=1.0):
self.address = address
self.ser = serial.Serial(
port=port,
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=timeout,
)
# Select the instrument on the bus (several supplies may share it)
self.write("*ADR %d" % self.address)
def write(self, command):
"""Send a SCPI command terminated with \\r and let the supply breathe."""
self.ser.write((command + "\r").encode("ascii"))
time.sleep(0.05) # the XG needs a small margin between commands
def query(self, command):
"""Send a query command and return the response as text."""
self.ser.reset_input_buffer()
self.write(command)
return self.ser.readline().decode("ascii", errors="ignore").strip()
def identify(self):
return self.query("*IDN?")
def set_current(self, amps):
"""Current setpoint (A). The bench XG goes up to 220 A."""
self.write("SOUR:CURR %.2f" % amps)
def set_voltage(self, volts):
"""Voltage setpoint (V). In fuse testing it is limited to 5 V."""
self.write("SOUR:VOLT %.2f" % volts)
def output(self, on):
"""Enable or cut the power output."""
self.write("OUTP %s" % ("ON" if on else "OFF"))
def read_current(self):
"""Current measured by the supply itself (SCPI telemetry)."""
try:
return float(self.query("MEAS:CURR?"))
except ValueError:
return 0.0
def close(self):
self.output(False)
self.ser.close()
def current_ramp(supply, target_a, step_a=5.0, wait_s=0.5):
"""Raise the setpoint in steps to avoid stressing shunt and wiring."""
setpoint = 0.0
while setpoint < target_a:
setpoint = min(setpoint + step_a, target_a)
supply.set_current(setpoint)
print(" setpoint %.1f A -> supply measures %.2f A"
% (setpoint, supply.read_current()))
time.sleep(wait_s)
def main():
supply = SorensenXG(port="/dev/ttyUSB0", address=1)
print("Supply detected:", supply.identify())
try:
# Typical configuration of a non-fusing test:
# voltage limited to 5 V and current as the controlled quantity.
supply.set_voltage(5.0)
supply.set_current(0.0)
supply.output(True)
print("Ramping up to 50 A...")
current_ramp(supply, target_a=50.0)
print("Holding 50 A for 10 s (the actual test would run here)...")
for _ in range(10):
print(" I supply = %.2f A" % supply.read_current())
time.sleep(1.0)
finally:
# No matter what happens, the output is cut on exit
print("Cutting output and closing port.")
supply.close()
if __name__ == "__main__":
main()
Download the full project pack — freeThis example + the related ones + bill of materials