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