These are notes for the a PIU upgrade to include newer hardware.
The PIU should be able to accept a variety of fare media, including magstripe cards (multiple track), RFIDs (125kHz and Mifare) and barcode/QR codes using a camera.
The display should be such that communication is given back to the rider. The camera can also be used to potentially help with QR code alignment to the (down facing) camera.
There's some investigation that should be done in terms of what, if any, gets offloaded to a microcontroller but the basic idea is:
By having a microcontroller (Arduino, say) that's in charge of communication or the RFID and Magstripe devices, this provides an 'instant on' for that portion of the PIU. Putting everything behind the Raspberry Pi is conceptually simpler but is sluggish to boot up.
Some electronics speak "Wiegand Protocol".
There's an Arduino library that has appropriate code (from monkeyboard).
The current system is focused on using HID prox card readers.
The proxmark3 is a general purpose RFID hardware tool that can be used to read and write low frequency (125 kHz - 134 kHz) as well as high frequency (12.57 MHz). The code is under LGPL and I assume the hardware files are under the same or similar license. The hardware files don't have a license on them but I think the proxmark3 is marketed as an open source hardware project so it's probably safe to assume this was the intent.
The proxmark3 is sold in the $250-$300 (as of this writing) but there are clones available at a fraction of the cost ($60-$80) on eBay.
The proxmark3 can read HID class cards so I think it's an ideal candidate to use as a general purpose RFID reader.
At the least, this will be a relatively low cost solution that will read a variety of cards that any organization can use.
If needed, there is the possibility of using it as a writer, either in the field or offline, for custom RFID tags.
There are few key points to setup the Proxmark3 for use:
proxmark3 project should be downloaded, compiled and flashed onto the device. Failure to do so was causing some basic functions to malfunction (hw version), slowness and inconsistent behavior of lf hid read. See an issue suggestion the flash would solve the issues being experiencedscripts directory? Be sure to place it where it can find itThe Lua script:
local cmds = require('commands')
local getopt = require('getopt')
local bin = require('bin')
local utils = require('utils')
local format=string.format
local floor=math.floor
local os = require("os")
function sleep (a)
local sec = tonumber(os.clock() + a);
while (os.clock() < sec) do
end
end
local function main(args)
print( string.rep('--',30) )
print( string.rep('--',30) )
-- core.console will go async when waiting for the proxmark to return
-- so need to wait for a response (sleep)
core.console('lf hid read')
os.execute("sleep 30")
print( string.rep('--',30) )
end
main(args)
The script should be put into the scripts directory and can be run via:
$ stdbuf -eL -oL ./proxmark3 /dev/ttyACM0 -l lf-hid-read.lua | tee test.log
The following will also work:
$ stdbuf -eL -oL ./proxmark3 /dev/ttyACM0 -c "script run lf-hid-read.lua" | tee test.log
Note that the stdbuf is needed to get results as soon as a line is read.
The current PIU has a dual head, 3-track magstripe reader. As of this writing, single head 3-track USB magstripe readers sell for around $15. I see some alternatives on Aliexpress and Alibaba that are in the range of $10 in single units and $6 in quantity 5k+.
Dual head, 3-track magstripe USB readers sell for around $60 on Amazon and Ebay.
The possible solutions to this are:
Some of the peripherals act as keyboard input devices. In order to not have them pollute input, they need to be taken over by a process.
SO has an article on it. More investigation needs to be done but the basics look to be:
/dev/input/event* to look for the deviceioctl EVIOCGRAB call to get exclusive use of the devicecat /proc/bus/input/devices | grep -P '^[NH]: ' | paste - -
There's a Python tutorial that looks to have an easy way to do this:
import evdev
import asyncio
dev = {}
evfns = [ "/dev/input/event0", "/dev/input/event1", ... , "/dev/input/event12" ]
for evfn in evfns:
_dev = evdev.InputDevice(evfn)
if dev.name == HID_NAME:
dev = _dev
break
dev.grab()
async def print_events(device):
async for event in device.async_read_loop():
print(device.path, evdev.categorize(event, sep=': '))
for device in dev:
asyncio.ensure_future(print_events(dev))
...
The above has to be played around with but it looks like the seeds are there.
If you don't care about capturing multiple devices at once (within a single program), the following does an active poll:
#!/usr/bin/python3
import os, sys, evdev, asyncio, os.path
from evdev import InputDevice, categorize, ecodes
dev_init = False
dev = { }
HID_NAME = 'HID c216:0180'
evfns_n = 13
evfns_bd = "/dev/input/"
evfns = [ ]
for x in range(evfns_n):
fn = evfns_bd + "event" + str(x)
if (os.path.exists(fn)):
evfns.append(fn)
for evfn in evfns:
_dev = evdev.InputDevice(evfn)
print("device:", _dev.name)
if _dev.name == HID_NAME:
dev_init = True
dev = _dev
break
if not dev_init:
print("could not find device, exiting")
sys.exit(0)
print(dev)
print(dev.capabilities())
print(dev.capabilities(verbose=True))
dev.grab()
for event in dev.read_loop():
if event.type == ecodes.EV_KEY:
print(categorize(event))
Note that the HID_NAME is specific to the USB magstripe reader I had on hand.
Multiple of the same could be attached and would need to be differentiated by the /dev/input/event* endpoints.
I'm still playing around with a policy on the USB magstripe reader use but there are a few things of note:
The passenger needs feedback so a display is required. I think it's good practice to provide a mechanism for user interaction and high resolution display.
The high resolution display can be used for fare media presentation feedback. For example, letting the rider see what the camera is seeing to better align a QR codes ticket.
The touch feedback can be used for complex fare media or for future options.
There's a 2.8 inch touch display that could be a candidate (MZDPI).
Apparently there's a simple install script:
cd ~/
git clone https://github.com/tianyoujian/MZDPI.git
cd MZDPI/vga
sudo chmod +x mzdpi-vga-autoinstall-online
sudo ./mzdpi-vga-autoinstall-online
sudo reboot
I much prefer capacitive touch screens to resistive ones so it'd be nice to have an alternative.
One option is Pimroni's HyperPixel 4.0 Display (~ $45).
I would recommend a Pi NoIR that can be hooked into the Pi Zero (Adafruit).
I've had success in getting it up and running with a Raspberry Pi Zero before and it might be easy to set up and fast enough to use.
zbar-tools (perhaps mcheheb/zbar?) looks to be a tool to scan QR codes.
Here's an example:
$ sudo apt-get install zbar python-zbar
$ git clone https://github.com/mchehab/zbar
$ cd zbar/python/examples
$ python ./scan_image.py ../../examples-qr-code.png
('decoded', 'QRCODE', 'symbol', '"https://github.com/mchehab/zbar"')