The serial port (a.k.a. COM-port, or RS232) is slowly disappearing from desktop and laptop computers, but it's still a very important tool for embedded engineers and people controlling equipment in general. The reason for this is that the serial port is very simple to set-up and use both from embedded software code (the vast majority of MCUs and controllers have UART ports) and from a PC.
A few years ago I've written several posts about working with the serial port using Perl and C++ on a Windows PC.
Since last year, having moved to Python, I've been happily employing the excellent PySerial module to do this. I want to write a couple of posts on combining PySerial with GUIs and dynamic charts, but first I'll have to explain how to set it up.
Installing a COM-port emulator
As I've mentioned, it's hard to come by a PC with real COM ports these days. Although there are many USB to RS-232 adapters on the market, the simplest thing to do if all you have is a single computer is installing an emulator. One that I recommend is called com0com. It is simple to install and works pretty well.
So, download com0com and follow the installation instructions carefully to create two virtual COM ports which are connected to each other. For me com0com created ports named CNCA0 and CNCB0 - in future versions the names may be different, but it really doesn't matter.
Make sure to read the Q&A section in the README file of com0com. For instance, I had turned on buffer overrun emulation because I didn't want the sender to hang when no receiver is connected at the paired port.
At this stage if you're familiar with HyperTerminal, you can check that the ports are indeed working and connected to each other [1].
Installing PySerial
Installing PySerial is very easy either by using easy_install or by downloading the source package and installing it with python setup.py install. PySerial doesn't require compilation on Windows.
A simple sender
Here's a simple sender script that opens a port, sends the string hello, and closes it:
import serial
port = "\\\\.\\CNCB0"
ser = serial.Serial(port, 38400)
x = ser.write('hello')
ser.close()
Note the port name. At least on Windows, when the serial port name is not one of COM[1..9], you'll have to use this funny quoting to make it work [2].
This sender can be tested by either opening HyperTerminal on port CNCA0 prior to running it (but make sure to set up the baudrate to 38400, parity to None, stop bits to 1 and hardware control to None) [3].
A non-blocking receiver
The beauty of PySerial is that it allows non-blocking reads from the serial port in a very simple way [4]. The following is a simple non-blocking receiver that listens on a port and prints out received data:
import serial
from time import sleep
port = "\\\\.\\CNCA0"
ser = serial.Serial(port, 38400, timeout=0)
while True:
data = ser.read(9999)
if len(data) > 0:
print 'Got:', data
sleep(0.5)
print 'not blocked'
ser.close()
What this does is basically sleep for half a second, and then check if new data has arrived on the serial port. If it has (the length of received data is not 0), the data is printed out. During the loop the receiver keeps printing not blocked to demonstrate that the serial.read call isn't blocking.
Closing the loop
Make sure HyperTerminal is not running. Run the receiver, and then in a separate window run the sender. You'll get something like the following output:
not blocked
not blocked
not blocked
not blocked
Got: hello
not blocked
not blocked
not blocked
The amount of not blocked messages depends on how long the receiver ran before and after you ran the sender.
[1] | It's worthwhile to learn how to use HyperTerminal if you're working with serial ports. HT is a convenient tool for "sanity checking" of your computer's ports and the programs you develop to communicate. |
[2] | Note that half of the backslashes are there to quote other backslashes. Using Python's raw strings it's definitely possible to write the port name as r"\\.\CNCB0", but I'm keeping the longer syntax for compatibility with standard notation and C code. |
[3] | I'm not just pulling these out of a sleeve. These settings are the defaults of PySerial. |
[4] | Non-blocking reads are very important in interactive applications - you surely don't want to hang your GUI until a new piece of data arrives on the serial port. |