The Lake Shore 336 is one of the most common temperature controllers in thermal and vacuum testing. On the camera test bench of PLATO, three of them ran at the same time. This is what you need to know to run it from a program instead of the front panel.
The connection: a TCP socket on port 7777
Over Ethernet, the 336 listens on TCP port 7777. No library is needed: a socket, the command sent as text, and a read up to the end of the line. The first thing to send is *IDN?; if the reply does not start with LSCI, you are not talking to a Lake Shore.
import socket
class LakeShore336:
def __init__(self, host, port=7777, timeout=3.0):
self.sock = socket.create_connection((host, port), timeout=timeout)
self.pending = b""
def write(self, command):
self.sock.sendall((command + "\r\n").encode("ascii"))
def query(self, command):
self.write(command)
while b"\r\n" not in self.pending:
chunk = self.sock.recv(1024)
if not chunk:
raise ConnectionError("the controller closed the connection")
self.pending += chunk
line, self.pending = self.pending.split(b"\r\n", 1)
return line.decode("ascii").strip()
ls = LakeShore336("192.168.1.50")
if not ls.query("*IDN?").startswith("LSCI"):
raise RuntimeError("not a Lake Shore")
print(float(ls.query("KRDG? A"))) # channel A temperature, in kelvin
KRDG? returns the reading in kelvin and CRDG? in degrees Celsius, followed by the channel (A, B, C or D).
Three details that cost hours
Always set a timeout when connecting. If the instrument is off or the IP is wrong, a connection without a timeout can hang for a couple of minutes before failing. With three seconds the program reports the problem straight away.
Terminate every command. Each command must be followed by the line terminator. The instrument does not process anything until it arrives, so an unterminated query gets no reply and the program sits waiting. Reviewing a real driver we found exactly that: one query that went out without it. Sending everything through a single method, like write() above, keeps any from slipping through.
It only accepts two connections at a time. This is the one that surprises people. If a GUI, a data logger and a test script each try to connect on their own, the third one fails. The solution used on PLATO, and the one we recommend, is for a single process to own the instrument and for everything else to ask it for readings or send it orders.
Ramps and setpoints
For a thermal cycle you set the ramp rate, the setpoint and the heater range of the relevant output:
ls.write("RAMP 1,1,2.0") # output 1: ramp on, 2 K/min
ls.write("SETP 1,250.0") # output 1 setpoint: 250 K
ls.write("RANGE 1,3") # output 1 heater on high range
Before applying power, the heater must be configured (resistance and maximum current, with HTRSET) and the PID values must be sensible; the 336 has an autotune (ATUNE) to find them. After that, the program only has to read every few seconds, store the data and check that the temperature follows the setpoint.
From script to test bench
This gives you a script that works. A real test bench needs what lets you leave it running on its own: reconnecting if the network drops, logging the temperatures alongside the other instruments, alerting when something goes out of range and chaining the thermal cycle with the acquisition. That is what we build, custom, for each lab.