How to set up USB-serial program connection #1

Meghan V
4 min readSep 26, 2021
By Louis Reed, Unsplash
$300? really?

01 Python and macOS

The use of usb-serial cable applies when the connection you aim to set up is between you computer or a mainframe that has only USB port and a device with only serial port. The computer also hosts the program by which we’d like to initiate the communication and control the flow to the terminal/device.

| Computer : My program | USB→ USB-SERIAL→ serial RS232 | Device|

Some facts

  • usb-serial cable relies on the chip that bridges usb protocols <->serial.
  • drivers are required to be installed, which usually can be found on manufacturer website

Cable used

USB-Serial Converter: ATEN USB to RS-232 Adapter (35cm) UC232A

System: macOS

Language: Python

Lib: PySerial

Install PySerial:

  • install from pip: python -m pip install pyserial
  • install pip in macOS ( in case you don't have pip already):

Use PySerial:

API document:

Connect

Connect usb — serial with Mac & the device

Install USB-serial converter drivers for macOS. Drivers are usually developed by the converter/bridge controller chip manufacturers of the USB-Serial converter. For example, ATEN USB to RS-232 Adapter (35cm) UC232A is using “PL2303hxd” chip (sorry forgot the name of the manufacturer, a Taiwan company as I recall).

Find port

in macOS terminal, type ioreg -p IOUSB, check if you can see “USB-serial controller D” (for ATEN UC232A) or any other serial controller name under “USB 2.0 hub” or “USB 3.0 hub”, or simply ls /dev/cu* to check if there is any “usbserial” port.

In python, use serial.tools.list_ports.comports() (offered by pyserial) to enumerate all the ports. check if any descriptor fits the USB-serial converter product you use. Record the port for later use.

python
import serial
from serial.tools import list_ports
enmu_ports = enumerate(list_ports.comports())
port = ""
for n, (p, descriptor, hid) in enmu_ports:
print(p, descriptor, hid)
if descriptor == “USB-Serial Controller D”:
port = p

I don’t know about your Mac, but there are 2 ports

/dev/cu.usbserial-14110/dev/cu.usbserial-14

that have the same descriptor in my Mac. No idea why. Pick one that functions… I picked the longer one as it is said that the number at the end of the port indicate its baud rate. So the longer one definitely looks more like it…

Configure the port

port = “/dev/cu.usbserial-14110”serial = serial.Serial()serial.port = portserial.baudrate = 115200serial.timeout = 1

serial.timeout: if set 0, means no wait. Immediately read/write and consider it done. Unit is second. For example if serial.timeout=2 and call read(1024) , it means: if received=1024 or 2seconds expired, it goes to execute next line.

serial.baudrate speed, I think by default its 9600 but its like the lowest? It is device - specific.

serial.parity odd or even

serial.stopbits by default 1

There are other parameters in pyserial that you can set to control the way that serial port transmits data but basically you can leave all default, but configure your desired baud rate, and timeout.

Write

  1. before sending anything, the most important thing is to pre-process the data. Sending char string is simple. But sending hex strings requires a bit of conversion — it takes bytes or bytearray:
message = “80FE0EAA81B9CDA60003”#message_hex = message.encode(“hex”) #python v2.7message_bytes = bytes.fromhex(message) #python V3+message_barry = bytearray.fromhex(message) #python v3+serial.open()# here apply some logics to make sure port is really openedserial.write(message_barry)serial.flush()

Read

  1. read is a little bit more tricky; usually it happens after sending, but may jam the main thread (requires waiting), and needs to be handled with delicacy.
  2. Depending on the actual device you deal with, receiving too soon or too late, could lead to complications. You may develop your own strategies, either wait it out on main thread or use a separate thread.
  3. but anyways the vanilla receiving is:
serial.timeout = 100 #up to youreceived = serial.read(1024) # either it collects all 1024 bytes or timeout, whichever happens ealier

4. The pyserial has a thread waiting to receive. Only after read() is explicitly called, it spits the data when given conditions are met. But if you fail to extract the data in a given time, the newer data might flush the receive buffer (up to its capacity). Pretty much you should already know what to expect of the data transmission to and from the device. So, go with the designated flow.

--

--

Meghan V

Full-stack programmer, product manager, politics enthusiast and won't shut up about it.