Reading input from an USB Rfid Card Reader
Why Bother
I had this cheap RFID reader lying around in the house. I can't right remember when and why I had bought it. I had some idea for a software, but then I never really needed to have it on the desk, therefore here you go...
When you plug it, it works like a normal keyboards, it just stays there, but then if you swipe a card or an RFID token, it will just write on your standard input, either on a terminal or whatever application you have useful at the moment.
In order to do anything useful with that piece of equipment, I had to capture the input from a single application, making sure it would not be spamming anything else.
The Name of the Beast
It turns out, what I wanted has a name, and is the EVIOCGRAB ioctl. Sure, in C that makes sense, but for higher level software components that's not a thing. I looked for examples around, and of course, Python got you covered.
But for myself I'd rather not depend on some fluffy PythonN library flying around that I need to remember to install dependencies for.
I'd rather go off and implement som kind-of-high-level Golang code.
To find my device, I had to plug it first, and look what I had available with:
find /dev/input/by-id
The Spell
Let's say now that my device has this name:
/dev/input/by-id/usb-something_Barcode_Reader-event-kbd
There you have the code to read and print the input exclusively, without affecting the other applications:
package main
import (
"encoding/binary"
"fmt"
"os"
"syscall"
"unsafe"
)
type inputEvent struct {
Sec uint64
Usec uint64
Type uint16
Code uint16
Value int32
}
const (
EVIOCGRAB = 0x40044590 // ioctl to grab exclusive access
EV_KEY = 0x01
KEY_ENTER = 28
)
var keyMap = map[uint16]rune{
2: '1', 3: '2', 4: '3', 5: '4', 6: '5',
7: '6', 8: '7', 9: '8', 10: '9', 11: '0',
16: 'Q', 17: 'W', 18: 'E', 19: 'R', 20: 'T',
21: 'Y', 22: 'U', 23: 'I', 24: 'O', 25: 'P',
30: 'A', 31: 'S', 32: 'D', 33: 'F', 34: 'G',
35: 'H', 36: 'J', 37: 'K', 38: 'L',
44: 'Z', 45: 'X', 46: 'C', 47: 'V', 48: 'B',
49: 'N', 50: 'M',
}
func main() {
devPath := "/dev/input/by-id/usb-something_Barcode_Reader-event-kbd"
if len(os.Args) > 1 {
devPath = os.Args[1]
}
f, err := os.Open(devPath)
if err != nil {
fmt.Fprintf(os.Stderr, "open %s: %v\n", devPath, err)
os.Exit(1)
}
defer f.Close()
// Grab exclusive access so the kernel doesn't forward events to X/Wayland.
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), EVIOCGRAB, 1); errno != 0 {
fmt.Fprintf(os.Stderr, "EVIOCGRAB: %v\n", errno)
os.Exit(1)
}
fmt.Println("Grabbed device. Waiting for card scans (Ctrl-C to exit)���")
var card []rune
var ev inputEvent
for {
if err := binary.Read(f, binary.LittleEndian, &ev); err != nil {
fmt.Fprintf(os.Stderr, "read: %v\n", err)
break
}
// We only care about key-press events (value == 1).
if ev.Type != EV_KEY || ev.Value != 1 {
continue
}
if ev.Code == KEY_ENTER {
// RFID readers terminate each card with Enter.
fmt.Printf("This is the RFID value: %s\n", string(card))
card = card[:0]
continue
}
if ch, ok := keyMap[ev.Code]; ok {
card = append(card, ch)
}
}
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), EVIOCGRAB, uintptr(unsafe.Pointer(nil)))
}
Consider joining The Post Cloud ML if you find those rambling to be interesting.
[python] [golang] [git]