Created
November 17, 2025 16:05
-
-
Save bruceravel/3529b59e4025de9da4f297df152ae331 to your computer and use it in GitHub Desktop.
simple code for running Anton-Parr temperature controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """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