How I Built an Employee Time Tracking System From Scratch (Part 1)

Written by sinenko | Published 2021/07/06
Tech Story Tags: arduino | rfid | employee-time-tracking | ethernet | tutorial | ethernet-shield | unix | programming

TLDR Using the Arduino board, Ethernet Shield, and RFID module, we learn to assemble, solder, and program the device to mark the time of arrival and departure of employees. In the next article, we will make a small server script that will receive and process requests. The device must read the RFID tag and send it to the server via HTTP requests. It should be possible to switch to the mirror server in case of problems with the main server. We will store all the stored information in a 4-byte int, which is sufficient for most tasks.via the TL;DR App

Today, using the Arduino board, Ethernet Shield, and RFID module, we will learn to assemble, solder, and program the device to mark the time of arrival and departure of employees. In the next article, we will make a small server script that will receive and process requests.

The task

We have lean manufacturing and tight deadlines, so the device needs to be made quickly and at a low cost.
  • The device must read the RFID tag and send it to the server via HTTP requests.
  • There must be protection against the accidental double application of RFID tags.
  • The device must send a timestamp in UNIX format and must also synchronize the time with the server.
  • If the server is unavailable, the tags should be saved to memory and, when the network is available, send tags from memory.
  • It is advisable to do without external flash drives.
  • It should be possible to save at least 300 records in case of communication interruptions.
  • It should be possible to switch to the mirror server in case of problems with the main server.
  • It should be possible to set both static and dynamic IPs for the device.
  • It should be possible to change device settings using requests from the server.
To create a system for tracking visits, I will use:
  • (Any) Duino Mega (ATmega2560) — Brains
  • Ethernet Shield W5100 — For sending TCP requests to the server
  • RFID reader on Mifare RC522 (13.56MHz) — For reading MIFARE cards
  • DS3231 High-precision clock AT24C32 with an I2C interface — for time tracking in case of communication interruptions
  • Buzzer — for sound notifications
The first thing we need to do is check the resistor assembly next to the Ethernet port. It should be 510 (51 ohms).
Chinese manufacturers do not have 510 (51 Ohm) resistor assemblies, and they solder 511 resistor assemblies. They probably think that the difference is not big, but the 511 marking means 510 ohms! The difference in resistance is 10 times.
If you are unlucky and you have an Ethernet Shield with a resistor assembly, not 51-ohm, then you need to rewire it. Otherwise, Ethernet Shield may not work and would conflict with many ethernet switches from different manufacturers.
If you have a 51-ohm resistor, continue. Now we need to connect everything. Below I drew a visual connection diagram:
In reality, it is better to solder all wire connections to eliminate bad contact. As a result, it will turn out something like this:
In the future, we will make a case for all electronics, but for now, let’s deal with the firmware. We will write the firmware in C ++ using the Arduino libraries and compile it in the Arduino IDE.
I recommend that you check the “use an external code editor” checkbox in the Arduino IDE settings:
We need libraries:
  • DS3231 — library for working with the time module.
  • SPI — required for the Ethernet library to work.
  • Ethernet — library for working with Ethernet Shield.
  • SimpleTimer — for pseudo-parallel task execution on Arduino.
  • MFRC522 — to work with the Mifare module.
  • OneWire — not necessary. We will only take the function of calculating the CRC amount to check the integrity of the data.
  • MemoryFree — for cleaning RAM from the garbage.
  • pgmStrToRAM — for storing and retrieving text and FLASH memory in RAM.
  • EEPROM — for working with EEPROM memory, we will store unsent marks in it.
