Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option for multiple RFID Readers #1012

Merged
merged 8 commits into from
Jul 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions scripts/Reader.py.Multi
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# There are a variety of RFID readers out there, USB and non-USB variants.
# This might create problems in recognizing the reader you are using.
# We haven't found the silver bullet yet. If you can contribute to this
# quest, please comment in the issue thread or create pull requests.
# ALTERNATIVE SCRIPTS:
# If you encounter problems with this script Reader.py
# consider and test one of the alternatives in the same scripts folder.
# Replace the Reader.py file with one of the following files:
# * Reader.py.experimental
# This alternative Reader.py script was meant to cover not only USB readers but more.
# It can be used to replace Reader.py if you have readers such as
# MFRC522, RDM6300 or PN532.
# * Reader.py.kkmoonRFIDreader
# KKMOON RFID Reader which appears twice in the devices list as HID 413d:2107
# and this required to check "if" the device is a keyboard.

# import string
# import csv
import os.path
import sys

from evdev import InputDevice, ecodes, list_devices
from select import select


def get_devices():
return [InputDevice(fn) for fn in list_devices()]


class Reader:
reader = None

def __init__(self):
self.reader = self
devs = list()
path = os.path.dirname(os.path.realpath(__file__))
self.keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
if not os.path.isfile(path + '/deviceName.txt'):
sys.exit('Please run RegisterDevice.py first')
else:
with open(path + '/deviceName.txt', 'r') as f:
device_keys = f.readlines()
devices = get_devices()
for device in devices:
for dev_key in device_keys:
dev_name, dev_phys = dev_key.rstrip().split(';', 1)
if device.name == dev_name and device.phys == dev_phys:
devs.append(device)
break
for dev in devs:
try:
dev
except:
sys.exit('Could not find the device %s\n. Make sure is connected' % dev.name)

str_devs = ','.join([str(x) for x in devs])
#print("Devs: " + str_devs)
self.devices = map(InputDevice, str_devs)
self.devices = {dev.fd: dev for dev in devs}

def readCard(self):
stri = ''
key = ''
while key != 'KEY_ENTER':
r, w, x = select(self.devices, [], [])
for fd in r:
for event in self.devices[fd].read():
if event.type == 1 and event.value == 1:
stri += self.keys[event.code]
# print( keys[ event.code ] )
key = ecodes.KEY[event.code]
return stri[:-1]
211 changes: 211 additions & 0 deletions scripts/Reader.py.experimental.Multi
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#!/usr/bin/env python3
# This alternative Reader.py script was meant to cover not only USB readers but more.
# It can be used to replace Reader.py if you have readers such as
# MFRC522, RDM6300 or PN532.
# Please use the github issue threads to share bugs and improvements
# or create pull requests.
import multiprocessing
try:
from multiprocessing import SimpleQueue
except ImportError:
from multiprocessing.queues import SimpleQueue
import os.path
import sys
import serial
import string
import RPi.GPIO as GPIO
import logging
from enum import Enum
from evdev import InputDevice, ecodes, list_devices
# Workaround: when using RC522 reader with pirc522 pkg the py532lib pkg may not be installed and vice-versa
try:
import pirc522
from py532lib.i2c import *
from py532lib.mifare import *
except ImportError:
pass

logger = logging.getLogger(__name__)

class EDevices(Enum):
MFRC522 = 0
RDM6300 = 1
PN532 = 2


def get_devices():
devices = [InputDevice(fn) for fn in list_devices()]
devices.append(NonUsbDevice(EDevices.MFRC522.name))
devices.append(NonUsbDevice(EDevices.RDM6300.name))
devices.append(NonUsbDevice(EDevices.PN532.name))
return devices


class NonUsbDevice(object):
name = None

def __init__(self, name, phys=''):
self.name = name
self.phys = phys


class UsbReader(object):
def __init__(self, device):
self.keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
self.dev = device

def readCard(self):
from select import select
stri = ''
key = ''
while key != 'KEY_ENTER':
select([self.dev], [], [])
for event in self.dev.read():
if event.type == 1 and event.value == 1:
stri += self.keys[event.code]
key = ecodes.KEY[event.code]
return stri[:-1]


class Mfrc522Reader(object):
def __init__(self):
self.device = pirc522.RFID()

def readCard(self):
# Scan for cards
self.device.wait_for_tag()
(error, tag_type) = self.device.request()

if not error:
logger.info("Card detected.")
# Perform anti-collision detection to find card uid
(error, uid) = self.device.anticoll()
if not error:
card_id = ''.join((str(x) for x in uid))
logger.info(card_id)
return card_id
logger.debug("No Device ID found.")
return None

@staticmethod
def cleanup():
GPIO.cleanup()


class Rdm6300Reader:
def __init__(self):
device = '/dev/ttyS0'
baudrate = 9600
ser_timeout = 0.1
self.last_card_id = ''
try:
self.rfid_serial = serial.Serial(device, baudrate, timeout=ser_timeout)
except serial.SerialException as e:
logger.error(e)
exit(1)

def readCard(self):
byte_card_id = b''

try:
while True:
try:
read_byte = self.rfid_serial.read()

if read_byte == b'\x02': # start byte
while read_byte != b'\x03': # end bye
read_byte = self.rfid_serial.read()
byte_card_id += read_byte

card_id = byte_card_id.decode('utf-8')
byte_card_id = ''
card_id = ''.join(x for x in card_id if x in string.printable)

# Only return UUIDs with correct length
if len(card_id) == 12 and card_id != self.last_card_id:
self.last_card_id = card_id
self.rfid_serial.reset_input_buffer()
return self.last_card_id

