SerialDevice

class dvg_devices.BaseDevice.SerialDevice(name='Dev_1', long_name='Serial Device')[source]

This class provides higher-level general I/O methods for a serial device, by wrapping the excellent pySerial library.

The following functionality is offered:

  • TODO: mention write(), query(), query_ascii_values(), query_bytes(), readline() and close().
  • Scanning over all serial ports to autoconnect to the desired serial device, based on the device’s reply to a validation query.
  • Autoconnecting to the device, based on the last known successful port that gets written to a configuration textfile.

Instances of this class will tie in nicely with dvg_qdeviceio.QDeviceIO.

Parameters:
  • long_name (str, optional) – Long display name of the device in a general sense. E.g., “Keysight N8700 PSU” or “Arduino M0 Pro”.

    Default: “Serial Device”

  • name (str, optional) – Short display name for the device. E.g., “PSU_1” or “blinker”.

    Default: “Dev_1”

Attributes:

long_name

Long display name of the device in a general sense. E.g, “Keysight N8700 PSU” or “Arduino M0 Pro”.

Type:str
name

Short display name for the device. E.g., “PSU_1” or “blinker”.

Type:str
serial_settings

Dictionary of keyword arguments to be passed directly to serial.Serial at initialization of the serial port when trying to connect. Do not specify port in this dictionary as it will be supplied by this class’s machinery.

Default: {“baudrate”: 9600, “timeout”: 2, “write_timeout”: 2}

Type:dict
ser

Will be set to a serial.Serial device instance when a connection has been established. Otherwise: None.

Type:serial.Serial | None
is_alive

Is the connection alive? I.e., Can we communicate?

Type:bool

Methods

SerialDevice.set_ID_validation_query(ID_validation_query: Callable[[], tuple], valid_ID_broad: object, valid_ID_specific: object = None)[source]

When this method is not called, then the following default scheme applies:

During connect_at_port(), scan_ports() or auto_connect() a succesful connection to any serial device will be accepted and be stored in class member ser.

When this method is called, then the following scheme applies:

During connect_at_port(), scan_ports() or auto_connect() a connection to a desired device will be attempted by performing a query for a device ID over the serial connection. The serial connection will be accepted and be stored in class member ser whenever the following scheme returns succesful:
Parameters:
  • ID_validation_query (Callable [[], tuple]) – Reference to a function to perform an ID validation query on the device when connecting. The function should take zero arguments and return a tuple consisting of two objects, as will be explained further down. Only when the outcome of the validation is successful, will the connection be accepted and remain open. Otherwise, the connection will be automatically closed again.

    The device’s reply on the validation query will be tested against the two other arguments: valid_ID_broad (required) and valid_ID_specific (optional).

    The broad reply can be used to allow connections to a device of a certain manufacturer and model. E.g., in a response to an *idn? validation query, one can test a part of the full reply – say, “THURLBY THANDAR, QL355TP, 279730, 1.00 – 1.00” – against “THURLBY THANDAR, QL” to allow connections to any Thurlby Thandar QL-series power supply.

    The specific reply can be used to narrow down to a specific device, once it has passed the broad validation test. In the example above, one could test against the series number “279730”, for instance. When argument valid_ID_specific is not supplied, any broad match will be accepted as connection.

    The function to be passed, being defined as a class method inside of a derived SerialDevice class, should have a general form like:

    def my_ID_validation_query(self) -> tuple[str, str]:
        # Expected: reply = "THURLBY THANDAR, QL355TP, 279730, 1.00 – 1.00"
        _success, reply = self.query("*idn?")
        reply_broad = ans[:19]                  # "THURLBY THANDAR, QL"
        reply_specific = ans.split(",")[2]      # "279730", i.e. serial number
    
        return reply_broad, reply_specific
    

    When ID_validation_query is set to None, no validation will take place and any successful connection will be accepted and remain open.

  • valid_ID_broad (object) – Reply to be broadly matched when a function reference is being passed onto ID_validation_query.

  • valid_ID_specific (object, optional) – Reply to be specifically matched when a function reference is being passed onto ID_validation_query. Note: When set to None, any connection that is broadly matched will be accepted and remain open.

    Default: None

SerialDevice.set_read_termination(termination: str | bytes | None, query_wait_time: float = 0.1)[source]

Set the termination character(s) for serial read.

Parameters:
  • termination (str | bytes | None) – Termination character(s). When set to None or empty the I/O operation query() will wait query_wait_time seconds before reading out the complete serial-input buffer, which by then should contain the device’s reply to the query.

  • query_wait_time (float, optional) – See above.

    Default: 0.1

SerialDevice.set_write_termination(termination: str | bytes | None)[source]

Set the termination character(s) for serial write.

Parameters:termination (str | bytes | None) – Termination character(s).
SerialDevice.readline(raises_on_timeout: bool = False, returns_ascii: bool = True) tuple[bool, str | bytes | None][source]

