3.13. SerialIO

Summary:

itom-plugin for a serial port communication

Type:

DataIO

License:

licensed under LGPL

Platforms:

Windows, Linux

Devices:

COM-Ports (Windows), ttySx and ttyUSBx (Linux)

Author:

H. Bieger, C. Kohler, ITO, University Stuttgart

3.13.1. Overview

This plugin encapsulates the access to the serial port. It can be used on both Windows and linux based operating systems.

In itom, this plugin is also interally used by other plugins (e.g. PIPiezoCtrl, UhlActuator, LeicaMotorFocus or PiezosystemJena).

Features are:

  • Access to the port settings by its internal parameters and the initialization.

  • Support for different baudrates, stop bits, parity and flow control.

  • Endline characters can automatically appended to each sent string and are split from received strings.

  • Any string can be sent with a certain delay in between each character (useful for older devices).

  • A debugging parameter allows displaying the entire data transfer in the optional toolbox.

  • The configuration dialog can be used to send user-defined strings via the opened connection.

    Use the checkBox enable $(ascii-code) statement parsing to enable ASCII code parsing to $(ascii-code) statement.

3.13.2. Initialization

For a connection to a serial port, create a new instance of this plugin using:

dataIO("SerialIO", port, baud, endline[, bits, stopbits, parity, flow, sendDelay, timeout, debug])

This is the necessary constructor of the class dataIO of itom in order to create a new instance of the plugin serialIO.

The parameter are as follows:

port

int

Windows: COM-port number (e.g. 1), Linux: ttySx, ttyUSBx or ttyACMx (x is port, see infos below)

baud

int

Baudrate (see baudrate in the parameter description below)

endline

str

endline character (see endline in the parameter description below)

bits

int, optional

number of bits to be written in line [5,8], default: 8

stopbits

int, optional

stop bits after every n bits [1,2], default: 1

parity

int, optional

0: no parity [default], 1: odd parity, 2: even parity

flow

int, optional

bitmask for the flow control (see flow in the parameter description below) [0, 127], default: 0

sendDelay

int, optional

0: write output buffer in one block, else: delay in ms after each character (same for input)

timeout

float, optional

Timeout for reading the current input buffer of the serial port in [s], [0,64], default: 4s

enableDebug

int, optional

0: no debug output [default], 1: all data transfer is printed to the toolbox

An example for opening port COM 1 with 9600 bauds is:

serial = dataIO("SerialIO", 1, 9600, endline="\n")

For linux, the port number may start with zero. If port numbers < 1000 are given, at first the serial port ttySx is checked, where x is the given port number. If ttySx does not exist, ttyUSBx is searched. If a port number in the range [1000,1999] is given, the device ttyUSBx is used, where x is (port - 1000). Finally, if port is in the range [2000,2999], the device ttyACMx is searched, where x is (port - 2000).

3.13.3. Parameters

These parameters are available and can be used to configure the serialIO instance. Many of them are directly initialized by the parameters of the constructor. During the runtime of an instance, the value of these parameters is obtained by the method getParam, writeable parameters can be changed using setParam.

name: {str}, read-only

name of the plugin (SerialIO)

port: {int}, read-only

connected COM port number (defined by initialization)

baud: {int}

current baudrate in symbols per second (approximately bits/sec). Allowed baudrates are:

50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
2500000, 3000000, 3500000, 4000000

However not all baudrates are supported on each operating system.

bits: {int}

Number of bits to be written in line [5,8]

stopbits: {int}

Stop bits after every n-th bit [1,2]

parity: {int}

Parity check 0: no parity 1: odd parity 2: even parity

flow: {int}

bitmask for the flow control. This mask is an or-combination of the following values (add values for resulting flow value):

Xon,Xoff: Xoff (0, default), Xon (1); 1. bit
rts control: disabled (0, default), enabled (2), handshake (4 or 4+2); 2. and 3. bit
cts control: disabled (0, default), enabled (8); 4. bit
dtr control: disabled (0, default), enabled (16), handshake (32 or 32+16); 5. and 6. bit
dsr control: disabled (0, default), enabled (64); 7. bit

Example: Xon, rts handshake, dsr enabled is 1 + 4 + 64 = 69 for the flow value

endline: {str}

If a string is put to the output buffer (and send via the opened port), the endline-string is automatically appended to the string. Typical values are:

"\n","\n\r","\r" or ""

Use the empty endline character string (“”) if you want to have full control.

endlineRead: {str}

Same behaviour like endline, however it determines the delimiter for incoming strings and is only used or evaluated in readline is 1. The user is referred to readline.

readline: {int}

Per default, readline is set to 0. This means that the getVal command returns the values that are currently available at the input buffer of the computer. If you call getVal too fast, it might be, that the full answer is not available yet. Then you need to recall getVal again. If you set readline to 1, getVal collects values from the input buffer and checks if endlineRead is contained in the string. If so, getVal writes all characters also those behind the first appearance of endlineRead into the bytearray. Neverteless the number of obtained signs returned by getVal just counts the signs to the first apperance of endlineRead. Remaining characters are recognized at the next call to getVal. If no endline characters is detected within timeout seconds, a timeout (error code: 256) is raised. The following code example demonstrates how to obtain the bytearray until the endlineRead sign.

