#!/usr/bin/env bash # Finds a FLASH_PAD value that moves the firmware off a defective SPI flash byte. # # The module this was developed against has one byte at flash address 0xFADE7 # whose bits 5..7 cannot be programmed, so whatever lands there gets corrupted # and the image checksum (and therefore the app) fails to boot. Growing # FLASH_PAD shifts .flash.text, which moves a different byte onto that address. # A byte is safe when its top three bits are zero, i.e. value <= 0x1F. # # Usage: scripts/pick-flash-pad.sh [max_attempts] # Prints the FLASH_PAD value to use, or exits non-zero if none was found. set -euo pipefail cd "$(dirname "$0")/.." # Flash address of the defective cell, and the offset of the app partition. readonly BAD_FLASH_ADDR="${BAD_FLASH_ADDR:-0xFADE7}" readonly APP_OFFSET="${APP_OFFSET:-0x10000}" readonly MAX_ATTEMPTS="${1:-40}" image=$(mktemp -t esp32-app.XXXXXX.bin) trap 'rm -f "$image"' EXIT bad_offset=$(python3 -c "print($BAD_FLASH_ADDR - $APP_OFFSET)") echo "bad cell: flash $BAD_FLASH_ADDR (image offset $bad_offset)" for ((pad = 0; pad < MAX_ATTEMPTS; pad++)); do FLASH_PAD="$pad" ./scripts/build.sh >/dev/null 2>&1 || { echo "build failed for FLASH_PAD=$pad" >&2 exit 1 } espflash save-image --chip esp32s3 --format esp-idf \ target/xtensa-esp32s3-espidf/release/esp32 "$image" >/dev/null 2>&1 byte=$(python3 - "$image" "$bad_offset" <<'PY' import sys data = open(sys.argv[1], "rb").read() off = int(sys.argv[2]) print(data[off] if off < len(data) else -1) PY ) if (( byte >= 0 && (byte & 0xE0) == 0 )); then echo "FLASH_PAD=$pad works: byte at $BAD_FLASH_ADDR would be 0x$(printf '%02X' "$byte")" echo "set FLASH_PAD to $pad (in src/main.rs, or export it at build time)" exit 0 fi echo " FLASH_PAD=$pad -> 0x$(printf '%02X' "$byte" 2>/dev/null || echo '??') (unsafe)" done echo "no safe FLASH_PAD found in 0..$((MAX_ATTEMPTS - 1)); this module's flash is too broken" >&2 exit 1