Lecture 39 GUI - PyQt V

Now, let's start the adventure of data acquisition using serial communication and Python!

When you start planning on a project, the first thing is to know the final purpose and all the objects that will be used for to achieve this purpose. The most common strategy is to test every single part of the entire project one-by-one using established equipment or tools so we are adding a new variable to the system one-by-one. If it doesn't work, we know where is the problem.

Let's take a look at the big picture of the project now:

There are several 'variables' for me before I start this project:

a. I don't know if the ADC converter IC chip will work properly with the RPI board.

b. I don't know if SPI digital communication between the ADC and the RPI works.

c. I don't know how to build the GUI with Python to plot a live data (animation).

However, based on our experience (if you took my ENGR 104), we know that the ADC converter on the Arduino Board works fine with Windows and Matlab. Now, let's don't touch the ADC converter IC chip yet, but let's use the 'Established Arduino Serial Communication Code', to test if PyQt Live Plot in Python works in the same way as Matlab, so we can get rid of the 
PyQt Live Plot 'Variable' from the Design Flow, which means we know PyQt Live Plot works and if the final system doesn't work, the problem must be somewhere else.

1. Arduino ADC + Windows + Matplotlib + PySerial (to test if PySerial works)

-------------------------update your RPI required

Matplotlib in RPI: (this is the only way I found works for installing matplotlib to RPI)
1) update first: sudo apt-get update
2) install matplotlib to your RPI: sudo apt-get install python3-matplotlib

-------------------------update your RPI required-------------------------

Refer to the serial communication lectures from Nov 2 to Nov 30 at this page:

If you were not in my ENGR 104, it is fine, just refer to the lectures on Nov 2 and Nov 5 will be good enough.

Frist, connect the potentiometer to the Arduino board as follows:

Open the portable Arduino IDE and download this code to the board:

Check the port being used by the Arduino board:

Run the code to the Arduino board to let the data stream to your PC.

Now we need a Python code on your PC to handle these data stream in through the USB port:

Twist the potentiometer when the data is being collected so you can get a curve:

A quick question: Given that the resolution of the ADC is 2^10. In the received data, why the maximum number is 1024 and why the minimum number is 0?

You don't have to just collect 200 points, you can collect more if you like but it will take longer time to complete it.

We saw the data was received and plotted using
Arduino ADC + Windows + Matplotlib + PySerial, now, the conlusion is the PySerial Class works. This is like if you'd like to drive to Denver, you know the highway is not jammed or broken or covered by snow. The highway is good and clean, you just need to find a ride to get there.

2. Use the Class 'pyqtgraph' to plot the data instead of using the Class 'matplotlib' (to test pyqtgraph offline)

Since we are going to use the 'pyqtgraph' to plot the data in our GUI, so let's test some simple plotting code with it:

The result from the code above:

Let's try to plot it in a GUI

Here is the result:

The pushbutton and the cells in this form do not have any functions behind it. The layout designated an area at the right side of the entire window

3. Live plot in a PyQt GUI using pyqtgraph (to test if the live plot with 'pyqtgraph' and PyQt5 works)

Here we build a Class called 'ExampleApp' to host the PlotWidget of pyqtgraph. The ExampleApp Class has a parent class of 'QMainWindow'. Refer to the 'PyQt GUI' lecture series here to recall the details of this inheritance:

The code for this application is:

This code is running independtly without calling any other QtGui .py files generated by the Qt Designer. You don't have to code up everything like what I did here in the code above in real-life. I'll show you the example of utilizing Qt Designer to make the GUI interface design easier in the end of this tutorial. Now, let's jsut use this code to complete the task first.

The hardware connection is still the same:

The live plot will look like this:

Now, we are pretty confident with PyQt5 and pyqtgraph for live plot received data in a GUI window. These libraries can be trasferred to the RPI board to do the similar job. We are about 70% done for our entire project.

4. ADC (SPI communication) to RPI Communication

-------------------------update your RPI required (Install pyqtgraph to the RPI:)-------------------------

sudo apt-get install python-pip

sudo pip3 install pyqtgraph

-------------------------update your RPI required-------------------------

The Raspberry Pi do not have the analog input pins which means that you cannot get the analog inputs with Raspberry Pi but we can overcome this problem by using the analog to digital converter which will help in interfacing the analog sensors with the Raspberry Pi. So in this tutorial, you are going to learn about MCP3008 Raspberry Pi interfacing.

We are going to use a 10-bit Analog-to-Digital Converter (ADC) - MCP 3008 to aquisite the analog voltage data from our potentiometer and send the data to the RPI board. There are many tutorials online:


It doesn't matter which one you want to use. The SPI communication protocol is different from the UART serials communication (which the PySerial does). So we are not going to use the PySerial to talk to the sensor anymore. However, all these communication protocol is complicated in logic and you don't need to know what is happening behind the scenes. You just need to know how to connect the wires together and how to use them using standard libraries.

You must be wondering why we don't use the UART communication to send data from Arduino to the RPI board since we already have the PySerial code. The reason is the Arduino is based on the 5 V logic but the RPI board uses the 3.3 V logic. You must use a Level Shifter between these two devices to bring them to the same digital logic which introduces unnecessary complexity to this class. Instead, we will use a single IC chip (MCP 3008) to replace the Arduino Board for the data acquisition.


MCP3008 is a 10bit 8-channel ADC (Analog to digital converter) which uses the SPI bus protocol and allows you to get the analog inputs with Raspberry Pi. It is cheap and don’t require any additional components with it. It gives you 8 analog inputs and it uses just four pins of Raspberry Pi excluding the power and ground pins. MCP3008 is a 10 bit ADC so it will give us output up to (2 to the power of 10) = 1023. So the output will be a range from 0-1023 where 0 means 0V and 1023 means 3.3V

