PyQt4: Getting into action

After a short tutorial, let’s make a “Serial Data Reader application”.
PROBLEM STATEMENT: The data has to be read from the serial port and to be displayed to a text box in the form.

SOLUTION: The first step of every solution is to make the Flow-Chart. The flow chart for this problem will be like this.

flowchart

The first step is to open the serial port. This will use pyserial package which has to be imported to the script using this line. 😛
import serial

However if the package is missing then it can be downloaded from the https://pypi.python.org/pypi/pyserial
Unpack it and follow the instruction written inside to install it.
Complete API list is provided on this page:

http://pyserial.sourceforge.net/pyserial_api.html
To open serial port:
try:
      self.port = serial.Serial(‘/dev/ttyUSB0’, 9600) 
      print”serial port opened” #print on the terminal
except serial.SerialException as error:
      print(“could not open serial port ‘{}’: {}”.format(‘/dev/ttyS0’, error))

To read data from the serial port:
#to read one byte
byte=self.port.read(size=1)

#to read x number of bytes
bytes=self.port.read(x)

#to read a complete line ended by ‘\n’
line=self.port.readline(size=None, eol=’\n’)

Note: The readline API didnot work well in my case. and I got following error working with ttyUSB0
TypeError: readline() takes no keyword arguments.

Dont know the reason. I read somewhere that the “readline(size=None, eol=’\n’)” API is supported in older pyserial versions. But i could not justify it.

2. Second step is to decide when to take the data from the serial port:

Basically, the application should continuously read the serial port for whole time. but putting the serial read program in while loop is a very huge mistake.

I got this problem while coding without using my brains 😛
while 1:
.        testbyte=port.read(1)
.       if(testbyte==”$”):
.              byte=port.readline(size=None,eol=’\n’,timeout=1)
.              time.sleep(1)
.              textEdit.setText(str)

Note: sys.exit(app.exec_()) – itself executes the application in an infinite loop and takes care of all the signals being generated. Using another while loop in the GUI application is “Strictly prohibited” as it will never allow the app.exec_() to take control and hence no GUI will appear.

To tackle this problem, There are two possible solution to this problem:
1. We can use a thread to run in background which will continously monitor the serial port. But this make this simple application more complex and teadious.
2. We can use some timer event to read the incoming serial bytes after a specific time-interval allowing the GUI to take care of timer and the corresponding slot function. This method is simple and is discussed here:

self.timer=QtCore.QTimer()
QtCore.QObject.connect(self.timer, QtCore.SIGNAL(“timeout()”), self.readData)
self.ctimer.start(1000)
#1000 ms is the timeout time for timer.

def readData(self):
print”timer function” 
str=self.port.read(9)
self.textEdit.setText(str)

This will make the serial data to be read every second.

Note: This is a very raw and basic form of reading the bytes from the serial port, where no conditions have been imposed to receive the data in correct format. As soon as Arduino boots-up, it will start sending the data. Now depending upon the time lag in opening this application, the alignment of data will certainly be different.

The whole code is presented here:

Pardon me for the alignment. Spaces dont work here in the blog. I guess you can figure it out.. however am attacing the python script file too if you have problem with sorting the alignment.

#!/usr/bin/python

import sys, serial,time
from PyQt4 import QtGui, QtCore

class main_window(QtGui.QWidget):
def __init__(self):
super(main_window, self).__init__()
self.initUI()
def initUI(self):
self.textEdit=QtGui.QTextEdit()
label=QtGui.QLabel()
label.setText(“12 bytes Read:”)

self.vlayout=QtGui.QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(textEdit)

self.setLayout(vbox)
self.setGeometry(0,0,150,200)
self.setWindowTitle(‘Serial port Reader’)

self.ctimer = QtCore.QTimer()
QtCore.QObject.connect(self.ctimer,                                                                         QtCore.SIGNAL(“timeout()”), self.readData)

               self.ctimer.start(1000) #to start timer with this time period
self.portOpen() #call the port open function


def readData(self):
print”timer function”
str=self.port.read(9)
self.textEdit.setText(str)

def portOpen(self):
try:
self.port = serial.Serial(‘/dev/ttyUSB0’, 9600)
print”serial port opened”
except serial.SerialException as e:
print(“could not open serial port ‘{}’: {}”.format(‘/dev/ttyS0’, e))

if __name__ == ‘__main__’:
app = QtGui.QApplication(sys.argv)
ex = main_window()
ex.show()
sys.exit(app.exec_())

The Corresponding arduino code snippet and outputs are shown in here.

 

arduino is sending "abc123xyz" continuously
arduino is sending “abc123xyz” continuously

 

 

 

 

 

 

 

 

 

 

 

 

 

Corresponding output screen in terminal and Qt form is :

Bytes read are shown here
Bytes read are shown here

 

 

Note:  As you can deduce from the figure, bytes are not displayed in the correct order. The data can be accessed in some arranged way also by adding some check bytes like ‘\n’ or ‘$’ ($abc123xyz) where the first byte can be read and checked for $ then only latter bytes are accepted.

Advertisements

About Shantanu Sharma

Currently working as Software R&D member in Samsung R&D India.

Posted on October 23, 2013, in Qt: The Face Of Embedded and tagged , , , , , . Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s