Files
nfc-pn532-warlord/start-firmware.sh
drjones 3be2d3f936 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
2026-04-02 18:32:06 -07:00

275 lines
6.4 KiB
Bash
Executable File

#!/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