I feel so bad, I thought I had posted part 4 of my RFID project but I completely forgot. I wrote most of the code while taking care of my mom at the hospital, I had even started to make a post, but life had been a mess and I forgot to publish this post. So I cleaned up the draft and here it is.
The source code is quite messy, but it works quite well. I'm proud to say that in the two years since the RFID boxes have been online, they've worked perfectly. We have 300+ employees in two shifts that scan their cards as they come to work and go off work every single day. The boxes surved storms and electrical failures and hardware failures and continued to work. Employees have tried to trick the system by reporting their cards being lost then requesting a second backup card, and the system was able to detect this (through the Windows side software). The only hardware failure was that the POE Ethernet Shield had a design problem where the POE module is way too close to the Ethernet chip and the magic smoke escaped. Out of the eight boxes I deployed, two failed this way.
I'm only posting the code for the Arduino. On the Windows side, there's a .NET program that handles pulling the data off the boxes into a SQL database, then calculates the data into working hours. There's also a separate Windows program for programming the RFID cards. The RFID cards are standard MiFARE Classic cards, and we print our employee photos on stickers and stick them to the cards.
Hmm, I can't figure out a way to attach the code and comment inline, so I'll do it in points. First up is rfid_standalone.ino which is the main program. There's a lot of bad code and I'm sure things could be improved, but I never found the energy to work on this again. (Besides, it wasn't broken!)
- The Sector 1 Key A is 0x102030405060 here (change as needed). It's to make sure we're reading our own cards and not random data from other people.
- MAC_ADDRESS can be defined as needed. I have a DHCP server so I put the MAC for each RFID box in there to let it pick up the IP address from there.
- I used a regular LCD module. The LCD module displays current time and whether the box is programmed for IN or OUT. In/Out can be swapped using a special card. If the box is booted up without network connectivity it shows -- otherwise it shows the day of the week.
- The RFID card reading code continuously polls the card until valid data is read then stops. There's no "SUCCESS" or "FAIL" unlike some systems. I discovered it confuses our employees.
- Data from the card is displayed on the LCD screen and logged to the SD card.
- A sprintf command that writes card data to the SD card. This can of course be customized, but as written it's RFID_UID, Employee_ID, In/Out, Time
- Along with that spaghetti code, there's a command to create directories on the SD card. So if the SD card is removed and read on the computer, each year is a separate directory and each date is a separate file.
- The command to read data off the SD card is http://ip_addr/yyyymmdd which prints in plaintext the log written from above. If yyyymmdd is omitted, then the RFID box data and uptime is printed.
Second up is rfid_util.ino which just separates some of the utilities used by the main program into a separate program to stop myself from confusing myself.
- Some code is used to display a ticking clock. When there's no activity the LCD backlight is turned off.
- NTP code. There's actually a RTC, so if the network is offline (such as during a power outage), the RFID boxes can continue to function and record by using a UPS.
Finally, rfid_admin.ino is the RFID card creation utility that sets up RFID cards for employees.
- The commands are handled using regular serial port messages. We write the employee names and codes to the cards, so they can be read back and displayed when the cards are scanned.
- The RFID boxes can have fixed IP addresses which can be set using the utility.
- As mentioned above, there's a Windows program to handle the interface, but all the commands can be easily accessed using a regular terminal program.
The code is probably really difficult to understand, but I did put in comments in the code. Please also refer back the previous parts for additional comments and libraries used. There's no guarantees whatsoever that this code will work for you, but if it does and you find it useful, please put it to good use.