MSHPro

The MSHPro is low-cost hotplate stirrer.

MSHPro Hotplate Stirrers

The hotplate has a RS232 9-pin connector on the rear that allows control of its functions. This package is a tool for control of these hotplates via serial interface.

A interface to all commands is provided by the Hotplates.MSHPro class. Commands are avilable to get information and control the hotplate’s speed and temperature.

All the formal communication structure is described by the Hotplates.MSHProCommunication module.

Serial communication is full duplex and this is acheived using Hotplates.SerialThreadedDuplex.Serial, an extension of PySerial’s serial.Serial.

Example usage:

>>> import Hotplates
>>> hp = Hotplates.MSHPro(port="/dev/ttyUSB0")
>>> hp.status()
{'success': True, 'stir_set': 'Off', 'stir_actual': 0, 'heat_set': 'Off', 'heat_actual': 17.5, 'stir_on': False, 'heat_on': False, 'heat_limit': 340.0}
>>> hp.stir(400)  # Wait after command for hotplate to reach speed
>>> hp.status()
{'success': True, 'stir_set': 400, 'stir_actual': 399, 'heat_set': 'Off', 'heat_actual': 17.7, 'stir_on': True, 'heat_on': False, 'heat_limit': 340.0}
>>> hp.off()
>>> hp.status()
{'success': True, 'stir_set': 'Off', 'stir_actual': 0, 'heat_set': 'Off', 'heat_actual': 1.1, 'stir_on': False, 'heat_on': False, 'heat_limit': 340.0}

Logging

Logging is implemented in MSHPro. The handler is logging.NullHandler. Communicated bytes are logged at logging.DEBUG level. Commands are logged at logging.INFO level. The logger is available through: logger = logging.getLogger("Hotplates.MSHPro").

class MSHPro(port: Optional[Union[int, str]] = None, timeout: float | int = 0.5)[source]

Serial communication with a MSHPro hotplate.

SERIAL_SETTINGS = {'baudrate': 9600, 'bytesize': 8, 'dsrdtr': False, 'parity': 'N', 'rtscts': False, 'stopbits': 1, 'xonxoff': False}

Communications settings for the MSHPro hotplate.

HEATLIMIT_MAX: float = 340.0

Maximum settable temperature (°C).

HEATLIMIT_MIN: float = 25.0

Minimum settable temperature (°C).

STIRLIMIT_MAX: int = 1500

Maximum settable stir speed (rpm).

STIRLIMIT_MIN: int = 100

Minimum settable stir speed (rpm).

__init__(port: Optional[Union[int, str]] = None, timeout: float | int = 0.5)[source]

Create a MSHPro object.

The serial settings of the hotplate are stored in MSHPro.SERIAL_SETTINGS.

Parameters:
  • port (Optional[Union[int, str]], optional) – Serial port name. If an integer is passed it will be converted to a serial port name depending on the current system using SerialThreadedDuplex.port_parser() The serial port will not be opened upon creation. Defaults to None.

  • timeout (float | int, optional) – Set the timeout for serial read. Passed into SerialThreadedDuplex.Serial creation. Defaults to 0.5.

property port: Any

Serial port name.

serial_open() None[source]

Open Serial.

serial_close() None[source]

Close Serial.

ping() bool[source]

Send a ping command.

Returns:

True if hotplate is online and responding correctly.

Return type:

bool

_status() Dict[str, Any][source]

Get status of the hotplate: stir_set, stir_actual, heat_set, heat_actual. See status().

_info() Dict[str, Any][source]

Get info from the hotplate: mode, stir_on, heat_on, heat_limit and heat_alarm. See status().

status(raw_values: bool = False) Dict[str, Any][source]

Get hotplate status. See also _status() and _info() for a subset of this data.

