Multiplexed RFID array not reading - python

I've got a 4x4 grid of RFID reader/writers. Borrowed circuit from here: RFGRID GitHub
The path for the circuit diagram is: docs/cctSchematics/rfgrid - Modular Array (schematic).svg
I've got the circuit working but my code for reading from it is broken. The readers aren't finding any of the tags I wave in front of them. I'm trying to loop through the multiplexed data and read it from a Raspberry Pi 3. Here's my python script:
import RPi.GPIO as GPIO
import mfrc522 as MFRC522
# Setup GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(11, GPIO.OUTPUT) # S0
GPIO.setup(12, GPIO.OUTPUT) # S1
GPIO.setup(15, GPIO.OUTPUT) # S2
GPIO.setup(16, GPIO.OUTPUT) # S3
# Setup address loop
board_state = []
s3_cycle = [0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1]
s2_cycle = [0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1]
s1_cycle = [0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1]
s0_cycle = [0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1]
try:
while (True):
board_state_temp = []
# Loop through addresses
for i in range(16):
GPIO.output(11, s0_cycle[i])
GPIO.output(12, s1_cycle[i])
GPIO.output(15, s2_cycle[i])
GPIO.output(16, s3_cycle[i])
# Read at current address
reader = MFRC522.MFRC522()
(status, TagType) = reader.MFRC522_Request(reader.PICC_REQIDL)
(status, uid) = reader.MFRC522_Anticoll()
# If tag is present, add data to temp state
if status == reader.MI_OK:
board_state_temp.append(uid)
reader.MFRC522_SelectTag(uid)
board_state.sort()
board_state_temp.sort()
# If temp state has changed since last loop, update state
if board_state != board_state_temp:
board_state = board_state_temp
# Send data to database/print the data
except KeyboardInterrupt:
GPIO.cleanup()
Currently I know that the 'status' variable is never in a state where it's conditional...
if status == reader.MI_OK:
is entered, hence the board_state is never updated.
Apologies if there's any typos in my code, I've had to retype it over from another machine so if you spot any errors like that, I don't think that would be the issue.
If there's nothing wrong with the code, it could be that my circuitry has errors.

Related

Read multiple DS18B20 temperature sensors faster using Raspberry Pi

