Building a Touchscreen RFID Reader

We’ve all been there: staring at a breadboard buried under a rat’s nest of jumper wires, trying to figure out why your standalone display isn’t communicating with your microcontroller. When I set out to build an RFID reader, I knew I wanted a clean, functional user interface without the usual hardware spaghetti. Enter the ESP32-CYD (Cheap Yellow Display). It will allow a user to scan an RFID tag and show the information on the CYD.

In this post, I’ll walk you through how to build a fully functional RFID reader using the CYD. We’ll cover the hardware connections, the software setup, and how to get a clean UI up and running. If you haven’t worked with the CYD yet, it’s about to become your go-to board for interactive projects. Here’s why it’s the perfect fit for this build:

  • All-in-One Integration: It combines a dual-core ESP32, a 2.8-inch TFT display, and a resistive touch screen on a single, affordable board. No more wiring displays from scratch.
  • Built-in Connectivity: Thanks to the ESP32 backbone, logging RFID scans to a local server or home network over Wi-Fi is practically native.
  • Accessible I/O: Despite the integrated screen, the CYD still exposes the necessary pins (like the SPI bus) to easily interface with common RFID modules like the RC522 or PN532.

Bill of Materials (BOM)

  • Display/MCU: ESP32-2432S028R (The standard 2.8″ CYD with ILI9341 display and resistive touch)
  • RFID Module: MFRC522 Reader (13.56 MHz)
  • RFID Tags: Mifare Classic 1K Cards or Keyfobs (usually included with the MFRC522)
  • Wiring: 7x Dupont jumper wires (female-to-female or soldered directly)
  • Power: Micro-USB or USB-C cable (depending on your specific CYD variant)

Wiring Guide (Shared SPI Bus)

MFRC522 PinESP32-2432S028R PinFunction
3.3V3V3Power (Do not use 5V)
RSTIO21Reset (Available on back JST/pads)
GNDGNDGround
MISOIO12Shared SPI Data Out
MOSIIO13Shared SPI Data In
SCKIO14Shared SPI Clock
SDA (SS)IO22Chip Select (Available on back JST/pads)

PlatformIO Configuration

Instead of modifying the TFT_eSPI library files manually, you can inject the hardware configuration directly into your platformio.ini file. This makes the project highly portable.

platform.ini

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

lib_deps =
bodmer/TFT_eSPI @ ^2.5.31
miguelbalboa/MFRC522 @ ^1.0.10

build_flags =
-D USER_SETUP_LOADED=1
-D ILI9341_2_DRIVER=1
-D TFT_WIDTH=240
-D TFT_HEIGHT=320
-D TFT_MISO=12
-D TFT_MOSI=13
-D TFT_SCLK=14
-D TFT_CS=15
-D TFT_DC=2
-D TFT_RST=-1
-D TFT_BL=21
-D TFT_BACKLIGHT_ON=HIGH
-D LOAD_GLCD=1
-D LOAD_FONT2=1
-D LOAD_FONT4=1
-D SPI_FREQUENCY=27000000

Application Code

This code initializes the shared SPI bus, waits for an RFID tag to be presented, and prints the Unique ID (UID) of the tag to the center of the display.

main.cpp
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <MFRC522.h>

// RFID Pins
#define RST_PIN 21
#define SS_PIN  22

// Initialize display and RFID instances
TFT_eSPI tft = TFT_eSPI(); 
MFRC522 mfrc522(SS_PIN, RST_PIN);

void setup() {
  Serial.begin(115200);

  // Initialize the TFT Display
  tft.init();
  tft.setRotation(1); // Landscape mode
  tft.fillScreen(TFT_BLACK);
  
  // Draw static UI elements (Dark Mode Aesthetic)
  tft.setTextColor(TFT_CYAN, TFT_BLACK);
  tft.setTextDatum(MC_DATUM); // Set text alignment to middle-center
  tft.drawString("RFID Scanner Ready", tft.width() / 2, 40, 4);
  tft.drawLine(20, 60, tft.width() - 20, 60, TFT_DARKGREY);

  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString("Please scan a tag...", tft.width() / 2, tft.height() / 2, 2);

  // Initialize SPI bus and MFRC522
  SPI.begin();
  mfrc522.PCD_Init();
  
  Serial.println(F("System initialized. Waiting for RFID tags..."));
}

void loop() {
  // Look for new cards
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Clear the middle area of the screen for the new ID
  tft.fillRect(0, 100, tft.width(), 100, TFT_BLACK);

  // Build the UID string
  String uidString = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (mfrc522.uid.uidByte[i] < 0x10) {
      uidString += "0";
    }
    uidString += String(mfrc522.uid.uidByte[i], HEX);
    if (i < mfrc522.uid.size - 1) {
      uidString += ":";
    }
  }
  
  uidString.toUpperCase();

  // Print to Serial
  Serial.print(F("Card UID: "));
  Serial.println(uidString);

  // Update TFT Display
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.drawString("Tag Detected!", tft.width() / 2, 120, 2);
  
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.drawString(uidString, tft.width() / 2, 160, 4);

  // Halt PICC to prevent spamming the display with the same tag
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
  
  // Brief delay before allowing the next scan
  delay(1000); 
  
  // Reset prompt
  tft.fillRect(0, 100, tft.width(), 100, TFT_BLACK);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);
  tft.drawString("Please scan a tag...", tft.width() / 2, tft.height() / 2, 2);
}

Wiring Schematic

Leave a Reply

Your email address will not be published. Required fields are marked *