Listen to the Arduino for incoming data. This method is blocking and returns when a full line has been received or when the serial read timeout has expired.

Parameters:
  • raises_on_timeout (bool, optional) – Should an exception be raised when a read timeout occurs?

    Default: False

  • returns_ascii (bool, optional) – When set to True the device’s reply will be returned as an ASCII string. Otherwise, it will return as bytes.

    Default: True

Returns:

success (bool):

True if successful, False otherwise.

reply (str | bytes | None):

Reply received from the device, either as ASCII string (default) or as bytes when returns_ascii was set to False. None if unsuccessful.

Return type:

tuple

SerialDevice.write(msg: str | bytes, raises_on_timeout: bool = False) bool[source]

Send a message to the serial device.

Parameters:
  • msg (str | bytes) – ASCII string or bytes to be sent to the serial device.

  • raises_on_timeout (bool, optional) – Should an exception be raised when a write timeout occurs?

    Default: False

Returns:

True if successful, False otherwise.

SerialDevice.query(msg: str | bytes, raises_on_timeout: bool = False, returns_ascii: bool = True) tuple[bool, str | bytes | None][source]

Send a message to the serial device and subsequently read the reply.

Parameters:
  • msg (str | bytes) – ASCII string or bytes to be sent to the serial device.

  • raises_on_timeout (bool, optional) – Should an exception be raised when a write or read timeout occurs?

    Default: False

  • returns_ascii (bool, optional) – When set to True the device’s reply will be returned as an ASCII string. Otherwise, it will return as bytes.

    TODO & NOTE: ASCII is a misnomer. The returned reply will be UTF-8 encoded, not ASCII. Need to fix the argument name somehow, without breaking code elsewhere.

    Default: True

Returns:

success (bool):

True if successful, False otherwise.

reply (str | bytes | None):

Reply received from the device, either as ASCII string (default) or as bytes when returns_ascii was set to False. None if unsuccessful.

Return type:

tuple

SerialDevice.query_bytes(msg: bytes, N_bytes_to_read: int, raises_on_timeout: bool = False) tuple[bool, bytes | None][source]

Send a message as bytes to the serial device and subsequently read the reply. Will block until reaching N_bytes_to_read or a read timeout occurs.

Parameters:
  • msg (bytes) – Bytes to be sent to the serial device.

  • N_bytes_to_read (int) – Number of bytes to read.

  • raises_on_timeout (bool, optional) – Should an exception be raised when a write or read timeout occurs?

    Default: False

Returns:

success (bool):

True when N_bytes_to_read bytes are indeed read within the timeout, False otherwise.

reply (bytes | None):

Reply received from the device as bytes.

If success is False and 0 bytes got returned from the device, then reply will be None. If success is False because the read timed out and too few bytes got returned, reply will contain the bytes read so far.

Return type:

tuple

SerialDevice.query_ascii_values(msg: str, delimiter='\t', raises_on_timeout: bool = False) tuple[bool, list][source]

Send a message to the serial device and subsequently read the reply. Expects a reply in the form of an ASCII string containing a list of numeric values, separated by a delimiter. These values will be parsed into a list and returned.

Parameters:
  • msg (str) – ASCII string to be sent to the serial device.

  • delimiter (str, optional) – Delimiter used in the device’s reply.

    Default: “\t”

  • raises_on_timeout (bool, optional) – Should an exception be raised when a write or read timeout occurs?

    Default: False

Returns:

success (bool):

True if successful, False otherwise.

reply_list (list):

Reply received from the device and parsed into a list of separate values. The list is empty if unsuccessful.

Return type:

tuple

SerialDevice.connect_at_port(port: str, verbose: bool = True) bool[source]

Open the serial port at address port and try to establish a connection.

If the connection is successful and set_ID_validation_query() was not set, then this function will return True.

If set_ID_validation_query() was set, then this function will return True or False depending on the validation scheme as explained in set_ID_validation_query().

Parameters:
  • port (str) – Serial port address to open.

  • verbose (bool, optional) – Print a “Connecting to: “-message to the terminal?

    Default: True

Returns:

True if successful, False otherwise.

SerialDevice.scan_ports(verbose: bool = True) bool[source]

Scan over all serial ports and try to establish a connection. See further the description at connect_at_port().

Parameters:

verbose (bool, optional) – Print a “Scanning ports for: “-message to the terminal?

Default: True

Returns:

True if successful, False otherwise.

SerialDevice.auto_connect(filepath_last_known_port: str = 'config/port.txt') bool[source]

Try to connect to the device using the last-known successful port as got written to the textfile filepath_last_known_port by the previous call to auto_connect().

When the file does not exist, can not be read or if the desired device can not be found at that specific port, then a scan over all ports will be performed by automatically invoking scan_ports().

Parameters:filepath_last_known_port (str) – Default: “config/port.txt”
Returns:True if successful, False otherwise.
SerialDevice.close(ignore_exceptions=False)[source]

Cancel all pending serial operations and close the serial port.