And so, to not use an external SD card, we will store all information in the EEPROM. The ATmega2560 has 4096 bytes of EEPROM memory. We need to store basic device settings that a command could change from the server. We will store all the stored information in a 4-byte int, which is sufficient for most tasks.
Basic settings will include:
  • Settings version (to determine if there are newer settings on the server) — 
    idSettings
    (int).
  • Debug mode — 
    a_debuging
    (bool).
  • Server ID to which requests will be sent by default —
    b_PriorityServer
    (int).
  • Limit of records with tags in the database to stop writing new tags in case of overflow — 
    c_CountStorageRecords
    (int).
  • The IP address of the device, for the sake of universality, that we will divide into 4 parameters (although one could do with one) — 
    d_ip1, e_ip2, f_ip3, g_ip4
    (int)Activate DHCP client — 
    h_DHCP
    (bool).
What are the prefixes
a_; b_; c_;
…? To save RAM, we will abbreviate variable names before sending and receiving HTTP requests. For example: 
/?
a
=1&
b
=1&
c
=300&
d
=192&
e
=168&
f
=0&
g
=27&
h
=0
And with prefixes, it’s easier to debug and read code. In total, we got 9 parameters. We will reserve 4 bytes for them, supposing we will have more parameters in the future, but not more than 32. In the EEPROM we reserve 32 * 4 = 128 bytes for settings.
At startup, we will save the EEPROM, for example, the number 25, so that the program can determine the replacement of the Arduino with a new one and write the default settings to empty cells. We will reserve 1 byte for this.
To control the integrity of the data, we will also calculate the CRC8 sum of all settings and store it. For the CRC sum, we will allocate 4 more bytes. It is also desirable to store the current number of records with tags in the database — another 4 bytes.
Total for storing records, we have 4096–128–1–4–4 = 3959 bytes. In our database, we will store RFID tag ID 4 bytes + UNIX TIMESTAMP 4 bytes = 8 bytes.
In total, if the server is not available, 494 records will fit in the EEPROM memory, which is more than enough. I’ll leave it just in case; I think 468 records in the database will be enough for us to store records of visits.
First of all, let’s write functions for outputting debug data so as not to waste RAM on lines. We will store lines in FLASH memory; we have ~ 248KB of it. We will have to separate the output of variable values and the output of static strings.
To print a string and a variable to the console, we will use 2 commands. This is not pretty, but it saves RAM.
We will also write a function to get strings from FLASH memory. To save a string to FLASH memory, we do not need to do anything, just call our function, the rest will be done by the pgmStrToRAM library.
The following function gets the time in UNIX format from the time module.
Now let’s write a function that will read the RFID card and write the card ID and UNIX TIMESTAMP into variables:
We need to make our function constantly work and check whether the user has presented the RFID card.
When the card is read, a short playTone() signal is played;
If, when reading, sending a request takes too long, the sound will last until the request is sent or an error is received. Therefore, the user will immediately hear that the request is being processed for too long. If everything is ok, the beep will last 500ms.
Now let’s write functions to read and write 4 bytes numbers to the EEPROM.
I will not insert screenshots of the functions for sending a request and receiving a response from the server, as well as functions for processing and saving the received settings in EEPROM and loading them into RAM, because they take up a lot of space, you can see them in the repository on GitHub.
I will only say that the format of the server response upon successful request processing will be as follows:
[ok] [uid_ok]
The server will transmit settings in the following format:
{sid6}a_1|b_1|c_255|d_192|e_168|f_0|g_26|h_0|
Where sid6 is the ID of the new settings (idSettings = 6), and the rest of the parameters are our variables and their values.
You can see the complete code and other functions in the repository on GitHub.
And so we got a compact device that works from any 5-volt power supply, PowerBank, or via USB. For the device to operate, a current of 300mA is sufficient. 
In the next article, we will write server-side code, and to improve the appearance of the device, I modeled the case in SolidWorks. The case can be cut from 3mm thick acrylic, plywood, and other materials, or you can customize the body as you like. I also posted the sources in the repository.
I cut the case of black acrylic, and here’s the result:

Written by sinenko | CEO, WEB Software Engineer, Production of 3D printers
Published by HackerNoon on 2021/07/06