Setting up Python to work with the serial port

July 30th, 2009 at 7:21 am

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.

http://eli.thegreenplace.net/wp-content/uploads/hline.jpg

[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:

  1. Listing all serial ports on Windows with Python
  2. Frames and protocols for the serial port – in Python
  3. perl master, C++ slave, bound for serial port programming
  4. A “live” data monitor with Python, PyQt and PySerial
  5. once again: perl, serial ports and what’s between them

17 Responses to “Setting up Python to work with the serial port”

  1. mgentiNo Gravatar Says:

    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.

  2. elibenNo Gravatar Says:

    @mgenti,

    No – because I’ve set timeout=0 in the initialization of the port, the read call 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″.

  3. mgentiNo Gravatar Says:

    There is still some time spent waiting even with a timeout of 0 with a big read value:
    import timeit

    setup_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

  4. TedNo Gravatar Says:

    Any idea if there is a similar COM port emulator under Linux?

  5. Seth KingNo Gravatar Says:

    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

  6. elibenNo Gravatar Says:

    @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. timeit runs 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!

  7. Seth KingNo Gravatar Says:

    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

  8. untumbeNo Gravatar Says:

    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 ?

  9. elibenNo Gravatar Says:

    @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.

  10. Ocky HarliansyahNo Gravatar Says:

    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 :)

  11. elibenNo Gravatar Says:

    Ocky,

    What port name did you use? Note that the port names used in this post are provided by the com0com emulator.

  12. Ocky HarliansyahNo Gravatar Says:

    I use com28. Is it need different handling?

  13. elibenNo Gravatar Says:

    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.

  14. BryanNo Gravatar Says:

    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!

  15. tsNo Gravatar Says:

    Asynchronous serial ports haven’t gone anywhere. They’re just buried inside USB devices and have automatic enumeration and other features built in. UARTs remain the same.

  16. benNo Gravatar Says:

    Is pySerial the standard way to access the serial port in python and pyQt?

    I’ve seen people use QT’s QtSerialPort and ‘QextSerialPort’ (libusb) but not sure how it fits together with python/pyQt?

  17. dickNo Gravatar Says:

    Dear Eli,

    the old PowerBasic had a nice interrupt statement as follows:

    ON COM(..) GOSUB … getmydata

    Q? does Python’s Pyserial have an equivalent statement / construct??

    regards,
    Dick

Leave a Reply

To post code with preserved formatting, enclose it in `backticks` (even multiple lines)