NFC meets LoRa — using Long Range Communication for Access Control Systems with ESP32
This is not a tutorial, but rather a proof-of-concept article in which I will demonstrate how to combine two technologies. The first is Near Field Communication (NFC) — the technology used, for instance, when a hotel key card is held against a door reader to verify credentials and grant access to a room. The second is Long Range Communication (LoRa), which is ideally suited for transmitting small amounts of data over significant distances (ranging from a few meters up to several kilometers). Both technologies share a common characteristic: they handle small data packets efficiently and have been optimized for energy efficiency.
Why does combining these two technologies make sense?
Many traditional access control systems operate autonomously; that is, their readers do not require direct access to a central “controller.” However, to update a reader with current information — such as the correct time or a list of revoked access cards — staff members must manually visit each individual reader to update it. Furthermore, if the access card is to serve a dual purpose — for example, as a payment method (e.g., at a beverage dispenser or equipment rental kiosk) — it becomes highly advantageous to transmit the card’s current credit balance and inventory levels to a central computer.
Within a single building, establishing such communication is usually quite simple using the existing Wi-Fi network. However, the task becomes significantly more challenging if the reader is located outdoors — for instance, on a golf course — several hundred meters away from the main clubhouse. This is where LoRa communication comes into play: it provides a wireless link that can be utilized license-free and implemented using small, low-cost devices. For example, if I use an ESP32 microcontroller as the foundation for the card reader, adding a LoRa interface is a quick and straightforward process.
What Hardware Do I Need?
For my test setup, on the card reader side, I am using an ESP32 development board with an integrated TFT display, an NFC card reader (Type PN532) with an SPI interface, and a LoRa transceiver equipped with a UART (serial) interface.
For the controller side, I am using an ESP32-S3 development board that is equipped with both a TFT display and a LoRa transceiver chip (Type: SX126X) (Heltec Vision Master T190).
I programmed this example to demonstrate the use of the popular Mifare Classic NFC cards, which are utilized as access control cards in nearly all hotels. To simulate the two verification states — “Access Granted” and “Access Denied” — I am using two different cards.
For an overview of the LoRa module with a UART interface, I recommend my tutorial “Simple LoRa transmissions over several kilometers using UART/Serial LoRa modules,” which explains this module and its usage in detail. For guidance on using off-the-shelf ESP32 development boards with LoRa capabilities, I recommend my article series “Working with Long Range Communication (“LoRa®”, “LoRaWAN®” and “Meshtastic®”) on an ESP32 platform.”
The reason I connected the card reader to a UART-based LoRa module (instead of utilizing the standard SPI interface) is simple: the wiring is significantly easier to manage — especially since the ESP32 is already utilizing both SPI interfaces (for the TFT display and the NFC reader), meaning I would otherwise have had to implement interface sharing (which is technically possible, but would have entailed considerably more effort).
What software do I use?
Programming is performed using the Arduino IDE version 2.3.8 and the esp32-boards version 3.3.8 on Windows 10. I incorporate the following additional libraries: the TFT_eSPI graphics library by Bodmer (for both devices), and the ST12XX-LoRa library by Stuart Projects for LoRa communication on the controller side. For reading NFC cards, I use the Adafruit PN532 library.
Basic Program Flow
- After both boards have started up, the controller side first establishes a connection to the Wi-Fi router, contacts the Network Time Protocol (NTP) server, and synchronizes its system time (which is Coordinated Universal Time, UTC).
2. Next, the program calculates the local time and displays it on the idle screen.
3. From this point on, the controller sends the current local time timestamp every minute as a LoRa broadcast — that is, to every LoRa device within its range.
4. The controller enters a waiting phase to await access permission requests.
5. The card reader side receives the timestamp via LoRa and synchronizes its system time with the controller’s local time (thus, unlike on the controller, the system time here corresponds to the actual local time).
6. The card reader also enters a waiting phase, waiting for an access card to be placed on the reader device.
At this point, both devices are in an inactive waiting phase, during which an “Idle” screen displaying the current local time is shown.
Get AndroidCrypto’s stories in your inbox
Join Medium for free to get updates from this writer.
7. An access card is held up to the card reader; it is read, and its details are displayed on the screen.
8. The read access data is transmitted to the controller via LoRa.
9. The controller receives the access data and displays it on its screen.
10. The controller verifies whether the card currently holds valid access authorization. In this specific program, authorization is granted solely if the room number is “12.” If access authorization is confirmed, the text “GRANTED” appears beneath the access data; otherwise, “DENIED” is displayed.
11. The controller transmits the verification result via LoRa (0 = No authorization; 1 = Access granted), along with a transmission ID, to enable — if necessary — the correlation of the result with the original verification request (this ID is not currently being processed).
12. The card reader receives the response from the controller and displays either “Access Granted” or “Access Denied” on its screen.
At this point, both devices return to an inactive waiting phase, during which an “Idle” screen displaying the current local time is shown.
Project Goal
With this test setup, I demonstrate that we can seamlessly combine both technologies — NFC and LoRa — and effortlessly bridge significant distances between a card reader and its controller using LoRa.
How does the NFC reading component work?
After initializing the UART interface, the display, and the NFC reader, the program waits for the receipt of a LoRa packet containing a local timestamp, which supplies the ESP32 with current time data. From this point on, the reading unit waits for a Mifare Classic NFC card to be placed on the reader. With these cards, each block consists of three data sectors and one sector dedicated to access control information. In terms of programming, sectors 4, 5, 6, 8, 9, and 10 are utilized for our data. For our “PoC” (Proof of Concept), we do not employ any access protection; consequently, these sectors must be freely readable and writable.
To populate our cards with test data, type “WRITE” into the Serial Monitor. This initiates a guided dialogue in which you sequentially enter the name (maximum 20 characters), the room number (maximum 2 characters), the card’s validity period (from–to; exactly 8 characters in the format DD.MM.YY), and a credit balance (maximum 8 characters, e.g., 123.45). If you prefer to use the two pre-configured test datasets, use the commands “WRITE1” or “WRITE2,” respectively.
To determine whether a card is “Accepted” or “Rejected” later in the process, I have implemented a very simple verification mechanism within the controller: if the room number is “12,” the access request is granted; for any other entry in the “Room” field, access is denied.
Once a card is placed on the reader, its contents are read, displayed in a structured format on the screen, and then transmitted via LoRa. After a brief waiting period, the standby screen is displayed once again. Since only small amounts of data are being transmitted, we receive a very rapid response from the controller — essentially a simple “Yes” or “No.” I also include the sequential request number in the transmission to facilitate correlation, although I do not currently process or evaluate this specific piece of information. The result is displayed on the screen — either with a green icon (access granted) or a red icon (access denied); after a brief waiting period, the idle screen reappears.
How does the controller section work?
After initializing the display and the LoRa chip, the board establishes a connection to the Wi-Fi router and synchronizes its system time with a Network Time Protocol (NTP) server. Thanks to the POSIX configuration string, the ESP32 can now calculate the local time and display it on its standby screen. Every minute, the controller transmits a timestamp containing the current local time via LoRa. In a practical application, this function would typically be executed — for instance — upon a request from a new card reader, or once nightly.
From this point on, the board awaits the receipt of a LoRa packet containing access credentials; following a brief validation check (verifying both the packet length and the “magic bytes” at the beginning and end), the data is read and displayed in a structured format on the screen. Access authorization is verified using a very simple logic: if the room number is “12,” access is granted; for any other value, it is denied. The result of this verification is displayed on the screen and transmitted back to the card reader unit via a LoRa packet; subsequently, the device returns to its standby state.
Note regarding both programs: Since I was too lazy to rewrite both programs from scratch, I slightly modified existing programs; as a result, they contain — in part — additional (unused) functions.
Summary
The interplay between the two technologies — NFC and LoRa — is easy to implement using an ESP32; it enables, for instance, the operation of access control systems across a wide spatial area while still maintaining “network” access from the controller to every individual reader.
Source code of the app
You find the complete code of the apps, the modified TFT_eSPI library and the device-display settings file in my GitHub repositories.
Happy coding !










