Signal-Routing for “Keyboard-like” RFID / Barcode Readers – and an Arduino-based Alternative
by JohnThinger in Circuits > Arduino
4725 Views, 16 Favorites, 0 Comments
Signal-Routing for “Keyboard-like” RFID / Barcode Readers – and an Arduino-based Alternative
“Sophisticated” RFID chip readers or barcode readers are shipped with drivers, libraries, or special apps which provide methods for routing the output of the reader to a specific target. If, for example, the information of the RFID tags or barcodes should simply go to a file, this kind of readers provide ways to define the location and format of that file.
Many “cheaper” readers, however, simply act as a USB keyboard. If they recognize an RFID tag (or barcode) these readers simulate keystrokes that represent the resulting code. Consequently, the code will always be sent to the active window. This is fine for many applications, as long as the desired window is always in the foreground and has the keyboard focus. It does make some trouble, however, if you want to do things in parallel to receiving RFID codes / barcodes.
This tutorial gives an overview on different concepts for capturing and re-routing the keyboard events from these devices.
Supplies
If you have already bought one of these “keyboard-like” RFID readers (or barcode readers), you do not need any additional hardware as this tutorial will highlight different software-based ways how to route their output to an application that does not currently have the keyboard focus.
If, in contrast, you did not yet buy one, you may consider building your own device that does not emulate keyboard events and thus is much easier to handle. For the device proposed here, you need:
- 1x PCB Prototyping Board: 20 x 14 Holes // 40 x 60 mm
- 1x Arduino Nano
- 1x RDM6300 RFID reader with some RFID tags or cards
- 1x 100 Ohm Resistor (0,25 W)
- 2x 220 Ohm Resistor (0,25 W)
- 2x 5mm LEDs (preferably in different colors)
- 2x 5mm LED Holder Clip
- 1x Passive Piezo Buzzer
- 1x 40 Pin Female Header
- Some 0.14…0.25 mm2 cable
- 4x M4x20 Screw / 4x M4 Nut
- 4x M2x10 Screw / 4x M2 Nut
- 3D Printer or Printing Service – or Self-made Case
Please note: The RDM 6300 module is available in different versions. The major optical difference is the color of the antenna: the older versions have a plain copper antenna while the newer (?) ones come with a reddish (painted?) antenna. I have ordered the “new” one and received the “old” one… ☹. According to this source, the major drawback of the “old” one is that it stops sending the tag code after some repeats while the newer one sends the code as long as the tag is above the antenna. In my experiments with the “old” version, I did not find this behavior – the “old” one was sending the code endlessly, too. In the end, I do not know whether the version correlates with the type/color of the antenna. Maybe just make sure that the PCB shows “V2.0” on the backside. Most pictures on the internet show “V2.0” with both the reddish and the plain copper antenna. Nevertheless, mine has the funny version “V0.3” …which sounds like something that is not a release version. But anyway: this version works properly, too.
An Overview
As a small scenario, let’s consider the following: The RFID reader is connected to a PC that hosts an Access database. Every time, someone is presenting his RFID tag to the reader, it should send the corresponding tag number to the database. It is easy to achieve this by creating a corresponding input form. The reader will simulate keystrokes including the enter key, so each time these keystrokes reach the input form, it will create a new dataset. Technically, however, the keystrokes simply always go to the foreground window, so you cannot work on anything else like editing a text document with the “real” keyboard. Activating the window with this text document would route all keyboard events here: that window would receive the events from all keyboards including the RFID/barcode reader now - even if you move that window to another screen as in the example sketch above.
According to a large number of posts in newsgroups and on Q&A websites, a many people have come across the same problem and tried to capture the keystrokes from the RFID or barcode readers - with more or less success. Their common goal is to prevent the key sequences from going to the window that has the keyboard focus, and to send the information to some other target, i.e. a different running application, a file, a server, or a database. They discussed many different approaches of which some work well and others do not really work at all. The table given above shows a summary of some of these concepts. Some solutions are based on pure software, some require additional hardware. only. As most discussions were about a solution for Windows, the subsequent sections will mainly address this operating system while mentioning Linux concepts, too.
Routing the Keys With Customized Software
Let’s start with the pure software concepts.
- (1) Since Windows NT (probably 4.0), Windows has a function that allows for capturing all key-up and key-down events in the entire system. The so-called "low level keyboard hook" is installed using the Windows API function SetWindowsHookEx. The hook-function (which is triggered anytime a user presses or releases a key) must follow the defined format of a LowLevelKeyboardProc. This is a very neat function: It is able to capture all keystrokes – including those simulated by an RFID or barcode reader – even if the application that contains the hook does currently not have the keyboard focus. Additionally, it can block key strokes from reaching the focused window, so it could be the optimal solution for our problem. However, it has a serious issue: It cannot distinguish where the keystrokes come from, i.e. it cannot find out which of the connected keyboards – or RFID/barcode readers – initiated a key event. Hence, this function alone would not offer a proper solution.
- (2) As Microsoft saw the need for determining the source of keyboard events, they have introduced another Windows API function starting with WindowsXP: RegisterRawInputDevices. Now this is great for getting all keyboard events from all keyboards, independently of the keyboard focus, and you can easily find out where the keystroke comes from. So, you could sort out the keystrokes generated by the RFID/barcode reader and send them to your target application while the events from “real keyboards” are passing your filter mechanism… but again Microsoft included some issue with this approach: it cannot block keyboard events. While you see keystrokes coming from an RFID/barcode reader und you can send it to its proper destination, it still will reach the window with the keyboard focus.
- (3) The next approach most people are looking for is a combination of #1 and #2: Firstly, a raw input callback should detect keystrokes coming from an RFID/barcode reader. This function should then re-construct the corresponding RFID codes and send them to the proper target. Secondly, it should tell a low-level keyboard hook to block all those keyboard events that are coming from the RFID/barcode readers from reaching the foreground window. As a matter of fun, Microsoft again provided an issue: the system always calls the low-level keyboard hook callback function first, and then it calls the raw input callback function. Consequently, there is no real chance from blocking keys from a specific source this way. For those who want to try themselves, you find a C++ program source attached to this step (winKeyboardHooks.txt). Just compile it as windows desktop application, and you can make your own tries – but it will probably not provide a successful solution on your PC either.
- (4) Now, while the Windows API functions do not provide a clean way, one user brought up an idea that might work in some cases – depending on the way the RFID/barcode reader sends its keystrokes. The concept is simple: typically, the readers simulate the keystrokes very quickly. For each digit in the RFID chip code / barcode, they usually send one keyup and one keydown event in less than 20 ms. The entire code (of typically 8 or 10 digits) is normally received in less than 100 ms. Now this is much faster as human users can type. Hence, the idea is: block all keys that could originate from the RFID/barcode reader (e.g. only the numpad keys, or all numerical keys, or all numerical keys plus A…F in case of hex codes) until the maximum number of characters that could make one code is completely received or until a keystroke from an enter key arrives. At this time, check if the entire sequence was "typed" in less than 100 ms, and if so, treat this input as a code coming from the RFID/barcode reader. While this approach technically works, it obviously produces false data if the user presses any “relevant” key "accidentally" just while the keystrokes from the readers reach the keyboard hook. Therefore, this idea will not fully tackle the problem.
- (5) In the end, under Windows there is probably only one fully working method if you want no additional hardware and no second virtualized operating system: a dedicated keyboard driver which catches keyboard events even before they reach the core of the operating system. A good library for doing this is Interception. Its website shows well documented C++ examples. If you are a skilled C++ developer, this is a promising way, so go for it! The following solutions are probably more relevant for those who are not going to dive into C++ and system internals.
- (6) For sake of completeness, another pure software solution should be mentioned: capturing USB data before it reaches any keyboard handler. Python has a handy library for doing this (pyusb). Technically, this library works under Linux and Windows, but it requires to assign the device to the libusb driver. Under Linux, this is not a real problem, but under Windows it may be tricky to bypass the standard drivers for keyboards. As a result, there is more specific Python library for Windows (WinUsbPy), but this is a wrapper over the original Microsoft WinUsb library which essentially handles events on a (keyboard driver) level. Thus, if it shall be software and Windows, #5 looks simpler and better documented.
If I have not confused you entirely by now, just read on - it's getting easier now. In summary, none of the approaches above really works except for the keyboard driver, which requires advanced programming skills, but a deeper tutorial on that would go beyond the scope here. There are, however, simpler concepts which require less or no programming skills – as shown in the next section.
Downloads
Routing the Keys Using Standard Software – or Hardware
Let's have a look at the approaches 7...11 of the overview table:
- (7) This is probably the most basic idea: if you want to do anything in parallel to reading barcodes/RFID tags, just do it on a second PC. As a matter of fact, this is probably also the most cost-intensive solution. In some situations, it may still fit the requirements if either the application receiving the RFID codes / barcode or your second application requires only little resources. In this case, you could run one of both applications on a low-cost hardware like a Raspberry Pi. On the other hand, running two PCs might not be a bad choice if you simply already have them around. As an example, sharing an Access database between an RFID reader on one PC and another PC doing manual operations on that same Access database is very simple. As an additional note, the specific Access example may not work as expected if the Access database file resides on one PC and another PC opens this file in parallel. The input from both sides may not automatically update the database properly. In this case, splitting the database will improve the performance.
- (8) As an alternative to the previous concept, you may want to run all desired applications on one PC but the second PC (or Rhaspberry Pi) only supervises the USB RFID/barcode reader. Consider again the Access database with the input form being shown on one PC. The RFID reader is connected to another PC capturing the its keystrokes by a small program. That program converts the single keystrokes to proper RFID codes/numbers and uses methods for accessing the database without running the database frontend software (i.e. here: Access). In this specific case it could be standard software such as Microsoft Excel or self-written code connecting to the software over ODBC drivers. A very convenient library for doing this is pyodbc. Examples for ding this will be shown below in approach #10.
- (9) This idea is about the same as #7, but “semi-virtualized”: Just run the (second) application not on a real secondary PC, but with a virtualized secondary operating system on the first PC. Again, think of the Access database with the user form that shall be linked to the USB RFID/barcode reader. If you have installed Windows and Access on both the “host” and the “guest” system – e.g. using VMware – do the following:
- Start that virtual machine (i.e. the “guest”).
- After Windows has started, select Player → Removable Devices from the VMware menu. Look for your reader device which will be probably contain something like “HID” or “key” in its name as shown in the attached picture. Press on Connect (Disconnect from host).
- Go to the menu bar of VMware again and select Player → Manage → Virtual Machine Settings….
- Select the Options tab, then Shared Folders. On the right side, press Add…. Follow the dialog and pick the folder where your Access database is stored on the host PC.
- Finally check Always enabled and Map as network drive in Windows guests.
- You can now navigate to your Access database in the Explorer on the “guest”, open it with Access and show the input form.
- The input form is now running in the instance of Access in this virtual machine.
- As the reader device belongs to the virtual machine now, any code will go to the form now – even if the virtual machine is moved on a second screen. All “normal” keyboards, in contrast, are handled by VMware, i.e.: if the “guest” is selected, keyboards will send their events here, but if the “host” is active, all “normal” keys will go to the “host”.
- Consequently, you can now open the same Access database on the "host" and modify the database in parallel to the input form.
Please note: Access does not “like” two instances modifying one local database. It works, if the database is stored on a network drive, but in the given set-up, Access will not update the contents in the client on the “host” after new codes are added by the client on the “guest” system. Nevertheless, the example works well if you split the database and make local "frontend" databases on “host” and “guest”, even if the backend is stored locally on the “host”.
Please note: Although this is probably the most efficient solution if you do not want to spend money on extra hardware and you do not want to code anything, it has a small drawback: depending on the performance of your PC and the type of RFID/barcode reader, the simulated keystrokes from the reader may come in too quick to be handled by the virtualized environment, i.e. there might be missing digits in the received codes. However, this does appear only very rarely under normal conditions, and if it is a problem, you should probably check the validity of codes e.g. by the number of expected digits in your target application.
- (10) As mentioned in solution #8, you probably do not want to install all your software on a second PC. Even running one single piece of software on a second PC can cause additional costs. For example, if we look at MS Access again, you might not want to install it neither on a second PC nor in a virtual machine because both requires another valid license you might not have, and do not want to buy. Nevertheless, the approach mentioned in #8 can be combined with approach #9 to save costs here. Think of an Access database (...again...) which is extremely minimalistic. It has only a single table which has two columns: one for the RFID chip number (CardID) and one for the time this RFID chip was recognized by an RFID reader (StampTime). Accompanying the table is a simple report that refreshes every second, so you can see any RFID event immediately (at least "mostly", as shown in the introductory video) . We want to handle an USB keyboard-like RFID reader now in a VMware. A few lines of Python will close the gap between the "RFID-keyboard" and Access. Just do this:
- Follow the sequence from #9 for attaching the reader to the virtual machine.
- Install Python in the virtual machine. In the installation process, make sure to agree to add Python to the system PATH variable.
- Install the Microsoft Access Database Runtime in the virtual machine.
- Fire up a command line window (cmd.exe) inside the virtual machine. Run the command pip install pyodbc.
- Download the Python script that is attached to this step and put it in a folder of the virtual machine.
- Open the Python script in a text editor and look for the pyodbc.connect method call. Adapt the path after DBQ according to your database name and location. (Again, make sure that the database is split and select the backend-DB here!).
- Save the script and start it (either by double clicking in the explorer or from the console).
- Now each time your RFID reader will “see” a tag, it will write to the database. (This is the first of the two solutions presented in the introduction video.)
Please note: Even after splitting the database, MS Access does not propagate all changes in the backend database to the frontend databases if the backend is placed on a local folder on the "host". Hence, if you modify the backend using pyodbc from the “guest”, that will techically work but the tables in the frontend database on the "host" (and thus the reports or forms) might not update as expected. To overcome this issue, there are two basic solutions: 1. Move the backend to another PC or server that both “host” and “guest” mount as network drive. 2. Move the backend to the “guest” and mount the corresponding folder as a network drive on the “host”. The latter is a pure local solution which worked for me.
Please note: All conventional 125 kHz tags or key cards can hold 10 hexadecimal digits of which 2 define a “version” and 8 an ID number. Some readers send all 10 hexadecimal digits, some reduce them to only 10 decimal digits as tags or key cards with hexadecimal numbers are less common. Some readers even rip the version number and send only 8 (decimal digits). The attached script just waits until a return key was “simulatively pressed” by the RFID reader and then sends what it has received to the database. You might enhance this behavior by checking the length of the codes according to your type of reader.
All this routing over secondary PCs or virtual machines does not do it for you? Still too confusing?? Well, then it might be best to build your own RFID reader and get rid of the simulated/emulated keyboard stuff completely. In this case, the next section is for you.
Downloads
The Real Stuff: Custom Hardware
As mentioned in the supplies section, the RDM6300 modules come in handy when you want to build your own custom reader that does not send keystrokes but sends the RFID code itself to the USB port. The RDM6300 module could do this mostly on its own – it has a serial interface, so all you need is an USB-UART interface. You will find cheap ones if you look for FTDI on the internet. The FTDI is converting a plain serial interface to a proper USB device which still transfers the raw data - which is what we want. However, this is not big fun, because neither RDM6300 nor the FTDI would not give much visible or audible response when the reader recognizes a tag.
Hence, we will combine RDM6300 it with a microcontroller (Arduino Nano) which might cost $1 more than the FTDI, but it provides advanced control and user feedback. Let's begin with the electrical design:
- Cut two 15 pin headers, one 3 pin header and one 5 pin header from the 40 pin header, and solder these headers in the positions shown in the circuit diagram.
- Add the 220 Ohm resistors and the 100 Ohm resistor in the defined positions.
- Add the “wire bridges” in the way shown in the diagrams.
- Solder two 15 cm wires to each LEDs. Connect the wires to the header pins as shown in the diagram.
- Solder the piezo buzzer in its place.
- Push the RDM module and the Arduino in the headers.
Please note: Both attached diagrams show the perfboard from the top. The first view visualizes the final perfboard with the Arduino, the RDM6300 and the buzzer in place, while the second view shows the perfboard without these elements. All solder bridges should be placed on the backside in the marked positions. The “wire bridges” are easier be placed on the front side. This way, ALL soldering operations can be done on the backside.
Please note: Most piezo buzzers have a higher voltage range than the Arduino output can provide (5V). Consequently, these buzzer are quite quiet (😊) when driven by a single output pin, and neither Arduino nor the piezos will take no harm when being operated without a resistor. Consequently, I skipped the 100 Ohm in my initial design shown in the photos. I have, however, added the resistor at a later stage, because I switched the software from the original tone() function from the toneAC library. This library drives the buzzer between two output pins, increasing the total voltage and thus the volume. Be sure to connect the piezo between pin D10 and D11. It is not possible to configure the library – it will use exactly these two pins.
If the electrical design is complete, go on with the software:
- Download the attached sketch, compile and upload it to the Arduino.
After you have uploaded the sketch to the Arduino, the device should...
- …light the first (red) LED and play some beeps to show it is ready to operate, and
- …light the second (green) LED for a about a second and play a tune each time it recognizes a key.
The code itself should be self-explanatory. It grabs the number from an RFID tag in its original format and converts it to a 10 digit number which again will be converted in a 10 character string. This will be sent to the USB port together with an end-of-line code (carriage return + line feed), so in total each code will come with 12 bytes.
You can customize the sounds by modifying the toneAC commands (or adding new ones). One point of interest could be the delay in the following lines:
if(millis() > lastTime + 2000){ lastTagNumber = 0; // Reset stored tag number after 2 seconds. }
The RDM6300 module will repeat sending the tag number as long as the tag is above the antenna. It does not really know whether a tag is “coming to” or “going from” the reader. Therefore, the “2000” in the code above will block the Arduino from re-sending the code of the same tag within 2000 milliseconds. Tweak this value if it is too fast or too slow for your application.
As a last construction step, build the housing. The attached STL files are easy to print without support structures. After printing, the assembly goes like this:
- Put the reader and the perfboard on the bottom side and screw-mount the perfboard with the M2 screws and nuts.
- As you see from the photos, the antenna may resist to stay in its holder. As a remedy, I have used some adhesive tape to keep it there. This is ok – it will not really have an effect on the signal processing.
- Push the LED clips from the top through the lid, then the LEDs from the bottom in the clips.
- Attach the USB cable to the Arduino and close the lid.
- Fix the lid with the M4 screws and nuts.
Please note: Attached you will find two files for the lid ("Housing - Top (Optimal Hole).stl" and Housing - Top (Bigger Hole).stl"). The first one has an "optimal" hole for most USB cable diameters, so the cable goes between bottom und top part of the housing. After this worked perfectly for me in the first place, I found my reader was dysfunctional after a week. The reason was simple: The lid pushed the cable downwards, and as it was considerably stiff, it forced some pressure on the plug - and that finally led to a permanent tension on the USB socket on the Arduino. Hence, the entire connector came eventually off. If you expect the same stress on your cable/connector, please use the version with the "bigger hole" which should not cause these "negative forces".
Finally, you need to do something for receiving the RFID card or tag numbers on your PC. Here, we have two options again, according to the solutions list from the first step:
- (11) You could try to capture all incoming card/tag numbers directly inside your target application. This, in particular, seems to be attractive for MS Office applications as they include the Visual Basic for Applications scripting engine. Standard VBA methods for reading data from a serial port require that you specify the number of bytes you want to receive when invoking the methods. This would be ok, as we know that our device sends 12 bytes for each tag/card now. Unfortunately, not only the script will wait for those 12 bytes, but also the whole application (i.e. Word, Excel, PowerPoint, Access, ...) will block until 12 bytes are received. Microsoft forgot to provide a VBA command for peeking the number of available bytes before invoking the “get” or “input” commands for reading from the port. As a remedy, you can use WinAPI functions. The attached Excel_Serial_VBA.txt shows an example how to do this. Create an empty Excel sheet, switch to the Visual Basic Editor, add a module file and paste the code. Then use the Arduino IDE (or the Windows Device Manager) to find out which COM-port your Arduino is connected to, and adapt the following line of the VBA code accordingly:
comPortHandle = CreateFile("COM3", GENERIC_READ Or GENERIC_WRITE, 0, Null, OPEN_EXISTING, 0, 0)
Now execute the SerialTest() macro, e.g. with the “Play” button in the Visual Basic editor. You will see that the first column of the Excel sheet beautifully fills with card/tag numbers from the reader – and the application will not block. Why? Because the included WindowsAPI commands provide ways for defining a (short) time-out, and Excel is now just trying to read data from the port in a loop. As long as it has not received 12 bytes, it triggers a “DoEvents” command, so Excel (and you) can do other stuff. The loop is limited to reading 10 tags (see the while loop at the end of the code). This limit was added because of the way Excel handles the macros: If you simply stop the macro in the middle of its execution, the serial port will remain opened, so you cannot restart the macro until you close and re-start Excel (although, as you could try to always close it prior to opening it). If you want to have an interruptible endless loop for receiving the RFID codes, think of an event that could force the script to gracefully close the port. As an example, the loop could check a certain cell on the sheet for the word “STOP”.
Another way to implement an endless loop that is not blocking the system would be multithreading. VBA itself does not know anything about multithreading, but again you could trick the system by adding more WindowsAPI code for making the script spawn a second thread for supervising the COM port. Multithreading in a VBA environment, however, is not the easiest to do, and probably not the most brilliant idea, as discussed here.
- (12) Finally, the optimal solution is to capture the data from the RFID reader with a dedicated application and then send it to the target. Let’s go back to the original MS Access scenario: The provided Python example code will grab each number and send it to an MS Access database. In contrast to solution 10, it requires Python to be installed on the same PC as the target application. After installing it, enter the following commands in a command line window:
- pip install pyserial
- pip install keyboard
Open the Python file in a text editor and set the correct serial port in the following the line:
ser.port = 'COM3' # ******** Adapt the serial port according to your connection. ********
Furthermore, adapt the path to the Access database in the corresponding line of code.
Again, the code is very basic and self-explaining. It does about the same as in solution #10, only that it does not capture keystrokes, but the tag/card numbers coming in from the serial port (i.e. USB port) directly. Thus, it cannot lose any digits as long as the transmission is stable. Concerning MS Access, it is still a good idea to split the database as in the concepts described above. Frontend and backend databases, however, can simply stay within one folder on the same PC in this solution. If there are any delays when receiving tag numbers specifically in this MS Access scenario, make sure to tweak the refresh rates in the client settings.
Please note: The two methods presented by (11) and (12) aim at interpreting the the raw data coming from the self-made device which is exactly what we want because the "commercial" USB-keyboard-like RFID / barcode readers cannot do this. If, for whatever reason, you want to re-engineer the behavior of the USB-keyboard-like readers, here is a good description on how to this.
Enjoy
I know that this is a very lengthy tutorial, covering many different aspects of
- reading RFID codes / barcodes and
- routing them to different applications (well, mainly MS Access which was obviously my target).
I hope the provided information is still helpful, and that it saves you some hours if you are stepping into similar situations.