问题
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 when the GPIO input is sensed. This python script needs to be continuously running on the pizero in order to recognize when the flowmeter is activated and process the input.
The problem is that I also have a barcode scanner attached via USB to the pizero. I would like the pizero to also recognize when a barcode is scanned and process that input as well.
The pizero should then send a message that incorporates both information from the flowmeter and information from the barcode scanner.
Is there a way to do this in the same python script? How can I have the pizero listen and process from two inputs simultaneously? Would separating this into two different scripts be easier to implement, and if so, can I run them both at the same time and somehow unify the information they provide in a 3rd continuously running script?
Thank you!
Some clarifications per comments (thank you for the input):
- input pin from the flowmeter is
GPIO 17
which is an SPI connection - also have a 5V power and ground pin connected.
The script needs to be run at system startup. I will look at systemctl
as I hadn't heard of it until it was mentioned.
The Pi normally recognizes a barcode being scanned as keyboard input (i.e. a series of digits followed by a newline character) when the flowmeter is not attached.
When I send a message that includes the flowmeter and barcode information, I need to send a JSON object from python that includes both pieces of information and a time stamp of when the information was received.
This JSON object will be sent over wifi to a raspberry pi server with a static ip on the same home network as the pizero. The raspberry pi server has access to a Django database that should incorporate the JSON object information into the database.
回答1:
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.
回答2:
Another, perhaps simpler option might be to use Redis. Redis is a very high performance, in-memory data-structure server. It is simple to install on a Raspberry Pi, Mac, Linux Windows or other machine. It allows you to share atomic integers, strings, lists, hashes, queues, sets and ordered sets between any number of clients across a network.
So the concept might be to have a separate program monitoring the flowmeter and stuffing the current reading into Redis as often as you like. Then another separate program reading barcodes and stuffing them into Redis as often as you like. And finally, have a control program, potentially somewhere else altogether on your network that can grab both the values as often as it likes.
Note you could run the Redis server on your Raspberry Pi or any other machine.
So, here is the flowmeter program - just change host
to the IP address of machine running Redis:
#!/usr/bin/env python3
import redis
import time
host='localhost'
port=6379
# Connect to Redis
r = redis.Redis(host,port)
reading = 0
while True:
# Generate synthetic reading that just increases every 500ms
reading +=1
# Stuff reading into Redis as "fmReading"
r.set("fmReading",reading)
time.sleep(0.5)
Here is the barcode reading program:
#!/usr/bin/env python3
import redis
import time
from random import random, seed
host='localhost'
port=6379
# Connect to local Redis server
r = redis.Redis(host,port)
# Generate repeatable random numbers
seed(42)
while True:
# Synthesize barcode and change every 2 seconds
barcode = "BC" + str(int((random()*1000)))
# Stuff barcode into Redis as "barcode"
r.set("barcode",barcode)
time.sleep(2)
And here is the master control program:
#!/usr/bin/env python3
import redis
import time
host='localhost'
port=6379
# Connect to Redis server
r = redis.Redis(host,port)
while True:
# Grab latest flowmeter reading and barcode
fmReading = r.get("fmReading")
barcode = r.get("barcode")
print(f"Main: fmReading={fmReading}, barcode={barcode}")
time.sleep(1)
Sample Output
Main: fmReading=b'10', barcode=b'BC676'
Main: fmReading=b'12', barcode=b'BC892'
Main: fmReading=b'14', barcode=b'BC892'
Main: fmReading=b'16', barcode=b'BC86'
Main: fmReading=b'18', barcode=b'BC86'
Main: fmReading=b'20', barcode=b'BC421'
Main: fmReading=b'22', barcode=b'BC421'
Main: fmReading=b'24', barcode=b'BC29'
Note that in order to debug this, you can also grab any readings using the command-line interface to Redis from any machine on your network, e.g. in Terminal:
redis-cli get barcode
"BC775"
If you wanted to show the values in a Web-browser written in PHP, you can use the PHP bindings to Redis to get the values too - very convenient!
Of course, you can adjust the program to send the timestamps of the readings with each one - possibly by using a Redis hash rather than a simple key. You could also implement a queue to send messages between program using Redis LPUSH and BRPOP.
Keywords: Redis, list, queue, hash, Raspberry Pi
来源:https://stackoverflow.com/questions/58492080/pizero-w-connected-to-two-peripherals-gpio-and-usb-how-to-continuously-read-f