PiZero W connected to two peripherals (GPIO and USB): how to continuously read from both at same time?

后端 未结 2 1619
说谎
说谎 2020-12-21 16:45

I have a raspberry pizero W that is connected via GPIO pins to a flowmeter and USB to a barcode scanner. I have python script that uses a callback function to be alerted wh

2条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-21 17:27

    Updated Answer

    I have added some code for the barcode reader. I made it so that the barcode reader takes a variable amount of time, up to 5 seconds to take a reading and the flowmeter takes a constant 0.5 seconds so you can see that different threads are progressing at different rates independently of one another.

    #!/usr/bin/env python3
    
    from threading import Lock
    import threading
    import time
    from random import seed
    from random import random
    
    # Dummy function to read SPI as I don't have anything attached
    def readSPI():
        # Take 0.5s to read
        time.sleep(0.5)
        readSPI.static += 1
        return readSPI.static
    readSPI.static=0
    
    class FlowMeter(threading.Thread):
        def __init__(self):
            super(FlowMeter, self).__init__()
            # Create a mutex
            self.mutex = Lock()
            self.currentReading = 0
    
        def run(self):
            # Continuously read flowmeter and safely update self.currentReading
            while True:
                value = readSPI()
                self.mutex.acquire()
                self.currentReading = value
                self.mutex.release()
    
        def read(self):
            # Main calls this to get latest reading, we just grab it from internal variable
            self.mutex.acquire()
            value = self.currentReading
            self.mutex.release()
            return value
    
    # Dummy function to read Barcode as I don't have anything attached
    def readBarcode():
        # Take variable time, 0..5 seconds, to read
        time.sleep(random()*5)
        result = "BC" + str(int(random()*1000))
        return result
    
    class BarcodeReader(threading.Thread):
        def __init__(self):
            super(BarcodeReader, self).__init__()
            # Create a mutex
            self.mutex = Lock()
            self.currentReading = 0
    
        def run(self):
            # Continuously read barcode and safely update self.currentReading
            while True:
                value = readBarcode()
                self.mutex.acquire()
                self.currentReading = value
                self.mutex.release()
    
        def read(self):
            # Main calls this to get latest reading, we just grab it from internal variable
            self.mutex.acquire()
            value = self.currentReading
            self.mutex.release()
            return value
    
    if __name__ == '__main__':
    
        # Generate repeatable random numbers
        seed(42)
    
        # Instantiate and start flow meter manager thread
        fmThread = FlowMeter()
        fmThread.daemon = True
        fmThread.start()
    
        # Instantiate and start barcode reader thread
        bcThread = BarcodeReader()
        bcThread.daemon = True
        bcThread.start()
    
        # Now you can do other things in main, but always get access to latest readings
        for i in range(20):
            fmReading = fmThread.read()
            bcReading = bcThread.read()
            print(f"Main: i = {i} FlowMeter reading = {fmReading}, Barcode={bcReading}")
            time.sleep(1)
    

    Sample Output

    Main: i = 0 FlowMeter reading = 0, Barcode=0
    Main: i = 1 FlowMeter reading = 1, Barcode=0
    Main: i = 2 FlowMeter reading = 3, Barcode=0
    Main: i = 3 FlowMeter reading = 5, Barcode=0
    Main: i = 4 FlowMeter reading = 7, Barcode=BC25
    Main: i = 5 FlowMeter reading = 9, Barcode=BC223
    Main: i = 6 FlowMeter reading = 11, Barcode=BC223
    Main: i = 7 FlowMeter reading = 13, Barcode=BC223
    Main: i = 8 FlowMeter reading = 15, Barcode=BC223
    Main: i = 9 FlowMeter reading = 17, Barcode=BC676
    Main: i = 10 FlowMeter reading = 19, Barcode=BC676
    Main: i = 11 FlowMeter reading = 21, Barcode=BC676
    Main: i = 12 FlowMeter reading = 23, Barcode=BC676
    Main: i = 13 FlowMeter reading = 25, Barcode=BC86
    Main: i = 14 FlowMeter reading = 27, Barcode=BC86
    Main: i = 15 FlowMeter reading = 29, Barcode=BC29
    Main: i = 16 FlowMeter reading = 31, Barcode=BC505
    Main: i = 17 FlowMeter reading = 33, Barcode=BC198
    Main: i = 18 FlowMeter reading = 35, Barcode=BC198
    Main: i = 19 FlowMeter reading = 37, Barcode=BC198
    

    Original Answer

    I would suggest you look at systemd and systemctl to get your application started at every system startup - example here.

    As regards monitoring two things at once, I would suggest you use Python's threading module. Here is a quick example, I create an object subclassed from threading that manages your flow meter by constantly reading it and holding the current value in a variable that the main program can read at any time. You could start another similar one that manages your bar code reader and run them I parallel. I didn't want to do that and confuse you with double the code.

    #!/usr/bin/env python3
    
    from threading import Lock
    import threading
    import time
    
    # Dummy function to read SPI as I don't have anything attached
    def readSPI():
        readSPI.static += 1
        return readSPI.static
    readSPI.static=0
    
    class FlowMeter(threading.Thread):
        def __init__(self):
            super(FlowMeter, self).__init__()
            # Create a mutex
            self.mutex = Lock()
            self.currentReading = 0
    
        def run(self):
            # Continuously read flowmeter and safely update self.currentReading
            while True:
                value = readSPI()
                self.mutex.acquire()
                self.currentReading = value
                self.mutex.release()
                time.sleep(0.01)
    
        def read(self):
            # Main calls this to get latest reading, we just grab it from internal variable
            self.mutex.acquire()
            value = self.currentReading
            self.mutex.release()
            return value
    
    if __name__ == '__main__':
    
        # Instantiate and start flow meter manager thread
        fmThread = FlowMeter()
        fmThread.start()
    
        # Now you can do other things in main, but always get access to latest reading
        for i in range(100000):
            fmReading = fmThread.read()
            print(f"Main: i = {i} FlowMeter reading = {fmReading}")
            time.sleep(1)
    

    You could look at using logging to coordinate and unify your debugging and logging messages - see here.

    You could look at events to let other threads know something needs doing when something reaches a critical level - example here.

提交回复
热议问题