My custom sensor dashboard requests new readings every second.
This worked well, until I hooked up 3 DS18B20 temperature sensors (1-wire protocol, so all on 1 pin), which each take 750ms to provide new data.
This is the class I currently use to read the temperature of each sensor:
# ds18b20.py
# written by Roger Woollett
import os
import glob
import time
class DS18B20:
# much of this code is lifted from Adafruit web site
# This class can be used to access one or more DS18B20 temperature sensors
# It uses OS supplied drivers and one wire support must be enabled
# To do this add the line
# dtoverlay=w1-gpio
# to the end of /boot/config.txt
#
# The DS18B20 has three pins, looking at the flat side with the pins pointing
# down pin 1 is on the left
# connect pin 1 to GPIO ground
# connect pin 2 to GPIO 4 *and* GPIO 3.3V via a 4k8 (4800 ohm) pullup resistor
# connect pin 3 to GPIO 3.3V
# You can connect more than one sensor to the same set of pins
# Only one pullup resistor is required
def __init__(self):
# Load required kernel modules
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')
# Find file names for the sensor(s)
base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')
self._num_devices = len(device_folder)
self._device_file = list()
i = 0
while i < self._num_devices:
self._device_file.append(device_folder[i] + '/w1_slave')
i += 1
def _read_temp(self, index):
# Issue one read to one sensor
# You should not call this directly
# First check if this index exists
if index >= len(self._device_file):
return False
f = open(self._device_file[index], 'r')
data = f.read()
f.close()
return data
def tempC(self, index=0):
# Call this to get the temperature in degrees C
# detected by a sensor
data = self._read_temp(index)
retries = 0
# Check for error
if data == False:
return None
while (not "YES" in data) and (retries > 0):
# Read failed so try again
time.sleep(0.1)
#print('Read Failed', retries)
data = self._read_temp(index)
retries -= 1
if (retries == 0) and (not "YES" in data):
return None
(discard, sep, reading) = data.partition(' t=')
if reading == 85000:
# 85ºC is the boot temperature of the sensor, so ignore that value
return None
temperature = float(reading) / 1000.0
return temperature
def device_count(self):
# Call this to see how many sensors have been detected
return self._num_devices
I already tried to return the previous temperature reading if the current one isn't finished yet, however this didn't reduce the time it took to read a sensor, so I guess the only way is to do things asynchronously.
I could reduce the precision to reduce the time it takes per reading, but ideally I would read all of the sensors simultaneously on separate threads.
How can I best implement this? Or are there other ways to improve the reading speed of multiple DS18B20 sensors?
Thanks for any insights!
You're facing some limitations introduced by the Linux kernel driver. If you were interacting with the OneWire protocol directly, you would only have a single 750ms read cycle for all three sensors, rather than (3 * 750ms). When speaking the 1-wire protocol directly, you can issue a single "convert temperature" command to all devices on the bus, as described here, and then read all the sensors.
The Linux driver explicitly doesn't support this mode of operation:
If none of the devices are parasite powered it would be possible to convert all the devices at the same time and then go back to read individual sensors. That isn’t currently supported. The driver also doesn’t support reduced precision (which would also reduce the conversion time) when reading values.
That means you're stuck with a 750ms per device read cycle. Your best option is probably placing the sensor reading code in a separate thread, e.g.:
import glob
import threading
import time
# Note that we're inheriting from threading.Thread here;
# see https://docs.python.org/3/library/threading.html
# for more information.
class DS18B20(threading.Thread):
default_base_dir = "/sys/bus/w1/devices/"
def __init__(self, base_dir=None):
super().__init__()
self._base_dir = base_dir if base_dir else self.default_base_dir
self.daemon = True
self.discover()
def discover(self):
device_folder = glob.glob(self._base_dir + "28*")
self._num_devices = len(device_folder)
self._device_file: list[str] = []
for i in range(self._num_devices):
self._device_file.append(device_folder[i] + "/w1_slave")
self._values: list[float | None] = [None] * self._num_devices
self._times: list[float] = [0.0] * self._num_devices
def run(self):
"""Thread entrypoint: read sensors in a loop.
Calling DS18B20.start() will cause this method to run in
a separate thread.
"""
while True:
for dev in range(self._num_devices):
self._read_temp(dev)
# Adjust this value as you see fit, noting that you will never
# read actual sensor values more often than 750ms * self._num_devices.
time.sleep(1)
def _read_temp(self, index):
for i in range(3):
with open(self._device_file[index], "r") as f:
data = f.read()
if "YES" not in data:
time.sleep(0.1)
continue
disacard, sep, reading = data.partition(" t=")
temp = float(reading) / 1000.0
self._values[index] = temp
self._times[index] = time.time()
break
else:
print(f"failed to read device {index}")
def tempC(self, index=0):
return self._values[index]
def device_count(self):
"""Return the number of discovered devices"""
return self._num_devices
Because this is a thread, you need to .start() it first, so your
code would look something like:
d = DS18B20()
d.start()
while True:
for i in range(d.device_count()):
print(f'dev {i}: {d.tempC(i)}')
time.sleep(0.5)
You can call the tempC method as often as you want, because it's
just return a value from the _values array. The actual update
frequency is controlled by the loop in the run method (and the
minimum cycle time imposed by the sensors).

pyfirmata is reading analog values but the if-else statement are proving it otherwise

So I've got a python script that reads sensor data from Arduino and stores it in the Firebase database. The program was working well until I added some if-else statement and now it looks like the values are being treated as Nonetype I'm stuck kindly help.
Here's the code:
from pyfirmata import Arduino, util
import time
board = Arduino('COM3')
from firebase import firebase
firebase=firebase.FirebaseApplication("https://xxxxx.firebaseio.com/",None)
it = util.Iterator(board)
it.start()
analog_input1 = board.get_pin('a:0:i')
analog_input2 = board.get_pin('a:1:i')
analog_input3 = board.get_pin('a:2:i')
analog_input4 = board.get_pin('a:3:i')
state="null"
while True:
analog_value1 = analog_input1.read()
analog_value2 = analog_input2.read()
analog_value3 = analog_input3.read()
analog_value4 = analog_input4.read()
if analog_value1>=2000 or analog_value2>=23 or analog_value3>=23 or analog_value4>=3000:
state="Slightly Alarming"
elif analog_value1>=4000 or analog_value2>=31 or analog_value3>=31 or analog_value4>=4000:
state="Alarming"
elif analog_value1>=5000 or analog_value2>=50 or analog_value3>=50 or analog_value4>=5000:
state="Danger!"
else:
state="Normal"
firebase.post('/xxxxx/',{'MQ4':analog_value1,'MQ7':analog_value2,'MQ9':analog_value3,'MQ135':analog_value4,'Status':state})
time.sleep(0.1)

