Python reading and writing to tty


BACKGROUND: If you want, skip to the problem section

I am working on a front end for test equipment. The purpose of the front end is to make it easier to write long test scripts. Pretty much just make them more human readable and writable.

The equipment will be tested using a Prologix GPIB-USB Controller (see We found a tutorial at and did all of the steps, and it worked!

As we don’t have the test equipment yet, we wanted to write an emulator in Python using openpty. We do have the GPIB-USB Controller, just not what gets connected to that. I got the emulator working as a perfect replacement for the GPIB-USB. This means that I would follow the “GPIB on Debian …” tutorial (above) and get output that I programmed the emulator to return. The input and output were done in the same manner as the tutorial just reading and writing to/from a pty device (ie /dev/pts/2) instead of the tty (ie /dev/ttyUSB0).

Now that the emulator works, we want to write a front end that can be used to write scripts easily. The goal is to make a kind of macro system that writes a bunch of commands when we call a function.

PROBLEM: exists using both the emulator and the device

I am using the following Python functions to read, write, and open the tty/pty devices, but I am not getting the same result that I get if I just use echo and cat in bash.

tty =, os.O_RDWR), 100)
os.write(tty, "++ver")

for example, I would expect the following to be equivalent

$ cat < /dev/pty/2 &   # According to the tutorial, this must be run in parallel
$ echo "++ver" > /dev/pty/2
Prologix GPIB Version ...


tty ="/dev/pyt/2", os.o_RDWR), 100) # In separate Thread to be run in parallel
os.write(tty, "++ver") # in main thread

The output is very different, please explain why and how I can fix it.

FULL CODE is here:

Asked By: ignorance



Well, I asked too soon. I hope someone benefits from this self answer.

So this works to read and write from both the emulator and the actual device. I am not exactly sure why, and would appreciate an explanation, but this does work in all of my tests

import serial

class VISA:
    def __init__(self, tty_name):
        self.ser = serial.Serial()
        self.ser.port = tty_name
        # If it breaks try the below
        #self.serConf() # Uncomment lines here till it works

        self.addr = None

    def cmd(self, cmd_str):
        self.ser.write(cmd_str + "n")
        return self.ser.readline()

    def serConf(self):
        self.ser.baudrate = 9600
        self.ser.bytesize = serial.EIGHTBITS
        self.ser.parity = serial.PARITY_NONE
        self.ser.stopbits = serial.STOPBITS_ONE
        self.ser.timeout = 0 # Non-Block reading
        self.ser.xonxoff = False # Disable Software Flow Control
        self.ser.rtscts = False # Disable (RTS/CTS) flow Control
        self.ser.dsrdtr = False # Disable (DSR/DTR) flow Control
        self.ser.writeTimeout = 2

    def close(self):
Answered By: ignorance

You do not actually have to use any special module to read from TTY.
Option O_NOCTTY solved my problems with CDCACM example MCU app.
I’m sure it will work for you (as you work on Linux too).

#!/usr/bin/env python3

import io, os

tty = io.TextIOWrapper(
                os.O_NOCTTY | os.O_RDWR),

for line in iter(tty.readline, None):
Answered By: eugene-bright

Stumbled on this while looking into pty/tty usage in python.

I think the original code did not work because echo will add a newline and the python os.write will not.

This is shown in your self answer here self.ser.write(cmd_str + "n")

So the original code may have worked if it were os.write(tty, "++vern")

Answered By: hardkrash
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.