MCP3008 Pin out

The pin1 of the MCP3008 starts from below the half circle you can see in the pin out diagram. MCP3008 ADC has a total of 16 pins out of which 8 pins are for taking the analog input. The analog input pins are from CH0-CH7 (Pins 1-8). On the other side, we have different pins which are as follows
DGND is digital ground pin for the chip.
CS is the chip select pin.
DIN is the data input pin from the Raspberry Pi.
DOUT is the data output pin.
CLK is the clock pin.
AGND is the analog ground pin.
VREF is the analog reference voltage. Connect to 3.3V. You can change it if you want to change the scale.
VDD is the power pin for the chip.

Hardware SPI (the 'spidev' class docs)

To use hardware SPI first make sure you've enabled SPI using the raspi-config tool. Be sure to answer yes to both enabling the SPI interface and loading the SPI kernel module, then reboot the Pi. Check you can see a /dev/spidev0.0 and /dev/spidev0.1 device when you run the ls -l /dev/spi* command before continuing. Now wire the MCP3008 to the Raspberry Pi as follows:

MCP3008 VDD to Raspberry Pi 3.3V
MCP3008 VREF to Raspberry Pi 3.3V
MCP3008 AGND to Raspberry Pi GND
MCP3008 DGND to Raspberry Pi GND
MCP3008 CLK to Raspberry Pi SCLK
MCP3008 DOUT to Raspberry Pi MISO
MCP3008 DIN to Raspberry Pi MOSI
MCP3008 CS/SHDN to Raspberry Pi CE0

Be careful about the SCLK/MISO/MOSI pins. There are two SPI modules on the RPI board. Use the ones highlighted below:

The MCP3008 connects to the Raspberry Pi using a SPI serial connection. You can use either the hardware SPI bus, or any four GPIO pins and software SPI to talk to the MCP3008.  This way you can access each of the legs of the chip from the breadboard. Note that the orientation of the chip matters! Be sure to place it with the half circle indention and dot towards the top. See the photo below for an example:

The first acquired SPI data:

Open the terminal and type the below command:

Then go to the interfacing options.

Connect the circuit: (Please do not try to copy the connection in the figures below. Refer to the connection list and the pin map of the RPI in the beginning of this tutorial section)

Write the following code into a .py file:

** To understand the SPI communication protocol, you need to understand DIGITAL LOGIC first. It's good we covered this before.

The datasheet for MCP3008. Please make sure you know the following things since they matter:
1) MCP3008 is a 10-bit ADC.
2) RPI is a 16-bit computer.
3) RPI has one SPI port but two SPI devices, which means the CPU in the RPI only has one SPI port but they are delivered to two devices. CE0 and CE1 are 'Chip Enable' pins to select which to be used. CE0 selects the uppper one, CE1 selects the lower one.
'spi.open(0,0)' opens port 0 device 0.
4) RPI is the master, MCP3008 is the slave.

In MCP3008's datasheet, we can found the SPI communication protocol of the MCP3008:

The communication between PI and MCP are bi-directional. The PI needs to write to MCP first to set up the configurations, and then read from MCP to receive the data.

adc = spi.xfer2([1, (8+channel)<<4, 0])

'spi.xfer2()' will start the conversation. This will send '1 (decimal)' to MCP first. '1 (decimal)' represents one byte, which is '0000 0001'. The zeros in the front doesn't matter but the last '1' matters. This '1' will turn on the Read Mode of MCP. So MCP starts listening. Then send '8+channel' is actually '0000 1000 + the 3-bit channel address'. The lower 4 bits will be '1000 + 3-bit address' and then being shifted by 4 bits to the left to move them to the upper 4 bits.

After the 'shifting', '1000 0000' will be written to MCP and setup 'SGL' and address D2D1D0 = 000. The lower 4 zeros doesn't matter.

Then, finally, give a '0' to let MCP 'WRITE' to the PI.

After this configuration, the MCP will start STREAMING the digital data to the PI from DOUT. The data will be saved in variable 'adc' in the PI's memory.

In variable 'adc', data are being sent in Bytes and stored in the PI in the form of WORDs, so the first received byte, for example 'xxxx xx01' should be ANDed to '0000 0000 0000 0011' bit-wisely to only keep the lower 2 bits which are the real data bits. Then the result should be shifted by 8 bits to the left to make '0000 0001 0000 0000', and then added to the second received byte to make up this 16-bit data in the PI's memory (only the lower 10 bits are data, all the rest bits in the memory are zeros).

Finally, 'data' will be the final result.

Run this script and if you see the following results then Congratulations!

5. Live plot SPI data in the GUI

Now, let's merge this code into the Serial Communication code in Section 3:

Run this script, if you see the following GUI with a live plot of the data from your potentiometer, show it to me and I'll give you the credit for this section.

Please not that I didn't polish this GUI. There are many things can be added to make this little Embedded Data Acquisition System more complete:

1. Add pushbuttons to start/stop the acquisition.
2. Add a menu bar to save the data to a file.
3. Enable a multi-line data live plot in the same window. (different colors for different data lines).
4. Add a title, modify the label and the style of the axis.
5. ..... and many more... until it looks like a commercial product.

As long as the data acquisition is succesful, all other polishments are easy.....

6. Push your updated code on your RPI back to GitHub to update the repository.


Follow the tutorial and repeat all the work I've done there. Demonstrate your work to me in class. Raise your hand when you are done for each task so I can check these boxes on mygrade paper. 

Section 1: 10 points
Section 2: 10 points
Section 3: 20 points
Section 4: 20 points
Section 5: 30 points
Section 6: 10 points

(There are no due dates for these demonstration tasks here and in the future. As long as you can get it done before the final exam, you'll be able to get the credit for these. You just need to demonstrate it to me in person).