* fix(device-install): flash littlefs at the offset from firmware metadata device-install.sh read the spiffs offset out of the .mt.json metadata into SPIFFS_OFFSET, but flashed the littlefs image at $OFFSET -- a separate variable still holding the hardcoded 0x300000 default. The metadata value was never used, so any board with a non-default partition table had its filesystem written to the wrong place. Unify on SPIFFS_OFFSET, and guard both metadata overrides so a table that omits ota_1 or spiffs falls back to the built-in default instead of handing esptool an empty offset. Quote both offsets at the call site: the spiffs one is newly consumed from jq, and a multi-line result would otherwise word-split into esptool's argv and silently mis-pair address with file. device-install.bat already does all of this correctly; this brings the shell script to parity. * fix(tlora-t3s3): give the 4MB T3-S3 boards an app partition that fits tlora-t3s3-v1 and tlora-t3s3-epaper have overflowed the shared 4MB partition-table.csv app slot (ota_0 = 0x250000 = 2,424,832 bytes) on every develop build since22072c5f4(2026-06-20). The last green build,ca7d82629, cleared it by 881 bytes; develop is now ~38.7KB over. These envs have no board_level, so they only build in the full matrix on push to develop -- which is why no PR ever caught it. Add partition-table-t3s3.csv, dedicated to the 4MB ESP32-S3FH4R2 T3-S3 boards, and point all three t3s3 envs at it: app ota_0 0x010000 0x290000 (was 0x250000, +256KB) flashApp ota_1 0x2A0000 0x0A0000 (unchanged size) spiffs 0x340000 0x0C0000 (was 0x100000, -256KB) The headroom comes out of spiffs, not flashApp: the unified BLE OTA image is 636,544 bytes against a 655,360 byte slot, so ota_1 has nothing to give. The table is contiguous, ends exactly on the 4MB boundary, keeps both app partitions 64KB-aligned, and leaves the littlefs image these boards ship well inside the remaining 768KB. Verified with a Docker build of all three envs: tlora-t3s3-v1 2,463,504 91.7% tlora-t3s3-epaper 2,458,487 91.5% tlora-t3s3-epaper-inkhud 2,400,851 89.4% Trimming was considered and rejected. The entire emoji set is 6,304 bytes (measured A/B build), and cutting traffic management plus the paxcounter, storeforward, rangetest and atak modules recovers 46,636 -- enough to land at 99.7% full, i.e. weeks of headroom at develop's observed growth rate, in exchange for shipping a feature-reduced board. The slot was simply mis-sized for a board this full. Note this changes flash layout: existing T3-S3 units must be erased and factory-flashed once to pick up the new offsets. They already cannot run a current develop build, so no working configuration regresses.
174 lines
5.2 KiB
Bash
Executable File
174 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
|
|
BPS_RESET=false
|
|
MCU=""
|
|
|
|
# Constants
|
|
RESET_BAUD=1200
|
|
FIRMWARE_OFFSET=0x00
|
|
# Fallback offsets, used only when firmware metadata carries no partition table.
|
|
# These track partition-table.csv; boards with their own table override them below.
|
|
SPIFFS_OFFSET=0x300000
|
|
OTA_OFFSET=0x260000
|
|
|
|
# Determine the correct esptool command to use
|
|
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
|
|
ESPTOOL_CMD="$PYTHON -m esptool"
|
|
elif command -v esptool >/dev/null 2>&1; then
|
|
ESPTOOL_CMD="esptool"
|
|
elif command -v esptool.py >/dev/null 2>&1; then
|
|
ESPTOOL_CMD="esptool.py"
|
|
else
|
|
echo "Error: esptool not found"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for jq
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
echo "Error: jq not found" >&2
|
|
echo "Install jq with your package manager." >&2
|
|
echo "e.g. 'apt install jq', 'dnf install jq', 'brew install jq', etc." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# esptool v5 supports commands with dashes and deprecates commands with
|
|
# underscores. Prior versions only support commands with underscores
|
|
if ${ESPTOOL_CMD} | grep --quiet write-flash
|
|
then
|
|
ESPTOOL_WRITE_FLASH=write-flash
|
|
ESPTOOL_ERASE_FLASH=erase-flash
|
|
ESPTOOL_READ_FLASH_STATUS=read-flash-status
|
|
else
|
|
ESPTOOL_WRITE_FLASH=write_flash
|
|
ESPTOOL_ERASE_FLASH=erase_flash
|
|
ESPTOOL_READ_FLASH_STATUS=read_flash_status
|
|
fi
|
|
|
|
set -e
|
|
|
|
# Usage info
|
|
show_help() {
|
|
cat <<EOF
|
|
Usage: $(basename "$0") [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME] [--1200bps-reset]
|
|
Flash image file to device, but first erasing and writing system information.
|
|
|
|
-h Display this help and exit.
|
|
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
|
|
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
|
|
-f FILENAME The firmware *.factory.bin file to flash. Custom to your device type and region.
|
|
--1200bps-reset Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
|
|
|
|
EOF
|
|
}
|
|
# Parse arguments using a single while loop
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-h | --help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
-p)
|
|
ESPTOOL_CMD="$ESPTOOL_CMD --port $2"
|
|
shift
|
|
;;
|
|
-P)
|
|
PYTHON="$2"
|
|
shift
|
|
;;
|
|
-f)
|
|
FILENAME="$2"
|
|
shift
|
|
;;
|
|
--1200bps-reset)
|
|
BPS_RESET=true
|
|
;;
|
|
--) # Stop parsing options
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift # Move to the next argument
|
|
done
|
|
|
|
if [[ $BPS_RESET == true ]]; then
|
|
$ESPTOOL_CMD --baud $RESET_BAUD --after no_reset ${ESPTOOL_READ_FLASH_STATUS}
|
|
exit 0
|
|
fi
|
|
|
|
[ -z "$FILENAME" ] && [ -n "$1" ] && {
|
|
FILENAME="$1"
|
|
shift
|
|
}
|
|
|
|
if [[ $(basename "$FILENAME") != firmware-*.factory.bin ]]; then
|
|
echo "Filename must be a firmware-*.factory.bin file."
|
|
exit 1
|
|
fi
|
|
|
|
# Extract PROGNAME from %FILENAME% for later use.
|
|
PROGNAME="${FILENAME/.factory.bin/}"
|
|
# Derive metadata filename from %PROGNAME%.
|
|
METAFILE="${PROGNAME}.mt.json"
|
|
|
|
if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
|
|
# Display metadata if it exists
|
|
if [[ -f "$METAFILE" ]]; then
|
|
echo "Firmware metadata: ${METAFILE}"
|
|
jq . "$METAFILE"
|
|
# Extract relevant fields from metadata
|
|
if [[ $(jq -r '.part' "$METAFILE") != "null" ]]; then
|
|
META_OTA_OFFSET=$(jq -r '.part[] | select(.subtype == "ota_1") | .offset' "$METAFILE")
|
|
META_SPIFFS_OFFSET=$(jq -r '.part[] | select(.subtype == "spiffs") | .offset' "$METAFILE")
|
|
# A partition table may omit either entry; keep the built-in default then.
|
|
if [[ -n "$META_OTA_OFFSET" && "$META_OTA_OFFSET" != "null" ]]; then
|
|
OTA_OFFSET="$META_OTA_OFFSET"
|
|
fi
|
|
if [[ -n "$META_SPIFFS_OFFSET" && "$META_SPIFFS_OFFSET" != "null" ]]; then
|
|
SPIFFS_OFFSET="$META_SPIFFS_OFFSET"
|
|
fi
|
|
fi
|
|
MCU=$(jq -r '.mcu' "$METAFILE")
|
|
else
|
|
echo "ERROR: No metadata file found at ${METAFILE}"
|
|
exit 1
|
|
fi
|
|
|
|
# Determine OTA filename based on MCU type (unified OTA format)
|
|
OTAFILE="mt-${MCU}-ota.bin"
|
|
|
|
# Set SPIFFS filename with "littlefs-" prefix.
|
|
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
|
|
|
|
if [[ ! -f "$FILENAME" ]]; then
|
|
echo "Error: file ${FILENAME} wasn't found. Terminating."
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "$OTAFILE" ]]; then
|
|
echo "Error: file ${OTAFILE} wasn't found. Terminating."
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "$SPIFFSFILE" ]]; then
|
|
echo "Error: file ${SPIFFSFILE} wasn't found. Terminating."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
|
|
$ESPTOOL_CMD ${ESPTOOL_ERASE_FLASH}
|
|
$ESPTOOL_CMD ${ESPTOOL_WRITE_FLASH} $FIRMWARE_OFFSET "${FILENAME}"
|
|
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
|
|
$ESPTOOL_CMD ${ESPTOOL_WRITE_FLASH} "$OTA_OFFSET" "${OTAFILE}"
|
|
echo "Trying to flash ${SPIFFSFILE}, at offset ${SPIFFS_OFFSET}"
|
|
$ESPTOOL_CMD ${ESPTOOL_WRITE_FLASH} "$SPIFFS_OFFSET" "${SPIFFSFILE}"
|
|
|
|
else
|
|
show_help
|
|
echo "Invalid file: ${FILENAME}"
|
|
fi
|
|
|
|
exit 0
|