Dictionary keys:
  • “success” (bool): True if command received correctly.

  • “stir_set” (int | “Off”): target speed (rpm) or “Off”. Set raw _values to be True to view value when off instead of “Off”.

  • “stir_actual” (int): measured speed (rpm).

  • “heat_set” (float | “Off”): target temperature (°C) or “Off”. Set raw _values to be True to view value when off instead of “Off”.

  • “heat_actual” (float): measured temperature (°C). N.B. The heat_actual reading is from an internal temperature sensor that doesn’t necesserily match the display value. If a temperature probe is attached then both the display and heat_actual reading is from the probe.

  • “stir_on” (bool): True if stirring.

  • “heat_on” (bool): True if heating.

  • “heat_limit” (float): maximum set temperature (°C).

  • “mode” ({“A”, “B”, “C”}): default is “A”. Undocumented. Possibly related to heat rate profile.

  • “heat_alarm”. Undocumented. Unknown. Only returned when raw _values is True.

Parameters:

raw_values – bool Default behaviour [False] is to convert values to more readable format, specifically related to set values when off and hiding unknown values. If True then the raw values from COMMUNICATION.parse_response() are returned.

Returns:

Dictionary of hotplate information.

Return type:

dict

off() None[source]

Turn off stirring and heating.

heat(val: float) None[source]

Control heating.

If val is not truthy, heating will be turned off. If an exception is encountered, heating will be turned off.

Parameters:

val (int) – Target temperature (1 decimal place float, rounded down, °C).

Raises:

ValueError – If val can not be converted to float or is outside permissable range.

heat_off() None[source]

Turn off heating.

stir(val: int) None[source]

Control stirring.

If val is not truthy, stirring will be turned off.

Parameters:

val (int) – Target stir speed (integer, rpm).

Raises:

ValueError – If val can not be converted to int or is outside permissable range.

stir_off() None[source]

Turn off stirring.

mode(mode: str) None[source]

Set hotplate mode. Warning: undocumented feature.

Parameters:

mode ({"A", "B", "C"}) – Choose mode A, B or C.

text_command(cmd: str) Any[source]

Interpret a text command. The command is one of {“PING”, “STATUS”, “OFF”, “STIR”, “HEAT”, “MODE”}. Text is not case-sensitive. Each of these will be sent to the corresponding method. i.e. for {“HEAT”, “STIR” and “MODE”} a whitespace separated value will be used for setting.

Examples:

>>> hp = Hotplates.MSHPro(0)
>>> hp.text_command("STATUS")
>>> hp.text_command("HEAT OFF")
>>> hp.text_command("MODE B")
>>> hp.text_command("stir 560")
>>> hp.text_command("PING")
>>> hp.text_command("OFF")

MSHPro Communication

class COMMUNICATION(value)[source]

Utilities for serial communication with a MSHPro hotplate. Contains all information about communication protocols, including functions for encoding and decoding.

Communications available with the hotplate:

  • COMMUNICATION.PING Ping command to see if the hotplate is on and responding correctly.

  • COMMUNICATION.INFO Get info from the hotplate: mode, stir_on, heat_on, heat_limit and heat_alarm.

  • COMMUNICATION.STATUS Get status of the hotplate: stir_set, stir_actual, heat_set and heat_actual. N.B. The heat_actual reading is from an internal temperature sensor that doesn’t necesserily match the display value. If a temperature probe is attached then both the display and heat_actual reading is from the probe.

  • COMMUNICATION.STIR Set stirring speed (rpm). Use to_bytes() with val = stir_set (int). Sending speed toggles on or off.

  • COMMUNICATION.HEAT Set temperature (°C). Use to_bytes() with val = heat_set (1 decimal place float, rounded down). Sending set temperature toggles on or off.

Warning

Communication values below this, prefixed with a single underscore, are undocumented. Proceed with extreme caution!

  • COMMUNICATION._MODE The hotplate default mode is “A”. This command toggles between [“A”, “B”, “C”] modes. The mode is beleived to be heating profiles, i.e. “A” will get to the target temperature fastest but may overshoot, “C” will try to avoid overshooting; and “B” is a balance between the two.

Example usage:

