Initial commit: project docs and ignore rules
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.DS_Store
|
||||
19
README.md
Normal file
19
README.md
Normal 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
390
src/main.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user