FluxBench.AIShared project · read-only

NFC Visitor Pass Reader

ESP32 DevKit (WROOM-32)·Arduino C++·Updated Jul 27, 2026·by FluxBench TeamIncludes Lab + Schematic + PCB

Tap-to-enter access reader: a PN532 NFC module (I2C) enrolls the first card tapped as a 3-use visitor pass, then answers each tap with a green "granted" or red "denied" LED and buzzer chirps — in the Lab simulator a virtual tag taps the antenna every few seconds, or use the reader panel to tap two virtual cards yourself and watch the pass get enrolled, used up and a foreign card rejected. Bundled Lab bench, schematic and routed PCB included.

sketch.ino
// NFC Visitor Pass Reader — PN532 (I2C) + green/red LEDs + buzzer
// The first card tapped is enrolled as a 3-use visitor pass. Later taps of
// the same card are granted until the 3 uses run out, then denied.
//   PN532: SDA = GPIO21, SCL = GPIO22, VCC = 3V3, GND
//   Buzzer + = GPIO5; green LED = GPIO4; red LED = GPIO2 (each LED via 330 Ω)

#include <Wire.h>
#include <Adafruit_PN532.h>

#define LED_OK 4
#define LED_NO 2
#define BUZZER 5

Adafruit_PN532 nfc(-1, -1);   // I2C mode (IRQ/RSTO unused)

uint8_t master[4];
bool enrolled = false;
int grantsLeft = 3;

void chirp(int freq, int ms) {
  tone(BUZZER, freq, ms);
  delay(ms + 30);
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_OK, OUTPUT);
  pinMode(LED_NO, OUTPUT);
  Wire.begin();
  nfc.begin();
  uint32_t ver = nfc.getFirmwareVersion();
  if (!ver) {
    Serial.println("PN532 not found - check wiring");
    while (true) delay(100);
  }
  char vbuf[40];
  sprintf(vbuf, "PN532 chip 0x%02X  firmware %d.%d", (ver >> 24) & 0xFF, (ver >> 16) & 0xFF, (ver >> 8) & 0xFF);
  Serial.println(vbuf);
  nfc.SAMConfig();
  Serial.println("Tap the first card to enroll it as the visitor pass (3 uses)...");
}

void loop() {
  uint8_t uid[7];
  uint8_t uidLen = 0;
  if (!nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLen)) {
    delay(150);                   // no card in the field
    return;
  }
  char idbuf[24];
  sprintf(idbuf, "%02X:%02X:%02X:%02X", uid[0], uid[1], uid[2], uid[3]);
  if (!enrolled) {
    for (int i = 0; i < 4; i++) master[i] = uid[i];
    enrolled = true;
    Serial.print("Enrolled visitor pass ");
    Serial.println(idbuf);
    chirp(1568, 120);
    chirp(2093, 160);
    delay(4000);                  // wait for the card to leave the field
    return;
  }
  bool ok = true;
  for (int i = 0; i < 4; i++) {
    if (uid[i] != master[i]) ok = false;
  }
  if (ok && grantsLeft <= 0) {
    ok = false;
    Serial.println("Pass expired - all 3 uses consumed");
  }
  if (ok) {
    grantsLeft--;
    Serial.print("Access GRANTED to ");
    Serial.print(idbuf);
    Serial.print("  (uses left: ");
    Serial.print(grantsLeft);
    Serial.println(")");
    digitalWrite(LED_OK, HIGH);
    chirp(1760, 200);
    digitalWrite(LED_OK, LOW);
  } else {
    Serial.print("Access DENIED for ");
    Serial.println(idbuf);
    digitalWrite(LED_NO, HIGH);
    chirp(220, 400);
    digitalWrite(LED_NO, LOW);
  }
  delay(4000);
}

Built with FluxBench.AI — the AI workbench for embedded projects.