>>> import Hotplates.MSHProCommunication
>>> comm = Hotplates.MSHProCommunication.COMMUNICATION.PING
>>> comm.to_bytes()
b'\xfe\xa0\x00\x00\x00\xa0'
>>> comm.len_rx
6
property len_rx: int

Length of expected response.

to_bytes(val: Optional[Any] = None) bytes[source]

Generate bytes for transmission to hotplate.

Parameters:

val (int | float, optional) – Value for setting where appropriate (i.e. COMMUNICATION.STIR, COMMUNICATION.HEAT).

Returns:

bytes for transmission.

Return type:

bytes

Raises:

ValueError – When val is incorectly formatted or outside of allowed range.

parse_response(b: bytes) Dict[str, Any][source]

Process bytes received from serial communication with hotplate.

Parameters:

b (bytes) – bytes for decoding.

Returns:

Parsed reponses. All responses have a key "success": bool that represents the overall outcome. INFO and STATUS responses have keys referencing data as described above.

Return type:

dict[str, Any]

Raises:
exception COMMUNICATIONException[source]

Base exception for exceptions that could be encountered using COMMUNICATION.

exception IncompleteResponseException[source]

Not enough bytes received before timeout.

exception ResponseFormatException[source]

Error with response bytes or checksum.

exception ResponseParseException[source]

Error parsing COMMUNICATION response.

exception HotplateException[source]

Hotplate indicated error.

checksum(val: Iterable[int] | str) int[source]

Checksum calculator for MSHPro communication.

Sum bytes discarding overflow.

Parameters:

val (Iterable[int] | str) – Value for checksum calculation.

Returns:

1 byte checksum.

Return type:

int

SerialThreadedDuplex

class Serial(*args: Any, **kwargs: Any)[source]

Extending PySerial serial.Serial to include full duplex commuication using threading.Thread.

Example usage:

>>> import Hotplates.SerialThreadedDuplex
>>> s = Hotplates.SerialThreadedDuplex.Serial(port="/dev/ttyUSB0", timeout=1.0)
>>> s.write_with_read_until(b"Hello!", expected="\n")
>>> s.value
# received bytes
__init__(*args: Any, **kwargs: Any) None[source]

All paramaters are passed to PySerial’s serial.Serial.

write_with_read_until(data: bytes, *, expected: str = '\n', size: Optional[int] = None) bytes[source]

Write data to the serial port using serial.Serial.write() while reading with serial.Serial.read_until().

Parameters:
  • data (bytes) – data for serial.Serial.write().

  • expected (str, optional) – expected for serial.Serial.read_until(). Defaults to “\n”.

  • size (int, optional) – size for serial.Serial.read_until(). Defaults to None.

Returns:

received bytes. Also accessable using the value property.

Return type:

bytes

write_with_read(data: bytes, size: int = 1) bytes[source]

Write data to the serial port using serial.Serial.write() while reading with serial.Serial.read().

Parameters:
  • data (bytes) – data for serial.Serial.write().

  • size (int, optional) – size for serial.Serial.read().

Returns:

received bytes. Also accessable using the value property.

Return type:

bytes

property value: bytes

Most recent received value.

Raises:
Returns:

received value.

Return type:

bytes

exception() None[source]

Raise any exception encountered during the process.

port_parser(port: int | str, check_exists: bool = True) str[source]

Parses serial port information.

If an int is passed then a formatted str of COM{} or \dev\ttyUSB{} will be generated.

If check_exists is True the an exception will be raised if the port can not be found.

Parameters:
  • port (int | str) – port name for parsing.

  • check_exists (bool, optional) – Check if port exists. Defaults to True.

Raises:

PortNotFoundException – If the port is not found.

Returns:

parsed port name.

Return type:

str

exception SerialException[source]

Base exception for serial exceptions.

exception NoCommunicationException[source]

No communication attempted.

exception NoDataException[source]

No data received.

exception ReadingNotCompleteException[source]

Thread not completed.

exception PortNotFoundException[source]

Port not found on system.

Indices and tables