GPIO output initially on and GPIO.output

I'm new to python and am attempting to control solenoid valves using a relay, RPi, PIR sensor and python script. I found a script posted online I used as a base and modified it a bit to switch my relay. Everything seems to be working as expected overall, but I've noticed the following:
As soon as I run the script in terminal, the relay switches are on. After first trigger , they will stay off until the following trigger. Why are they intially on?
In my code I'm using GPIO.output(<variable>,False) to switch on and GPIO.output(<variable>,True) to turn off which seems backward. Shouldn't the False argument turn the switch off and True turn on?
Code below. Any pointers or insight would be appreciated! Thanks!
# Import required Python libraries
import RPi.GPIO as GPIO
import time
import random
# Use BCM GPIO references
# instead of physical pin numbers
GPIO.setmode(GPIO.BCM)
# Define GPIO to use on Pi
pir = 17
arm_one = 23
arm_two = 24
print "PIR Module Test (CTRL-C to exit)"
# Set pin as input
GPIO.setup(pir,GPIO.IN)
GPIO.setup(arm_one,GPIO.OUT)
GPIO.setup(arm_two,GPIO.OUT)
current_state = 0
previous_state = 0
def getRandomNum():
random_sleep=random.uniform(0,1)
return random_sleep;
try:
print "Waiting for PIR to settle ..."
# Loop until PIR output is 0
while GPIO.input(pir)==1:
current_state = 0
print " Ready"
# Loop until users quits with CTRL-C
while True:
# Read PIR state
current_state = GPIO.input(pir)
if current_state==1 and previous_state==0:
# PIR is triggered
print " Motion detected!"
for i in range(5):
GPIO.output(arm_one,False)
GPIO.output(arm_two,False)
time.sleep(1)
GPIO.output(arm_one,True)
time.sleep(getRandomNum())
GPIO.output(arm_two,True)
time.sleep(getRandomNum())
GPIO.output(arm_one,False)
time.sleep(getRandomNum())
GPIO.output(arm_one,True)
GPIO.output(arm_two,False)
time.sleep(getRandomNum())
GPIO.output(arm_two,True)
# Record previous state
previous_state=1
elif current_state==0 and previous_state==1:
# PIR has returned to ready state
print " Ready"
GPIO.output(arm_one,True)
GPIO.output(arm_two,True)
previous_state=0
# Wait for 10 milliseconds
time.sleep(0.01)
except KeyboardInterrupt:
print " Quit"
# Reset GPIO settings
GPIO.cleanup()

Pulse width reader printing same value every time

