feat: resilient boot + full polish pass
Firmware:
- nfc_engine: add nfc_engine_try_init() (non-fatal, sets s_pn532_ready),
nfc_engine_try_reattach() (soft re-init, skips bus re-init),
nfc_engine_is_ready() accessor
- main: replace ESP_ERROR_CHECK(nfc_engine_init) with nfc_engine_try_init;
device boots and serves web UI even with no PN532 connected
- app_net: add reconnect_task — every 5s retries nfc_engine_try_reattach()
and broadcasts {"channel":"pn532","payload":{"connected":true}} over WS
- app_net: scan_loop_task skips polling when !nfc_engine_is_ready()
- app_net/api_status: always emit pn532Connected bool; null-guard pn532 fw object
- find_sector_hit / program_classic_snapshot_locked: null-guard cJSON array items
- session_capture: abort if xSemaphoreCreateMutex() returns NULL
Web:
- NfcWsContext: track pn532Connected state from WS pn532 channel + status fetch on connect
- App.tsx: live HeaderBadge (LIVE/NO RF/WAIT) replacing static text
- Dashboard: READY/SEARCHING pill with fw version when available
- api.ts: add pn532Connected to Status type
- toast.tsx: fix ID collision (Date.now + Math.random)
- Capture: surface status fetch errors
- ReadAnalyze: add error feedback for readUl when no data returned
- WriteClone: busy state on both write buttons
- RawConsole: toast when frame returns error not response
- Emulate: validate hex before send (non-empty, even length, hex chars only)
- Brute: warn and skip invalid custom key lines
Made-with: Cursor
This commit is contained in:
@@ -368,6 +368,7 @@ static cJSON *find_sector_hit(const cJSON *attack, int sector)
|
||||
int n = cJSON_GetArraySize(hits);
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON *it = cJSON_GetArrayItem(hits, i);
|
||||
if (!it) continue;
|
||||
cJSON *s = cJSON_GetObjectItemCaseSensitive(it, "sector");
|
||||
if (cJSON_IsNumber(s) && (int)cJSON_GetNumberValue(s) == sector) {
|
||||
return it;
|
||||
@@ -587,6 +588,7 @@ static cJSON *program_classic_snapshot_locked(const nfc_tag_info_t *tag, const c
|
||||
int sectors_n = cJSON_GetArraySize(sectors);
|
||||
for (int si = 0; si < sectors_n; si++) {
|
||||
cJSON *sec = cJSON_GetArrayItem(sectors, si);
|
||||
if (!sec) { skipped++; continue; }
|
||||
cJSON *blocks = cJSON_GetObjectItemCaseSensitive(sec, "blocksHex");
|
||||
cJSON *first = cJSON_GetObjectItemCaseSensitive(sec, "firstBlock");
|
||||
cJSON *count = cJSON_GetObjectItemCaseSensitive(sec, "blockCount");
|
||||
@@ -661,16 +663,22 @@ static esp_err_t api_status(httpd_req_t *req)
|
||||
wifi_mode_t mode;
|
||||
esp_wifi_get_mode(&mode);
|
||||
cJSON_AddNumberToObject(o, "wifiMode", mode);
|
||||
uint8_t ic = 0, hi = 0, lo = 0;
|
||||
nfc_access_lock();
|
||||
if (pn532_get_firmware_version(&ic, &hi, &lo) == ESP_OK) {
|
||||
cJSON *pn = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(pn, "ic", ic);
|
||||
cJSON_AddNumberToObject(pn, "fwHi", hi);
|
||||
cJSON_AddNumberToObject(pn, "fwLo", lo);
|
||||
cJSON_AddItemToObject(o, "pn532", pn);
|
||||
bool pn532_ok = nfc_engine_is_ready();
|
||||
cJSON_AddBoolToObject(o, "pn532Connected", pn532_ok);
|
||||
if (pn532_ok) {
|
||||
uint8_t ic = 0, hi = 0, lo = 0;
|
||||
nfc_access_lock();
|
||||
if (pn532_get_firmware_version(&ic, &hi, &lo) == ESP_OK) {
|
||||
cJSON *pn = cJSON_CreateObject();
|
||||
if (pn) {
|
||||
cJSON_AddNumberToObject(pn, "ic", ic);
|
||||
cJSON_AddNumberToObject(pn, "fwHi", hi);
|
||||
cJSON_AddNumberToObject(pn, "fwLo", lo);
|
||||
cJSON_AddItemToObject(o, "pn532", pn);
|
||||
}
|
||||
}
|
||||
nfc_access_unlock();
|
||||
}
|
||||
nfc_access_unlock();
|
||||
cJSON_AddBoolToObject(o, "scanning", s_scan);
|
||||
cJSON_AddBoolToObject(o, "targetActive", s_target_active);
|
||||
size_t cap_u = 0;
|
||||
@@ -1710,12 +1718,33 @@ static esp_err_t ws_handler(httpd_req_t *req)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void reconnect_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
if (!nfc_engine_is_ready()) {
|
||||
nfc_access_lock();
|
||||
bool ok = nfc_engine_try_reattach();
|
||||
nfc_access_unlock();
|
||||
if (ok) {
|
||||
ESP_LOGI(TAG, "PN532 reattached — broadcasting status");
|
||||
app_net_broadcast_json("pn532", "{\"connected\":true}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_loop_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
nfc_tag_info_t last;
|
||||
memset(&last, 0, sizeof(last));
|
||||
while (1) {
|
||||
if (!nfc_engine_is_ready()) {
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
continue;
|
||||
}
|
||||
if (!s_scan) {
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
continue;
|
||||
@@ -2049,5 +2078,8 @@ esp_err_t app_net_init(void)
|
||||
s_server = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
if (xTaskCreate(reconnect_task, "pn532_reconnect", 4096, NULL, 3, NULL) != pdPASS) {
|
||||
ESP_LOGW(TAG, "reconnect task create failed — hot-plug retry disabled");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -26,8 +26,19 @@ typedef struct {
|
||||
bool key_b;
|
||||
} nfc_mifare_key_t;
|
||||
|
||||
/** Legacy: full init including transport — aborts on failure via caller's ESP_ERROR_CHECK. */
|
||||
esp_err_t nfc_engine_init(void);
|
||||
|
||||
/** Non-fatal first-boot init. Returns true if PN532 is present and configured. */
|
||||
bool nfc_engine_try_init(void);
|
||||
|
||||
/** Soft re-attach after transport is already open — skips bus re-init, tries chip commands.
|
||||
* Call from a background retry loop; always safe to call even if already ready. */
|
||||
bool nfc_engine_try_reattach(void);
|
||||
|
||||
/** Returns true when the PN532 was successfully initialised (or re-attached). */
|
||||
bool nfc_engine_is_ready(void);
|
||||
|
||||
esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out);
|
||||
bool nfc_tag_is_mifare_classic(const nfc_tag_info_t *tag);
|
||||
bool nfc_tag_is_mifare_classic_4k(const nfc_tag_info_t *tag);
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
static const char *TAG = "nfc_engine";
|
||||
|
||||
static uint8_t s_tg = 1;
|
||||
static volatile bool s_pn532_ready = false;
|
||||
|
||||
bool nfc_engine_is_ready(void) { return s_pn532_ready; }
|
||||
|
||||
static void hint_type(nfc_tag_info_t *t)
|
||||
{
|
||||
@@ -102,9 +105,51 @@ esp_err_t nfc_engine_init(void)
|
||||
if (pn532_rf_max_retries() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "RF max retries config failed");
|
||||
}
|
||||
s_pn532_ready = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool nfc_engine_try_init(void)
|
||||
{
|
||||
esp_err_t e = pn532_core_init();
|
||||
if (e != ESP_OK) {
|
||||
ESP_LOGW(TAG, "PN532 not found (%s) — AP running, will retry every 5 s", esp_err_to_name(e));
|
||||
s_pn532_ready = false;
|
||||
return false;
|
||||
}
|
||||
e = pn532_sam_config_normal();
|
||||
if (e != ESP_OK) {
|
||||
ESP_LOGW(TAG, "SAM config: %s", esp_err_to_name(e));
|
||||
}
|
||||
uint8_t ic = 0, hi = 0, lo = 0;
|
||||
if (pn532_get_firmware_version(&ic, &hi, &lo) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "PN532 fw ic=0x%02x %u.%u", ic, hi, lo);
|
||||
}
|
||||
if (pn532_rf_max_retries() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "RF max retries config failed");
|
||||
}
|
||||
s_pn532_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nfc_engine_try_reattach(void)
|
||||
{
|
||||
/* Transport already open — just ping the chip and re-apply configuration. */
|
||||
esp_err_t e = pn532_sam_config_normal();
|
||||
if (e != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
uint8_t ic = 0, hi = 0, lo = 0;
|
||||
if (pn532_get_firmware_version(&ic, &hi, &lo) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "PN532 reattached ic=0x%02x %u.%u", ic, hi, lo);
|
||||
}
|
||||
if (pn532_rf_max_retries() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "RF max retries config failed");
|
||||
}
|
||||
s_pn532_ready = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t nfc_poll_passive_target(nfc_tag_info_t *out)
|
||||
{
|
||||
if (!out) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "nfc_engine/session_capture.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include <string.h>
|
||||
|
||||
#define SESSION_CAPTURE_BYTES (48 * 1024)
|
||||
@@ -15,6 +16,10 @@ static SemaphoreHandle_t s_mu;
|
||||
void session_capture_init(void)
|
||||
{
|
||||
s_mu = xSemaphoreCreateMutex();
|
||||
if (!s_mu) {
|
||||
ESP_LOGE("session_capture", "mutex create failed — aborting");
|
||||
abort();
|
||||
}
|
||||
session_capture_clear();
|
||||
s_deep = false;
|
||||
}
|
||||
|
||||
1
firmware/data/assets/index-Bb0xTRCn.css
Normal file
1
firmware/data/assets/index-Bb0xTRCn.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@
|
||||
href="https://fonts.googleapis.com/css2?family=Audiowide&family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=Orbitron:wght@500;600;700;800&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script type="module" crossorigin src="/assets/index-9oJp152C.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-hoMg1Qkq.css">
|
||||
<script type="module" crossorigin src="/assets/index-BmIGATlK.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Bb0xTRCn.css">
|
||||
</head>
|
||||
<body class="bg-bubble-950 text-slate-200 antialiased selection:bg-bubble-accent/40 selection:text-bubble-950">
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -12,8 +12,12 @@ void app_main(void)
|
||||
(void)esp_ota_mark_app_valid_cancel_rollback();
|
||||
board_rgb_led_quiet();
|
||||
ESP_LOGI(TAG, "PN532 NFC Toolkit starting");
|
||||
ESP_ERROR_CHECK(nfc_engine_init());
|
||||
nfc_engine_try_init(); /* non-fatal: logs warning if PN532 absent, AP starts regardless */
|
||||
session_capture_init();
|
||||
ESP_ERROR_CHECK(app_net_init());
|
||||
ESP_LOGI(TAG, "Open AP SSID PN532-Toolkit — http://192.168.4.1");
|
||||
if (nfc_engine_is_ready()) {
|
||||
ESP_LOGI(TAG, "PN532 ready · AP SSID PN532-Toolkit → http://192.168.4.1");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "PN532 not found at boot — AP running, retrying · http://192.168.4.1");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user