Setting up Python to work with the serial port
July 30th, 2009 at 7:21 amThe 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. |
Related posts:

July 30th, 2009 at 15:31
For better performance don’t try to read 9999 bytes because it will block for your entire read timeout waiting for 9999 bytes. Then it will return the actual number of bytes read. The following should work better:
self.serial.read(self.serial.inWaiting())
This method checks how many bytes are currently waiting to be read and then reads that exact number of bytes.
July 30th, 2009 at 15:42
@mgenti,
No – because I’ve set
timeout=0in the initialization of the port, thereadcall is non-blocking.ser.read(9999)says “read up to 9999 bytes, but return immediately even if there are less bytes to read, even 0″.July 30th, 2009 at 16:02
There is still some time spent waiting even with a timeout of 0 with a big read value:
import timeitsetup_stmt = """
import serial
ser = serial.Serial('COM1', 38400, timeout=0)
"""
t=timeit.Timer("ser.read(9999)", setup_stmt)
print t.timeit()
t=timeit.Timer("ser.read(ser.inWaiting())", setup_stmt)
print t.timeit()
In python 2.5.4 I get the following times:
20.0060160043
14.5915722404
July 30th, 2009 at 22:30
Any idea if there is a similar COM port emulator under Linux?
July 31st, 2009 at 01:09
I am currently working on a little project that combines pyserial and pyqt.
I have a thread that reads the comm port with pyserial and stuffs the data in a queue.
I have gotten pyQT to display the data live in a edit box but I would like to be able to have a scrolling graph of the data. I noticed that you had a post about a live graph with WxPython. do you plan do implement that in pyQT?
Thanks,
Seth
July 31st, 2009 at 06:28
@mgenti:
You’re right – I can confirm this benchmark on my PC. Looking at the code of serialwin32.py in PySerial, I can only say that this probably happens when the read queue is empty. When it isn’t, the difference gets smaller.
In any case, we’re talking about a very small time here.
timeitruns 1000000 times. So that’s 20 microseconds per invocation versus 14 microseconds. Shouldn’t have a big impact, I think.@Ted:
I don’t know about Linux sorry, your Google search is as good as mine here.
@Seth:
Yes, I plan to write code combining PyQt and PySerial. I’ve already written several programs at work, just have to pick up what I want to show here in the blog. P.S. Your Arduino projects look like fun!
August 1st, 2009 at 04:36
I am most interested in the graphing of live data. Currently I am messing around with the Matplotlibwidget in PyQT. I am redoing some aspects of the arduino project so I can have a better serial interface so I can analysis the data better.
Thanks,
Seth
August 15th, 2009 at 12:59
Very interesting subject. I’m doing something similar but using pyVISA and PyQT. Could you write something about multitasking in case of communication with several devices with dedicated GUI for each of them ?
August 15th, 2009 at 15:09
@untumbe,
Take a look at this post. The code uses a threaded COM monitor. That object can be instantiated several times for different ports, all monitors running in parallel, if that’s what you need.
February 12th, 2012 at 05:16
Hi, I’m in NFC project that using your code above, thanks to your A Non Blocking Receiver code.
I’m using PyQt and PySerial.
But when I tried to execute the non-blocking receiver, the window is Not Responding.
Why it can happen? And please give me a solution.
Many thanks
February 12th, 2012 at 05:20
Ocky,
What port name did you use? Note that the port names used in this post are provided by the com0com emulator.
February 12th, 2012 at 06:37
I use com28. Is it need different handling?
February 12th, 2012 at 06:40
Ocky,
I’m not sure. It’s been ages since I’ve last touched the serial port. I suggest you take the code you’re trying to run and doesn’t work and ask about it on StackOverflow – you have a much better chance of getting a good answer there.
March 10th, 2012 at 19:56
for Python32, change
print ‘Got:’, data
print ‘not blocked’
to
print (‘Got:’, data)
print (‘not blocked’)
I am using Teraterm. So I change the com to COM4 instead of CNCA0
program is perfectly working. Thanks, Eli!