Fix PN532 transport and NFC tag handling.
Tighten PN532 framing and NFC access locking so scan, read, write, and raw operations behave reliably on hardware, and correct Classic versus Type 2 tag classification so common cards hit the right code paths. Refresh the embedded web assets and launcher files to match the corrected firmware behavior. Made-with: Cursor
This commit is contained in:
274
start-firmware.sh
Executable file
274
start-firmware.sh
Executable file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FW_DIR="$ROOT/firmware"
|
||||
WEB_DIR="$ROOT/web"
|
||||
|
||||
ESP_IDF_REF="${ESP_IDF_REF:-release/v5.3}"
|
||||
ESP_IDF_DIR="${ESP_IDF_DIR:-$HOME/.pn532-toolkit/esp-idf}"
|
||||
ESP_IDF_REPO="${ESP_IDF_REPO:-https://github.com/espressif/esp-idf.git}"
|
||||
IDF_TOOLS_PATH="${IDF_TOOLS_PATH:-$HOME/.pn532-toolkit/espressif}"
|
||||
PORT="${ESPPORT:-}"
|
||||
|
||||
BUILD_WEB=1
|
||||
FLASH=1
|
||||
FLASH_ONLY=0
|
||||
MONITOR=0
|
||||
MENUCONFIG=0
|
||||
CLEAN=0
|
||||
SKIP_HOST_DEPS=0
|
||||
|
||||
log() {
|
||||
printf '[pn532] %s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '[pn532] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
have() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [options]
|
||||
|
||||
One-command runner for the PN532 firmware. By default it:
|
||||
1. bootstraps ESP-IDF if needed
|
||||
2. builds the web UI into firmware/data
|
||||
3. configures the firmware target for ESP32-S3
|
||||
4. auto-detects the serial port
|
||||
5. builds and flashes the device
|
||||
|
||||
Options:
|
||||
--port PORT Use a specific serial port
|
||||
--build-only Build only, do not flash
|
||||
--flash-only Flash an existing build, skip firmware build
|
||||
--monitor Open idf.py monitor after flashing
|
||||
--menuconfig Open menuconfig before building/flashing
|
||||
--no-web Skip rebuilding the web UI
|
||||
--clean Run idf.py fullclean before building
|
||||
--skip-host-deps Do not install host packages automatically
|
||||
--help Show this help
|
||||
|
||||
Environment overrides:
|
||||
ESP_IDF_REF ESP-IDF branch or tag to bootstrap (default: $ESP_IDF_REF)
|
||||
ESP_IDF_DIR Directory for the ESP-IDF checkout
|
||||
IDF_TOOLS_PATH Directory for Espressif tool downloads
|
||||
ESPPORT Serial port if you prefer env over --port
|
||||
EOF
|
||||
}
|
||||
|
||||
detect_port() {
|
||||
local candidates=()
|
||||
local path
|
||||
|
||||
for path in \
|
||||
/dev/cu.usbmodem* \
|
||||
/dev/cu.usbserial* \
|
||||
/dev/cu.wchusbserial* \
|
||||
/dev/cu.SLAB_USBtoUART* \
|
||||
/dev/cu.usb-serial* \
|
||||
/dev/ttyUSB* \
|
||||
/dev/ttyACM*; do
|
||||
[ -e "$path" ] && candidates+=("$path")
|
||||
done
|
||||
|
||||
if [ "${#candidates[@]}" -eq 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "${candidates[0]}"
|
||||
}
|
||||
|
||||
ensure_host_deps() {
|
||||
[ "$SKIP_HOST_DEPS" -eq 1 ] && return 0
|
||||
|
||||
if [[ "$(uname -s)" == "Darwin" ]] && have brew; then
|
||||
local missing=()
|
||||
have git || missing+=(git)
|
||||
have cmake || missing+=(cmake)
|
||||
have ninja || missing+=(ninja)
|
||||
have dfu-util || missing+=(dfu-util)
|
||||
have ccache || missing+=(ccache)
|
||||
|
||||
if [ "${#missing[@]}" -gt 0 ]; then
|
||||
log "Installing host packages via Homebrew: ${missing[*]}"
|
||||
brew install "${missing[@]}"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
have git || fail "git is required"
|
||||
have cmake || fail "cmake is required"
|
||||
have ninja || fail "ninja is required"
|
||||
}
|
||||
|
||||
bootstrap_idf_checkout() {
|
||||
mkdir -p "$(dirname "$ESP_IDF_DIR")"
|
||||
if [ ! -d "$ESP_IDF_DIR/.git" ]; then
|
||||
log "Cloning ESP-IDF $ESP_IDF_REF into $ESP_IDF_DIR"
|
||||
git clone --branch "$ESP_IDF_REF" --depth 1 --recursive --shallow-submodules \
|
||||
"$ESP_IDF_REPO" "$ESP_IDF_DIR"
|
||||
else
|
||||
log "Using existing ESP-IDF checkout at $ESP_IDF_DIR"
|
||||
(cd "$ESP_IDF_DIR" && git submodule update --init --recursive --depth 1)
|
||||
fi
|
||||
}
|
||||
|
||||
load_idf_env() {
|
||||
export IDF_PATH="$ESP_IDF_DIR"
|
||||
export IDF_TOOLS_PATH
|
||||
export IDF_CCACHE_ENABLE=1
|
||||
|
||||
bootstrap_idf_checkout
|
||||
|
||||
if [ ! -f "$ESP_IDF_DIR/export.sh" ]; then
|
||||
fail "ESP-IDF export.sh missing in $ESP_IDF_DIR"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
. "$ESP_IDF_DIR/export.sh" >/dev/null 2>&1 || true
|
||||
if ! have idf.py; then
|
||||
log "Installing ESP-IDF tools for esp32s3"
|
||||
(cd "$ESP_IDF_DIR" && ./install.sh esp32s3)
|
||||
# shellcheck disable=SC1090
|
||||
. "$ESP_IDF_DIR/export.sh" >/dev/null
|
||||
fi
|
||||
|
||||
have idf.py || fail "idf.py is still unavailable after sourcing ESP-IDF"
|
||||
}
|
||||
|
||||
build_web_assets() {
|
||||
[ "$BUILD_WEB" -eq 1 ] || return 0
|
||||
|
||||
have npm || fail "npm is required to build the embedded web UI"
|
||||
if [ ! -d "$WEB_DIR/node_modules" ]; then
|
||||
log "Installing web dependencies"
|
||||
(cd "$WEB_DIR" && npm ci)
|
||||
fi
|
||||
|
||||
log "Building web UI into firmware/data"
|
||||
(cd "$WEB_DIR" && npm run build:fw)
|
||||
}
|
||||
|
||||
cleanup_partial_build_dir() {
|
||||
local build_dir="$FW_DIR/build"
|
||||
|
||||
if [ -d "$build_dir" ] && [ ! -f "$build_dir/CMakeCache.txt" ]; then
|
||||
log "Removing partial firmware/build left by an interrupted or failed configure"
|
||||
rm -rf "$build_dir"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_target() {
|
||||
cd "$FW_DIR"
|
||||
cleanup_partial_build_dir
|
||||
|
||||
if [ ! -f sdkconfig ] || ! grep -q '^CONFIG_IDF_TARGET="esp32s3"$' sdkconfig; then
|
||||
log "Configuring firmware target: esp32s3"
|
||||
idf.py set-target esp32s3
|
||||
fi
|
||||
|
||||
if [ "$MENUCONFIG" -eq 1 ]; then
|
||||
log "Opening menuconfig"
|
||||
idf.py menuconfig
|
||||
fi
|
||||
|
||||
if [ "$CLEAN" -eq 1 ]; then
|
||||
log "Cleaning previous build"
|
||||
idf.py fullclean
|
||||
fi
|
||||
}
|
||||
|
||||
run_firmware_flow() {
|
||||
cd "$FW_DIR"
|
||||
|
||||
local -a cmd=(idf.py)
|
||||
if [ -n "$PORT" ]; then
|
||||
cmd+=(-p "$PORT")
|
||||
fi
|
||||
|
||||
if [ "$FLASH" -eq 0 ]; then
|
||||
log "Building firmware only"
|
||||
"${cmd[@]}" build
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -z "$PORT" ]; then
|
||||
PORT="$(detect_port || true)"
|
||||
fi
|
||||
[ -n "$PORT" ] || fail "No ESP32 serial port detected. Plug the device in or pass --port."
|
||||
|
||||
cmd=(idf.py -p "$PORT")
|
||||
if [ "$FLASH_ONLY" -eq 1 ] && [ "$MONITOR" -eq 1 ]; then
|
||||
log "Flashing existing build and opening monitor on $PORT"
|
||||
"${cmd[@]}" flash monitor
|
||||
elif [ "$FLASH_ONLY" -eq 1 ]; then
|
||||
log "Flashing existing build on $PORT"
|
||||
"${cmd[@]}" flash
|
||||
log "Flash complete. Join Wi-Fi SSID PN532-Toolkit and open http://192.168.4.1"
|
||||
elif [ "$MONITOR" -eq 1 ]; then
|
||||
log "Building, flashing, and opening monitor on $PORT"
|
||||
"${cmd[@]}" build flash monitor
|
||||
else
|
||||
log "Building and flashing on $PORT"
|
||||
"${cmd[@]}" build flash
|
||||
log "Flash complete. Join Wi-Fi SSID PN532-Toolkit and open http://192.168.4.1"
|
||||
fi
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--port)
|
||||
[ "$#" -ge 2 ] || fail "--port requires a value"
|
||||
PORT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--build-only)
|
||||
FLASH=0
|
||||
shift
|
||||
;;
|
||||
--flash-only)
|
||||
BUILD_WEB=0
|
||||
FLASH_ONLY=1
|
||||
shift
|
||||
;;
|
||||
--monitor)
|
||||
MONITOR=1
|
||||
shift
|
||||
;;
|
||||
--menuconfig)
|
||||
MENUCONFIG=1
|
||||
shift
|
||||
;;
|
||||
--no-web)
|
||||
BUILD_WEB=0
|
||||
shift
|
||||
;;
|
||||
--clean)
|
||||
CLEAN=1
|
||||
shift
|
||||
;;
|
||||
--skip-host-deps)
|
||||
SKIP_HOST_DEPS=1
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
fail "Unknown option: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
ensure_host_deps
|
||||
load_idf_env
|
||||
build_web_assets
|
||||
prepare_target
|
||||
run_firmware_flow
|
||||
Reference in New Issue
Block a user