Add PN532 toolkit firmware, web UI, and embedded SPIFFS assets

Includes ESP-IDF NFC stack (deep capture, 4K Classic geometry, UL write API,
open SoftAP), React dashboard with live tag diagnostics, and docs. README
updated for APIs and lab Wi-Fi defaults.

Made-with: Cursor
This commit is contained in:
drjones
2026-03-29 09:32:55 -07:00
parent d1068d965b
commit 8968560565
73 changed files with 8802 additions and 28 deletions

View File

@@ -0,0 +1,26 @@
#pragma once
#include "esp_err.h"
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*job_progress_cb_t)(int pct, const char *msg, void *ctx);
typedef struct {
uint32_t id;
char name[32];
volatile int pct;
volatile int done;
} nfc_job_t;
uint32_t jobs_create(const char *name);
void jobs_set_progress(uint32_t id, int pct, const char *msg);
void jobs_finish(uint32_t id);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,26 @@
#pragma once
#include "nfc_engine/nfc_engine.h"
#include "cJSON.h"
#include "esp_err.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Dictionary attack on MIFARE Classic sector trailer (Key A then Key B per candidate).
* `extra` = packed 6-byte keys, `extra_n` = number of keys.
* If `variations`, expands each base key with bounded bit/nibble tweaks (not full 2^48 space).
* Returns JSON object: { "attempts": N, "sectorHits": [ {sector, keyHex, keyType}, ... ] }
* Caller cJSON_Delete().
*/
cJSON *nfc_mifare_dictionary_attack(nfc_tag_info_t *tag, uint8_t sector_first, uint8_t sector_last,
const uint8_t *extra, size_t extra_n, bool variations,
int *attempts_out);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,18 @@
#pragma once
#include "nfc_engine/nfc_engine.h"
#include "cJSON.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Best-effort deep read for PN532 path: inventory + general status + MIFARE sector sweep
* (common keys) and/or Ultralight/NTAG page sweep. Caller must cJSON_Delete() result.
*/
cJSON *nfc_tag_deep_profile(nfc_tag_info_t *tag);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,48 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "cJSON.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NFC_MAX_UID_LEN 10
#define NFC_BLOCK_LEN 16
typedef struct {
uint8_t uid_len;
uint8_t uid[NFC_MAX_UID_LEN];
uint16_t atqa;
uint8_t sak;
uint8_t type_hint; /* 0 unknown, 1 classic, 2 ultralight/ntag */
} nfc_tag_info_t;
typedef struct {
uint8_t key[6];
bool key_b;
} nfc_mifare_key_t;
esp_err_t nfc_engine_init(void);
esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out);
esp_err_t nfc_mifare_authenticate_block(const nfc_tag_info_t *tag, uint8_t block_no,
const nfc_mifare_key_t *key);
esp_err_t nfc_mifare_read_block(uint8_t block_no, uint8_t block[NFC_BLOCK_LEN]);
esp_err_t nfc_mifare_write_block(uint8_t block_no, const uint8_t block[NFC_BLOCK_LEN]);
esp_err_t nfc_ultralight_read_page(uint8_t page, uint8_t data[4]);
esp_err_t nfc_ultralight_write_page(uint8_t page, const uint8_t data[4]);
esp_err_t nfc_ul_fast_read(uint8_t start_page, uint8_t *out, size_t out_max, size_t *got);
/** Build JSON snapshot of last seen tag + optional blocks (caller frees cJSON). */
cJSON *nfc_tag_to_json(const nfc_tag_info_t *tag);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,35 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/** RAM-only capture buffer (NDJSON lines). When full, NFC deep capture pauses until clear. */
void session_capture_init(void);
void session_capture_clear(void);
bool session_capture_is_full(void);
bool session_capture_deep_enabled(void);
void session_capture_set_deep(bool on);
size_t session_capture_max(void);
void session_capture_get_status(size_t *used_bytes, uint32_t *line_count, bool *full);
/** Append one NDJSON line (no trailing newline in `line`). Returns false if buffer full. */
bool session_capture_append_line(const char *line);
/** Export raw bytes (entire buffer) for HTTP download. */
size_t session_capture_export_size(void);
const char *session_capture_export_ptr(void);
/** Copy up to `cap` bytes (typically cap >= session_capture_export_size()). */
void session_capture_copy_to(char *dst, size_t cap, size_t *out_len);
#ifdef __cplusplus
}
#endif