serial.setParam('readline', True)
b = bytearray(100)
num = serial.getVal(b)
signs = b[0:num]
print(signs)
sendDelay: {str}

This value represents a delay (in ms) after each character that is send and received

timeout: {double}

Timeout in seconds. If the incoming buffer of the serial port cannot be read within this time, the call returns. [0,65]

enableDebug: {int}

Set this value to 1 if you want to read the entire data transfer in the toolbox of an instance (disabled: 0).

3.13.4. Usage

Lets assume a serial port connection should be established with the following properties:

  • COM 1

  • 9600 baud

  • 8 bits

  • 1 stopbit

  • no parity

  • every command should finish with “\n”

Then open the serial port and assign it to the variable serial

serial = dataIO("SerialIO",1,9600,endline="\n",bits=8,stopbits=1,parity=0)

If you have a scenario that you need to ask for the position of an actuator. Maybe the string to send in order to ask for the current position is POS?n, then use the setVal method to send this string (\n is automatically appended):

serial.setVal("POS?")

Then it is necessary to get the result. Therefore create a bytearray with enough space and pass this array to the getVal method:

ba = bytearray(9) #array with nine elements
len = serial.getVal(ba)

len finally contains the number of characters that have been filled by the serial port, of course, len cannot be bigger than the size of the allocated buffer ba. If the serial port does not respond at all within the given timeout time, an error is raised. getVal does not wait until the entire buffer is filled or the timeout occurs but returns immediately with the content of the buffer that has been filled until this time. In order to get the full answer, it is also possible to repeatedly call getVal.

3.13.5. Configure the serialIO plugin to wait for full answers

Per default, the user will send a request to a device. The device will then probably answer to this request using a specified string pattern. If the parameter readline is set to 0 (default), a call to getVal (see usage above), will only return all characters that fit into the buffer size and are currently available at the input buffer of the serial port. If the device need more time for the entire answer, the remaining part of the answer can only be obtained by continuously calling getVal until the entire answer is obtained.

Usually, an answer always ends by a certain endline character sequence. If this sequence contains 1 or 2 characters, you can also configure the serialIO plugin such that getVal will continuously check the input buffer of the serial port and collect all characters until the endline sequence (parameter endlineRead) is detected or a timeout occurred. If the endline sequence was found, the characters until but without the first endline sequence are returned by the passed buffer. Remaining characters are put onto an internal buffer and considered at the next call to getVal. By this configuration you will automatically wait for the full answer without further programming work and without idle delay times.

An example for this alternative approach is:

serial.setParam("readline", 1)
serial.setParam("endlineRead", "\n")
buffer = bytearray(20)
serial.setVal("POS?")
num = serial.getVal(buffer)
print("full answer from device", buffer[0:num])

3.13.6. Clear input or output buffer

Sometimes, it is necessary to immediately clear all characters inside of the input buffer (obtained by getVal) and/or output buffer (send by setVal). This can be done using specific exec-functions:

serial.exec("clearInputBuffer") #clear input buffer
serial.exec("clearOutputBuffer") #clear output buffer

#alternative:
serial.exec("clearBuffer", 0) #clear input buffer
serial.exec("clearBuffer", 1) #clear output buffer

3.13.7. Get a list of available COM ports under Windows

In order to get a list of all available COM ports under Windows, the following python code snippet can be used. It reads the corresponding registry entries:

#This script can be used as example for Windows
#to detect registered COM ports for this computer
import winreg as wreg

def DetectCOMPorts():
    try:
        regconn = wreg.ConnectRegistry( None, wreg.HKEY_LOCAL_MACHINE )
        key = wreg.OpenKey( regconn, "HARDWARE\\DEVICEMAP\\SERIALCOMM", wreg.KEY_READ )
        values_count = wreg.QueryInfoKey( key )[1]
        values_list = []
        for i in range( values_count ):
            values_list.append( wreg.EnumValue( key, i ) )
    except ( WindowsError, EnvironmentError ):
        print( "Unable to Connect to the Window Registry and read keys" )
    finally:
        key.Close()
    return values_list

def NumberOfCOMPorts( values_list ):
    for subkey in iter( values_list ):
        print( "Name : " + subkey[0] )
        print( "Data : " + subkey[1] )

NumberOfCOMPorts( DetectCOMPorts() )

3.13.8. Installation

For using this plugin no further 3rd party libraries or drivers are necessary.

If you are using linux to open a serial connection and you are running itom without root privileges, which is recommended, you have to add your user to the dialout group, logout once and login again.

sudo adduser USERNAME dialout

where USERNAME ist the username under which you are running itom

3.13.9. Changelog

  • itom 1.2.0 is shipped with version 0.0.2 of serialIO

  • parameters ‘readline’ and ‘endlineRead’ are available in serialIO version >= 1.0.0