Search code examples
pythonpyqtpyqt4python-multithreading

raspberry pi threading


I am new to programming. I am jumping in with both feet and building a project which will read a GPIO pin (the machine is a Raspberry Pi) input voltages and ultimately will display on an lcdnumber widget in qt creator. For now I'm using a photocell as a surrogate for the o2 cell that will be the final component. Thanks to everyone posting code, I have working code for the photocell which prints voltages just fine. In order to get constant and continuous voltages I'm using a while true loop. Of course when I run the program, the loop takes over and the other part of my program doesn't run. The other part is a gui with the lcdnumber displays and some push buttons which will ultimately be connected to a digital pot. The gui runs great, by itself. The photocell code works great, by itself. Combining the two is where I have issues. From what I've read over the past few days, threading either in python or Qt seems to be the way to go. I've read all of the examples and explanations I could find and I'm stuck. The gui pops up and functions, the loop doesn't seem to run. The output of the photocell is printing to the terminal right now, or normally does when I run that code by itself, since I don't know yet how to connect that to the Qt lcd.

I would very much appreciate it if someone could have a look at my code and let me know where I am going wrong with the threading and anything else you may see since I am still learning. Any hints as to connecting the photocell signal to the Qt LCD would also be a huge help.

I am using Raspbian (a variant of Debian Linux) and Qt4/Qt Creator 3.2.1

Please let me know what other information you may need or want.

import sys
import RPi.GPIO as GPIO
import time
import re
from PyQt4 import QtGui, uic, QtCore
from PyQt4.QtCore import QThread
import spidev
import os
import threading
from threading import Thread

GPIO.setmode(GPIO.BCM) 
GPIO.setwarnings(False)

o2zero = 26
o2span = 19
cozero = 13
cospan = 6
co2zero = 5
co2span = 21

status = "nil"

light_channel = 0
spi = spidev.SpiDev()
spi.open(0,0)

class MyWindow(QtGui.QMainWindow): 

    def __init__(self):
        super(MyWindow, self).__init__()
        uic.loadUi('mainwindow.ui', self)
        self.show()
        QtCore.QObject.connect(self.o2_zero_up,QtCore.SIGNAL("clicked()"), self.o2zeroup) 
        QtCore.QObject.connect(self.o2_zero_down,QtCore.SIGNAL("clicked()"), self.o2zerodown)
        QtCore.QObject.connect(self.o2_span_up,QtCore.SIGNAL("clicked()"), self.o2spanup)
        QtCore.QObject.connect(self.o2_span_down,QtCore.SIGNAL("clicked()"), self.o2spandown)
        QtCore.QObject.connect(self.co_zero_up,QtCore.SIGNAL("clicked()"), self.cozeroup)
        QtCore.QObject.connect(self.co_zero_down,QtCore.SIGNAL("clicked()"), self.cozerodown)
        QtCore.QObject.connect(self.co_span_up,QtCore.SIGNAL("clicked()"), self.cospanup)
        QtCore.QObject.connect(self.co_span_down,QtCore.SIGNAL("clicked()"), self.cospandown)
        QtCore.QObject.connect(self.co2_zero_up,QtCore.SIGNAL("clicked()"), self.co2zeroup)
        QtCore.QObject.connect(self.co2_zero_down,QtCore.SIGNAL("clicked()"), self.co2zerodown)
        QtCore.QObject.connect(self.co2_span_up,QtCore.SIGNAL("clicked()"), self.co2spanup)
        QtCore.QObject.connect(self.co2_span_down,QtCore.SIGNAL("clicked()"), self.co2spandown)  
        QtCore.QObject.connect(self.close_button,QtCore.SIGNAL("clicked()"), self.gpiocleanup) 

    def o2zeroup(self):  
        GPIO.setup(o2zero, GPIO.OUT)   
        GPIO.output(o2zero, 1) 
    def o2zerodown(self): 
        GPIO.setup(o2zero, GPIO.OUT) 
        GPIO.output(o2zero, 0) 

    def o2spanup(self):  
        GPIO.setup(o2span, GPIO.OUT)  
        GPIO.output(o2span, 1) 
    def o2spandown(self): 
        GPIO.setup(o2span, GPIO.OUT) 
        GPIO.output(o2span, 0) 

    def cozeroup(self):  
        GPIO.setup(cozero, GPIO.OUT)  
        GPIO.output(cozero, 1) 
    def cozerodown(self):
        GPIO.setup(cozero, GPIO.OUT)  
        GPIO.output(cozero, 0) 

    def cospanup(self):
        GPIO.setup(cospan, GPIO.OUT)   
        GPIO.output(cospan, 1) 
    def cospandown(self): 
        GPIO.setup(cospan, GPIO.OUT) 
        GPIO.output(cospan, 0)

    def co2zeroup(self):
        GPIO.setup(co2zero, GPIO.OUT)   
        GPIO.output(co2zero, 1) 
    def co2zerodown(self): 
        GPIO.setup(co2zero, GPIO.OUT) 
        GPIO.output(co2zero, 0)

    def co2spanup(self):
        GPIO.setup(co2span, GPIO.OUT)   
        GPIO.output(co2span, 1) 
    def co2spandown(self): 
        GPIO.setup(co2span, GPIO.OUT) 
        GPIO.output(co2span, 0)   

    def gpiocleanup(self): 
        GPIO.cleanup() 

    def closeEvent(self, event): 
        print ("GPIO CleanUP")
        GPIO.cleanup() 
        event.accept()       

