mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-08-29 17:28:17 +00:00
380 lines
16 KiB
Bash
380 lines
16 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
mkdir -p firmware
|
|
|
|
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
|
|
nRF_boards=(
|
|
rak4631
|
|
rak3401_1watt
|
|
wio_tracker_l1
|
|
t1000_e
|
|
meshtracker_x1
|
|
thinknode_m1
|
|
thinknode_m3
|
|
thinknode_m6
|
|
rak_wismesh_tag
|
|
ikoka_nano_30dbm
|
|
sensecap_solar
|
|
xiao_nrf52840
|
|
lilygo_techo
|
|
lilygo_timpulse_plus
|
|
promicro_sx1262
|
|
heltec_t114
|
|
heltec_t096
|
|
gat562_30s
|
|
muziworks_r1neo
|
|
)
|
|
|
|
# Native-Linux presets (not Zephyr boards — built with -b native_sim plus an
|
|
# EXTRA_CONF_FILE). Each targets a real SBC arch, so it is cross-compiled.
|
|
Linux_boards=(
|
|
femtofox
|
|
rak6421
|
|
rak6421_pi5
|
|
)
|
|
|
|
# nRF54L15 boards. No bootloader exists for this SoC (it has no USB
|
|
# peripheral at all), so these build with --no-sysbuild and the app links at
|
|
# RRAM base 0x0 -- zephyr.hex IS the whole image. Flashing is SWD only, which
|
|
# is why they publish a .hex and no .uf2/.zip, and why they are download-only
|
|
# in the Mesh America catalog.
|
|
nRF54L_boards=(
|
|
me25ls02/nrf54l15/cpuapp
|
|
xiao_nrf54l15/nrf54l15/cpuapp
|
|
)
|
|
|
|
# SWD-only ARM platforms. Neither SoC has a USB device peripheral, so there is
|
|
# no bootloader, no UF2 and no DFU path -- zephyr.hex links at the flash origin
|
|
# and IS the whole image, written with an external probe. Same story as the
|
|
# nRF54L boards above, which is why they publish a .hex and nothing else and are
|
|
# download-only in the Mesh America catalog.
|
|
MG24_boards=(
|
|
xiao_mg24
|
|
)
|
|
|
|
STM32WL_boards=(
|
|
lora_e5_mini
|
|
)
|
|
|
|
ESP32_boards=(
|
|
xiao_esp32c3
|
|
xiao_esp32c6/esp32c6/hpcore
|
|
xiao_esp32s3/esp32s3/procpu
|
|
lilygo_tlora_c6/esp32c6/hpcore
|
|
lilygo_t3s3/esp32s3/procpu
|
|
station_g2/esp32s3/procpu
|
|
heltec_wifi_lora32_v3/esp32s3/procpu
|
|
heltec_wifi_lora32_v4/esp32s3/procpu
|
|
heltec_wifi_lora32_v43/esp32s3/procpu
|
|
heltec_wireless_tracker/esp32s3/procpu
|
|
heltec_wireless_tracker_v2/esp32s3/procpu
|
|
ttgo_tbeam/esp32/procpu
|
|
)
|
|
|
|
if [[ $1 == "nrf" ]]; then
|
|
for board in "${nRF_boards[@]}"; do
|
|
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
|
|
|
|
# build nRF companions (production is the default — no extra conf needed)
|
|
echo "Now building $board companion"
|
|
if [[ $board == "wio_tracker_l1" ]]; then
|
|
west build -b "$board" zephcore --pristine -- -DCONFIG_ZEPHCORE_EASTER_EGG_DOOM=y
|
|
else
|
|
west build -b "$board" zephcore --pristine
|
|
fi
|
|
mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-"$COMMIT_HASH".uf2
|
|
mv build/zephyr/zephyr.zip firmware/"$board"-companion-"$COMMIT_HASH".zip
|
|
|
|
# build nRF repeaters
|
|
echo "Now building $board repeater"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-"$COMMIT_HASH".uf2
|
|
mv build/zephyr/zephyr.zip firmware/"$board"-repeater-"$COMMIT_HASH".zip
|
|
|
|
# Heltec T114 is sold both with and without the TFT module.
|
|
# Build dedicated screenless variants matching upstream's
|
|
# Heltec_t114_without_display_* PIO envs.
|
|
if [[ $board == "heltec_t114" ]]; then
|
|
echo "Now building $board companion (noscreen)"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/nrf52840/heltec_t114/no_display.conf"
|
|
mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-noscreen-"$COMMIT_HASH".uf2
|
|
mv build/zephyr/zephyr.zip firmware/"$board"-companion-noscreen-"$COMMIT_HASH".zip
|
|
|
|
echo "Now building $board repeater (noscreen)"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/nrf52840/heltec_t114/no_display.conf"
|
|
mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".uf2
|
|
mv build/zephyr/zephyr.zip firmware/"$board"-repeater-noscreen-"$COMMIT_HASH".zip
|
|
fi
|
|
done
|
|
|
|
# ZephCore's storage formatter — published as the `erase` package for the
|
|
# Mesh America configurator (spec §4a). MeshCore's official erase targets a
|
|
# different flash layout and only partially wipes a ZephCore node, so each
|
|
# nRF52 board points `erase` at the formatter for its SoftDevice (v6/v7 have
|
|
# different partition maps; see SOFTDEVICE in gen_provider_catalog.py).
|
|
# Copied under stable, un-hashed names so the catalog's erase URLs stay
|
|
# stable. The .zip drives the configurator's automated DFU erase flow; the
|
|
# .uf2 is the manual drag-and-drop fallback. Skipped if not built yet.
|
|
for sd in 6 7; do
|
|
for ext in zip uf2; do
|
|
f="formatter/SoftDevice_v${sd}_formatter.${ext}"
|
|
if [[ -f "$f" ]]; then
|
|
cp "$f" firmware/
|
|
echo "Published formatter: $f"
|
|
else
|
|
echo "NOTE: $f not present — erase package for SoftDevice v${sd} will 404"
|
|
fi
|
|
done
|
|
done
|
|
|
|
# Device art we ship ourselves (see `own_img` in gen_provider_catalog.py).
|
|
# Published alongside the firmware so the catalog resolves it against the
|
|
# same --url-base as everything else, rather than hardcoding a host.
|
|
if compgen -G "img/*" > /dev/null; then
|
|
cp img/* firmware/
|
|
echo "Published device art: $(ls img/ | tr '\n' ' ')"
|
|
fi
|
|
fi
|
|
|
|
if [[ $1 == "nrf54l" ]]; then
|
|
for board in "${nRF54L_boards[@]}"; do
|
|
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
|
|
|
|
echo "Now building $board companion"
|
|
west build -b "$board" zephcore --pristine --no-sysbuild
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH".hex
|
|
|
|
echo "Now building $board repeater"
|
|
west build -b "$board" zephcore --pristine --no-sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH".hex
|
|
done
|
|
fi
|
|
|
|
# Silicon Labs EFR32MG24. Needs the Silabs BLE controller blob (west blobs fetch
|
|
# hal_silabs) before the first build -- CI fetches it in the build-swd job.
|
|
# Flashed over SWD with pyocd/J-Link; the XIAO's USB-C is a debug bridge, not a
|
|
# device port, so there is no browser-flashable path.
|
|
if [[ $1 == "mg24" ]]; then
|
|
for board in "${MG24_boards[@]}"; do
|
|
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
|
|
|
|
echo "Now building $board companion"
|
|
west build -b "$board" zephcore --pristine
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH".hex
|
|
|
|
echo "Now building $board repeater"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH".hex
|
|
done
|
|
fi
|
|
|
|
# STM32WL. No Bluetooth and no USB device: the companion speaks MeshCore serial
|
|
# framing over USART1 (bridged to USB-C by the onboard USB-UART chip) and the
|
|
# repeater uses the same UART for its CLI. Flashed over SWD/ST-Link.
|
|
if [[ $1 == "stm32wl" ]]; then
|
|
for board in "${STM32WL_boards[@]}"; do
|
|
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
|
|
|
|
echo "Now building $board companion"
|
|
west build -b "$board" zephcore --pristine
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH".hex
|
|
|
|
echo "Now building $board repeater"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
mv build/zephyr/zephyr.hex firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH".hex
|
|
done
|
|
fi
|
|
|
|
if [[ $1 == "linux" ]]; then
|
|
for board in "${Linux_boards[@]}"; do
|
|
# Pick the native_sim variant + cross toolchain for the target SBC arch.
|
|
case "$board" in
|
|
femtofox)
|
|
# Luckfox Pico Mini — ARMv7-A (32-bit) → native_sim (32-bit)
|
|
zboard="native_sim"
|
|
host="arm"
|
|
cross="/usr/bin/arm-linux-gnueabihf-"
|
|
;;
|
|
rak6421|rak6421_pi5)
|
|
# Raspberry Pi — aarch64 (64-bit) → native_sim/native/64
|
|
zboard="native_sim/native/64"
|
|
host="aarch64"
|
|
cross="/usr/bin/aarch64-linux-gnu-"
|
|
;;
|
|
*)
|
|
echo "Unknown linux board: $board"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# build native-Linux companion (TCP transport — the default role)
|
|
echo "Now building $board companion (native linux)"
|
|
west build -b "$zboard" zephcore --pristine -- \
|
|
-DZEPHYR_TOOLCHAIN_VARIANT=cross-compile \
|
|
-DNATIVE_TARGET_HOST="$host" \
|
|
-DCROSS_COMPILE="$cross" \
|
|
-DEXTRA_CONF_FILE="boards/linux_native/$board.conf"
|
|
# native_sim emits zephcore_native_linux.exe; ship it as an extension-less
|
|
# per-board name (zephcore_linux_<board>-<role>-<hash>).
|
|
mv build/zephyr/zephcore_native_linux.exe firmware/zephcore_linux_"$board"-companion-"$COMMIT_HASH"
|
|
|
|
# build native-Linux repeater
|
|
echo "Now building $board repeater (native linux)"
|
|
west build -b "$zboard" zephcore --pristine -- \
|
|
-DZEPHYR_TOOLCHAIN_VARIANT=cross-compile \
|
|
-DNATIVE_TARGET_HOST="$host" \
|
|
-DCROSS_COMPILE="$cross" \
|
|
-DEXTRA_CONF_FILE="boards/linux_native/$board.conf;boards/common/repeater.conf"
|
|
mv build/zephyr/zephcore_native_linux.exe firmware/zephcore_linux_"$board"-repeater-"$COMMIT_HASH"
|
|
done
|
|
fi
|
|
|
|
if [[ $1 == "esp32" ]]; then
|
|
for board in "${ESP32_boards[@]}"; do
|
|
board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g')
|
|
|
|
if [[ $board =~ (esp32[^/]*) ]]; then
|
|
chip="${BASH_REMATCH[1]}"
|
|
else
|
|
echo "Unknown chip for: $board"
|
|
exit 1;
|
|
fi
|
|
|
|
# Classic ESP32 (e.g. T-Beam, PICO-D4) uses Zephyr's simple-boot path for
|
|
# both roles: a self-contained zephyr.bin flashed at the 0x1000 ROM
|
|
# bootloader offset. The companion keeps BLE (whose controller reserves
|
|
# ~50KB DRAM, leaving no room for MCUboot) and the repeater is CLI-only
|
|
# (WiFi OTA's functional driver + 64KB heap overflow DRAM by ~10KB). The
|
|
# S3/C-series have the DRAM headroom and use sysbuild + MCUboot below.
|
|
if [[ $chip == "esp32" ]]; then
|
|
if [[ $2 == "companions" ]]; then
|
|
role="companion"
|
|
echo "Now building $board companion (simple boot)"
|
|
west build -b "$board" zephcore --pristine
|
|
elif [[ $2 == "repeaters" ]]; then
|
|
role="repeater"
|
|
echo "Now building $board repeater (simple boot)"
|
|
west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
else
|
|
continue
|
|
fi
|
|
# Simple-boot zephyr.bin is already the complete bootable image;
|
|
# wrap it in a full-flash merged image at the 0x1000 offset.
|
|
python -m esptool --chip "$chip" merge-bin \
|
|
--output firmware/"$board_clean_for_path"-"$role"-"$COMMIT_HASH"-merged.bin \
|
|
--flash-mode dio --flash-freq 40m --flash-size 4MB \
|
|
0x1000 build/zephyr/zephyr.bin
|
|
cp build/zephyr/zephyr.bin firmware/"$board_clean_for_path"-"$role"-"$COMMIT_HASH".bin
|
|
continue
|
|
fi
|
|
|
|
if [[ $2 == "companions" ]]; then
|
|
# build ESP32 companions (production is the default)
|
|
echo "Now building $board companion"
|
|
west build -b "$board" zephcore --pristine --sysbuild
|
|
FLASH_SIZE=$(
|
|
python3 -c '
|
|
import re
|
|
import sys
|
|
|
|
path = sys.argv[1] if len(sys.argv) > 1 else "build/zephcore/zephyr/zephyr.dts"
|
|
dts = open(path, encoding="utf-8", errors="replace").read()
|
|
|
|
def parse_cell(tok: str) -> int:
|
|
t = tok.strip()
|
|
return int(t, 16) if t.lower().startswith("0x") else int(t)
|
|
|
|
m = re.search(
|
|
r"flash0:\s*flash@[^{]*\{[^}]*?reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+"
|
|
r"((?:0x)?[0-9a-fA-F]+)\s*>",
|
|
dts,
|
|
re.DOTALL,
|
|
)
|
|
if not m:
|
|
m = re.search(
|
|
r"compatible\s*=\s*\"soc-nv-flash\"\s*;\s*[\s\S]*?"
|
|
r"reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+((?:0x)?[0-9a-fA-F]+)\s*>",
|
|
dts,
|
|
)
|
|
if not m:
|
|
raise SystemExit("flash reg not found in " + path)
|
|
|
|
size = parse_cell(m.group(1))
|
|
print(str(size // 1048576) + "MB")
|
|
' "${ZEPHYR_DTS:-build/zephcore/zephyr/zephyr.dts}"
|
|
)
|
|
# MCUboot/sysbuild boards: only the merged (MCUboot + signed app)
|
|
# image is bootable on a bare/existing chip at 0x0. The signed app
|
|
# alone requires MCUboot already present and must land at 0x10000 —
|
|
# never publish it as a bootable "plain .bin" (bricks boards when
|
|
# flashed like classic-ESP32's self-contained zephyr.bin, see GH #42).
|
|
python -m esptool --chip "$chip" merge-bin \
|
|
--output firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH"-merged.bin \
|
|
--flash-mode dio --flash-freq 40m --flash-size "$FLASH_SIZE" \
|
|
0x00000 build/mcuboot/zephyr/zephyr.bin \
|
|
0x10000 build/zephcore/zephyr/zephyr.signed.bin
|
|
|
|
# Signed app image (slot0 @ 0x10000). App-only update payload: used by
|
|
# the Mesh America configurator's "flash-update" (esptool writes it at
|
|
# 0x10000 over an existing MCUboot) and as a WiFi-OTA payload. NOT
|
|
# bootable standalone — never flash at 0x0 (see GH #42).
|
|
cp build/zephcore/zephyr/zephyr.signed.bin \
|
|
firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH"-update.bin
|
|
fi
|
|
|
|
if [[ $2 == "repeaters" ]]; then
|
|
# build ESP32 repeaters
|
|
echo "Now building $board repeater"
|
|
west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
|
FLASH_SIZE=$(
|
|
python3 -c '
|
|
import re
|
|
import sys
|
|
|
|
path = sys.argv[1] if len(sys.argv) > 1 else "build/zephcore/zephyr/zephyr.dts"
|
|
dts = open(path, encoding="utf-8", errors="replace").read()
|
|
|
|
def parse_cell(tok: str) -> int:
|
|
t = tok.strip()
|
|
return int(t, 16) if t.lower().startswith("0x") else int(t)
|
|
|
|
m = re.search(
|
|
r"flash0:\s*flash@[^{]*\{[^}]*?reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+"
|
|
r"((?:0x)?[0-9a-fA-F]+)\s*>",
|
|
dts,
|
|
re.DOTALL,
|
|
)
|
|
if not m:
|
|
m = re.search(
|
|
r"compatible\s*=\s*\"soc-nv-flash\"\s*;\s*[\s\S]*?"
|
|
r"reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+((?:0x)?[0-9a-fA-F]+)\s*>",
|
|
dts,
|
|
)
|
|
if not m:
|
|
raise SystemExit("flash reg not found in " + path)
|
|
|
|
size = parse_cell(m.group(1))
|
|
print(str(size // 1048576) + "MB")
|
|
' "${ZEPHYR_DTS:-build/zephcore/zephyr/zephyr.dts}"
|
|
)
|
|
# See companion branch above — the signed app alone isn't bootable
|
|
# standalone, so only publish the merged image for these boards.
|
|
python -m esptool --chip "$chip" merge-bin \
|
|
--output firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH"-merged.bin \
|
|
--flash-mode dio --flash-freq 40m --flash-size "$FLASH_SIZE" \
|
|
0x00000 build/mcuboot/zephyr/zephyr.bin \
|
|
0x10000 build/zephcore/zephyr/zephyr.signed.bin
|
|
|
|
# Signed app image (slot0 @ 0x10000). App-only update payload: the
|
|
# Mesh America configurator's "flash-update" (esptool writes it at
|
|
# 0x10000 over an existing MCUboot) and the WiFi-OTA payload uploaded
|
|
# to slot1 via the repeater's /update page. NOT bootable standalone —
|
|
# never flash this at 0x0 (see GH #42).
|
|
cp build/zephcore/zephyr/zephyr.signed.bin \
|
|
firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH"-update.bin
|
|
fi
|
|
done
|
|
fi
|