- Use shared ESPNOW payload limit and message splitting - Expand default tab names and add flash/build artifacts. Made-with: Cursor
245 lines
8.0 KiB
Bash
Executable File
245 lines
8.0 KiB
Bash
Executable File
#!/usr/bin/env sh
|
|
set -eu
|
|
|
|
# Environment variables:
|
|
# PORT - serial port (default: /dev/ttyUSB0)
|
|
# BAUD - baud rate (default: 460800)
|
|
# FIRMWARE - local path to firmware .bin
|
|
# FW_URL - URL to download firmware if FIRMWARE not provided or missing
|
|
|
|
PORT=${PORT:-}
|
|
BAUD=${BAUD:-460800}
|
|
CHIP=${CHIP:-esp32} # esp32 | esp32c3
|
|
|
|
# Map chip-specific settings
|
|
ESPT_CHIP="$CHIP"
|
|
FLASH_OFFSET=0x1000
|
|
DEFAULT_DOWNLOAD_PAGE="https://micropython.org/download/ESP32/"
|
|
BOARD_ID="ESP32_GENERIC"
|
|
BOARD_PAGE="https://micropython.org/download/${BOARD_ID}/"
|
|
case "$CHIP" in
|
|
esp32c3)
|
|
ESPT_CHIP="esp32c3"
|
|
FLASH_OFFSET=0x0
|
|
DEFAULT_DOWNLOAD_PAGE="https://micropython.org/download/ESP32C3/"
|
|
BOARD_ID="ESP32_GENERIC_C3"
|
|
BOARD_PAGE="https://micropython.org/download/${BOARD_ID}/"
|
|
;;
|
|
esp32)
|
|
ESPT_CHIP="esp32"
|
|
FLASH_OFFSET=0x1000
|
|
DEFAULT_DOWNLOAD_PAGE="https://micropython.org/download/ESP32/"
|
|
BOARD_ID="ESP32_GENERIC"
|
|
BOARD_PAGE="https://micropython.org/download/${BOARD_ID}/"
|
|
;;
|
|
*)
|
|
echo "Unsupported CHIP: $CHIP (supported: esp32, esp32c3)" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Download-only mode: fetch the appropriate firmware and exit
|
|
if [ -n "${DOWNLOAD_ONLY:-}" ]; then
|
|
# Prefer resolving latest if nothing provided
|
|
if [ -z "${FIRMWARE:-}" ] && [ -z "${FW_URL:-}" ]; then
|
|
LATEST=1
|
|
fi
|
|
if ! resolve_firmware; then
|
|
echo "Failed to resolve firmware for CHIP=$CHIP" >&2
|
|
exit 1
|
|
fi
|
|
echo "$FIRMWARE"
|
|
exit 0
|
|
fi
|
|
|
|
# Helper: resolve the latest firmware URL for a given board pattern with multiple fallbacks
|
|
resolve_latest_url() {
|
|
board_pattern="$1" # e.g., ESP32_GENERIC_C3-.*\.bin
|
|
# Candidate pages to try in order
|
|
pages="${BOARD_PAGE} ${DOWNLOAD_PAGE:-$DEFAULT_DOWNLOAD_PAGE} https://micropython.org/download/ https://micropython.org/resources/firmware/"
|
|
for page in $pages; do
|
|
echo "Trying to resolve latest from $page" >&2
|
|
html=$(curl -fsSL -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36' -e 'https://micropython.org/download/' "$page" || true)
|
|
[ -z "$html" ] && continue
|
|
# Prefer matching the board pattern
|
|
url=$(printf "%s" "$html" \
|
|
| sed -n 's/.*href=\"\([^\"]*\.bin\)\".*/\1/p' \
|
|
| grep -E "$board_pattern" \
|
|
| head -n1)
|
|
if [ -n "$url" ]; then
|
|
case "$url" in
|
|
http*) echo "$url"; return 0 ;;
|
|
/*) echo "https://micropython.org$url"; return 0 ;;
|
|
*) echo "$page$url"; return 0 ;;
|
|
esac
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# If LATEST is set and neither FIRMWARE nor FW_URL are provided, auto-detect latest URL
|
|
if [ -n "${LATEST:-}" ] && [ -z "${FIRMWARE:-}" ] && [ -z "${FW_URL:-}" ]; then
|
|
# Default board identifiers for each chip
|
|
case "$CHIP" in
|
|
esp32c3) BOARD_ID="ESP32_GENERIC_C3" ;;
|
|
esp32) BOARD_ID="ESP32_GENERIC" ;;
|
|
*) BOARD_ID="ESP32_GENERIC" ;;
|
|
esac
|
|
pattern="${BOARD_ID}-.*\\.bin"
|
|
echo "Resolving latest firmware for $BOARD_ID"
|
|
if FW_URL=$(resolve_latest_url "$pattern"); then
|
|
export FW_URL
|
|
echo "Latest firmware resolved to: $FW_URL"
|
|
else
|
|
echo "Failed to resolve latest firmware for pattern $pattern" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Resolve firmware path, downloading if needed
|
|
resolve_firmware() {
|
|
if [ -z "${FIRMWARE:-}" ]; then
|
|
if [ -n "${FW_URL:-}" ] || [ -n "${LATEST:-}" ]; then
|
|
# If FW_URL still unset, resolve latest using board-specific pattern
|
|
if [ -z "${FW_URL:-}" ]; then
|
|
case "$CHIP" in
|
|
esp32c3) BOARD_ID="ESP32_GENERIC_C3" ;;
|
|
esp32) BOARD_ID="ESP32_GENERIC" ;;
|
|
*) BOARD_ID="ESP32_GENERIC" ;;
|
|
esac
|
|
pattern="${BOARD_ID}-.*\\.bin"
|
|
echo "Resolving latest firmware for $BOARD_ID"
|
|
if ! FW_URL=$(resolve_latest_url "$pattern"); then
|
|
echo "Failed to resolve latest firmware for pattern $pattern" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
mkdir -p .cache
|
|
FIRMWARE=".cache/$(basename "$FW_URL")"
|
|
if [ ! -f "$FIRMWARE" ]; then
|
|
echo "Downloading firmware from $FW_URL to $FIRMWARE"
|
|
curl -L --fail -o "$FIRMWARE" "$FW_URL"
|
|
else
|
|
echo "Firmware already downloaded at $FIRMWARE"
|
|
fi
|
|
else
|
|
# Default fallback: fetch latest using board-specific pattern
|
|
case "$CHIP" in
|
|
esp32c3) BOARD_ID="ESP32_GENERIC_C3" ;;
|
|
esp32) BOARD_ID="ESP32_GENERIC" ;;
|
|
*) BOARD_ID="ESP32_GENERIC" ;;
|
|
esac
|
|
pattern="${BOARD_ID}-.*\\.bin"
|
|
echo "No FIRMWARE or FW_URL specified. Auto-fetching latest for $BOARD_ID"
|
|
if ! FW_URL=$(resolve_latest_url "$pattern"); then
|
|
echo "Failed to resolve latest firmware for pattern $pattern" >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p .cache
|
|
FIRMWARE=".cache/$(basename "$FW_URL")"
|
|
if [ ! -f "$FIRMWARE" ]; then
|
|
echo "Downloading firmware from $FW_URL to $FIRMWARE"
|
|
curl -L --fail -o "$FIRMWARE" "$FW_URL"
|
|
else
|
|
echo "Firmware already downloaded at $FIRMWARE"
|
|
fi
|
|
fi
|
|
else
|
|
if [ ! -f "$FIRMWARE" ]; then
|
|
if [ -n "${FW_URL:-}" ]; then
|
|
mkdir -p "$(dirname "$FIRMWARE")"
|
|
echo "Firmware not found at $FIRMWARE. Downloading from $FW_URL"
|
|
curl -L --fail -o "$FIRMWARE" "$FW_URL"
|
|
else
|
|
echo "Firmware file not found: $FIRMWARE. Provide FW_URL to download automatically." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Auto-detect PORT if not specified
|
|
if [ -z "$PORT" ]; then
|
|
candidates="$(ls /dev/tty/ACM* /dev/tty/USB* 2>/dev/null || true)"
|
|
# Some systems expose without /dev/tty/ prefix patterns; try common Linux paths
|
|
[ -z "$candidates" ] && candidates="$(ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null || true)"
|
|
# Prefer ACM (often for C3) then USB
|
|
PORT=$(printf "%s\n" $candidates | grep -E "/dev/ttyACM[0-9]+" | head -n1 || true)
|
|
[ -z "$PORT" ] && PORT=$(printf "%s\n" $candidates | grep -E "/dev/ttyUSB[0-9]+" | head -n1 || true)
|
|
if [ -z "$PORT" ]; then
|
|
echo "No serial port detected. Connect the board and set PORT=/dev/ttyACM0 (or /dev/ttyUSB0)." >&2
|
|
exit 1
|
|
fi
|
|
echo "Auto-detected PORT=$PORT"
|
|
fi
|
|
|
|
# Preflight: ensure port exists
|
|
if [ ! -e "$PORT" ]; then
|
|
echo "Port $PORT does not exist. Detected candidates:" >&2
|
|
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
|
|
ESPL="python -m esptool"
|
|
|
|
detect_chip() {
|
|
# Try to detect actual connected chip using esptool and override if needed
|
|
out=$($ESPL --port "$PORT" --baud "$BAUD" chip_id 2>&1 || true)
|
|
case "$out" in
|
|
*"ESP32-C3"*) DETECTED_CHIP=esp32c3 ;;
|
|
*"ESP32"*) DETECTED_CHIP=esp32 ;;
|
|
*) DETECTED_CHIP="" ;;
|
|
esac
|
|
if [ -n "$DETECTED_CHIP" ] && [ "$DETECTED_CHIP" != "$ESPT_CHIP" ]; then
|
|
echo "Detected chip $DETECTED_CHIP differs from requested $ESPT_CHIP. Using detected chip."
|
|
ESPT_CHIP="$DETECTED_CHIP"
|
|
case "$ESPT_CHIP" in
|
|
esp32c3) FLASH_OFFSET=0x0 ;;
|
|
esp32) FLASH_OFFSET=0x1000 ;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
detect_chip
|
|
|
|
# Now that we know the actual chip, resolve the correct firmware for it
|
|
resolve_firmware
|
|
|
|
# Validate firmware matches detected chip; if not, auto-correct by fetching the right image
|
|
EXPECTED_BOARD_ID="ESP32_GENERIC"
|
|
case "$ESPT_CHIP" in
|
|
esp32c3) EXPECTED_BOARD_ID="ESP32_GENERIC_C3" ;;
|
|
esp32) EXPECTED_BOARD_ID="ESP32_GENERIC" ;;
|
|
|
|
esac
|
|
|
|
FW_BASENAME="$(basename "$FIRMWARE")"
|
|
case "$FW_BASENAME" in
|
|
${EXPECTED_BOARD_ID}-*.bin) : ;; # ok
|
|
*)
|
|
echo "Firmware $FW_BASENAME does not match detected chip ($ESPT_CHIP). Fetching correct image for $EXPECTED_BOARD_ID..."
|
|
pattern="${EXPECTED_BOARD_ID}-.*\\.bin"
|
|
if ! FW_URL=$(resolve_latest_url "$pattern"); then
|
|
echo "Failed to resolve a firmware matching $EXPECTED_BOARD_ID" >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p .cache
|
|
FIRMWARE=".cache/$(basename "$FW_URL")"
|
|
if [ ! -f "$FIRMWARE" ]; then
|
|
echo "Downloading firmware from $FW_URL to $FIRMWARE"
|
|
curl -L --fail -o "$FIRMWARE" "$FW_URL"
|
|
else
|
|
echo "Firmware already downloaded at $FIRMWARE"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
$ESPL --chip "$ESPT_CHIP" --port "$PORT" --baud "$BAUD" erase_flash
|
|
|
|
echo "Writing firmware $FIRMWARE to $FLASH_OFFSET..."
|
|
$ESPL --chip "$ESPT_CHIP" --port "$PORT" --baud "$BAUD" write_flash -z "$FLASH_OFFSET" "$FIRMWARE"
|
|
|
|
echo "Done."
|
|
|
|
|