else: # wrong UUID length or already send that UUID last time
self.rfid_serial.reset_input_buffer()

except ValueError as ve:
logger.errror(ve)

except serial.SerialException as se:
logger.error(se)

def cleanup(self):
self.rfid_serial.close()


class Pn532Reader:
def __init__(self):
pn532 = Pn532_i2c()
self.device = Mifare()
self.device.SAMconfigure()
self.device.set_max_retries(MIFARE_WAIT_FOR_ENTRY)

def readCard(self):
return str(+int('0x' + self.device.scan_field().hex(), 0))

def cleanup(self):
# Not sure if something needs to be done here.
logger.debug("PN532Reader clean up.")


class Reader(object):
def __init__(self):
self.reader = self
self.devs = list()
path = os.path.dirname(os.path.realpath(__file__))
if not os.path.isfile(path + '/deviceName.txt'):
sys.exit('Please run RegisterDevice.py first')
else:
with open(path + '/deviceName.txt', 'r') as f:
device_keys = f.readlines()
devices = get_devices()
for device in devices:
for dev_key in device_keys:
dev_name_phys = dev_key.rstrip().split(';', 1)
dev_name = dev_name_phys[0]
dev_phys = ''
if len(dev_name_phys) > 1:
dev_phys = dev_name_phys[1]
if device.name == dev_name and device.phys == dev_phys:
if dev_name == 'MFRC522':
self.devs.append(Mfrc522Reader())
elif dev_name == 'RDM6300':
self.devs.append(Rdm6300Reader())
elif dev_name == 'PN532':
self.devs.append(Pn532Reader())
else:
try:
usb_reader = UsbReader(device)
self.devs.append(usb_reader)
except IndexError:
sys.exit('Could not find the device %s.\n Make sure it is connected' % dev_name)
break

def readCard(self):
que = SimpleQueue()
threads_list = list()

for dev in self.devs:
t = multiprocessing.Process(target=lambda q: q.put(dev.readCard()), args=(que,))
t.start()
threads_list.append(t)

found_result = False
while not found_result:
for process in threads_list:
process.join(0.001)
if not process.is_alive():
found_result = True
break

for process in threads_list:
process.terminate()

return que.get()
103 changes: 103 additions & 0 deletions scripts/RegisterDevice.py.Multi
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env python3

import os.path
import subprocess

JUKEBOX_HOME_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))

def runCmd(cmd, wait=True):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()
if wait:
p.wait()
return output

def setupPN532():
answer = input('Please make sure that the PN532 reader is wired up correctly '
'to the GPIO ports before continuing...\n Continue?: [Y/n]')
if not answer or answer[0] != 'Y':
return False
print("Activating I2C interface...\n")
runCmd("sudo raspi-config nonint do_i2c 0")
print("Installing i2c-tools...\n")
runCmd("sudo apt-get -qq -y install i2c-tools")
print("Checking if PN532 RFID reader is found through I2C...\n")
output = runCmd("sudo i2cdetect -y 1")
if "24" in str(output):
print(" PN532 was found.\n")
else:
print(" ERROR: PN532 was not found.\n")
print(str(output))
return False
print("Installing Python requirements for PN532...\n")
runCmd("sudo python3 -m pip install --upgrade --force-reinstall "
"-q -r {}/components/rfid-reader/PN532/requirements.txt".format(JUKEBOX_HOME_DIR))
print("Done")
return True


def setupMFRC522():
answer = input('Please make sure that the RC522 reader is wired up correctly '
'to the GPIO ports before continuing...\n Continue?: [Y/n]')
if not answer or answer[0] != 'Y':
return False
print("Installing Python requirements for RC522...\n")
runCmd("sudo python3 -m pip install --upgrade --force-reinstall "
"-q -r {}/components/rfid-reader/RC522/requirements.txt".format(JUKEBOX_HOME_DIR))
print("Done")
return True


runCmd("cp {0}/scripts/Reader.py.experimental.Multi {1}/scripts/Reader.py".format(JUKEBOX_HOME_DIR, JUKEBOX_HOME_DIR))
from Reader import get_devices, EDevices
list_dev_ids = list()
devices = get_devices()

def addDevice():
i = 0
print("Choose the reader from list")
for dev in devices:
if i not in list_dev_ids:
print(i, dev.name + str(dev.phys))
i += 1
dev_id = int(input('Device Number: '))
if dev_id not in list_dev_ids:
if devices[dev_id].name == EDevices.PN532.name:
if not setupPN532():
return
if devices[dev_id].name == EDevices.MFRC522.name:
if not setupMFRC522():
return
list_dev_ids.append(dev_id)


def configureDevices():
addDevice()
while True:
answer = input('Do you want to add another device: [Y/n]')
if not answer or answer[0] != 'Y':
break
addDevice()


print("Stopping phoniebox-rfid-reader service...\n")
runCmd("sudo systemctl stop phoniebox-rfid-reader.service")

configureDevices()

path = os.path.dirname(os.path.realpath(__file__))
with open(path + '/deviceName.txt', 'w') as f:
for sel_dev_id in list_dev_ids:
f.write(devices[sel_dev_id].name + ";" + devices[sel_dev_id].phys + '\n')
f.close()

print("Restarting phoniebox-rfid-reader service...\n")
runCmd("sudo systemctl start phoniebox-rfid-reader.service")

runCmd("sudo chown pi:www-data {}/scripts/deviceName.txt".format(JUKEBOX_HOME_DIR))
runCmd("sudo chmod 644 {}/scripts/deviceName.txt".format(JUKEBOX_HOME_DIR))

print("Register Device(s) Done!")



Loading