This morning, I realized that I’d never really done anything with the cheap Chinese USB RFID reader I picked up ages ago, so I decided to whip up a little proof of concept for practice: A mock timeclock for tracking worker presence.
To accomplish this, I’ll need several tweaks to how it functions:
- Working around the fact that all of my demonstration RFID tokens have their values printed on them for all to see.
- Ignoring incomplete input on the virtual keyboard it exposes
- Debouncing input, since the reader isn’t good enough at that
- Preventing it from typing junk into arbitrary applications in my GUI
- Some way to distingush check-in from check-out
1. Obscuring RFID IDs
In order to make it more difficult for a casual exploiter to just manually enter the ID somewhere, I found that one of the DIP switches on the back of my reader changes the interpretation of the token’s ID into something not visibly related to what was printed on it. Not perfect, but definitely good enough to prevent casual meddling.
(I have no idea what the actual difference is, since I can’t read Chinese.)
2. Guarding Against Incomplete Data
There are two ways in which one of these RFID readers can produce bad data:
The reader can misread the token
This isn’t a huge problem, since I’ve only managed to trigger it as the first scan after I’ve fiddled with the DIP switches while it’s powered… but it’s easy to guard against:
Just check that the received token ID is the expected length.
Something can go wrong between the reader and the software
To guard against this, I fiddled with the DIP switches further and found a mode which inserts a semicolon before the token ID and a question mark after.
This allows me to set up the following mappings for raw keypress events:
KEY_SEMICOLON
: Clear the bufferKEY_#
: Add the typed character to the bufferKEY_SLASH
: Process the buffer’s contents and then clear it- Everything else: Ignore it
Thus, any incomplete read will be prevented from polluting a future swipe and causing it to fail the length check.
3. Debouncing Input
Anyone who’s played around with an Arduino will be familiar with the need to debounce the signal from a pushbutton, but cheap Chinese RFID readers occasionally suffer from this too.
If you swipe the card in just the wrong way, it’ll register twice in rapid succession.
The simple solution is to store the timestamp of the last registered event for a given ID and require that a grace period have passed before any new scans will register.
(I chose 3 seconds since that seems like a nice balance between allowing quick testing and preventing the reader from getting confused by any especially sloppy token-waving a novice user might do.)
4. Claiming the Device Exclusively
This was actually the simplest part. All I had to do was add a udev rule to allow unprivileged users to open the evdev node for the reader and then use python-evdev to interface with the device. The InputDevice.grab()
method will send EVIOCGRAB
to exclusively claim it.
(If you’re familiar with X11 development, think XGrabKeyboard
, but for only one device.)
5. Indicator Tones
Given that this is intended to run in the background and be as effortless as possible, tones to distinguish “check in” from “check out” seem the most unobtrusive solution.
I googled up some of the simplest possible example code for generating tones on Linux without additional dependencies and put together a couple of two-tone sequences using the same low-high and high-low patterns everyone is familiar with from the Windows “device inserted” and “device removed” notifications.
Putting It All Together
So, with all that said, let me show you some code.
The following GitHub Gist has been tested under Python 2.7 and 3.4 and requires the evdev
package (available as python-evdev
or python3-evdev
on Debian-family distros).
USB RFID Reader on Linux: Proof of Concept by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution under the same terms as the associated post.
All comments are moderated. If your comment is generic enough to apply to any post, it will be assumed to be spam. Borderline comments will have their URL field erased before being approved.