{"id":324,"date":"2026-06-23T12:30:49","date_gmt":"2026-06-23T17:30:49","guid":{"rendered":"https:\/\/anthony.sleck.us\/?p=324"},"modified":"2026-06-23T12:44:05","modified_gmt":"2026-06-23T17:44:05","slug":"building-a-touchscreen-rfid-reader","status":"publish","type":"post","link":"https:\/\/anthony.sleck.us\/?p=324","title":{"rendered":"Building a Touchscreen RFID Reader"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">We\u2019ve all been there: staring at a breadboard buried under a rat&#8217;s nest of jumper wires, trying to figure out why your standalone display isn&#8217;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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post, I\u2019ll walk you through how to build a fully functional RFID reader using the CYD. We&#8217;ll cover the hardware connections, the software setup, and how to get a clean UI up and running. If you haven&#8217;t worked with the CYD yet, it\u2019s about to become your go-to board for interactive projects. Here\u2019s why it\u2019s the perfect fit for this build:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>All-in-One Integration:<\/strong> 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.<\/li>\n\n\n\n<li><strong>Built-in Connectivity:<\/strong> Thanks to the ESP32 backbone, logging RFID scans to a local server or home network over Wi-Fi is practically native.<\/li>\n\n\n\n<li><strong>Accessible I\/O:<\/strong> 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.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bill of Materials (BOM)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Display\/MCU:<\/strong> ESP32-2432S028R (The standard 2.8&#8243; CYD with ILI9341 display and resistive touch)<\/li>\n\n\n\n<li><strong>RFID Module:<\/strong> MFRC522 Reader (13.56 MHz)<\/li>\n\n\n\n<li><strong>RFID Tags:<\/strong> Mifare Classic 1K Cards or Keyfobs (usually included with the MFRC522)<\/li>\n\n\n\n<li><strong>Wiring:<\/strong> 7x Dupont jumper wires (female-to-female or soldered directly)<\/li>\n\n\n\n<li><strong>Power:<\/strong> Micro-USB or USB-C cable (depending on your specific CYD variant)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wiring Guide (Shared SPI Bus)<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>MFRC522 Pin<\/strong><\/td><td><strong>ESP32-2432S028R Pin<\/strong><\/td><td><strong>Function<\/strong><\/td><\/tr><tr><td>3.3V<\/td><td>3V3<\/td><td>Power (Do not use 5V)<\/td><\/tr><tr><td>RST<\/td><td>IO21<\/td><td>Reset (Available on back JST\/pads)<\/td><\/tr><tr><td>GND<\/td><td>GND<\/td><td>Ground<\/td><\/tr><tr><td>MISO<\/td><td>IO12<\/td><td>Shared SPI Data Out<\/td><\/tr><tr><td>MOSI<\/td><td>IO13<\/td><td>Shared SPI Data In<\/td><\/tr><tr><td>SCK<\/td><td>IO14<\/td><td>Shared SPI Clock<\/td><\/tr><tr><td>SDA (SS)<\/td><td>IO22<\/td><td>Chip Select (Available on back JST\/pads)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PlatformIO Configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of modifying the <code>TFT_eSPI<\/code> library files manually, you can inject the hardware configuration directly into your <code>platformio.ini<\/code> file. This makes the project highly portable.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>platform.ini<\/summary>\n<p class=\"wp-block-paragraph\">[env:esp32dev]<br>platform = espressif32<br>board = esp32dev<br>framework = arduino<br>monitor_speed = 115200<br><br>lib_deps =<br>    bodmer\/TFT_eSPI @ ^2.5.31<br>    miguelbalboa\/MFRC522 @ ^1.0.10<br><br>build_flags =<br>    -D USER_SETUP_LOADED=1<br>    -D ILI9341_2_DRIVER=1<br>    -D TFT_WIDTH=240<br>    -D TFT_HEIGHT=320<br>    -D TFT_MISO=12<br>    -D TFT_MOSI=13<br>    -D TFT_SCLK=14<br>    -D TFT_CS=15<br>    -D TFT_DC=2<br>    -D TFT_RST=-1<br>    -D TFT_BL=21<br>    -D TFT_BACKLIGHT_ON=HIGH<br>    -D LOAD_GLCD=1<br>    -D LOAD_FONT2=1<br>    -D LOAD_FONT4=1<br>    -D SPI_FREQUENCY=27000000<\/p>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Application Code<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">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.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>main.cpp<\/summary>\n<pre class=\"wp-block-code\"><code>#include &lt;Arduino.h&gt;\n#include &lt;SPI.h&gt;\n#include &lt;TFT_eSPI.h&gt;\n#include &lt;MFRC522.h&gt;\n\n\/\/ RFID Pins\n#define RST_PIN 21\n#define SS_PIN  22\n\n\/\/ Initialize display and RFID instances\nTFT_eSPI tft = TFT_eSPI(); \nMFRC522 mfrc522(SS_PIN, RST_PIN);\n\nvoid setup() {\n  Serial.begin(115200);\n\n  \/\/ Initialize the TFT Display\n  tft.init();\n  tft.setRotation(1); \/\/ Landscape mode\n  tft.fillScreen(TFT_BLACK);\n  \n  \/\/ Draw static UI elements (Dark Mode Aesthetic)\n  tft.setTextColor(TFT_CYAN, TFT_BLACK);\n  tft.setTextDatum(MC_DATUM); \/\/ Set text alignment to middle-center\n  tft.drawString(\"RFID Scanner Ready\", tft.width() \/ 2, 40, 4);\n  tft.drawLine(20, 60, tft.width() - 20, 60, TFT_DARKGREY);\n\n  tft.setTextColor(TFT_WHITE, TFT_BLACK);\n  tft.drawString(\"Please scan a tag...\", tft.width() \/ 2, tft.height() \/ 2, 2);\n\n  \/\/ Initialize SPI bus and MFRC522\n  SPI.begin();\n  mfrc522.PCD_Init();\n  \n  Serial.println(F(\"System initialized. Waiting for RFID tags...\"));\n}\n\nvoid loop() {\n  \/\/ Look for new cards\n  if (!mfrc522.PICC_IsNewCardPresent()) {\n    return;\n  }\n\n  \/\/ Select one of the cards\n  if (!mfrc522.PICC_ReadCardSerial()) {\n    return;\n  }\n\n  \/\/ Clear the middle area of the screen for the new ID\n  tft.fillRect(0, 100, tft.width(), 100, TFT_BLACK);\n\n  \/\/ Build the UID string\n  String uidString = \"\";\n  for (byte i = 0; i &lt; mfrc522.uid.size; i++) {\n    if (mfrc522.uid.uidByte&#91;i] &lt; 0x10) {\n      uidString += \"0\";\n    }\n    uidString += String(mfrc522.uid.uidByte&#91;i], HEX);\n    if (i &lt; mfrc522.uid.size - 1) {\n      uidString += \":\";\n    }\n  }\n  \n  uidString.toUpperCase();\n\n  \/\/ Print to Serial\n  Serial.print(F(\"Card UID: \"));\n  Serial.println(uidString);\n\n  \/\/ Update TFT Display\n  tft.setTextColor(TFT_GREEN, TFT_BLACK);\n  tft.drawString(\"Tag Detected!\", tft.width() \/ 2, 120, 2);\n  \n  tft.setTextColor(TFT_YELLOW, TFT_BLACK);\n  tft.drawString(uidString, tft.width() \/ 2, 160, 4);\n\n  \/\/ Halt PICC to prevent spamming the display with the same tag\n  mfrc522.PICC_HaltA();\n  mfrc522.PCD_StopCrypto1();\n  \n  \/\/ Brief delay before allowing the next scan\n  delay(1000); \n  \n  \/\/ Reset prompt\n  tft.fillRect(0, 100, tft.width(), 100, TFT_BLACK);\n  tft.setTextColor(TFT_WHITE, TFT_BLACK);\n  tft.drawString(\"Please scan a tag...\", tft.width() \/ 2, tft.height() \/ 2, 2);\n}<\/code><\/pre>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wiring Schematic<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"552\" height=\"413\" src=\"https:\/\/anthony.sleck.us\/wp-content\/uploads\/2026\/06\/image.png\" alt=\"\" class=\"wp-image-327\" srcset=\"https:\/\/anthony.sleck.us\/wp-content\/uploads\/2026\/06\/image.png 552w, https:\/\/anthony.sleck.us\/wp-content\/uploads\/2026\/06\/image-300x224.png 300w\" sizes=\"auto, (max-width: 552px) 100vw, 552px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We\u2019ve all been there: staring at a breadboard buried under a rat&#8217;s nest of jumper wires, trying to figure out why your standalone display isn&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-324","post","type-post","status-publish","format-standard","hentry","category-esp32-projects"],"_links":{"self":[{"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/posts\/324","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=324"}],"version-history":[{"count":5,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/posts\/324\/revisions"}],"predecessor-version":[{"id":335,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=\/wp\/v2\/posts\/324\/revisions\/335"}],"wp:attachment":[{"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=324"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=324"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/anthony.sleck.us\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=324"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}