Skip to content

Instantly share code, notes, and snippets.

@bruceravel
Created November 17, 2025 16:05
Show Gist options
  • Select an option

  • Save bruceravel/3529b59e4025de9da4f297df152ae331 to your computer and use it in GitHub Desktop.

Select an option

Save bruceravel/3529b59e4025de9da4f297df152ae331 to your computer and use it in GitHub Desktop.
simple code for running Anton-Parr temperature controller
"""Modbus client for CCU100 heating stage control unit.
Protocol Modbus RTU
Baud rate 9600
Parity none
Address 1 *
"""
import serial # pip install pyserial (not serial)
import minimalmodbus
from dataclasses import dataclass
CCU100_ADDRESS = 1
@dataclass
class CCUStatus:
"""CCU100 status code structure."""
heater_off: bool
sensor_break: bool
loop_break: bool
cooling_failure: bool
housing_overtemp: bool
chamber_not_closed: bool
current_cable_disconnected: bool
null: bool
heater_on_sby: bool
heater_on: bool
safety_3: bool
safety_2: bool
safety_1: bool
power_stage_fault: bool
flow_det: bool
power_rel: bool
class CCU100(minimalmodbus.Instrument):
def __init__(self, port="/dev/ttyUSB0", address=CCU100_ADDRESS):
"""CCU100 modbus client.
Note: may need `close_port_after_each_call` set, particularly on windows.
Note: the docs specify types (float32, bool, etc),
but the instrument uses full registers (16-bit int) for many parameters
"""
super().__init__(port, address)
# set serial interface settings
self.serial.baudrate = 9600
self.serial.parity = serial.PARITY_NONE
def temperature(self) -> int:
"""Read temperature in celcius."""
return self.read_register(1)
def read_temperature_setpoint(self) -> int:
"""R/W float32 tempSetPoint
Note: the docs say float32, but the instrument supplies a 16-bit int
"""
return self.read_register(2)
def working_setpoint(self) -> int:
"""Read float32 tempWorkingSetPoint
Note: the docs say float32, but the instrument supplies a 16-bit int
"""
return self.read_register(5)
def write_temperature_setpoint(self, value: int):
"""R/W float32 tempSetPoint
Note: the docs say float32, but the instrument supplies a 16-bit int
"""
self.write_register(2, value)
def status(self) -> CCUStatus:
"""uint16 status code.
Bit 0: heaterOff
Bit 1: sensorBreak
Bit 2: loopBreak
Bit 3: coolingFailure
Bit 4: housingOvertemp
Bit 5: chamberNotClosed
Bit 6: currentCableDisconnected
Bit 7: ---
Bit 8: heaterOnSBY
Bit 9: heaterOn
Bit 10: safety3
Bit 11: safety2
Bit 12: safety1
Bit 13: powerStage_fault
Bit 14: flowDet
Bit 15:powerRel
"""
status_code = self.read_register(6298)
# reinterpret status code as a bitstring
status_code = bin(status_code)[2:]
status = [v == "1" for v in status_code]
# return status in structured format
return CCUStatus(*status)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment