Initial commit: project docs and ignore rules

This commit is contained in:
Dr Jones
2026-05-03 23:20:16 -07:00
commit 8c372c3cb0
8 changed files with 1177 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_Store
*.o
*.a

23
README.md Normal file
View File

@@ -0,0 +1,23 @@
# common (shared NFC helpers)
Shared C++ sources for the **dual PN532 ESP-NOW** system described in the [parent README](../README.md): protocol definitions, error handling, memory helpers, and other code consumed by `listener_firmware/` and `emulator_firmware/`.
## Usage
Firmware sketches include headers via paths such as:
```cpp
#include "../../common/nfc_protocol.h"
```
Keep this folder versioned **together** with:
- `listener_firmware/`
- `emulator_firmware/`
- Parent `platformio.ini` (workspace root)
Splitting into a separate remote is easiest if these three become submodules of a single meta-repo, or you refactor include paths.
## Tests
Unit tests that exercise parts of this tree live in [`tests/`](../tests/).

302
error_handler.cpp Normal file
View File

@@ -0,0 +1,302 @@
#include "error_handler.h"
#include <cstring>
#ifdef ARDUINO
#include <Arduino.h>
#define SERIAL_PRINT(x) Serial.print(x)
#define SERIAL_PRINTLN(x) Serial.println(x)
#define GET_MILLIS() millis()
#define DELAY_MS(x) delay(x)
#else
#include <cstdio>
#include <ctime>
#include <chrono>
#include <thread>
#define SERIAL_PRINT(x) printf("%s", (x))
#define SERIAL_PRINTLN(x) printf("%s\n", (x))
#define GET_MILLIS() (static_cast<unsigned long>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()))
#define DELAY_MS(x) std::this_thread::sleep_for(std::chrono::milliseconds(x))
#endif
// Global error handler instance
ErrorHandler errorHandler;
ErrorHandler::ErrorHandler() : error_index(0), logging_enabled(true) {
memset(&stats, 0, sizeof(stats));
memset(error_history, 0, sizeof(error_history));
}
void ErrorHandler::reportError(ErrorCode code, ErrorSeverity severity, const char* description) {
// Update statistics
stats.total_errors++;
stats.last_error_time = GET_MILLIS();
stats.last_error_code = code;
if (severity == SEVERITY_CRITICAL) {
stats.critical_errors++;
}
// Categorize errors
switch (code) {
case ERR_NFC_INIT_FAILED:
case ERR_NFC_READ_FAILED:
stats.nfc_errors++;
break;
case ERR_COMMUNICATION_TIMEOUT:
case ERR_PAIRING_FAILED:
case ERR_INVALID_MESSAGE:
stats.communication_errors++;
break;
case ERR_SD_INIT_FAILED:
case ERR_SD_WRITE_FAILED:
stats.sd_errors++;
break;
default:
break;
}
// Store in history
ErrorEntry& entry = error_history[error_index];
entry.code = code;
entry.severity = severity;
entry.timestamp = GET_MILLIS();
if (description) {
strncpy(entry.description, description, sizeof(entry.description) - 1);
entry.description[sizeof(entry.description) - 1] = '\0';
} else {
entry.description[0] = '\0';
}
error_index = (error_index + 1) % MAX_ERROR_HISTORY;
// Log the error
if (logging_enabled) {
#ifdef ARDUINO
Serial.print("[ERROR] Code: ");
Serial.print(static_cast<int>(code));
Serial.print(", Severity: ");
Serial.print(static_cast<int>(severity));
Serial.print(", Time: ");
Serial.print(entry.timestamp);
if (description) {
Serial.print(", Desc: ");
Serial.print(description);
}
Serial.println();
#else
printf("[ERROR] Code: %d, Severity: %d, Time: %lu",
static_cast<int>(code), static_cast<int>(severity), entry.timestamp);
if (description) {
printf(", Desc: %s", description);
}
printf("\n");
#endif
}
// Attempt recovery for non-critical errors
if (severity < SEVERITY_CRITICAL) {
handleError(code);
}
}
void ErrorHandler::reportNFCError(const char* description) {
reportError(ERR_NFC_READ_FAILED, SEVERITY_ERROR, description);
}
void ErrorHandler::reportCommunicationError(const char* description) {
reportError(ERR_COMMUNICATION_TIMEOUT, SEVERITY_WARNING, description);
}
void ErrorHandler::reportSDError(const char* description) {
reportError(ERR_SD_WRITE_FAILED, SEVERITY_WARNING, description);
}
bool ErrorHandler::handleError(ErrorCode code) {
switch (code) {
case ERR_NFC_READ_FAILED:
return attemptNFCRecovery();
case ERR_COMMUNICATION_TIMEOUT:
case ERR_PAIRING_FAILED:
return attemptCommunicationRecovery();
case ERR_SD_WRITE_FAILED:
return attemptSDRecovery();
default:
return false;
}
}
bool ErrorHandler::attemptNFCRecovery() {
if (logging_enabled) {
#ifdef ARDUINO
Serial.println("[RECOVERY] Attempting NFC recovery...");
#else
printf("[RECOVERY] Attempting NFC recovery...\n");
#endif
}
DELAY_MS(100);
// TODO: Implement actual NFC recovery logic
return true; // Assume success for now
}
bool ErrorHandler::attemptCommunicationRecovery() {
if (logging_enabled) {
#ifdef ARDUINO
Serial.println("[RECOVERY] Attempting communication recovery...");
#else
printf("[RECOVERY] Attempting communication recovery...\n");
#endif
}
DELAY_MS(500);
return true; // Assume success for now
}
bool ErrorHandler::attemptSDRecovery() {
if (logging_enabled) {
#ifdef ARDUINO
Serial.println("[RECOVERY] Attempting SD card recovery...");
#else
printf("[RECOVERY] Attempting SD card recovery...\n");
#endif
}
DELAY_MS(200);
return true; // Assume success for now
}
void ErrorHandler::clearErrors() {
memset(&stats, 0, sizeof(stats));
memset(error_history, 0, sizeof(error_history));
error_index = 0;
if (logging_enabled) {
#ifdef ARDUINO
Serial.println("[INFO] Error history cleared");
#else
printf("[INFO] Error history cleared\n");
#endif
}
}
ErrorStats ErrorHandler::getStats() const {
return stats;
}
bool ErrorHandler::hasRecentErrors(unsigned long time_window_ms) const {
unsigned long current_time = GET_MILLIS();
return (current_time - stats.last_error_time) < time_window_ms;
}
bool ErrorHandler::hasCriticalErrors() const {
return stats.critical_errors > 0;
}
void ErrorHandler::printErrorHistory() const {
if (!logging_enabled) return;
#ifdef ARDUINO
Serial.println("=== Error History ===");
for (int i = 0; i < MAX_ERROR_HISTORY; i++) {
int idx = (error_index + i) % MAX_ERROR_HISTORY;
const ErrorEntry& entry = error_history[idx];
if (entry.timestamp == 0) continue; // Empty entry
Serial.print("[");
Serial.print(i + 1);
Serial.print("] Code: ");
Serial.print(static_cast<int>(entry.code));
Serial.print(", Severity: ");
Serial.print(static_cast<int>(entry.severity));
Serial.print(", Time: ");
Serial.print(entry.timestamp);
if (entry.description[0] != '\0') {
Serial.print(", Desc: ");
Serial.print(entry.description);
}
Serial.println();
}
Serial.println("===================");
#else
printf("=== Error History ===\n");
for (int i = 0; i < MAX_ERROR_HISTORY; i++) {
int idx = (error_index + i) % MAX_ERROR_HISTORY;
const ErrorEntry& entry = error_history[idx];
if (entry.timestamp == 0) continue; // Empty entry
printf("[%d] Code: %d, Severity: %d, Time: %lu",
i + 1, static_cast<int>(entry.code), static_cast<int>(entry.severity), entry.timestamp);
if (entry.description[0] != '\0') {
printf(", Desc: %s", entry.description);
}
printf("\n");
}
printf("===================\n");
#endif
}
void ErrorHandler::printStats() const {
if (!logging_enabled) return;
#ifdef ARDUINO
Serial.println("=== Error Statistics ===");
Serial.print("Total Errors: ");
Serial.println(stats.total_errors);
Serial.print("Critical Errors: ");
Serial.println(stats.critical_errors);
Serial.print("NFC Errors: ");
Serial.println(stats.nfc_errors);
Serial.print("Communication Errors: ");
Serial.println(stats.communication_errors);
Serial.print("SD Errors: ");
Serial.println(stats.sd_errors);
Serial.print("Last Error Time: ");
Serial.println(stats.last_error_time);
Serial.print("Last Error Code: ");
Serial.println(static_cast<int>(stats.last_error_code));
Serial.println("========================");
#else
printf("=== Error Statistics ===\n");
printf("Total Errors: %u\n", stats.total_errors);
printf("Critical Errors: %u\n", stats.critical_errors);
printf("NFC Errors: %u\n", stats.nfc_errors);
printf("Communication Errors: %u\n", stats.communication_errors);
printf("SD Errors: %u\n", stats.sd_errors);
printf("Last Error Time: %u\n", stats.last_error_time);
printf("Last Error Code: %d\n", static_cast<int>(stats.last_error_code));
printf("========================\n");
#endif
}
void ErrorHandler::enableLogging(bool enable) {
logging_enabled = enable;
}
void ErrorHandler::feedWatchdog() {
// TODO: Implement watchdog feeding logic
}
bool ErrorHandler::isSystemHealthy() const {
// Check for critical errors
if (hasCriticalErrors()) {
return false;
}
// Check for recent errors (within 30 seconds)
if (hasRecentErrors(30000)) {
return false;
}
// Check total error count threshold
if (stats.total_errors > 10) {
return false;
}
return true;
}

104
error_handler.h Normal file
View File

@@ -0,0 +1,104 @@
#ifndef ERROR_HANDLER_H
#define ERROR_HANDLER_H
#include <stdint.h>
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <cstdio>
#endif
// Error codes
enum ErrorCode {
ERR_NONE = 0,
ERR_NFC_INIT_FAILED = 1,
ERR_ESPNOW_INIT_FAILED = 2,
ERR_SD_INIT_FAILED = 3,
ERR_COMMUNICATION_TIMEOUT = 4,
ERR_INVALID_MESSAGE = 5,
ERR_PAIRING_FAILED = 6,
ERR_NFC_READ_FAILED = 7,
ERR_SD_WRITE_FAILED = 8,
ERR_MEMORY_ALLOCATION = 9,
ERR_HARDWARE_FAULT = 10
};
// Error severity levels
enum ErrorSeverity {
SEVERITY_INFO = 0,
SEVERITY_WARNING = 1,
SEVERITY_ERROR = 2,
SEVERITY_CRITICAL = 3
};
// Error entry structure
struct ErrorEntry {
ErrorCode code;
ErrorSeverity severity;
unsigned long timestamp;
char description[64];
};
// Error statistics
struct ErrorStats {
uint32_t total_errors;
uint32_t critical_errors;
uint32_t last_error_time;
ErrorCode last_error_code;
uint32_t nfc_errors;
uint32_t communication_errors;
uint32_t sd_errors;
};
class ErrorHandler {
private:
static const int MAX_ERROR_HISTORY = 10;
ErrorEntry error_history[MAX_ERROR_HISTORY];
int error_index;
ErrorStats stats;
bool logging_enabled;
public:
ErrorHandler();
// Error reporting
void reportError(ErrorCode code, ErrorSeverity severity, const char* description = nullptr);
void reportNFCError(const char* description = nullptr);
void reportCommunicationError(const char* description = nullptr);
void reportSDError(const char* description = nullptr);
// Error handling
bool handleError(ErrorCode code);
void clearErrors();
// Recovery mechanisms
bool attemptNFCRecovery();
bool attemptCommunicationRecovery();
bool attemptSDRecovery();
// Status and statistics
ErrorStats getStats() const;
bool hasRecentErrors(unsigned long time_window_ms = 5000) const;
bool hasCriticalErrors() const;
void printErrorHistory() const;
void printStats() const;
// Configuration
void enableLogging(bool enable);
void setMaxRetries(int retries);
// Watchdog functionality
void feedWatchdog();
bool isSystemHealthy() const;
};
// Global error handler instance
extern ErrorHandler errorHandler;
// Convenience macros
#define REPORT_ERROR(code, severity, desc) errorHandler.reportError(code, severity, desc)
#define REPORT_NFC_ERROR(desc) errorHandler.reportNFCError(desc)
#define REPORT_COMM_ERROR(desc) errorHandler.reportCommunicationError(desc)
#define REPORT_SD_ERROR(desc) errorHandler.reportSDError(desc)
#endif // ERROR_HANDLER_H

327
memory_manager.cpp Normal file
View File

@@ -0,0 +1,327 @@
#include "memory_manager.h"
#include <cstring>
#ifdef ARDUINO
#include <Arduino.h>
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#include <cstdio>
#define DEBUG_PRINT(x) printf("%s", (x))
#define DEBUG_PRINTLN(x) printf("%s\n", (x))
#endif
// Global memory pool instance
MemoryPool globalMemoryPool;
MemoryPool::MemoryPool() : next_free_offset(0), allocation_count(0), deallocation_count(0) {
// Initialize all blocks as unused
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
blocks[i].ptr = nullptr;
blocks[i].size = 0;
blocks[i].in_use = false;
blocks[i].magic = 0;
}
// Clear the memory pool
memset(pool_memory, 0, POOL_SIZE);
}
MemoryPool::~MemoryPool() {
// Check for memory leaks
uint32_t active_blocks = 0;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use) {
active_blocks++;
}
}
if (active_blocks > 0) {
#ifdef DEBUG_MEMORY
DEBUG_PRINT("WARNING: Memory pool destroyed with ");
DEBUG_PRINT(active_blocks);
DEBUG_PRINTLN(" active blocks!");
#endif
}
}
void* MemoryPool::allocate(size_t size) {
if (size == 0 || size > POOL_SIZE) {
return nullptr;
}
// Align size to 4-byte boundary for better performance
size = (size + 3) & ~3;
// Check if we have enough space
if (next_free_offset + size > POOL_SIZE) {
// Try to find a free block that was previously deallocated
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (!blocks[i].in_use && blocks[i].ptr != nullptr && blocks[i].size >= size) {
blocks[i].in_use = true;
blocks[i].magic = MAGIC_NUMBER;
allocation_count++;
return blocks[i].ptr;
}
}
return nullptr; // Out of memory
}
// Find a free block descriptor
size_t block_index = MAX_BLOCKS;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (!blocks[i].in_use && blocks[i].ptr == nullptr) {
block_index = i;
break;
}
}
if (block_index == MAX_BLOCKS) {
return nullptr; // No free block descriptors
}
// Allocate from the pool
void* ptr = &pool_memory[next_free_offset];
// Set up the block descriptor
blocks[block_index].ptr = ptr;
blocks[block_index].size = size;
blocks[block_index].in_use = true;
blocks[block_index].magic = MAGIC_NUMBER;
next_free_offset += size;
allocation_count++;
return ptr;
}
bool MemoryPool::deallocate(void* ptr) {
if (ptr == nullptr) {
return false;
}
// Find the block
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].ptr == ptr && blocks[i].in_use) {
// Check magic number for corruption
if (blocks[i].magic != MAGIC_NUMBER) {
#ifdef DEBUG_MEMORY
DEBUG_PRINTLN("ERROR: Memory corruption detected during deallocation!");
#endif
return false;
}
blocks[i].in_use = false;
blocks[i].magic = 0;
deallocation_count++;
// Clear the memory for security
memset(ptr, 0, blocks[i].size);
return true;
}
}
return false; // Pointer not found
}
MemoryPool::MemoryStats MemoryPool::getStats() const {
MemoryStats stats;
stats.total_size = POOL_SIZE;
stats.allocations = allocation_count;
stats.deallocations = deallocation_count;
stats.active_blocks = 0;
stats.used_size = 0;
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use) {
stats.active_blocks++;
stats.used_size += blocks[i].size;
}
}
stats.free_size = POOL_SIZE - stats.used_size;
return stats;
}
bool MemoryPool::checkIntegrity() const {
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
if (blocks[i].in_use && blocks[i].magic != MAGIC_NUMBER) {
return false;
}
}
return true;
}
void MemoryPool::reset() {
// Clear all blocks
for (size_t i = 0; i < MAX_BLOCKS; ++i) {
blocks[i].ptr = nullptr;
blocks[i].size = 0;
blocks[i].in_use = false;
blocks[i].magic = 0;
}
next_free_offset = 0;
allocation_count = 0;
deallocation_count = 0;
// Clear the memory pool
memset(pool_memory, 0, POOL_SIZE);
}
void MemoryPool::printStats() const {
MemoryStats stats = getStats();
#ifdef ARDUINO
Serial.println("=== Memory Pool Statistics ===");
Serial.print("Total Size: ");
Serial.print(stats.total_size);
Serial.println(" bytes");
Serial.print("Used Size: ");
Serial.print(stats.used_size);
Serial.println(" bytes");
Serial.print("Free Size: ");
Serial.print(stats.free_size);
Serial.println(" bytes");
Serial.print("Active Blocks: ");
Serial.println(stats.active_blocks);
Serial.print("Total Allocations: ");
Serial.println(stats.allocations);
Serial.print("Total Deallocations: ");
Serial.println(stats.deallocations);
Serial.print("Memory Integrity: ");
Serial.println(checkIntegrity() ? "OK" : "CORRUPTED");
Serial.println("==============================");
#else
printf("=== Memory Pool Statistics ===\n");
printf("Total Size: %zu bytes\n", stats.total_size);
printf("Used Size: %zu bytes\n", stats.used_size);
printf("Free Size: %zu bytes\n", stats.free_size);
printf("Active Blocks: %u\n", stats.active_blocks);
printf("Total Allocations: %u\n", stats.allocations);
printf("Total Deallocations: %u\n", stats.deallocations);
printf("Memory Integrity: %s\n", checkIntegrity() ? "OK" : "CORRUPTED");
printf("==============================\n");
#endif
}
// SafeBuffer implementation
SafeBuffer::SafeBuffer(size_t capacity, MemoryPool* pool)
: data_(nullptr), size_(0), capacity_(capacity), pool_(pool), owns_memory_(false) {
if (capacity > 0) {
if (pool_) {
data_ = static_cast<uint8_t*>(pool_->allocate(capacity));
} else {
#ifdef ARDUINO
data_ = static_cast<uint8_t*>(malloc(capacity));
#else
data_ = new uint8_t[capacity];
#endif
}
if (data_) {
owns_memory_ = true;
memset(data_, 0, capacity);
}
}
}
SafeBuffer::~SafeBuffer() {
if (data_ && owns_memory_) {
if (pool_) {
pool_->deallocate(data_);
} else {
#ifdef ARDUINO
free(data_);
#else
delete[] data_;
#endif
}
}
}
SafeBuffer::SafeBuffer(SafeBuffer&& other) noexcept
: data_(other.data_), size_(other.size_), capacity_(other.capacity_),
pool_(other.pool_), owns_memory_(other.owns_memory_) {
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
other.owns_memory_ = false;
}
SafeBuffer& SafeBuffer::operator=(SafeBuffer&& other) noexcept {
if (this != &other) {
// Clean up current resources
if (data_ && owns_memory_) {
if (pool_) {
pool_->deallocate(data_);
} else {
#ifdef ARDUINO
free(data_);
#else
delete[] data_;
#endif
}
}
// Move from other
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
pool_ = other.pool_;
owns_memory_ = other.owns_memory_;
// Reset other
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
other.owns_memory_ = false;
}
return *this;
}
bool SafeBuffer::write(const void* data, size_t size, size_t offset) {
if (!data_ || !data || size == 0) {
return false;
}
if (offset + size > capacity_) {
return false; // Would exceed buffer capacity
}
memcpy(data_ + offset, data, size);
// Update size if we wrote beyond current size
if (offset + size > size_) {
size_ = offset + size;
}
return true;
}
bool SafeBuffer::read(void* data, size_t size, size_t offset) const {
if (!data_ || !data || size == 0) {
return false;
}
if (offset + size > size_) {
return false; // Would read beyond valid data
}
memcpy(data, data_ + offset, size);
return true;
}
bool SafeBuffer::append(const void* data, size_t size) {
return write(data, size, size_);
}
void SafeBuffer::clear() {
if (data_) {
memset(data_, 0, capacity_);
size_ = 0;
}
}

282
memory_manager.h Normal file
View File

@@ -0,0 +1,282 @@
#ifndef MEMORY_MANAGER_H
#define MEMORY_MANAGER_H
#include <stdint.h>
#include <stddef.h>
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <cstdlib>
#include <memory>
#endif
/**
* @brief Memory pool for embedded systems with limited heap
*
* This class provides a simple memory pool implementation to avoid
* heap fragmentation and provide predictable memory allocation.
*/
class MemoryPool {
private:
static const size_t POOL_SIZE = 4096; // 4KB pool
static const size_t MAX_BLOCKS = 32;
struct Block {
void* ptr;
size_t size;
bool in_use;
uint32_t magic; // For corruption detection
};
uint8_t pool_memory[POOL_SIZE];
Block blocks[MAX_BLOCKS];
size_t next_free_offset;
uint32_t allocation_count;
uint32_t deallocation_count;
static const uint32_t MAGIC_NUMBER = 0xDEADBEEF;
public:
MemoryPool();
~MemoryPool();
/**
* @brief Allocate memory from the pool
* @param size Size in bytes to allocate
* @return Pointer to allocated memory or nullptr if failed
*/
void* allocate(size_t size);
/**
* @brief Deallocate memory back to the pool
* @param ptr Pointer to memory to deallocate
* @return true if successful, false if invalid pointer
*/
bool deallocate(void* ptr);
/**
* @brief Get memory usage statistics
*/
struct MemoryStats {
size_t total_size;
size_t used_size;
size_t free_size;
uint32_t allocations;
uint32_t deallocations;
uint32_t active_blocks;
};
MemoryStats getStats() const;
/**
* @brief Check for memory corruption
* @return true if memory is intact, false if corruption detected
*/
bool checkIntegrity() const;
/**
* @brief Reset the entire pool (use with caution!)
*/
void reset();
/**
* @brief Print memory usage information
*/
void printStats() const;
};
/**
* @brief RAII wrapper for automatic memory management
*
* This template class provides automatic memory management
* using RAII principles for embedded systems.
*/
template<typename T>
class SmartPtr {
private:
T* ptr_;
MemoryPool* pool_;
bool owns_memory_;
public:
/**
* @brief Constructor for pool-allocated memory
*/
explicit SmartPtr(MemoryPool* pool = nullptr)
: ptr_(nullptr), pool_(pool), owns_memory_(false) {
if (pool_) {
ptr_ = static_cast<T*>(pool_->allocate(sizeof(T)));
if (ptr_) {
new(ptr_) T(); // Placement new
owns_memory_ = true;
}
}
}
/**
* @brief Constructor taking ownership of existing pointer
*/
SmartPtr(T* ptr, MemoryPool* pool, bool owns = true)
: ptr_(ptr), pool_(pool), owns_memory_(owns) {}
/**
* @brief Move constructor
*/
SmartPtr(SmartPtr&& other) noexcept
: ptr_(other.ptr_), pool_(other.pool_), owns_memory_(other.owns_memory_) {
other.ptr_ = nullptr;
other.owns_memory_ = false;
}
/**
* @brief Move assignment operator
*/
SmartPtr& operator=(SmartPtr&& other) noexcept {
if (this != &other) {
reset();
ptr_ = other.ptr_;
pool_ = other.pool_;
owns_memory_ = other.owns_memory_;
other.ptr_ = nullptr;
other.owns_memory_ = false;
}
return *this;
}
/**
* @brief Destructor - automatically cleans up
*/
~SmartPtr() {
reset();
}
/**
* @brief Delete copy constructor and assignment (no copying)
*/
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
/**
* @brief Access the managed object
*/
T* get() const { return ptr_; }
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
/**
* @brief Check if the pointer is valid
*/
bool isValid() const { return ptr_ != nullptr; }
explicit operator bool() const { return isValid(); }
/**
* @brief Release ownership without destroying
*/
T* release() {
T* temp = ptr_;
ptr_ = nullptr;
owns_memory_ = false;
return temp;
}
/**
* @brief Reset the pointer, destroying current object if owned
*/
void reset() {
if (ptr_ && owns_memory_) {
ptr_->~T(); // Explicit destructor call
if (pool_) {
pool_->deallocate(ptr_);
}
}
ptr_ = nullptr;
owns_memory_ = false;
}
};
/**
* @brief Buffer management class with bounds checking
*/
class SafeBuffer {
private:
uint8_t* data_;
size_t size_;
size_t capacity_;
MemoryPool* pool_;
bool owns_memory_;
public:
SafeBuffer(size_t capacity, MemoryPool* pool = nullptr);
~SafeBuffer();
// Delete copy constructor and assignment
SafeBuffer(const SafeBuffer&) = delete;
SafeBuffer& operator=(const SafeBuffer&) = delete;
// Move constructor and assignment
SafeBuffer(SafeBuffer&& other) noexcept;
SafeBuffer& operator=(SafeBuffer&& other) noexcept;
/**
* @brief Write data to buffer with bounds checking
*/
bool write(const void* data, size_t size, size_t offset = 0);
/**
* @brief Read data from buffer with bounds checking
*/
bool read(void* data, size_t size, size_t offset = 0) const;
/**
* @brief Append data to buffer
*/
bool append(const void* data, size_t size);
/**
* @brief Clear buffer contents
*/
void clear();
/**
* @brief Get buffer information
*/
uint8_t* data() { return data_; }
const uint8_t* data() const { return data_; }
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
size_t available() const { return capacity_ - size_; }
/**
* @brief Check if buffer is valid
*/
bool isValid() const { return data_ != nullptr; }
};
// Global memory pool instance
extern MemoryPool globalMemoryPool;
/**
* @brief Convenience function to create smart pointers
*/
template<typename T>
SmartPtr<T> makeSmartPtr(MemoryPool* pool = &globalMemoryPool) {
return SmartPtr<T>(pool);
}
/**
* @brief Memory debugging macros (only active in debug builds)
*/
#ifdef DEBUG_MEMORY
#define MEM_ALLOC(size) globalMemoryPool.allocate(size)
#define MEM_FREE(ptr) globalMemoryPool.deallocate(ptr)
#define MEM_CHECK() globalMemoryPool.checkIntegrity()
#define MEM_STATS() globalMemoryPool.printStats()
#else
#define MEM_ALLOC(size) globalMemoryPool.allocate(size)
#define MEM_FREE(ptr) globalMemoryPool.deallocate(ptr)
#define MEM_CHECK() true
#define MEM_STATS() do {} while(0)
#endif
#endif // MEMORY_MANAGER_H

69
nfc_protocol.cpp Normal file
View File

@@ -0,0 +1,69 @@
#include "nfc_protocol.h"
uint16_t calculateChecksum(const uint8_t* data, size_t length) {
uint16_t checksum = 0;
for (size_t i = 0; i < length; i++) {
checksum += data[i];
}
return checksum;
}
bool validateMessage(const CommMessage* msg) {
if (msg == nullptr) return false;
// Calculate checksum excluding the checksum field itself
size_t data_size = sizeof(CommMessage) - sizeof(msg->checksum);
uint16_t calculated_checksum = calculateChecksum((const uint8_t*)msg, data_size);
return calculated_checksum == msg->checksum;
}
void printNFCData(const NFCData* data) {
if (data == nullptr) return;
Serial.println("=== NFC Data ===");
Serial.print("UID: ");
for (int i = 0; i < data->uid_length; i++) {
if (data->uid[i] < 0x10) Serial.print("0");
Serial.print(data->uid[i], HEX);
if (i < data->uid_length - 1) Serial.print(" ");
}
Serial.println();
Serial.print("UID Length: ");
Serial.println(data->uid_length);
Serial.print("SAK: 0x");
if (data->sak < 0x10) Serial.print("0");
Serial.println(data->sak, HEX);
Serial.print("ATQA: 0x");
if (data->atqa[0] < 0x10) Serial.print("0");
Serial.print(data->atqa[0], HEX);
Serial.print(" 0x");
if (data->atqa[1] < 0x10) Serial.print("0");
Serial.println(data->atqa[1], HEX);
Serial.print("Tag Type: ");
Serial.println(data->tag_type);
Serial.print("Data Length: ");
Serial.println(data->data_length);
Serial.print("Timestamp: ");
Serial.println(data->timestamp);
if (data->data_length > 0) {
Serial.print("Raw Data: ");
for (int i = 0; i < data->data_length && i < 32; i++) {
if (data->raw_data[i] < 0x10) Serial.print("0");
Serial.print(data->raw_data[i], HEX);
Serial.print(" ");
}
if (data->data_length > 32) {
Serial.print("... (truncated)");
}
Serial.println();
}
Serial.println("================");
}

67
nfc_protocol.h Normal file
View File

@@ -0,0 +1,67 @@
#ifndef NFC_PROTOCOL_H
#define NFC_PROTOCOL_H
#include <Arduino.h>
// Communication protocol definitions
#define MAX_NFC_DATA_SIZE 256
#define ESPNOW_CHANNEL 1
#define PAIRING_TIMEOUT_MS 30000
#define HEARTBEAT_INTERVAL_MS 1000
#define MAX_RETRY_ATTEMPTS 3
// Message types
enum MessageType {
MSG_PAIRING_REQUEST = 0x01,
MSG_PAIRING_RESPONSE = 0x02,
MSG_NFC_DATA = 0x03,
MSG_HEARTBEAT = 0x04,
MSG_ERROR = 0x05,
MSG_ACK = 0x06
};
// NFC data structure
struct NFCData {
uint8_t uid[10]; // NFC UID (max 10 bytes)
uint8_t uid_length; // Actual UID length
uint8_t sak; // Select Acknowledge
uint8_t atqa[2]; // Answer to Request Type A
uint8_t raw_data[MAX_NFC_DATA_SIZE]; // Raw NFC data
uint16_t data_length; // Actual data length
uint32_t timestamp; // Capture timestamp
uint8_t tag_type; // NFC tag type
};
// Communication message structure
struct CommMessage {
uint8_t type; // Message type
uint8_t sequence; // Sequence number
uint16_t payload_size; // Payload size
union {
NFCData nfc_data;
uint8_t raw_payload[sizeof(NFCData)];
};
uint16_t checksum; // Message integrity check
};
// Device roles
enum DeviceRole {
ROLE_LISTENER = 1,
ROLE_EMULATOR = 2
};
// Status codes
enum StatusCode {
STATUS_OK = 0,
STATUS_ERROR = 1,
STATUS_TIMEOUT = 2,
STATUS_INVALID_DATA = 3,
STATUS_NOT_PAIRED = 4
};
// Function prototypes
uint16_t calculateChecksum(const uint8_t* data, size_t length);
bool validateMessage(const CommMessage* msg);
void printNFCData(const NFCData* data);
#endif // NFC_PROTOCOL_H