This project is an ATmega chip acting as an USB keyboard that types out the data from a RFID reader (the idea is that the ID strings can be used as login passwords). I used an ATmega168 with V-USB hardware and a ID-12 RFID reader.
This is my 2nd USB device project using AVR microcontrollers. For another project that explains how I use V-USB, see my Wii Nunchuk USB Gamepad / Mouse. (A lot of this page will refer to this as the setup procedure are very similar)
The hardware is based around V-USB's recommended hardware, although it's been modified slightly to work at 5V. My first V-USB project, the Wii Nunchuk USB Gamepad / Mouse uses a very VERY similar circuit and on that page I explain how I came up with the circuit.
Here's the circuit for the RFID keyboard:
See? The only difference is that this project connects a RFID reader to the USART serial port instead of connecting a Nunchuk to the TWI port.
The software is based around this example keyboard project: The Real USB Model-M by Chris Lee.
Once again, I use my own AVR Project IDE as the editor for this project. Setting up the project is very similar to how I setup the project for my USB Nunchuk (mentioned above). One difference is that the HID descriptor is contained in "main.c" instead of a separate file, and the report data structure is a simple array of bytes. Also, a serial port library I wrote is used ("ser.c" and "ser.h").
In "usbconfig.h", the things that are edited are the device and vendor names. Of course "USB_CFG_IOPORTNAME", "USB_CFG_DMINUS_BIT", and "USB_CFG_DPLUS_BIT" must reflect the physical circuit setup (port D, bit 4 for D- and bit 2 for D+). "USB_CFG_INTR_POLL_INTERVAL" is set to 10 ms, "USB_CFG_MAX_BUS_POWER" is set to 500 (I just prefer a higher value, don't worry about this unless you want your operating system to give you warnings when your device malfunctions), and the device is self powered (see "USB_CFG_IS_SELF_POWERED").
Pay attention around here:
This makes the device a keyboard that can be used before the operating system loads up.
In "usbconfig.h", "USB_CFG_IMPLEMENT_FN_WRITE" is set to 1, which enables you to write a function that handles incoming data from the computer. The computer would sometimes like to send data that turns on caps lock, num lock, scroll lock, and that sort of thing. We write this function to handle it:
V-USB also needs you to write something to handle some requests from the computer:
That function allows the computer to set the idle rate of the keyboard (something related to repeat keystoke setting). "protocolver" can be in boot mode or report mode. And sometimes the computer asks for the size of the report.
So the stuff related to V-USB is done. Let's move on to how to read data from the RFID reader! According to the ID-12's datasheet
My serial port library ("ser.c" and "ser.h") uses a
The main loop of my code will check if any characters have been received. If there are new characters, it gets translated (see "set_buffer") to keycode and stored in the report data buffer, and sent to the computer.
You will notice that timer1 (running with a prescaler of 256) of the ATmega is used to keep timing. Since the computer sets the idle rate, we should respect it and report idle once in a while. The timer runs and when enough time has passed, the code reports no keystrokes back to the computer, the timer is then reset. This is used for other timekeeping as well, when I send a keystroke, I send it for at least 60 ms, just to make sure it gets registered (and it makes a cool "typing" effect).
My code's "set_buffer" function is a really quick and dirty way of translating ASCII characters to keycodes that can be sent through a keyboard protocol. It only works for alphanumeric characters. To implement more keys, you should look up more keycodes yourself.
So load up the project, compile, and build! The AVR fuses should be identical to the ones used in the USB Nunchuk.
That's basically it. This isn't really secure for logging into anything since anybody can steal your RFID tag and figure out the password. But you can be creative! I'd suggest a system where the string from one tag is used as a salt to encrypt the string from a second tag, which would increase security quite a bit (of course only if you keep the encryption technique unique and private, and never lose your tags).
while (1) // main loop
{
uint8_t read_cnt;
uint8_t rx = ser_rx(0, &read_cnt);
if (read_cnt > 0) // if char is received
{
set_buffer(rx); // sets the buffer with keycodes according to character to send
...
...
...
// basically translate ascii to a keycode and place it in the buffer if the character is allowed
void set_buffer(uint8_t c)
{
memset(reportBuffer, 0, sizeof(reportBuffer)); // clear all of the buffer
if (c < 0)
{
return; // nothing was received
}
else if (c >= 'A' && c <= 'Z') // capital letters
{
reportBuffer[2] = 4 + c - 'A'; // set keycode
reportBuffer[0] = _BV(1); // left shift active
}
else if (c >= 'a' && c <= 'z') // lowercase letters
{
reportBuffer[2] = 4 + c - 'a';
}
else if (c >= '0' && c <= '9') // numbers
{
if (c == '0')
{
reportBuffer[2] = 0x27;
}
else
{
reportBuffer[2] = 30 + c - '1'; // set keycode
}
}
}