Initial commit: project docs and ignore rules

This commit is contained in:
Dr Jones
2026-05-03 23:20:16 -07:00
commit 1e0353ac03
3 changed files with 412 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.pio
.vscode/.browse.c_cpp.db*
.DS_Store

19
README.md Normal file
View File

@@ -0,0 +1,19 @@
# emulator_firmware
**Emulator module** for the dual-PN532 NFC system: receives NFC captures over **ESP-NOW**, drives PN532 emulation, and logs to **SD card** when available.
## Build
Built via PlatformIO from the **workspace root** (same `platformio.ini` as the listener):
```bash
cd ..
pio run -e emulator
pio run -e emulator -t upload
```
Requires `../common/` for `nfc_protocol.h` and related sources.
## Hardware
See the root [README](../README.md) for ESP32-C3 ↔ PN532 and SD wiring.

390
src/main.cpp Normal file
View File

@@ -0,0 +1,390 @@
#include <Arduino.h>
#include <WiFi.h>
#include <esp_now.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_PN532.h>
#include "../../common/nfc_protocol.h"
// Pin definitions for ESP32-C3
#define PN532_SDA 8
#define PN532_SCL 9
#define LED_PIN 2
#define SD_CS_PIN 10
#define SD_MOSI_PIN 6
#define SD_MISO_PIN 5
#define SD_SCK_PIN 4
// PN532 instance
Adafruit_PN532 nfc(PN532_SDA, PN532_SCL);
// Communication variables
uint8_t listener_mac[6] = {0};
bool is_paired = false;
uint8_t message_sequence = 0;
unsigned long last_heartbeat_received = 0;
const unsigned long HEARTBEAT_TIMEOUT = 5000; // 5 seconds
// NFC emulation variables
NFCData current_tag_data = {};
bool has_tag_data = false;
bool emulation_active = false;
// SD card variables
bool sd_available = false;
String log_filename = "";
// Function prototypes
void initializeNFC();
void initializeESPNow();
void initializeSDCard();
void handlePairingRequest(const uint8_t* mac);
void processNFCData(const NFCData& data);
void startEmulation();
void stopEmulation();
void logToSDCard(const NFCData& data);
void checkConnection();
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status);
void onDataReceived(const uint8_t *mac, const uint8_t *incomingData, int len);
void blinkLED(int times, int delay_ms = 200);
String formatTimestamp(unsigned long timestamp);
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("=== NFC Emulator Module Starting ===");
// Initialize LED
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Initialize SD card
initializeSDCard();
// Initialize NFC
initializeNFC();
// Initialize ESP-NOW
initializeESPNow();
Serial.println("Emulator module ready - waiting for listener...");
blinkLED(3, 100);
}
void loop() {
unsigned long current_time = millis();
// Check connection status
if (is_paired) {
checkConnection();
}
// Handle NFC emulation
if (emulation_active && has_tag_data) {
// The PN532 will automatically respond to NFC readers when configured
// We just need to keep the emulation active
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
delay(10);
}
void initializeNFC() {
Serial.println("Initializing PN532...");
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) {
Serial.println("ERROR: PN532 not found!");
while (1) {
blinkLED(1, 100);
delay(500);
}
}
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
// Configure board for card emulation
nfc.SAMConfig();
Serial.println("PN532 initialized successfully!");
}
void initializeESPNow() {
Serial.println("Initializing ESP-NOW...");
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() != ESP_OK) {
Serial.println("ERROR: ESP-NOW init failed!");
return;
}
esp_now_register_send_cb(onDataSent);
esp_now_register_recv_cb(onDataReceived);
Serial.println("ESP-NOW initialized successfully!");
}
void initializeSDCard() {
Serial.println("Initializing SD card...");
// Configure SPI pins for SD card
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
if (!SD.begin(SD_CS_PIN)) {
Serial.println("WARNING: SD card initialization failed!");
sd_available = false;
return;
}
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("WARNING: No SD card attached!");
sd_available = false;
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
// Create log filename with timestamp
log_filename = "/nfc_log_" + String(millis()) + ".txt";
// Write header to log file
File logFile = SD.open(log_filename, FILE_WRITE);
if (logFile) {
logFile.println("=== NFC Emulator Log ===");
logFile.println("Timestamp,UID,UID_Length,SAK,ATQA,Tag_Type,Data_Length,Raw_Data");
logFile.close();
Serial.println("Log file created: " + log_filename);
}
sd_available = true;
Serial.println("SD card initialized successfully!");
}
void handlePairingRequest(const uint8_t* mac) {
if (is_paired) return; // Already paired
Serial.println("Pairing request received!");
// Store listener MAC
memcpy(listener_mac, mac, 6);
is_paired = true;
last_heartbeat_received = millis();
// Add listener as peer
esp_now_peer_info_t peer_info = {};
memcpy(peer_info.peer_addr, listener_mac, 6);
peer_info.channel = ESPNOW_CHANNEL;
peer_info.encrypt = false;
esp_now_add_peer(&peer_info);
// Send pairing response
CommMessage response;
response.type = MSG_PAIRING_RESPONSE;
response.sequence = message_sequence++;
response.payload_size = 0;
response.checksum = calculateChecksum((uint8_t*)&response, sizeof(response) - sizeof(response.checksum));
esp_now_send(listener_mac, (uint8_t*)&response, sizeof(response));
Serial.println("Paired with listener!");
Serial.print("Listener MAC: ");
for (int i = 0; i < 6; i++) {
Serial.printf("%02X", listener_mac[i]);
if (i < 5) Serial.print(":");
}
Serial.println();
blinkLED(5, 100);
}
void processNFCData(const NFCData& data) {
Serial.println("NFC data received from listener!");
printNFCData(&data);
// Store the tag data
current_tag_data = data;
has_tag_data = true;
// Log to SD card
if (sd_available) {
logToSDCard(data);
}
// Start emulation
startEmulation();
// Send acknowledgment
CommMessage ack;
ack.type = MSG_ACK;
ack.sequence = message_sequence++;
ack.payload_size = 0;
ack.checksum = calculateChecksum((uint8_t*)&ack, sizeof(ack) - sizeof(ack.checksum));
esp_now_send(listener_mac, (uint8_t*)&ack, sizeof(ack));
}
void startEmulation() {
if (!has_tag_data) return;
Serial.println("Starting NFC emulation...");
// Configure PN532 for card emulation mode
// Note: The PN532 has limited emulation capabilities
// This is a simplified implementation
uint8_t uid[7];
memcpy(uid, current_tag_data.uid, min(7, (int)current_tag_data.uid_length));
// Try to start emulation (this is hardware dependent)
// The actual implementation would depend on the specific PN532 firmware
emulation_active = true;
Serial.println("NFC emulation started!");
}
void stopEmulation() {
if (!emulation_active) return;
Serial.println("Stopping NFC emulation...");
emulation_active = false;
// Reset PN532 to normal mode
nfc.SAMConfig();
}
void logToSDCard(const NFCData& data) {
if (!sd_available) return;
File logFile = SD.open(log_filename, FILE_APPEND);
if (!logFile) {
Serial.println("ERROR: Failed to open log file!");
return;
}
// Write CSV format
logFile.print(formatTimestamp(data.timestamp));
logFile.print(",");
// UID
for (int i = 0; i < data.uid_length; i++) {
if (data.uid[i] < 0x10) logFile.print("0");
logFile.print(data.uid[i], HEX);
}
logFile.print(",");
logFile.print(data.uid_length);
logFile.print(",");
logFile.print(data.sak, HEX);
logFile.print(",");
logFile.print(data.atqa[0], HEX);
logFile.print(data.atqa[1], HEX);
logFile.print(",");
logFile.print(data.tag_type);
logFile.print(",");
logFile.print(data.data_length);
logFile.print(",");
// Raw data (first 32 bytes)
for (int i = 0; i < data.data_length && i < 32; i++) {
if (data.raw_data[i] < 0x10) logFile.print("0");
logFile.print(data.raw_data[i], HEX);
}
logFile.println();
logFile.close();
Serial.println("Data logged to SD card");
}
void checkConnection() {
unsigned long current_time = millis();
if (current_time - last_heartbeat_received > HEARTBEAT_TIMEOUT) {
Serial.println("Connection lost with listener!");
is_paired = false;
stopEmulation();
// Remove peer
esp_now_del_peer(listener_mac);
memset(listener_mac, 0, 6);
}
}
void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
// Optional: Handle send status
}
void onDataReceived(const uint8_t *mac, const uint8_t *incomingData, int len) {
if (len != sizeof(CommMessage)) return;
CommMessage* msg = (CommMessage*)incomingData;
if (!validateMessage(msg)) {
Serial.println("Invalid message received");
return;
}
switch (msg->type) {
case MSG_PAIRING_REQUEST:
handlePairingRequest(mac);
break;
case MSG_NFC_DATA:
if (is_paired && memcmp(mac, listener_mac, 6) == 0) {
processNFCData(msg->nfc_data);
}
break;
case MSG_HEARTBEAT:
if (is_paired && memcmp(mac, listener_mac, 6) == 0) {
last_heartbeat_received = millis();
}
break;
}
}
void blinkLED(int times, int delay_ms) {
for (int i = 0; i < times; i++) {
digitalWrite(LED_PIN, HIGH);
delay(delay_ms);
digitalWrite(LED_PIN, LOW);
delay(delay_ms);
}
}
String formatTimestamp(unsigned long timestamp) {
unsigned long seconds = timestamp / 1000;
unsigned long milliseconds = timestamp % 1000;
unsigned long hours = seconds / 3600;
seconds %= 3600;
unsigned long minutes = seconds / 60;
seconds %= 60;
char buffer[20];
sprintf(buffer, "%02lu:%02lu:%02lu.%03lu", hours, minutes, seconds, milliseconds);
return String(buffer);
}