I'm connecting my raspberry pi to a 2.4ghz rc reciever, and I am trying to use python to interpret the pulse width signal. I am using an input pin to read the time while there is no input, then the time when there is an input, then subtracting the two.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(13,GPIO.IN)
GPIO.setup(15,GPIO.OUT)
GPIO.output(15,GPIO.HIGH)
start = time.time()
stop = time.time()
x = 0
y = 0
while(x == 0):
if(GPIO.input(13) == 0):
start = time.time()
x = 1
while(y == 0):
if(GPIO.input(13) == 1):
stop = time.time()
y = 1
Width = stop-start
print(Width)
GPIO.cleanup()
The issue I am having is that no matter how long I make the pulse width (by manually connecting and disconnecting pin 13 and 15), it prints ~.006. It also will not print until I disconnect the pins from each other, although I haven't been able to figure out why.
Pin 13 might be floating. I'd suggest you replace ...
GPIO.setup(13,GPIO.IN)
... with ...
GPIO.setup(13,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
Here's an example of how to do this with interrupts:
#!/usr/bin/env python3
# example of reading PWM with GPIO interrupts
# Warning: Linux isn't built for real-time applications.
# A Raspberry Pi with Jessie will not produce reliable results
# That said...
import RPi.GPIO as GPIO
import time
GPIOpin_IN = 13
GPIOpin3v3 = 1 # fixed at 3v3 volts
myStart = None
myStop = None
GPIO.setmode(GPIO.BOARD)
GPIO.setup(GPIOpin_IN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
def pinGoesUp(gpioIdentity):
myStart = time.time()
def pinGoesDown(gpioIdentity):
myStop = time.time()
GPIO.add_event_detect(GPIOpin_IN, GPIO.RISING, callback=pinGoesUp)
GPIO.add_event_detect(GPIOpin_IN, GPIO.FALLING, callback=pinGoesDown)
while True:
if myStop < myStart:
Width = stop-start
print(Width)`
documentation on gpio input.
On a side note, you're going to have difficulty getting reliable readings. Jessie is not intended to provide real-time interaction, and frequently takes small vacations to do other tasks.

Use only the most recent value read from an Arduino in a python script, not the values stored in the buffer?

I have been working on a project using Python to read values from an arduino and then control video cameras. The Arduino controls two ultrasonic sensors and reports distance in cm. The python script then reads the distances from the Arduino using ser.readline(). When the script reads values outside the range everything works fine. However if it goes into the loop for the distance inside the required range it works correctly once and then reads old values from the Arduino instead of current "live" values which causes it to continue the record loop instead of exiting the loop. What can I do to get rid of the old values in the buffer and only read the most current value? I have found several methods and tested them but so far no luck.
Here is the code I am using (i know its not well written but its my first try using python and writing code outside of matlab)
import sys, time
import serial
import cv
import os
from time import strftime
#Create window for Camera 0
cv.NamedWindow("Camera 0", cv.CV_WINDOW_AUTOSIZE)
capture0 = cv.CreateCameraCapture(2)
cv.ResizeWindow("Camera 1", 640, 480)
cv.MoveWindow("Camera 0", 0, 0)
#Create window for Camera 1
cv.NamedWindow("Camera 1", cv.CV_WINDOW_AUTOSIZE)
capture1 = cv.CreateCameraCapture(1)
cv.MoveWindow("Camera 1", 150, 150)
#Initialize connection to Arduino
arduino = serial.Serial('COM12', 9600)
connected = False
#Confirm that Arduino is connected and software is able to read inputs
while not connected:
serin = arduino.readline()
connected = True
f = 'Sensor Connected'
print f
'''#Dummy variables for testing
value1 = 145
value2 = 30'''
#Initialize video record on as false (0)
vid = 0
#Initialize counter
counter_vid = 0
counter = 0
Accel = 1
def Camera0():
frame0=cv.QueryFrame(capture0)
cv.WriteFrame(writer0,frame0)
cv.ShowImage("Camera 0", frame0)
def Camera1():
frame1=cv.QueryFrame(capture1)
cv.WriteFrame(writer1,frame1)
cv.ShowImage("Camera 1", frame1)
while True:
status = arduino.readline()
value1=int((status[6:10]))-1000
value2=int((status[17:21]))-1000
print(value1)
print(value2)
if value1>180 and value2>180 and vid==0:
vid = 0
elif value1>180 and value2>180 and vid==1:
vid = 0
elif value1<180 and vid==0 or value2<180 and vid==0:
filename0 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera0.avi"
writer0=cv.CreateVideoWriter(filename0, 1, 15.0, (640,480), is_color=1)
filename1 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera1.avi"
writer1=cv.CreateVideoWriter(filename1, 1, 15.0, (640,480), is_color=1)
vid=1
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
else:
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
cv.WaitKey(25)
counter_vid = 0
counter += 1
print('End of Loop Counter')
print(counter)
You're right about the buffer filling up. You need a way to always get the most recent value out of the buffer.
I would suggest replacing this:
status = arduino.readline()
with this:
status = getLatestStatus()
and then further up towards the top, by your camera functions:
def getLatestStatus():
while arduino.inWaiting() > 0:
status = arduino.readline()
return status
This function getLatestStatus will go through the entire buffer every time it is called and only return the latest status, disregarding all the statuses returned in the meantime.
Your other option is to modify the "firmware" for your arduino to return a distance sensor value every time it receives a command, (say "M\n") so that way you don't have to worry about buffer problems. That's what I did for an arduino-powered ultrasonic distance device and I felt it was cleaner than the "read through the entire buffer" solution. It will introduce a bit more latency into your distance measurement though.

Categories