Using Temperature Controllers - OE-FET/JISA GitHub Wiki
:warning: Temperature controllers have recently undergone a revamp, please read the PID and Temperature Controllers page for how to use temperature controllers.
Temperature and Temperature Control
JISA
provides drivers for a number of different temperature-related
instrumentation. The first and most basic of these are thermometers or
temperature sensors. These are represented as TMeter
objects in JISA
. Often,
such sensors are found as part of a temperature controller. These are
represented by TC
objects in JISA
The first three sections apply to both TMeter
and TC
objects, whereas later
sections only apply to TC
objects.
Contents
- Contents
- Temperature Readings
- Temperature Stability
- Multiple Sensors
- Temperature Set-Points
- Heater Control
- Heater Range
- PID Values
- Auto PID Zoning
- Multiple Outputs
- Example
Temperature Readings
All TMeter
objects allow you to take temperature readings by use of the
getTemperature()
method like so:
Java
double temperature = tmeter.getTemperature();
Kotlin
val temperature = tmeter.getTemperature()
// == or, using Kotlin magic ==
val temperature = tmeter.temperature
Python
temperature = tmeter.getTemperature()
Most instruments have the ability to change their measurement range, allowing
you to trade off between precision and maximum measurable value. For TMeter
instruments this can be achieved by use of the setTemperatureRange(...)
method. For example, if we knew that the largest temperature we could record was
500 K we would write:
tmeter.setTemperatureRange(500.0);
The number you give this method should be the maximum value you wish to record.
JISA
will select the smallest available measurement range that contains this
value.
Temperature Stability
You can make your program wait until a reading from a TMeter
or a TC
is
stable. This stability is defined as the temperature remaining within a certain
range for at least a specified amount of time.
The first, and simplest means of doing this, is to specify a temperature value that you want the sensor to report (within a percentage margin) for an amount of time before continuing. For example, 300 K within 1.0% for 10 seconds (10000 ms):
tmeter.waitForStableTemperature(300.0, 1.0, 10000);
the thread will not continue execution until the temperature has been within 1% of 300 K for at least 10 continuous seconds.
Alternatively you can specify stability around no temperature in-particular. This way stability is considered to be achieved if temperature measurements have stayed within a range of themselves for at least a given amount of time:
tmeter.waitForStableTemperature(1.0, 10000);
In the example above, the program will wait until the past 10 seconds worth of temperature readings are all within 1% of each other.
Multiple Sensors
If your instrument has more than one sensor, then you can extract each as its
own TMeter
object by use of getSensor(...)
like so:
Java
TMeter sensorA = tmeter.getSensor(0);
TMeter sensorB = tmeter.getSensor(1);
TMeter sensorC = tmeter.getSensor(2);
Kotlin
val sensorA = tmeter.getSensor(0)
val sensorB = tmeter.getSensor(1)
val sensorC = tmeter.getSensor(2)
Python
sensorA = tmeter.getSensor(0)
sensorB = tmeter.getSensor(1)
sensorC = tmeter.getSensor(2)
You can then use all the standard TMeter
methods on these objects and they
will act on the sensor they represtent.
sensorA.setTemperatureRange(1e3);
sensorB.setTemperatureRange(100.0);
double tempA = sensorA.getTemperature();
double tempB = sensorB.getTemperature();
sensorA.waitForStableTemperature(1.0, 10000);
Temperature Set-Points
If you are using a TC
object, then you are controlling an instrument that
doesn't just measure temperature, but controls it. To set a new temperature
set-point, use the setTargetTemperature(...)
method like so:
tc.setTargetTemperature(300.0); // 300 Kelvin
You can also check what the current set-point is by use of:
double setPoint = tc.getTargetTemperature();
If your controller has multiple sensors, you can configure which one is used as
the reference value (ie the one you want to make equal to your set-point) by use
of useSensor(...)
. For example, if we have three sensors (referred to as 0, 1
and 2 by JISA
) and we wanted the third sensor to be used:
tc.useSensor(2);
Heater Control
Most temperature controllers control temperature by use of a heat source working against a constant heat sink. This is normally done using some form of PID control. To enable this automatic control of the heater output, call:
tc.useAutoHeater();
To check whether the heater is currently being automatically operated use:
boolean auto = tc.isUsingAutoHeater();
Sometimes, you may wish to manually control the heater output. This can be done
by use of setManualHeater(...)
. This will set the heater to manual mode and
set it to the specified value (percentage of max power). For example, if we
wanted 100% output power:
tc.setManualHeater(100.0);
You can check what the percentage heater output is at any time by use of:
double heaterPCT = tc.getHeaterPower();
Heater Range
Most temperature controllers allow you to limit the maximum output power of the
heater. You can do this through JISA
by use of the setHeaterRange(...)
method, specifying the percentage of the absolute maximum output power the
controller can output that it should consider to be the maximum allowed output:
tc.setHeaterRange(10.0); // Max heater power is now 10% of absolute max
You can also check this value by use of:
double range = tc.getHeaterRange();
Therefore, if you wanted to know the output power as a percentage of the absolute maximum that controller can output:
double absPCT = tc.getHeaterPower() * (tc.getHeaterRange() / 100.0);
For some models, there are only discrete options available for this limit. In such a case, the nearest over-estimate is chosen.
PID Values
Since temperature controllers are PID controllers, you may often find that you need to adjust the PID parameters. To do this, you can use the following:
tc.setPValue(10.0); // P
tc.setIValue(0.5); // I
tc.setDValue(0.5); // D
You can also check what their values currently are by use of:
double P = tc.getPValue();
double I = tc.getIValue();
double D = tc.getDValue();
Auto PID Zoning
JISA
offers the ability for you to define temperature zones in which different
PID values are to be used. To use this you must first configure the zones by
creating TC.PIDZone
objects like so:
new TC.PIDZone(minT, maxT, P, I, D, heaterRange);
and give them to setAutoPIDZones(...)
like so:
tc.setAutoPIDZones(
new TC.PIDZone( 0.0, 100.0, 20.0, 0.5, 0.5, 100.0), // 0 K to 100 K
new TC.PIDZone(100.0, 200.0, 10.0, 0.5, 0.5, 100.0), // 100 K to 200 K
new TC.PIDZone(200.0, 300.0, 5.0, 0.0, 0.0, 50.0) // 200 K to 300 K
);
Kotlin / Python
tc.setAutoPIDZones(
TC.PIDZone(0.0, 100.0, 20.0, 0.5, 0.5, 100.0),
TC.PIDZone(100.0, 200.0, 10.0, 0.5, 0.5, 100.0),
TC.PIDZone(200.0, 300.0, 5.0, 0.0, 0.0, 50.0)
)
In this example we have basically defined the following:
Min T | Max T | P | I | D | Heater Range |
---|---|---|---|---|---|
000.0 K | 100.0 K | 20.0 | 0.5 | 0.5 | 100.0% |
100.0 K | 200.0 K | 10.0 | 0.5 | 0.5 | 100.0% |
200.0 K | 300.0 K | 52.0 | 0.0 | 0.0 | 50.0% |
Then you can enable and disable the auto-PID zoning by:
tc.useAutoPID(true); // Enable zoning
tc.useAutoPID(false); // Disable zoning
Multiple Outputs
If your temperature controller has multiple controllable outputs then you can extract each as its own temperature controller object like so:
Java
MSTC controllerA = tc.getOutput(0);
MSTC controllerB = tc.getOutput(1);
Kotlin
val controllerA = tc.getOutput(0)
val controllerB = tc.getOutput(1)
Python
controllerA = tc.getOutput(0)
controllerB = tc.getOutput(1)
You can then use all the previously mentioned methods on these as if they were separate instruments.
controllerA.setTargetTemperature(0.0);
controllerB.setTargetTemperature(300.0);
controllerA.setManualHeater(0.0);
controllerB.useAutoHeater();
Example
In this example, we are connecting to a LakeShore 336 controller (which has 2 outputs and 4 sensors). Controller 1 controls the sample-stage temperature and controller 2 controls the base temperature of a cryostat. Sensor A is located on the sample stage, B on the radiation shield, C on heat exchanger and D on the cold base.
We want to make the sample stage temperature reach 100 K, 200 K then 300 K. Waiting for the temperature to stabilise before taking temperature readings from all sensors then moving onto the next temperature.
Java
public class Main {
public static void main(String[] args) throws Exception {
LS336 tc = new LS336(new SerialAddress("COM4"));
MSTC sample = tc.getOutput(0);
MSTC base = tc.getOutput(1);
TMeter rad = tc.getSensor(1);
TMeter exchange = tc.getSensor(2);
sample.useSensor(0);
base.useSensor(3);
// Want the base to be as cold as possible
base.setTargetTemperature(0.0);
base.setManualHeater(0.0);
sample.useAutoHeater();
for (double temperature : Range.manual(100, 200, 300)) {
sample.setTargetTemperature(temperature);
sample.waitForStableTemperature(temperature, 1.0, 60000);
double sTemp = sample.getTemperature();
double rTemp = rad.getTemperature();
double eTemp = exchange.getTemperature();
double bTemp = base.getTemperature();
System.out.printf("S = %e K, R = %e K, E = %e K, B = %e K\n", sTemp, rTemp, eTemp, bTemp);
}
}
}
Kotlin
fun main() {
val tc = LS336(SerialAddress("COM4"))
val sample = tc.getOutput(0)
val base = tc.getOutput(1)
val rad = tc.getSensor(1)
val exchange = tc.getSensor(2)
sample.useSensor(0)
base.useSensor(3)
// Want the base to be as cold as possible
base.setTargetTemperature(0.0)
base.setManualHeater(0.0)
sample.useAutoHeater()
for (temperature in Range.manual(100, 200, 300)) {
sample.setTargetTemperature(temperature)
sample.waitForStableTemperature(temperature, 1.0, 60000)
val sTemp = sample.getTemperature()
val rTemp = rad.getTemperature()
val eTemp = exchange.getTemperature()
val bTemp = base.getTemperature()
println("S = %e K, R = %e K, E = %e K, B = %e K".format(sTemp, rTemp, eTemp, bTemp))
}
}
Python
def main():
tc = LS336(SerialAddress("COM4"))
sample = tc.getOutput(0)
base = tc.getOutput(1)
rad = tc.getSensor(1)
exchange = tc.getSensor(2)
sample.useSensor(0)
base.useSensor(3)
# Want the base to be as cold as possible
base.setTargetTemperature(0.0)
base.setManualHeater(0.0)
sample.useAutoHeater()
for temperature in Range.manual(100, 200, 300):
sample.setTargetTemperature(temperature)
sample.waitForStableTemperature(temperature, 1.0, 60000)
double sTemp = sample.getTemperature();
double rTemp = rad.getTemperature();
double eTemp = exchange.getTemperature();
double bTemp = base.getTemperature();
print("S = %e K, R = %e K, E = %e K, B = %e K" % (sTemp, rTemp, eTemp, bTemp))
main()