class O2_Channel(QtCore.QThread):

    def __init__(self):
        QtCore.QThread.__init__(self)

    def O2_Channel(self):

        ReadChannel(channel)
        adc = spi.xfer2([1,(8+channel)<<4,0])
        data = ((adc[1]&3) << 8) + adc[2]
        return data

        ConvertVolts(data,places)
        volts = (data * 3.3) / float(1023)
        volts = round(volts,places)
        return volts 

    def run(self):

        while True:

            light_level = ReadChannel(light_channel)
            light_volts = ConvertVolts(light_level,2)

            print "--------------------------------------------"
            print("Light: {} ({}V)".format(light_level,light_volts))

            light_channel = 0
            temp_channel  = 1

            delay = .3                         

            time.sleep(delay)

            self.O2_Channel.start()

if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window = MyWindow()
    sys.exit(app.exec_())

Solution

  • You must create an instance of your Thread and start it with the start () function from the main thread. If you want to display the data in your GUI you must do it from the main thread for this I have created a slot and a signal that is emitted when there is a new reading.

    I have also changed the connection style of connection between the signals and the slot to a more modern one.

    import sys
    import RPi.GPIO as GPIO
    import time
    import re
    from PyQt4 import QtGui, uic, QtCore
    from PyQt4.QtCore import QThread
    import spidev
    
    GPIO.setmode(GPIO.BCM) 
    GPIO.setwarnings(False)
    
    o2zero = 26
    o2span = 19
    cozero = 13
    cospan = 6
    co2zero = 5
    co2span = 21
    
    status = "nil"
    
    light_channel = 0
    spi = spidev.SpiDev()
    spi.open(0,0)
    
    class MyWindow(QtGui.QMainWindow): 
    
        def __init__(self):
            super(MyWindow, self).__init__()
            uic.loadUi('mainwindow.ui', self)
    
            self.o2_zero_upclicked.connect(self.o2zeroup) 
            self.o2_zero_down.clicked.connect(self.o2zerodown)
            self.o2_span_up.clicked.connect(self.o2spanup)
            self.o2_span_down.clicked.connect(self.o2spandown)
            self.co_zero_up.clicked.connect(self.cozeroup)
            self.co_zero_down.clicked.connect(self.cozerodown)
            self.co_span_up.clicked.connect(self.cospanup)
            self.co_span_down.clicked.connect(self.cospandown)
            self.co2_zero_up.clicked.connect(self.co2zeroup)
            self.co2_zero_down.clicked.connect( self.co2zerodown)
            self.co2_span_up.clicked.connect(self.co2spanup)
            self.co2_span_down.clicked.connect(self.co2spandown)  
            self.close_button.clicked.connect(self.gpiocleanup) 
    
            self.thread = O2_Channel()
            self.thread.changedValue.connect(self.onChangeValue)
            self.thread.start()
    
            self.show()
    
        def onChangeValue(self, values):
            light_level, light_volts = values
    
            print "--------------------------------------------"
            print("Light: {} ({}V)".format(light_level,light_volts))
    
    
        def o2zeroup(self):  
            GPIO.setup(o2zero, GPIO.OUT)   
            GPIO.output(o2zero, 1) 
        def o2zerodown(self): 
            GPIO.setup(o2zero, GPIO.OUT) 
            GPIO.output(o2zero, 0) 
    
        def o2spanup(self):  
            GPIO.setup(o2span, GPIO.OUT)  
            GPIO.output(o2span, 1) 
        def o2spandown(self): 
            GPIO.setup(o2span, GPIO.OUT) 
            GPIO.output(o2span, 0) 
    
        def cozeroup(self):  
            GPIO.setup(cozero, GPIO.OUT)  
            GPIO.output(cozero, 1) 
        def cozerodown(self):
            GPIO.setup(cozero, GPIO.OUT)  
            GPIO.output(cozero, 0) 
    
        def cospanup(self):
            GPIO.setup(cospan, GPIO.OUT)   
            GPIO.output(cospan, 1) 
        def cospandown(self): 
            GPIO.setup(cospan, GPIO.OUT) 
            GPIO.output(cospan, 0)
    
        def co2zeroup(self):
            GPIO.setup(co2zero, GPIO.OUT)   
            GPIO.output(co2zero, 1) 
        def co2zerodown(self): 
            GPIO.setup(co2zero, GPIO.OUT) 
            GPIO.output(co2zero, 0)
    
        def co2spanup(self):
            GPIO.setup(co2span, GPIO.OUT)   
            GPIO.output(co2span, 1) 
        def co2spandown(self): 
            GPIO.setup(co2span, GPIO.OUT) 
            GPIO.output(co2span, 0)   
    
        def gpiocleanup(self): 
            GPIO.cleanup() 
    
        def closeEvent(self, event):
            self.thread.stop()
            self.thread.quit()
            self.thread.wait()
            self.thread.deleteLater() 
            print ("GPIO CleanUP")
            GPIO.cleanup() 
            event.accept()   
    
    def ReadChannel(channel):
        adc = spi.xfer2([1,(8+channel)<<4,0])
        data = ((adc[1]&3) << 8) + adc[2]
        return data
    
    def ConvertVolts(data, places):
        volts = (data * 3.3) / float(1023)
        volts = round(volts,places)
        return volts    
    
    class O2_Channel(QtCore.QThread):
        changedValue = QtCore.pyqtSignal(tuple)
        def __init__(self):
            QtCore.QThread.__init__(self)
            self.mRunning = True
    
        def run(self):
            while self.mRunning:
    
                light_level = ReadChannel(light_channel)
                light_volts = ConvertVolts(light_level,2)
    
                print "--------------------------------------------"
                print("Light: {} ({}V)".format(light_level,light_volts))
                self.changedValue.emit((light_level, light_volts))
                light_channel = 0
                temp_channel  = 1
                delay = .3                         
                time.sleep(delay)
    
        def stop(self):
            self.mRunning = False
    
    if __name__ == '__main__':
        app = QtGui.QApplication(sys.argv)
        window = MyWindow()
        sys.exit(app.exec_())