mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 06:49:52 +00:00
build: board manifests, shared board resolver, manifest-driven release matrix
- boards/**/zephcore.yml: one manifest per board/preset (release roles, variants, catalog metadata, capabilities); scripts/board_manifest.py validates and serves them to build.sh, gen_provider_catalog.py and docs/supported_boards.md - cmake/zephcore_board.cmake: board dir -> platform, shared by app and sysbuild; removes the board-name regex lists and 7 separate globs - conf chain: exact-name role/debug classification checked against Kconfig, idempotent across non-pristine reconfigures, version string single-sourced (BT DIS rev generated), day-granular build epoch - Doom easter egg: Kconfig default for the wio_tracker_l1 companion instead of a build.sh-only flag - catalog: maker for the LR2021 EVK (KeyError before); CI installs PyYAML - user -DEXTRA_DTC_OVERLAY_FILE overlays now take precedence over derived overlays Verified: identical .config, DTS and source lists for 97 board/role configs; identical images for 4 reference builds; identical build.sh command/artifact set and catalog content.
This commit is contained in:
@@ -526,6 +526,9 @@ jobs:
|
||||
with:
|
||||
python-version: '${{ env.python_version }}'
|
||||
|
||||
# gen_provider_catalog.py reads the board manifests (PyYAML).
|
||||
- run: pip install -r requirements.txt
|
||||
|
||||
# Pull every board's firmware back into a single firmware/ directory. This
|
||||
# branch is force-pushed, so an incomplete set here silently DELETES boards
|
||||
# from the live configurator feed — which is exactly why this handoff must
|
||||
|
||||
@@ -1,127 +1,123 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Release matrix.
|
||||
#
|
||||
# ./build.sh nrf | nrf54l | mg24 | stm32wl | linux
|
||||
# ./build.sh esp32 companions | repeaters
|
||||
#
|
||||
# WHICH boards are built, in which roles and variants, lives in the board
|
||||
# manifests (zephcore/boards/<platform>/<board>/zephcore.yml and
|
||||
# zephcore/boards/linux_native/<preset>.zephcore.yml; schema and validator in
|
||||
# zephcore/scripts/board_manifest.py). This script only knows how each platform
|
||||
# packages its images. A board without a `release:` section is not published.
|
||||
#
|
||||
# Everything a release build puts in the firmware comes from the build config,
|
||||
# never from extra -D flags here, so a local build of the same board + role is
|
||||
# the same firmware. Release-only differences are layout and packaging.
|
||||
|
||||
set -euo pipefail
|
||||
mkdir -p firmware
|
||||
|
||||
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
GROUP="${1:?usage: build.sh <nrf|nrf54l|mg24|stm32wl|linux> | build.sh esp32 <companions|repeaters>}"
|
||||
ROLE_FILTER="${2:-}"
|
||||
if [[ $GROUP == "esp32" && -z $ROLE_FILTER ]]; then
|
||||
echo "build.sh esp32 needs 'companions' or 'repeaters'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nRF_boards=(
|
||||
rak4631
|
||||
rak3401_1watt
|
||||
wio_tracker_l1
|
||||
wio_tracker_l1_1w
|
||||
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
|
||||
heltec_t1
|
||||
gat562_30s
|
||||
muziworks_r1neo
|
||||
)
|
||||
# Move the finished image(s) of one build into firmware/ under the published name.
|
||||
package() {
|
||||
local platform=$1 target=$2 stem=$3 tag=$4
|
||||
local out="firmware/$stem-$tag-$COMMIT_HASH"
|
||||
case $platform in
|
||||
nrf)
|
||||
# UF2 for drag-and-drop, DFU .zip for the configurator.
|
||||
mv build/zephyr/zephyr.uf2 "$out.uf2"
|
||||
mv build/zephyr/zephyr.zip "$out.zip"
|
||||
;;
|
||||
nrf54l | mg24 | stm32wl)
|
||||
# No USB bootloader on these SoCs (nRF54L15, EFR32MG24 and STM32WL
|
||||
# have no USB device peripheral): zephyr.hex links at the flash origin,
|
||||
# IS the whole image, and is flashed over SWD. Download-only in the
|
||||
# Mesh America catalog.
|
||||
mv build/zephyr/zephyr.hex "$out.hex"
|
||||
;;
|
||||
linux)
|
||||
# native_sim emits zephcore_native_linux.exe; ship it extension-less.
|
||||
mv build/zephyr/zephcore_native_linux.exe "$out"
|
||||
;;
|
||||
esp32)
|
||||
[[ $target =~ (esp32[^/]*) ]] || { echo "Unknown chip for: $target" >&2; exit 1; }
|
||||
local chip=${BASH_REMATCH[1]}
|
||||
if [[ $chip == "esp32" ]]; then
|
||||
# Classic ESP32 (T-Beam, PICO-D4): simple boot for both roles. The
|
||||
# companion's BLE controller leaves no DRAM for MCUboot and the
|
||||
# repeater is CLI-only (WiFi OTA overflows DRAM by ~10 KB), so
|
||||
# zephyr.bin is the complete bootable image at the 0x1000 ROM
|
||||
# bootloader offset; also wrapped as a full-flash merged image.
|
||||
python -m esptool --chip "$chip" merge-bin \
|
||||
--output "$out-merged.bin" \
|
||||
--flash-mode dio --flash-freq 40m --flash-size 4MB \
|
||||
0x1000 build/zephyr/zephyr.bin
|
||||
cp build/zephyr/zephyr.bin "$out.bin"
|
||||
else
|
||||
# S3/C-series: sysbuild + MCUboot. Only the merged image (MCUboot @
|
||||
# 0x0 + signed app @ 0x10000) is bootable on a bare chip. The signed
|
||||
# app alone is the app-only update payload (configurator
|
||||
# "flash-update" at 0x10000, WiFi-OTA upload) and is NOT bootable
|
||||
# standalone -- never publish it as a plain .bin (bricked boards when
|
||||
# flashed like classic-ESP32's image, GH #42).
|
||||
local flash_size
|
||||
flash_size=$(python3 zephcore/scripts/dts_flash_size.py \
|
||||
"${ZEPHYR_DTS:-build/zephcore/zephyr/zephyr.dts}")
|
||||
python -m esptool --chip "$chip" merge-bin \
|
||||
--output "$out-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
|
||||
cp build/zephcore/zephyr/zephyr.signed.bin "$out-update.bin"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 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
|
||||
)
|
||||
# One manifest row: target|role|variant|confs|stem|sysbuild|host|cross_compile
|
||||
build_row() {
|
||||
local target=$1 role=$2 variant=$3 confs=$4 stem=$5 sysbuild=$6 host=$7 cross=$8
|
||||
local tag="$role${variant:+-$variant}"
|
||||
local cmd=(west build -b "$target" zephcore --pristine)
|
||||
[[ -n $sysbuild ]] && cmd+=("--$sysbuild")
|
||||
local extra=()
|
||||
if [[ -n $host ]]; then
|
||||
# Native-Linux presets are cross-compiled for their SBC's arch.
|
||||
extra+=(-DZEPHYR_TOOLCHAIN_VARIANT=cross-compile
|
||||
-DNATIVE_TARGET_HOST="$host" -DCROSS_COMPILE="$cross")
|
||||
fi
|
||||
[[ -n $confs ]] && extra+=(-DEXTRA_CONF_FILE="$confs")
|
||||
((${#extra[@]})) && cmd+=(-- "${extra[@]}")
|
||||
|
||||
# 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
|
||||
seeed_lr2021_evk/nrf54l15/cpuapp
|
||||
)
|
||||
echo "Now building $target $tag"
|
||||
"${cmd[@]}"
|
||||
package "$GROUP" "$target" "$stem" "$tag"
|
||||
}
|
||||
|
||||
# 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
|
||||
)
|
||||
python3 zephcore/scripts/board_manifest.py check
|
||||
mapfile -t ROWS < <(python3 zephcore/scripts/board_manifest.py matrix "$GROUP" $ROLE_FILTER)
|
||||
for row in "${ROWS[@]}"; do
|
||||
row=${row%$'\r'} # Python on Windows prints CRLF
|
||||
IFS='|' read -r target role variant confs stem sysbuild host cross <<<"$row"
|
||||
build_row "$target" "$role" "$variant" "$confs" "$stem" "$sysbuild" "$host" "$cross"
|
||||
done
|
||||
|
||||
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_wifi_lora32_v4_r8/esp32s3/procpu
|
||||
heltec_wireless_tracker/esp32s3/procpu
|
||||
heltec_wireless_tracker_v2/esp32s3/procpu
|
||||
meshnology_w12/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
|
||||
if [[ $GROUP == "nrf" ]]; then
|
||||
# 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.
|
||||
# different partition maps). 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.
|
||||
for sd in 6 7; do
|
||||
for ext in zip uf2; do
|
||||
f="formatter/SoftDevice_v${sd}_formatter.${ext}"
|
||||
@@ -134,261 +130,11 @@ if [[ $1 == "nrf" ]]; then
|
||||
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.
|
||||
# Device art we ship ourselves (`own_img` in the manifests). 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)
|
||||
#
|
||||
# The USB CDC-ACM companion transport (boards/common/esp32s3_usb.conf)
|
||||
# is NOT selected here. zephcore/CMakeLists.txt auto-includes it for
|
||||
# companion builds on every S3 board whose board.overlay declares the
|
||||
# USB OTG CDC-ACM node, so a plain build below already gets it — and a
|
||||
# developer building the same board by hand gets identical firmware.
|
||||
# Keeping the choice in one place is the point: release artifacts
|
||||
# already diverge from a plain build in layout (sysbuild/MCUboot), and
|
||||
# a second divergence in which transports the app speaks would make
|
||||
# user bug reports unreproducible from source.
|
||||
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
|
||||
|
||||
+41
-27
@@ -1,33 +1,37 @@
|
||||
# Supported Boards
|
||||
|
||||
Board strings for use with `west build -b <board> zephcore`.
|
||||
Board strings for use with `west build -b <board> zephcore`. The lists are generated from the board
|
||||
manifests (`zephcore/boards/**/zephcore.yml`) by `python zephcore/scripts/board_manifest.py docs`;
|
||||
edit the manifests, not the lists.
|
||||
|
||||
## nRF52840
|
||||
|
||||
<!-- boards:nrf52 -->
|
||||
```
|
||||
rak4631
|
||||
rak3401_1watt
|
||||
wio_tracker_l1
|
||||
wio_tracker_l1_1w
|
||||
t1000_e
|
||||
gat562_30s
|
||||
heltec_t096
|
||||
heltec_t1
|
||||
heltec_t114
|
||||
ikoka_nano_30dbm
|
||||
lilygo_techo
|
||||
lilygo_timpulse_plus
|
||||
meshtracker_x1
|
||||
muziworks_r1neo
|
||||
promicro_lr2021 # source-only, no published firmware
|
||||
promicro_sx1262
|
||||
rak3401_1watt
|
||||
rak4631
|
||||
rak_wismesh_tag
|
||||
sensecap_solar
|
||||
t1000_e
|
||||
thinknode_m1
|
||||
thinknode_m3
|
||||
thinknode_m6
|
||||
rak_wismesh_tag
|
||||
ikoka_nano_30dbm
|
||||
sensecap_solar
|
||||
wio_tracker_l1
|
||||
wio_tracker_l1_1w
|
||||
xiao_nrf52840
|
||||
lilygo_techo
|
||||
promicro_sx1262
|
||||
promicro_lr2021
|
||||
heltec_t114
|
||||
heltec_t096
|
||||
heltec_t1
|
||||
gat562_30s
|
||||
muziworks_r1neo
|
||||
lilygo_timpulse_plus
|
||||
```
|
||||
<!-- /boards -->
|
||||
|
||||
> **RAK WisMesh Pocket** (WisBlock pocket): use `-b rak4631` — same board string and firmware as **RAK4631**.
|
||||
>
|
||||
@@ -45,23 +49,26 @@ lilygo_timpulse_plus
|
||||
|
||||
## ESP32
|
||||
|
||||
<!-- boards:esp32 -->
|
||||
```
|
||||
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_wifi_lora32_v4_r8/esp32s3/procpu
|
||||
heltec_wireless_tracker/esp32s3/procpu
|
||||
heltec_wireless_tracker_v2/esp32s3/procpu
|
||||
thinknode_m9/esp32s3/procpu
|
||||
lilygo_t3s3/esp32s3/procpu
|
||||
lilygo_tlora_c6/esp32c6/hpcore
|
||||
meshnology_w12/esp32s3/procpu
|
||||
station_g2/esp32s3/procpu
|
||||
thinknode_m9/esp32s3/procpu # source-only, no published firmware
|
||||
ttgo_lora32/esp32/procpu # source-only, no published firmware
|
||||
ttgo_tbeam/esp32/procpu
|
||||
ttgo_lora32/esp32/procpu # source-only, no published firmware
|
||||
xiao_esp32c3
|
||||
xiao_esp32c6/esp32c6/hpcore
|
||||
xiao_esp32s3/esp32s3/procpu
|
||||
```
|
||||
<!-- /boards -->
|
||||
|
||||
> ESP32 boards require `west blobs fetch hal_espressif` before first build.
|
||||
>
|
||||
@@ -124,9 +131,11 @@ ttgo_lora32/esp32/procpu # source-only, no published firmware
|
||||
|
||||
## STM32WL
|
||||
|
||||
<!-- boards:stm32wl -->
|
||||
```
|
||||
lora_e5_mini
|
||||
```
|
||||
<!-- /boards -->
|
||||
|
||||
> **Seeed LoRa-E5 mini** (`lora_e5_mini`): STM32WLE5JC with the integrated
|
||||
> SX1262-class sub-GHz radio. No BLE and no USB device — the companion
|
||||
@@ -135,18 +144,23 @@ lora_e5_mini
|
||||
|
||||
## MG24 (Silicon Labs)
|
||||
|
||||
<!-- boards:mg24 -->
|
||||
```
|
||||
xiao_mg24
|
||||
```
|
||||
<!-- /boards -->
|
||||
|
||||
> Requires `west blobs fetch hal_silabs` and pyocd.
|
||||
|
||||
## nRF54L
|
||||
|
||||
<!-- boards:nrf54l -->
|
||||
```
|
||||
xiao_nrf54l15/nrf54l15/cpuapp
|
||||
me25ls02/nrf54l15/cpuapp
|
||||
seeed_lr2021_evk/nrf54l15/cpuapp
|
||||
xiao_nrf54l15/nrf54l15/cpuapp
|
||||
```
|
||||
<!-- /boards -->
|
||||
|
||||
> Requires `--no-sysbuild` flag: `west build -b xiao_nrf54l15/nrf54l15/cpuapp zephcore --no-sysbuild`
|
||||
>
|
||||
|
||||
+48
-156
@@ -58,51 +58,6 @@ MAKERS = {
|
||||
"meshnology": "Meshnology",
|
||||
}
|
||||
|
||||
# Manufacturer per device name.
|
||||
MAKER_BY_DEVICE = {
|
||||
"RAK WisBlock / WisMesh (RAK 4631)": "rak",
|
||||
"RAK WisMesh 1W Booster (3401 + 13302)": "rak",
|
||||
"RAK WisMesh Tag": "rak",
|
||||
"Seeed Studio Wio Tracker L1 Pro": "seeed",
|
||||
"Seeed Studio SenseCAP T1000-E": "seeed",
|
||||
"Seeed Studio SenseCAP Solar": "seeed",
|
||||
"Seeed Studio SenseCAP MeshTracker X1": "seeed",
|
||||
"Seeed Studio Xiao nRF52 WIO": "seeed",
|
||||
"Seeed Studio Xiao C3": "seeed",
|
||||
"Seeed Studio Xiao S3 WIO": "seeed",
|
||||
"Seeed Studio Xiao ESP32-C6": "seeed",
|
||||
"Elecrow ThinkNode M1": "elecrow",
|
||||
"Elecrow ThinkNode M3": "elecrow",
|
||||
"Elecrow ThinkNode M6": "elecrow",
|
||||
"Elecrow ThinkNode M9": "elecrow",
|
||||
"Ikoka Nano": "Ikoka",
|
||||
"LilyGo T-Echo": "lilygo",
|
||||
"LilyGo T-Beam (SX1262)": "lilygo",
|
||||
"LilyGo T-Impulse Plus": "lilygo",
|
||||
"LilyGo T-Lora C6": "lilygo",
|
||||
"LilyGo T3 S3 (SX126x)": "lilygo",
|
||||
"ProMicro nrf52 (faketec)": "promicro",
|
||||
"Heltec T114": "heltec",
|
||||
"Heltec Mesh Node T096": "heltec",
|
||||
"Heltec Mesh Node T1": "heltec",
|
||||
"Heltec v3": "heltec",
|
||||
"Heltec v4": "heltec",
|
||||
"Heltec v4.3": "heltec",
|
||||
"Heltec Wireless Tracker": "heltec",
|
||||
"Heltec Wireless Tracker v2": "heltec",
|
||||
"GAT-IoT GAT562 30s": "gat-iot",
|
||||
"UnitEng Station G2": "uniteng",
|
||||
"Femtofox (Luckfox Pico Mini)": "femtofox",
|
||||
"RAK6421 WisMesh (Raspberry Pi)": "rak",
|
||||
"RAK6421 WisMesh (Raspberry Pi 5)": "rak",
|
||||
"muzi works R1 Neo": "muziworks",
|
||||
"MinewSemi ME25LS02": "minewsemi",
|
||||
"Seeed Studio Xiao nRF54L15": "seeed",
|
||||
"Seeed Studio Xiao MG24": "seeed",
|
||||
"Seeed Studio LoRa-E5 mini": "seeed",
|
||||
"Meshnology W12": "meshnology",
|
||||
}
|
||||
|
||||
DESCRIPTION = (
|
||||
"ZephCore is an independent, ground-up implementation of the MeshCore "
|
||||
"protocol on the Zephyr RTOS. It is wire-compatible with stock MeshCore "
|
||||
@@ -140,124 +95,69 @@ CUSTOM_ROLES = {
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Board -> device mapping
|
||||
# Board -> device mapping, from the board manifests
|
||||
#
|
||||
# `stem` : the exact filename stem build.sh emits (board_clean_for_path).
|
||||
# Every published board has a `catalog:` section in its manifest
|
||||
# (zephcore/boards/<platform>/<board>/zephcore.yml, Linux presets
|
||||
# zephcore/boards/linux_native/<preset>.zephcore.yml; schema in
|
||||
# zephcore/scripts/board_manifest.py). Each entry below carries:
|
||||
# `stem` : the exact filename stem build.sh emits (target with / -> -).
|
||||
# `kind` : nrf | esp32 | nrf54l | mg24 | stm32wl | linux -- drives device
|
||||
# type, file types, extensions.
|
||||
# `device` : the MeshCore-canonical device name to FOLD into, or a new tile name.
|
||||
# Folding is by exact name, so a `new` tile named to MeshCore's
|
||||
# convention merges by itself the day MeshCore adds that device.
|
||||
# `maker` : key of MAKERS (the hardware manufacturer).
|
||||
# `new` : True if this device is not in the official catalog (its own tile).
|
||||
# `img` : filename in MeshCore's own image set (resolved against IMG_BASE).
|
||||
# `own_img`: filename we ship from this repo's img/ dir (resolved against
|
||||
# --url-base). Takes precedence over `img`.
|
||||
# `variant`: optional filename infix (e.g. "noscreen") + shown as subTitle.
|
||||
# `subtitle`: optional role-row subTitle to disambiguate hardware variants.
|
||||
# `softdevice`: nRF52 only, 6 or 7, from the board's CONFIG_ZEPHCORE_SD_FWID.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
BOARDS = [
|
||||
# --- nRF52: fold into existing MeshCore devices -----------------------
|
||||
dict(stem="rak4631", kind="nrf", device="RAK WisBlock / WisMesh (RAK 4631)"),
|
||||
dict(stem="rak3401_1watt", kind="nrf", device="RAK WisMesh 1W Booster (3401 + 13302)"),
|
||||
dict(stem="wio_tracker_l1", kind="nrf", device="Seeed Studio Wio Tracker L1 Pro"),
|
||||
dict(stem="t1000_e", kind="nrf", device="Seeed Studio SenseCAP T1000-E"),
|
||||
dict(stem="thinknode_m1", kind="nrf", device="Elecrow ThinkNode M1"),
|
||||
dict(stem="thinknode_m3", kind="nrf", device="Elecrow ThinkNode M3"),
|
||||
dict(stem="thinknode_m6", kind="nrf", device="Elecrow ThinkNode M6"),
|
||||
dict(stem="rak_wismesh_tag", kind="nrf", device="RAK WisMesh Tag"),
|
||||
dict(stem="ikoka_nano_30dbm", kind="nrf", device="Ikoka Nano", subtitle="30 dBm"),
|
||||
dict(stem="sensecap_solar", kind="nrf", device="Seeed Studio SenseCAP Solar"),
|
||||
dict(stem="xiao_nrf52840", kind="nrf", device="Seeed Studio Xiao nRF52 WIO"),
|
||||
dict(stem="lilygo_techo", kind="nrf", device="LilyGo T-Echo"),
|
||||
dict(stem="promicro_sx1262", kind="nrf", device="ProMicro nrf52 (faketec)"),
|
||||
dict(stem="heltec_t114", kind="nrf", device="Heltec T114"),
|
||||
dict(stem="heltec_t114", kind="nrf", device="Heltec T114",
|
||||
variant="noscreen", subtitle="No screen"),
|
||||
dict(stem="heltec_t1", kind="nrf", device="Heltec Mesh Node T1"),
|
||||
dict(stem="gat562_30s", kind="nrf", device="GAT-IoT GAT562 30s"),
|
||||
dict(stem="muziworks_r1neo", kind="nrf", device="muzi works R1 Neo"),
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "zephcore", "scripts"))
|
||||
import board_manifest # noqa: E402
|
||||
|
||||
# --- nRF52: new ZephCore-only hardware (own tile) ---------------------
|
||||
dict(stem="lilygo_timpulse_plus", kind="nrf", device="LilyGo T-Impulse Plus", new=True, img="lora.svg"),
|
||||
dict(stem="heltec_t096", kind="nrf", device="Heltec Mesh Node T096", new=True, img="lora.svg"),
|
||||
# Name is pre-matched to MeshCore's flasher convention ("Seeed Studio" +
|
||||
# full product name, as with "Seeed Studio SenseCAP T1000-E"), so this
|
||||
# folds into their tile automatically the moment they add one -- the merge
|
||||
# is by exact name. Their flasher config has no X1 entry yet even though
|
||||
# MeshCore firmware supports it, hence new=True and our own photo for now.
|
||||
# WHEN IT APPEARS: drop new=True and own_img so their art and tile win.
|
||||
dict(stem="meshtracker_x1", kind="nrf", device="Seeed Studio SenseCAP MeshTracker X1",
|
||||
new=True, own_img="meshtracker_x1.jpg"),
|
||||
KIND = {"nrf52": "nrf", "esp32": "esp32", "nrf54l": "nrf54l",
|
||||
"mg24": "mg24", "stm32wl": "stm32wl", "linux": "linux"}
|
||||
KIND_ORDER = ["nrf", "esp32", "nrf54l", "mg24", "stm32wl", "linux"]
|
||||
SD_FWID = {0x00B6: 6, 0x0123: 7} # S140 v6.1.1 / v7.3.0
|
||||
|
||||
# --- ESP32 (sysbuild/MCUboot, -merged.bin): fold ---------------------
|
||||
dict(stem="xiao_esp32c3", kind="esp32", device="Seeed Studio Xiao C3"),
|
||||
dict(stem="xiao_esp32s3-esp32s3-procpu", kind="esp32", device="Seeed Studio Xiao S3 WIO"),
|
||||
dict(stem="station_g2-esp32s3-procpu", kind="esp32", device="UnitEng Station G2"),
|
||||
dict(stem="heltec_wifi_lora32_v3-esp32s3-procpu", kind="esp32", device="Heltec v3"),
|
||||
dict(stem="heltec_wifi_lora32_v4-esp32s3-procpu", kind="esp32", device="Heltec v4"),
|
||||
# MeshCore folds the R8 (ESP32-S3R8, 8 MB octal PSRAM) into its "Heltec v4"
|
||||
# tile as "[R8] ..." role rows rather than giving it a device of its own, so
|
||||
# it folds here too, told apart by the subTitle.
|
||||
dict(stem="heltec_wifi_lora32_v4_r8-esp32s3-procpu", kind="esp32", device="Heltec v4",
|
||||
subtitle="R8"),
|
||||
dict(stem="heltec_wireless_tracker-esp32s3-procpu", kind="esp32", device="Heltec Wireless Tracker"),
|
||||
dict(stem="heltec_wireless_tracker_v2-esp32s3-procpu", kind="esp32", device="Heltec Wireless Tracker v2"),
|
||||
# MeshCore splits this board by radio -- "(SX126x)" and "(SX127x)" are two
|
||||
# separate tiles sharing the "LilyGo T3 S3" group. We only build the SX1262
|
||||
# variant, so fold into the SX126x tile; an SX127x port would be its own entry.
|
||||
dict(stem="lilygo_t3s3-esp32s3-procpu", kind="esp32", device="LilyGo T3 S3 (SX126x)"),
|
||||
# ThinkNode M9 pulled from the build.sh release matrix 2026-07-22 (bring-up
|
||||
# still in progress — keypad/GPS/battery unverified on hardware). Restore
|
||||
# this line together with the build.sh entry once it's release-ready.
|
||||
# dict(stem="thinknode_m9-esp32s3-procpu", kind="esp32", device="Elecrow ThinkNode M9"),
|
||||
# Classic ESP32 T-Beam: ships -merged.bin (full-flash, 0x0) too.
|
||||
dict(stem="ttgo_tbeam-esp32-procpu", kind="esp32", device="LilyGo T-Beam (SX1262)"),
|
||||
|
||||
# --- ESP32: new ZephCore-only hardware (own tile) --------------------
|
||||
dict(stem="xiao_esp32c6-esp32c6-hpcore", kind="esp32", device="Seeed Studio Xiao ESP32-C6", new=True, img="xiao_esp32c6.svg"),
|
||||
dict(stem="lilygo_tlora_c6-esp32c6-hpcore", kind="esp32", device="LilyGo T-Lora C6", new=True, img="lilygo_tlora_c6.svg"),
|
||||
dict(stem="heltec_wifi_lora32_v43-esp32s3-procpu", kind="esp32", device="Heltec v4.3", new=True, img="heltec_v4.svg"),
|
||||
# MeshCore firmware has a meshnology_w12 variant but their flasher config has
|
||||
# no W12 device and no meshnology maker, so there is nothing to fold into yet.
|
||||
# The name follows their maker-plus-product convention, so it folds by itself
|
||||
# if they add one. WHEN IT APPEARS: drop new=True (and own_img, if one is set).
|
||||
dict(stem="meshnology_w12-esp32s3-procpu", kind="esp32", device="Meshnology W12", new=True, img="lora.svg"),
|
||||
def softdevice_of(board):
|
||||
text = open(os.path.join(board.board_dir, "board.conf"), encoding="utf-8").read()
|
||||
m = re.search(r"^CONFIG_ZEPHCORE_SD_FWID=(0x[0-9A-Fa-f]+)", text, re.M)
|
||||
if not m or int(m.group(1), 16) not in SD_FWID:
|
||||
raise SystemExit(f"{board.name}: no known CONFIG_ZEPHCORE_SD_FWID in board.conf")
|
||||
return SD_FWID[int(m.group(1), 16)]
|
||||
|
||||
# --- nRF54L15 (SWD only, download-only tile): new tile ----------------
|
||||
# The nRF54L15 has no USB peripheral, so there is no bootloader and no
|
||||
# WebUSB/DFU path the configurator could drive -- the Type-C port on the
|
||||
# MX25LE02 carrier is a CH340x UART bridge. The tile therefore publishes
|
||||
# the .hex as a plain download and the user flashes it over SWD.
|
||||
dict(stem="me25ls02-nrf54l15-cpuapp", kind="nrf54l", device="MinewSemi ME25LS02",
|
||||
new=True, img="lora.svg"),
|
||||
# Same story, except the XIAO carries a SAMD11 CMSIS-DAP bridge, so its own
|
||||
# USB cable is enough (openocd/pyocd) -- still not a browser flash flow.
|
||||
dict(stem="xiao_nrf54l15-nrf54l15-cpuapp", kind="nrf54l",
|
||||
device="Seeed Studio Xiao nRF54L15", new=True, img="lora.svg"),
|
||||
# Same XIAO nRF54L15 host and the same CMSIS-DAP story, but a different kit:
|
||||
# the Semtech LoRa Plus expansion board with a Wio-LR2021 module. Separate
|
||||
# tile because the radio and the whole D0..D10 wiring differ.
|
||||
dict(stem="seeed_lr2021_evk-nrf54l15-cpuapp", kind="nrf54l",
|
||||
device="Semtech LR2021 LoRa Plus EVK", new=True, img="lora.svg"),
|
||||
|
||||
# --- SWD-only ARM boards (download-only tiles): new tiles -------------
|
||||
# Same shape as the nRF54L entries: no USB device peripheral means no
|
||||
# bootloader and no browser-flashable path, so the tile publishes the .hex
|
||||
# and the user flashes it with an external probe. MeshCore has no MG24 or
|
||||
# STM32WL support at all, so both are ZephCore-only tiles.
|
||||
dict(stem="xiao_mg24", kind="mg24", device="Seeed Studio Xiao MG24",
|
||||
new=True, img="lora.svg"),
|
||||
# No Bluetooth on this SoC -- the companion speaks MeshCore serial framing
|
||||
# over USART1, bridged to USB-C by the onboard USB-UART chip, so it is
|
||||
# offered under companionUsb (a wired serial port, same as the app sees on
|
||||
# any CDC-ACM board) rather than companionBle.
|
||||
dict(stem="lora_e5_mini", kind="stm32wl", device="Seeed Studio LoRa-E5 mini",
|
||||
new=True, img="lora.svg"),
|
||||
def load_catalog_boards():
|
||||
"""Catalog entries in tile order: platform group, folded before new, then name."""
|
||||
out = []
|
||||
for b in board_manifest.load_boards():
|
||||
if b.release is None or b.catalog is None:
|
||||
continue
|
||||
c = b.catalog
|
||||
base = dict(stem=b.stem, kind=KIND[b.platform], device=c["device"], maker=c["maker"])
|
||||
for k in ("new", "img", "own_img", "subtitle"):
|
||||
if c.get(k):
|
||||
base[k] = c[k]
|
||||
if b.platform == "nrf52":
|
||||
base["softdevice"] = softdevice_of(b)
|
||||
out.append(base)
|
||||
for v in b.release.get("variants") or []:
|
||||
e = dict(base, variant=v["id"])
|
||||
e.pop("subtitle", None)
|
||||
if v.get("subtitle"):
|
||||
e["subtitle"] = v["subtitle"]
|
||||
out.append(e)
|
||||
out.sort(key=lambda e: (KIND_ORDER.index(e["kind"]), bool(e.get("new")), e["device"].lower(),
|
||||
e.get("variant") or ""))
|
||||
return out
|
||||
|
||||
# --- Native Linux (noflash, download only): new tiles ----------------
|
||||
dict(stem="zephcore_linux_femtofox", kind="linux", device="Femtofox (Luckfox Pico Mini)", new=True, img="lora.svg"),
|
||||
dict(stem="zephcore_linux_rak6421", kind="linux", device="RAK6421 WisMesh (Raspberry Pi)", new=True, img="rpi.svg"),
|
||||
dict(stem="zephcore_linux_rak6421_pi5", kind="linux", device="RAK6421 WisMesh (Raspberry Pi 5)", new=True, img="rpi.svg"),
|
||||
]
|
||||
|
||||
DEVICE_TYPE = {"nrf": "nrf52", "esp32": "esp32", "linux": "noflash",
|
||||
"nrf54l": "noflash", "mg24": "noflash", "stm32wl": "noflash"}
|
||||
@@ -267,14 +167,6 @@ DEVICE_TYPE = {"nrf": "nrf52", "esp32": "esp32", "linux": "noflash",
|
||||
# own formatter tool instead. The formatter is SoftDevice-specific (partition map
|
||||
# differs between SD v6 and v7), so each board maps to its SD's formatter .zip.
|
||||
# Files are published to firmware-dist by build.sh (stable names, no hash).
|
||||
SOFTDEVICE = {
|
||||
"rak4631": 6, "rak3401_1watt": 6, "thinknode_m1": 6, "thinknode_m3": 6,
|
||||
"thinknode_m6": 6, "rak_wismesh_tag": 6, "lilygo_techo": 6,
|
||||
"lilygo_timpulse_plus": 6, "promicro_sx1262": 6, "heltec_t114": 6,
|
||||
"heltec_t096": 6, "heltec_t1": 6, "gat562_30s": 6, "muziworks_r1neo": 6,
|
||||
"wio_tracker_l1": 7, "t1000_e": 7, "ikoka_nano_30dbm": 7,
|
||||
"sensecap_solar": 7, "xiao_nrf52840": 7, "meshtracker_x1": 7,
|
||||
}
|
||||
FORMATTER_FILE = {6: "SoftDevice_v6_formatter.zip", 7: "SoftDevice_v7_formatter.zip"}
|
||||
|
||||
# Companion firmware is a single image that serves both transports (and, on
|
||||
@@ -370,7 +262,7 @@ def build(assets, url_base, version):
|
||||
dev["firmware"].append(opt)
|
||||
used_roles.add(role)
|
||||
|
||||
for board in BOARDS:
|
||||
for board in load_catalog_boards():
|
||||
name = board["device"]
|
||||
subtitle = board.get("subtitle")
|
||||
comp = files_for(assets, board, "companion")
|
||||
@@ -381,7 +273,7 @@ def build(assets, url_base, version):
|
||||
continue
|
||||
|
||||
if name not in devices:
|
||||
maker = MAKER_BY_DEVICE[name] # KeyError if a device is unmapped
|
||||
maker = board["maker"]
|
||||
used_makers.add(maker)
|
||||
dev = {
|
||||
"maker": maker,
|
||||
@@ -402,7 +294,7 @@ def build(assets, url_base, version):
|
||||
# nRF52 boards get ZephCore's own SoftDevice-specific formatter as `erase`.
|
||||
erase = None
|
||||
if board["kind"] == "nrf":
|
||||
erase = f"{base}/{FORMATTER_FILE[SOFTDEVICE[board['stem']]]}"
|
||||
erase = f"{base}/{FORMATTER_FILE[board['softdevice']]}"
|
||||
|
||||
if comp:
|
||||
for role in COMPANION_ROLES[board["kind"]]:
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
adafruit-nrfutil==0.5.3.post16
|
||||
# Board manifests (zephcore/scripts/board_manifest.py), read by build.sh and
|
||||
# gen_provider_catalog.py.
|
||||
PyYAML>=6.0
|
||||
|
||||
+265
-473
@@ -28,419 +28,244 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/zephyr_new_files.cmake)
|
||||
# Custom board root: boards/<mcu>/<board>/ discovered via BOARD_ROOT
|
||||
list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# ========== Auto-Pairing Helper ==========
|
||||
# For each .conf file, check if a same-named .overlay exists in the same directory.
|
||||
# If it does, auto-include it in EXTRA_DTC_OVERLAY_FILE. This enables variants to
|
||||
# pack both Kconfig and device tree changes together (e.g., no_display.conf + no_display.overlay).
|
||||
#
|
||||
# Usage: zephcore_auto_pair_overlay("path/to/config.conf")
|
||||
#
|
||||
function(zephcore_auto_pair_overlay CONF_FILE)
|
||||
if(NOT CONF_FILE)
|
||||
return()
|
||||
endif()
|
||||
# User-supplied EXTRA_CONF_FILE entries are relative to the application
|
||||
# source dir (e.g. "boards/linux_native/femtofox.conf"). CMake's EXISTS does
|
||||
# not resolve relative paths against it, so resolve to absolute first — else
|
||||
# the paired .overlay is silently skipped (which dropped the SX1262 node and
|
||||
# broke ZEPHCORE_LORA when device wiring moved out of linux_common.overlay).
|
||||
if(NOT IS_ABSOLUTE "${CONF_FILE}")
|
||||
set(CONF_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${CONF_FILE}")
|
||||
endif()
|
||||
if(NOT EXISTS "${CONF_FILE}")
|
||||
return()
|
||||
endif()
|
||||
string(REPLACE ".conf" ".overlay" OVERLAY_FILE "${CONF_FILE}")
|
||||
if(EXISTS "${OVERLAY_FILE}")
|
||||
# Append once, keeping the FIRST position. The user-extras pairing loop
|
||||
# below walks the whole combined EXTRA_CONF_FILE, so it re-pairs every
|
||||
# conf the auto chain already paired -- board.overlay used to land in
|
||||
# the list three times that way. Position is precedence in devicetree,
|
||||
# so silently re-appending an overlay also moved it later and let it
|
||||
# override things it was never meant to.
|
||||
if(EXTRA_DTC_OVERLAY_FILE)
|
||||
if(NOT "${OVERLAY_FILE}" IN_LIST EXTRA_DTC_OVERLAY_FILE)
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${EXTRA_DTC_OVERLAY_FILE};${OVERLAY_FILE}" PARENT_SCOPE)
|
||||
message(STATUS " [auto-pair] ${CONF_FILE} → ${OVERLAY_FILE}")
|
||||
endif()
|
||||
else()
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${OVERLAY_FILE}" PARENT_SCOPE)
|
||||
message(STATUS " [auto-pair] ${CONF_FILE} → ${OVERLAY_FILE}")
|
||||
endif()
|
||||
# Single source of truth for the firmware version string (vMAJOR.MINOR.PATCH-zephyr).
|
||||
# This exact string is what the device reports, what the BLE Device Information
|
||||
# Service advertises (CONFIG_BT_DIS_FW_REV_STR, generated from it below), what the
|
||||
# GitHub release is tagged/named, and what the Mesh America catalog uses as its
|
||||
# version key. The release workflow reads this line directly — keep its form.
|
||||
set(ZEPHCORE_FIRMWARE_VERSION "1.17.5c-zephcore")
|
||||
|
||||
# ========== Helpers ==========
|
||||
# Append FILE to the overlay list once, keeping its FIRST position. Position is
|
||||
# precedence in devicetree, so a re-append would silently move an overlay later
|
||||
# and let it override things it was never meant to.
|
||||
function(zephcore_add_overlay FILE)
|
||||
if(EXISTS "${FILE}" AND NOT "${FILE}" IN_LIST ZEPHCORE_OVERLAYS)
|
||||
set(ZEPHCORE_OVERLAYS ${ZEPHCORE_OVERLAYS} "${FILE}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ========== Board Configuration Hierarchy ==========
|
||||
# prj.conf → zephcore_common.conf → <platform>_common.conf → <board>/board.conf
|
||||
# Append a conf to the chain as given (a missing file stays Zephyr's error to
|
||||
# report), plus its same-named .overlay if one exists, so a variant can ship
|
||||
# Kconfig and devicetree changes together (no_display.conf + no_display.overlay).
|
||||
# The overlay lookup resolves relative paths against the app source dir: CMake's
|
||||
# EXISTS does not, and a skipped pairing once dropped the SX1262 node from a
|
||||
# native-Linux preset.
|
||||
function(zephcore_add_conf FILE)
|
||||
set(ZEPHCORE_CONFS ${ZEPHCORE_CONFS} "${FILE}" PARENT_SCOPE)
|
||||
if(NOT IS_ABSOLUTE "${FILE}")
|
||||
set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}")
|
||||
endif()
|
||||
string(REGEX REPLACE "\\.conf$" ".overlay" _ov "${FILE}")
|
||||
if(EXISTS "${_ov}" AND NOT "${_ov}" IN_LIST ZEPHCORE_OVERLAYS)
|
||||
set(ZEPHCORE_OVERLAYS ${ZEPHCORE_OVERLAYS} "${_ov}" PARENT_SCOPE)
|
||||
message(STATUS " [auto-pair] ${FILE} → ${_ov}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Recover BOARD and EXTRA_CONF_FILE from sysbuild cache (needed before find_package)
|
||||
# ========== User input ==========
|
||||
# The confs/overlays the user asked for, kept apart from what we derive so that
|
||||
# a reconfigure without --pristine starts from the same input instead of from
|
||||
# our own previous output (which used to get the auto chain prepended again).
|
||||
# A value different from the one we last wrote is new user input.
|
||||
foreach(_kind CONF_FILE DTC_OVERLAY_FILE)
|
||||
if(NOT DEFINED ZEPHCORE_WRITTEN_EXTRA_${_kind}
|
||||
OR NOT "${EXTRA_${_kind}}" STREQUAL "${ZEPHCORE_WRITTEN_EXTRA_${_kind}}")
|
||||
set(ZEPHCORE_USER_EXTRA_${_kind} "${EXTRA_${_kind}}" CACHE INTERNAL "")
|
||||
endif()
|
||||
endforeach()
|
||||
set(ZEPHCORE_USER_CONFS "${ZEPHCORE_USER_EXTRA_CONF_FILE}")
|
||||
|
||||
# Under sysbuild the user's EXTRA_CONF_FILE goes to sysbuild, not the app, and
|
||||
# BOARD is not set yet: recover both from the sysbuild cache.
|
||||
if(NOT BOARD AND DEFINED SYSBUILD_CACHE AND EXISTS "${SYSBUILD_CACHE}")
|
||||
file(STRINGS "${SYSBUILD_CACHE}" _sysbuild_strings ENCODING UTF-8)
|
||||
foreach(_str ${_sysbuild_strings})
|
||||
if(_str MATCHES "^BOARD:STRING=(.+)")
|
||||
set(BOARD "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
# User-specified EXTRA_CONF_FILE (e.g., repeater.conf) is passed to sysbuild,
|
||||
# not the app. Recover it so our auto-include logic can detect it.
|
||||
if(_str MATCHES "^EXTRA_CONF_FILE:UNINITIALIZED=(.+)")
|
||||
set(_SYSBUILD_USER_CONF "${CMAKE_MATCH_1}")
|
||||
list(APPEND ZEPHCORE_USER_CONFS "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(BOARD)
|
||||
message(STATUS "Sysbuild: BOARD=${BOARD}")
|
||||
endif()
|
||||
if(_SYSBUILD_USER_CONF)
|
||||
# Merge user extras into EXTRA_CONF_FILE so downstream checks see them
|
||||
if(EXTRA_CONF_FILE)
|
||||
set(EXTRA_CONF_FILE "${EXTRA_CONF_FILE};${_SYSBUILD_USER_CONF}")
|
||||
else()
|
||||
set(EXTRA_CONF_FILE "${_SYSBUILD_USER_CONF}")
|
||||
endif()
|
||||
message(STATUS "Sysbuild: user EXTRA_CONF_FILE=${_SYSBUILD_USER_CONF}")
|
||||
endif()
|
||||
message(STATUS "Sysbuild: BOARD=${BOARD} user confs=${ZEPHCORE_USER_CONFS}")
|
||||
endif()
|
||||
|
||||
# Build common config file list based on detected platform
|
||||
set(ZEPHCORE_COMMON_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/zephcore_common.conf")
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/zephcore_board.cmake)
|
||||
|
||||
# Detect platform from board name and add platform-specific common config
|
||||
# For boards with qualifiers like "wio_tracker_l1/nrf52840", check both the full BOARD
|
||||
# string and the BOARD_QUALIFIERS which contains just the qualifier (e.g., "nrf52840")
|
||||
if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "rak3401_1watt" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "meshtracker_x1" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021" OR BOARD MATCHES "promicro_sx1262" OR BOARD MATCHES "sensecap_solar" OR BOARD MATCHES "xiao_nrf52840" OR BOARD MATCHES "lilygo_techo" OR BOARD MATCHES "lilygo_timpulse_plus" OR BOARD MATCHES "heltec_t114" OR BOARD MATCHES "heltec_t096" OR BOARD MATCHES "heltec_t1" OR BOARD MATCHES "gat562" OR BOARD MATCHES "muziworks_r1neo")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf52.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf")
|
||||
elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES "thinknode_m9" OR BOARD MATCHES "lilygo_tlora_c6" OR BOARD MATCHES "heltec_wifi_lora32_v3" OR BOARD MATCHES "heltec_wifi_lora32_v4" OR BOARD MATCHES "meshnology_w12")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*esp32.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf")
|
||||
elseif(BOARD MATCHES ".*nrf54l.*" OR BOARD MATCHES "me25ls02")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf54l_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf54l.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf54l_common.conf")
|
||||
elseif(BOARD MATCHES ".*mg24.*" OR BOARD MATCHES ".*efr32.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/mg24_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*mg24.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/mg24_common.conf")
|
||||
elseif(BOARD MATCHES "lora_e5" OR BOARD MATCHES ".*stm32wl.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/stm32wl_common.conf")
|
||||
elseif(BOARD MATCHES "native_sim" OR BOARD MATCHES "native_posix")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/linux_native/linux_common.conf")
|
||||
# ========== Build class ==========
|
||||
# Role and debug class decide the auto-includes below, which have to be chosen
|
||||
# before Kconfig exists. They are derived from the user's conf *names* (exact
|
||||
# match, not substring) or from a -DCONFIG_ZEPHCORE_ROLE_* on the command line,
|
||||
# and checked against the resolved Kconfig after find_package().
|
||||
set(_user_conf_names "")
|
||||
foreach(_c IN LISTS ZEPHCORE_USER_CONFS)
|
||||
get_filename_component(_n "${_c}" NAME_WE)
|
||||
list(APPEND _user_conf_names "${_n}")
|
||||
endforeach()
|
||||
if("observer" IN_LIST _user_conf_names OR CONFIG_ZEPHCORE_ROLE_OBSERVER)
|
||||
set(ZEPHCORE_BUILD_ROLE observer)
|
||||
elseif("room_server" IN_LIST _user_conf_names OR CONFIG_ZEPHCORE_ROLE_ROOM_SERVER)
|
||||
set(ZEPHCORE_BUILD_ROLE room_server)
|
||||
elseif("repeater" IN_LIST _user_conf_names OR CONFIG_ZEPHCORE_ROLE_REPEATER)
|
||||
set(ZEPHCORE_BUILD_ROLE repeater)
|
||||
else()
|
||||
set(ZEPHCORE_PLATFORM_CONF "")
|
||||
set(ZEPHCORE_BUILD_ROLE companion)
|
||||
endif()
|
||||
|
||||
# Check for board-specific config in new folder structure
|
||||
# Priority: boards/<board>/board.conf > boards/<board>_<variant>.conf (legacy)
|
||||
#
|
||||
# For custom boards (e.g., wio_tracker_l1/nrf52840), BOARD contains the base name
|
||||
# For Zephyr boards with qualifiers (e.g., rak4631/nrf52840), BOARD contains base/qualifier
|
||||
# We extract just the base board name for folder lookup
|
||||
if(BOARD)
|
||||
string(REPLACE "/" ";" BOARD_PARTS "${BOARD}")
|
||||
list(GET BOARD_PARTS 0 BOARD_BASE)
|
||||
else()
|
||||
set(BOARD_BASE "")
|
||||
endif()
|
||||
|
||||
# Search for board.conf in possible locations
|
||||
set(ZEPHCORE_BOARD_CONF "")
|
||||
file(GLOB_RECURSE BOARD_CONF_CANDIDATES "${CMAKE_CURRENT_SOURCE_DIR}/boards/*/${BOARD_BASE}/board.conf")
|
||||
if(BOARD_CONF_CANDIDATES)
|
||||
list(GET BOARD_CONF_CANDIDATES 0 ZEPHCORE_BOARD_CONF)
|
||||
endif()
|
||||
# Fallback: direct path without vendor subdirectory
|
||||
if(NOT ZEPHCORE_BOARD_CONF AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/board.conf")
|
||||
set(ZEPHCORE_BOARD_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/board.conf")
|
||||
endif()
|
||||
|
||||
# ========== DTC Overlay Precedence ==========
|
||||
# board.overlay goes in FIRST, so everything applied after it can override it.
|
||||
#
|
||||
# Devicetree is last-writer-wins per property, and this list is that order. The
|
||||
# board overlay is the board's *baseline*; the overlays paired with the confs
|
||||
# that follow (repeater/pm_esp32/esp32s3_usb/no_display/...) are deliberate
|
||||
# per-role deviations from it, and the conf chain already documents that same
|
||||
# precedence ("user extras last so they can override"). Until 2026-08-28 the
|
||||
# overlay order was the exact opposite of the conf order, with two live
|
||||
# consequences:
|
||||
# - esp32s3_usb.overlay could not move the console off USB Serial/JTAG on the
|
||||
# boards that pick a console in board.overlay (heltec V4/V43, wireless
|
||||
# tracker v2) -- the reroute was overridden and the console pointed at a
|
||||
# peripheral that no longer owned the pins.
|
||||
# - pm_esp32.overlay could not hold the SX1262 NSS across light sleep on
|
||||
# heltec_wireless_tracker, whose board.overlay re-declares spi2 cs-gpios
|
||||
# without ESP32_GPIO_SLEEP_HOLD_EN -- i.e. the exact floating-NSS failure
|
||||
# that overlay exists to prevent.
|
||||
# partitions.overlay is appended after everything (see below) and stays there:
|
||||
# it must also be fed verbatim to the MCUboot image.
|
||||
set(ZEPHCORE_BOARD_OVERLAY "")
|
||||
file(GLOB_RECURSE BOARD_OVERLAY_CANDIDATES "${CMAKE_CURRENT_SOURCE_DIR}/boards/*/${BOARD_BASE}/board.overlay")
|
||||
if(BOARD_OVERLAY_CANDIDATES)
|
||||
list(GET BOARD_OVERLAY_CANDIDATES 0 ZEPHCORE_BOARD_OVERLAY)
|
||||
endif()
|
||||
# Fallback: direct path without vendor subdirectory
|
||||
if(NOT ZEPHCORE_BOARD_OVERLAY AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/board.overlay")
|
||||
set(ZEPHCORE_BOARD_OVERLAY "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/board.overlay")
|
||||
endif()
|
||||
|
||||
# Append to EXTRA_DTC_OVERLAY_FILE (preserves any user-specified extras)
|
||||
if(ZEPHCORE_BOARD_OVERLAY AND EXISTS ${ZEPHCORE_BOARD_OVERLAY})
|
||||
if(EXTRA_DTC_OVERLAY_FILE)
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${EXTRA_DTC_OVERLAY_FILE};${ZEPHCORE_BOARD_OVERLAY}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${ZEPHCORE_BOARD_OVERLAY}" CACHE STRING "" FORCE)
|
||||
# Diagnostic overlays count as debug builds: on ESP32 they keep the USB port for
|
||||
# the console and get debug_esp32.conf's console log backend.
|
||||
set(ZEPHCORE_BUILD_DEBUG FALSE)
|
||||
foreach(_n debug debug_esp32 hangdebug gps_debug)
|
||||
if(_n IN_LIST _user_conf_names)
|
||||
set(ZEPHCORE_BUILD_DEBUG TRUE)
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Build the EXTRA_CONF_FILE list (append to any user-provided extras)
|
||||
# Auto-pair each .conf with a same-named .overlay if it exists.
|
||||
set(ZEPHCORE_CONF_FILES "")
|
||||
if(EXISTS ${ZEPHCORE_COMMON_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_COMMON_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_COMMON_CONF}")
|
||||
endif()
|
||||
if(ZEPHCORE_PLATFORM_CONF AND EXISTS ${ZEPHCORE_PLATFORM_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_PLATFORM_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_PLATFORM_CONF}")
|
||||
# ========== Config chain ==========
|
||||
# Kconfig and devicetree share one precedence order, later wins:
|
||||
# zephcore_common → <platform>_common → esp32c3s3_common → version → board
|
||||
# → auto-included feature confs → user extras → (debug_esp32) → partitions.overlay
|
||||
#
|
||||
# board.overlay goes FIRST among overlays: it is the board's baseline, and the
|
||||
# overlays paired with later confs (repeater/pm_esp32/esp32s3_usb/no_display/...)
|
||||
# are deliberate per-role deviations from it. Until 2026-08-28 the overlay order
|
||||
# was the opposite of the conf order, which broke esp32s3_usb.overlay's console
|
||||
# reroute on boards that pick a console in board.overlay (heltec V4/V43, wireless
|
||||
# tracker v2) and pm_esp32.overlay's SX1262 NSS sleep-hold on
|
||||
# heltec_wireless_tracker.
|
||||
set(ZEPHCORE_CONFS "")
|
||||
set(ZEPHCORE_OVERLAYS "")
|
||||
zephcore_board_file(ZEPHCORE_BOARD_OVERLAY board.overlay)
|
||||
zephcore_add_overlay("${ZEPHCORE_BOARD_OVERLAY}")
|
||||
|
||||
set(_zc "${CMAKE_CURRENT_SOURCE_DIR}/boards/common")
|
||||
set(_platform_conf_of_nrf52 ${_zc}/nrf52_common.conf)
|
||||
set(_platform_conf_of_nrf54l ${_zc}/nrf54l_common.conf)
|
||||
set(_platform_conf_of_esp32 ${_zc}/esp32_common.conf)
|
||||
set(_platform_conf_of_mg24 ${_zc}/mg24_common.conf)
|
||||
set(_platform_conf_of_stm32wl ${_zc}/stm32wl_common.conf)
|
||||
set(_platform_conf_of_linux ${CMAKE_CURRENT_SOURCE_DIR}/boards/linux_native/linux_common.conf)
|
||||
zephcore_add_conf(${_zc}/zephcore_common.conf)
|
||||
if(ZEPHCORE_PLATFORM)
|
||||
zephcore_add_conf(${_platform_conf_of_${ZEPHCORE_PLATFORM}})
|
||||
endif()
|
||||
# ESP32-S3 and ESP32-C3 (shared BLE controller): overrides esp32_common.conf,
|
||||
# stays below board.conf. Every S3 board target carries the /esp32s3/
|
||||
# qualifier; the C3 boards are single-SoC and are named without one
|
||||
# (xiao_esp32c3), so match the chip in the name (see esp32c3s3_common.conf).
|
||||
# stays below board.conf. Every S3 target carries the /esp32s3/ qualifier; the
|
||||
# C3 boards are single-SoC and named without one (xiao_esp32c3).
|
||||
if(BOARD MATCHES "/esp32s3/" OR BOARD MATCHES "esp32c3")
|
||||
list(APPEND ZEPHCORE_CONF_FILES "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32c3s3_common.conf")
|
||||
zephcore_add_conf(${_zc}/esp32c3s3_common.conf)
|
||||
endif()
|
||||
if(ZEPHCORE_BOARD_CONF AND EXISTS ${ZEPHCORE_BOARD_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_BOARD_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_BOARD_CONF}")
|
||||
# The BLE DIS firmware revision is Kconfig, so it is generated from the one
|
||||
# version string above rather than kept by hand in a second place.
|
||||
set(_version_conf "${CMAKE_BINARY_DIR}/zephcore_version.conf")
|
||||
file(WRITE "${_version_conf}" "CONFIG_BT_DIS_FW_REV_STR=\"${ZEPHCORE_FIRMWARE_VERSION}\"\n")
|
||||
list(APPEND ZEPHCORE_CONFS "${_version_conf}")
|
||||
zephcore_board_file(ZEPHCORE_BOARD_CONF board.conf)
|
||||
if(ZEPHCORE_BOARD_CONF)
|
||||
zephcore_add_conf("${ZEPHCORE_BOARD_CONF}")
|
||||
endif()
|
||||
|
||||
# Production is the default. Debug logging is opt-in via debug.conf.
|
||||
|
||||
# Auto-include WiFi OTA for ESP32 repeater builds.
|
||||
# Enables WiFi AP + HTTP server + MCUboot image management.
|
||||
# Requires --sysbuild to build MCUboot alongside the app.
|
||||
# Non-ESP32 builds and companion builds are unaffected.
|
||||
#
|
||||
# Excluded on the classic ESP32 (e.g. T-Beam, board qualifier ".../esp32/...").
|
||||
# It builds, but only because the WiFi DT node would be left disabled; with WiFi
|
||||
# actually functional, the driver buffers + 64KB OTA heap overflow the classic
|
||||
# ESP32's DRAM by ~10KB. Rather than trim WiFi/heap to an unverifiable margin,
|
||||
# the classic ESP32 repeater stays CLI-only on simple boot (handled in build.sh).
|
||||
# The ESP32-S3/C-series have the DRAM headroom and keep WiFi OTA.
|
||||
# ========== ESP32 CPU clock ==========
|
||||
# Espressif parts idle in the tens of mA (WAITI gates the core clock but leaves
|
||||
# PLL/peripherals/RAM powered), so CPU frequency is a first-order term in a
|
||||
# battery node's draw — unlike nRF52, which already idles at microamps. Pull
|
||||
# every ESP32 build down to 80 MHz, matching what Arduino MeshCore ships.
|
||||
# hal_espressif derives ESP_DEFAULT_CPU_FREQ_MHZ from cpu@0 directly, so 80
|
||||
# needs no module patch (our 0002 carried that until the Zephyr-main bump).
|
||||
#
|
||||
# Observers are the exception and keep the SoC default (240 MHz on S3/classic,
|
||||
# 160 MHz on C3/C6): they run a full WiFi + TLS + MQTT stack and are mains- or
|
||||
# solar-with-a-big-panel powered, so throughput matters more than current.
|
||||
# Implemented as "don't apply the overlay" rather than an override, so the
|
||||
# per-SoC maximum is inherited instead of hardcoded here.
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common" AND NOT EXTRA_CONF_FILE MATCHES "observer")
|
||||
set(ZEPHCORE_ESP32_CPU_OVERLAY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_cpu_80mhz.overlay")
|
||||
if(EXISTS ${ZEPHCORE_ESP32_CPU_OVERLAY})
|
||||
if(EXTRA_DTC_OVERLAY_FILE)
|
||||
set(EXTRA_DTC_OVERLAY_FILE
|
||||
"${EXTRA_DTC_OVERLAY_FILE};${ZEPHCORE_ESP32_CPU_OVERLAY}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(EXTRA_DTC_OVERLAY_FILE
|
||||
"${ZEPHCORE_ESP32_CPU_OVERLAY}" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
# ---- Auto-included per-role features (ESP32 only today) ----
|
||||
if(ZEPHCORE_PLATFORM STREQUAL "esp32")
|
||||
# CPU clock. Espressif parts idle in the tens of mA (WAITI gates the core
|
||||
# clock but leaves PLL/peripherals/RAM powered), so CPU frequency is a
|
||||
# first-order term in a battery node's draw — unlike nRF52, which already
|
||||
# idles at microamps. Every ESP32 build runs at 80 MHz, matching Arduino
|
||||
# MeshCore; hal_espressif derives ESP_DEFAULT_CPU_FREQ_MHZ from cpu@0, so no
|
||||
# module patch is needed. Observers keep the SoC maximum (240 MHz S3/classic,
|
||||
# 160 MHz C3/C6): full WiFi + TLS + MQTT on mains or big-panel solar, so
|
||||
# throughput matters more than current. "Don't apply the overlay" rather than
|
||||
# an override, so the per-SoC maximum is inherited, not hardcoded.
|
||||
if(NOT ZEPHCORE_BUILD_ROLE STREQUAL "observer")
|
||||
zephcore_add_overlay(${_zc}/esp32_cpu_80mhz.overlay)
|
||||
message(STATUS " ESP32 CPU: 80 MHz (observers keep the SoC maximum)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ========== ESP32 light sleep (allowlisted repeater boards) ==========
|
||||
# 80 MHz above cuts the idle draw; light sleep is what removes it. See
|
||||
# boards/common/pm_esp32.conf for the mechanism and its requirements — this
|
||||
# block is only the policy that decides who gets it automatically.
|
||||
#
|
||||
# REPEATERS ONLY, and the gate is deliberately the same one WiFi OTA uses
|
||||
# below: the Espressif HCI driver takes no pm_policy locks, so a companion
|
||||
# build would light-sleep mid-advertising or mid-connection. repeater.conf
|
||||
# sets CONFIG_BT=n, which removes the problem instead of racing it.
|
||||
#
|
||||
# BOARD ALLOWLIST, not a platform check. The hard requirement is DIO1 on an
|
||||
# RTC-capable pin (GPIO 0-21 on the S3) so a received packet can wake the SoC.
|
||||
# Every board below wires DIO1 to GPIO14. Deliberately ABSENT and must stay
|
||||
# absent unless their wiring changes:
|
||||
# xiao_esp32s3 (DIO1=GPIO39), station_g2 (GPIO48), thinknode_m9 (GPIO42),
|
||||
# lilygo_tlora_c6 (GPIO23, outside the C6's LP range 0-7),
|
||||
# lilygo_t3s3 (GPIO33)
|
||||
# -> cannot wake on a received packet; the node would go deaf while
|
||||
# looking excellent on a current meter.
|
||||
# ttgo_lora32 -> SX127x; the DIO1 wake flag lives in the sx126x half of
|
||||
# patches/zephyr/0003, so nothing marks the pin as a wake source.
|
||||
# heltec_wifi_lora32_v4_r8 -> absent for a different reason: its wiring does
|
||||
# qualify (DIO1=GPIO14, NSS=GPIO8, RESET=GPIO12 on &spi2, button on
|
||||
# gpio0 0), so every criterion stated here and in pm_esp32.conf is met.
|
||||
# It is out because CONFIG_PM has never been validated on the board.
|
||||
# Without the anchoring below it would be swept in by substring match
|
||||
# rather than by decision. Add it once someone has run pm_esp32.conf's
|
||||
# three checks on hardware.
|
||||
#
|
||||
# CONSOLE, and why it does NOT gate this list. A native USB Serial/JTAG console
|
||||
# cannot survive light sleep: the peripheral drops off the bus and the HAL has
|
||||
# no USJ wake source (only UART0/1/2 and LP-UART). Of the boards below, only
|
||||
# Heltec V3 consoles on a physical uart0 (CP2102). V4, V4.3 and Wireless
|
||||
# Tracker V2 choose usb_serial in their board.overlay, and the uart0 reroute in
|
||||
# boards/common/esp32s3_usb.overlay is COMPANION-ONLY (the auto-include below
|
||||
# excludes repeater/observer/room_server), so a repeater build of those boards
|
||||
# keeps a USJ console.
|
||||
#
|
||||
# The consequence is accepted, not overlooked: on V4/V4.3/WT-V2 the USB CLI
|
||||
# stops answering once ZEPHCORE_PM_BOOT_AWAKE_MS expires, and the console flush
|
||||
# in helpers/pm_esp32_console.c targets UART0, which is not their console, so it
|
||||
# is inert there. Remote admin over LoRa is unaffected, and connecting a
|
||||
# terminal normally resets the board (the bridge drives EN from DTR) which
|
||||
# re-arms the window. These boards are in the field deliberately to get light
|
||||
# sleep exercised; drop them from the regex to take it away.
|
||||
#
|
||||
# Anchored, and every member spelled out. CMake MATCHES is a substring search,
|
||||
# so the bare alternation matched any BOARD merely *containing* one of these
|
||||
# names at any offset: "heltec_wifi_lora32_v4" also matches
|
||||
# "heltec_wifi_lora32_v4_r8", so that board would get CONFIG_PM=y without
|
||||
# anyone deciding it should. The leading "^" is what stops mid-string matches
|
||||
# and is not made redundant by the tail.
|
||||
#
|
||||
# BOARD carries its qualifiers (heltec_wifi_lora32_v4/esp32s3/procpu), and may
|
||||
# carry a revision as heltec_wifi_lora32_v3@1/esp32s3/procpu, so the tail
|
||||
# accepts "@", "/" or end-of-string rather than "$" alone. No board here
|
||||
# declares a revision today; without "@" one that later did would drop out of
|
||||
# this list silently.
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common" AND EXTRA_CONF_FILE MATCHES "repeater"
|
||||
AND BOARD MATCHES "^(heltec_wifi_lora32_v3|heltec_wifi_lora32_v4|heltec_wifi_lora32_v43|heltec_wireless_tracker|heltec_wireless_tracker_v2)(@|/|$)")
|
||||
set(ZEPHCORE_PM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/pm_esp32.conf")
|
||||
if(EXISTS ${ZEPHCORE_PM_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_PM_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_PM_CONF}")
|
||||
# Light sleep: REPEATERS on boards that declare it. 80 MHz cuts the idle draw;
|
||||
# light sleep is what removes it (mechanism and requirements:
|
||||
# boards/common/pm_esp32.conf). Repeaters only: the Espressif HCI driver takes
|
||||
# no pm_policy locks, so a companion would light-sleep mid-advertising or
|
||||
# mid-connection; repeater.conf sets CONFIG_BT=n. Which boards qualify is a
|
||||
# hardware fact (DIO1 on an RTC-wake-capable GPIO, and validated on hardware),
|
||||
# so the board declares it: `capabilities: light_sleep: true` in its
|
||||
# zephcore.yml. pm_esp32.conf lists which boards fail the wiring test and why;
|
||||
# a board that passes it but was never validated (v4_r8) stays undeclared.
|
||||
if(ZEPHCORE_BUILD_ROLE STREQUAL "repeater" AND ZEPHCORE_BOARD_LIGHT_SLEEP)
|
||||
zephcore_add_conf(${_zc}/pm_esp32.conf)
|
||||
message(STATUS " ESP32 light sleep: auto-enabled (DIO1 wake-capable repeater board)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common" AND EXTRA_CONF_FILE MATCHES "repeater"
|
||||
AND NOT BOARD MATCHES "/esp32/")
|
||||
set(ZEPHCORE_WIFI_OTA_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/wifi_ota.conf")
|
||||
if(EXISTS ${ZEPHCORE_WIFI_OTA_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_WIFI_OTA_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_WIFI_OTA_CONF}")
|
||||
# WiFi OTA for S3/C-series repeaters: WiFi AP + HTTP server + MCUboot image
|
||||
# management, so these builds need --sysbuild. Not on the classic ESP32
|
||||
# (".../esp32/..."): with WiFi functional, the driver buffers + 64 KB OTA heap
|
||||
# overflow its DRAM by ~10 KB, so the classic repeater stays CLI-only on simple
|
||||
# boot.
|
||||
if(ZEPHCORE_BUILD_ROLE STREQUAL "repeater" AND NOT BOARD MATCHES "/esp32/")
|
||||
zephcore_add_conf(${_zc}/wifi_ota.conf)
|
||||
message(STATUS " WiFi OTA: auto-enabled (ESP32 repeater)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Auto-include the ESP32-S3 USB CDC-ACM companion transport.
|
||||
#
|
||||
# The policy lives here rather than in build.sh so that a locally built
|
||||
# companion is the same firmware as the published release. The ESP32 release
|
||||
# flow already diverges from a plain build in its *layout* (sysbuild/MCUboot);
|
||||
# letting it diverge in which *transports* the app speaks as well would make
|
||||
# user bug reports unreproducible from source.
|
||||
#
|
||||
# Detection is the board's own devicetree, not a name list: a board that
|
||||
# includes esp32s3_usb_otg.dtsi has declared the cdc_acm_uart node, which is
|
||||
# exactly the precondition the USB companion call sites are compiled behind
|
||||
# (see ZEPHCORE_USB_STACK in src/main_companion.cpp). Boards without it are
|
||||
# skipped automatically -- Heltec V3 (USB-C via CP2102, uses
|
||||
# serial_companion.conf), Wireless Tracker V1, and every C3/C6/classic-ESP32
|
||||
# part, which has no DWC2 controller at all.
|
||||
#
|
||||
# COMPANION ROLE ONLY. esp32s3_usb.conf sets CONFIG_ZEPHCORE_COMPANION_USB=y,
|
||||
# which carries no role dependency of its own, and its paired overlay moves the
|
||||
# console to uart0. In a repeater/observer/room-server build that would both
|
||||
# compile the companion USB stack and strand the serial CLI on GPIO43/44.
|
||||
# serial_companion.conf is excluded for a different reason: COMPANION_SERIAL
|
||||
# `depends on !COMPANION_USB`, so adding this would silently disable it.
|
||||
#
|
||||
# DEBUG BUILDS ARE EXCLUDED. ESP32 has no RTT (debug_esp32.conf routes logs to
|
||||
# the console backend) and this conf hands that USB port to the binary
|
||||
# companion protocol, so an auto-enabled debug build would have nowhere to log.
|
||||
# Pass both confs explicitly to debug the USB transport itself; logs then need
|
||||
# a UART adapter on GPIO43/44.
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common" AND ZEPHCORE_BOARD_CONF
|
||||
AND NOT EXTRA_CONF_FILE MATCHES "repeater|observer|room_server|serial_companion|esp32s3_usb|debug")
|
||||
get_filename_component(ZEPHCORE_BOARD_DIR "${ZEPHCORE_BOARD_CONF}" DIRECTORY)
|
||||
if(EXISTS "${ZEPHCORE_BOARD_DIR}/board.overlay")
|
||||
file(READ "${ZEPHCORE_BOARD_DIR}/board.overlay" ZEPHCORE_BOARD_OVERLAY_TEXT)
|
||||
if(ZEPHCORE_BOARD_OVERLAY_TEXT MATCHES "esp32s3_usb_otg\\.dtsi")
|
||||
set(ZEPHCORE_USB_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32s3_usb.conf")
|
||||
if(EXISTS ${ZEPHCORE_USB_CONF})
|
||||
list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_USB_CONF})
|
||||
zephcore_auto_pair_overlay("${ZEPHCORE_USB_CONF}")
|
||||
message(STATUS " USB CDC companion: auto-enabled (board declares USB OTG CDC-ACM)")
|
||||
endif()
|
||||
# USB CDC-ACM companion transport on S3 boards whose board.overlay includes
|
||||
# esp32s3_usb_otg.dtsi (that include declares the cdc_acm_uart node the USB
|
||||
# companion call sites are compiled behind — see ZEPHCORE_USB_STACK in
|
||||
# src/main_companion.cpp). Lives here, not in build.sh, so a locally built
|
||||
# companion is the same firmware as the release. Companion only:
|
||||
# esp32s3_usb.conf enables the companion USB stack and its overlay moves the
|
||||
# console to uart0, which would strand a server role's CLI on GPIO43/44. Not
|
||||
# with serial_companion (COMPANION_SERIAL depends on !COMPANION_USB) and not in
|
||||
# debug builds (ESP32 has no RTT; the port must stay the log console).
|
||||
if(ZEPHCORE_BUILD_ROLE STREQUAL "companion" AND NOT ZEPHCORE_BUILD_DEBUG
|
||||
AND NOT "serial_companion" IN_LIST _user_conf_names
|
||||
AND NOT "esp32s3_usb" IN_LIST _user_conf_names
|
||||
AND ZEPHCORE_BOARD_OVERLAY)
|
||||
file(READ "${ZEPHCORE_BOARD_OVERLAY}" _board_overlay_text)
|
||||
if(_board_overlay_text MATCHES "esp32s3_usb_otg\\.dtsi")
|
||||
zephcore_add_conf(${_zc}/esp32s3_usb.conf)
|
||||
message(STATUS " USB CDC companion: auto-enabled (board declares USB OTG CDC-ACM)")
|
||||
endif()
|
||||
unset(ZEPHCORE_BOARD_OVERLAY_TEXT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Append to EXTRA_CONF_FILE — auto-generated configs first, user extras last.
|
||||
# User-specified extras (repeater.conf, debug.conf) MUST come after the auto chain
|
||||
# so they can override settings (e.g. CONFIG_BT=n in repeater.conf).
|
||||
if(ZEPHCORE_CONF_FILES)
|
||||
if(EXTRA_CONF_FILE)
|
||||
set(EXTRA_CONF_FILE "${ZEPHCORE_CONF_FILES};${EXTRA_CONF_FILE}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(EXTRA_CONF_FILE "${ZEPHCORE_CONF_FILES}" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
# ---- User extras last, so they override everything above ----
|
||||
foreach(_c IN LISTS ZEPHCORE_USER_CONFS)
|
||||
zephcore_add_conf("${_c}")
|
||||
endforeach()
|
||||
|
||||
# ESP32 debug builds: debug.conf is nRF/SEGGER-RTT shaped — on ESP32 RTT yields
|
||||
# no output (no J-Link; logs go out the UART console) and the deferred-log DRAM
|
||||
# overflows the RAM-tight classic ESP32. Append debug_esp32.conf LAST so it wins
|
||||
# over debug.conf (console backend, smaller buffers, trimmed companion arrays).
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common"
|
||||
AND EXTRA_CONF_FILE MATCHES "debug" AND NOT EXTRA_CONF_FILE MATCHES "debug_esp32")
|
||||
set(ZEPHCORE_DEBUG_ESP32_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/debug_esp32.conf")
|
||||
if(EXISTS ${ZEPHCORE_DEBUG_ESP32_CONF})
|
||||
set(EXTRA_CONF_FILE "${EXTRA_CONF_FILE};${ZEPHCORE_DEBUG_ESP32_CONF}" CACHE STRING "" FORCE)
|
||||
message(STATUS " ESP32 debug: appended debug_esp32.conf (no RTT, smaller buffers)")
|
||||
endif()
|
||||
# overflows the classic ESP32. debug_esp32.conf goes LAST so it wins over
|
||||
# debug.conf (console backend, smaller buffers, trimmed companion arrays).
|
||||
if(ZEPHCORE_PLATFORM STREQUAL "esp32" AND ZEPHCORE_BUILD_DEBUG
|
||||
AND NOT "debug_esp32" IN_LIST _user_conf_names)
|
||||
zephcore_add_conf(${_zc}/debug_esp32.conf)
|
||||
message(STATUS " ESP32 debug: appended debug_esp32.conf (no RTT, smaller buffers)")
|
||||
endif()
|
||||
|
||||
# Auto-pair user-supplied EXTRA_CONF_FILE entries with same-named .overlay files.
|
||||
# This allows repeater.conf + repeater.overlay, wifi_ota.conf + wifi_ota.overlay, etc.
|
||||
if(EXTRA_CONF_FILE)
|
||||
foreach(_conf IN LISTS EXTRA_CONF_FILE)
|
||||
zephcore_auto_pair_overlay("${_conf}")
|
||||
endforeach()
|
||||
endif()
|
||||
# User-supplied overlays (-DEXTRA_DTC_OVERLAY_FILE) win over everything derived
|
||||
# above, like user confs do. Passed through as given — Zephyr resolves relative
|
||||
# paths and reports a missing file — and moved to the end if already listed.
|
||||
# (Before 2026-09-23 they went in FIRST, i.e. lowest precedence.)
|
||||
foreach(_ov IN LISTS ZEPHCORE_USER_EXTRA_DTC_OVERLAY_FILE)
|
||||
list(REMOVE_ITEM ZEPHCORE_OVERLAYS "${_ov}")
|
||||
list(APPEND ZEPHCORE_OVERLAYS "${_ov}")
|
||||
endforeach()
|
||||
|
||||
# ========== DTC Overlay Hierarchy ==========
|
||||
# Flash partition layout lives in a separate boards/<...>/<board>/partitions.overlay
|
||||
# so the SAME file can be fed to the MCUboot image too (see sysbuild/CMakeLists.txt).
|
||||
# The app and MCUboot MUST share one flash map — otherwise MCUboot stages/looks for
|
||||
# the OTA image at a different address than the app writes it to, and OTA silently
|
||||
# reverts on reboot. Keeping it in its own file is what makes that sharing possible.
|
||||
set(ZEPHCORE_BOARD_PARTITIONS "")
|
||||
file(GLOB_RECURSE BOARD_PARTITIONS_CANDIDATES "${CMAKE_CURRENT_SOURCE_DIR}/boards/*/${BOARD_BASE}/partitions.overlay")
|
||||
if(BOARD_PARTITIONS_CANDIDATES)
|
||||
list(GET BOARD_PARTITIONS_CANDIDATES 0 ZEPHCORE_BOARD_PARTITIONS)
|
||||
endif()
|
||||
if(NOT ZEPHCORE_BOARD_PARTITIONS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/partitions.overlay")
|
||||
set(ZEPHCORE_BOARD_PARTITIONS "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_BASE}/partitions.overlay")
|
||||
endif()
|
||||
if(ZEPHCORE_BOARD_PARTITIONS AND EXISTS ${ZEPHCORE_BOARD_PARTITIONS})
|
||||
if(EXTRA_DTC_OVERLAY_FILE)
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${EXTRA_DTC_OVERLAY_FILE};${ZEPHCORE_BOARD_PARTITIONS}" CACHE STRING "" FORCE)
|
||||
else()
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${ZEPHCORE_BOARD_PARTITIONS}" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
# Flash partitions last, even after user overlays: the SAME file is fed to the
|
||||
# MCUboot image
|
||||
# (sysbuild/CMakeLists.txt). App and MCUboot must share one flash map, otherwise
|
||||
# MCUboot looks for the OTA image at a different address than the app writes it
|
||||
# to and OTA silently reverts on reboot.
|
||||
zephcore_board_file(ZEPHCORE_BOARD_PARTITIONS partitions.overlay)
|
||||
zephcore_add_overlay("${ZEPHCORE_BOARD_PARTITIONS}")
|
||||
|
||||
set(EXTRA_CONF_FILE "${ZEPHCORE_CONFS}" CACHE STRING "" FORCE)
|
||||
set(EXTRA_DTC_OVERLAY_FILE "${ZEPHCORE_OVERLAYS}" CACHE STRING "" FORCE)
|
||||
set(ZEPHCORE_WRITTEN_EXTRA_CONF_FILE "${EXTRA_CONF_FILE}" CACHE INTERNAL "")
|
||||
set(ZEPHCORE_WRITTEN_EXTRA_DTC_OVERLAY_FILE "${EXTRA_DTC_OVERLAY_FILE}" CACHE INTERNAL "")
|
||||
|
||||
# Debug: Show what configs are being used
|
||||
message(STATUS "ZephCore config hierarchy:")
|
||||
message(STATUS " Common: ${ZEPHCORE_COMMON_CONF}")
|
||||
message(STATUS " Platform: ${ZEPHCORE_PLATFORM_CONF}")
|
||||
message(STATUS " Board conf: ${ZEPHCORE_BOARD_CONF}")
|
||||
message(STATUS " Board overlay: ${ZEPHCORE_BOARD_OVERLAY}")
|
||||
message(STATUS " Board dir: ${ZEPHCORE_BOARD_DIR}")
|
||||
message(STATUS " Platform: ${ZEPHCORE_PLATFORM} Role: ${ZEPHCORE_BUILD_ROLE} Debug: ${ZEPHCORE_BUILD_DEBUG}")
|
||||
message(STATUS " EXTRA_CONF_FILE: ${EXTRA_CONF_FILE}")
|
||||
message(STATUS " EXTRA_DTC_OVERLAY_FILE: ${EXTRA_DTC_OVERLAY_FILE}")
|
||||
|
||||
@@ -452,27 +277,39 @@ set(TF_PSA_CRYPTO_FATAL_WARNINGS OFF CACHE BOOL "" FORCE)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(zephcore)
|
||||
|
||||
# Dynamic build date - always current, even without --pristine
|
||||
# The build-class guess made before Kconfig existed must match the role Kconfig
|
||||
# actually resolved; otherwise the auto-included confs above were chosen for the
|
||||
# wrong role (e.g. the ESP32 USB companion transport in a server build, which
|
||||
# strands the serial CLI). Pass the role via boards/common/<role>.conf.
|
||||
set(_kconfig_role companion)
|
||||
foreach(_r repeater room_server observer)
|
||||
string(TOUPPER "${_r}" _R)
|
||||
if(CONFIG_ZEPHCORE_ROLE_${_R})
|
||||
set(_kconfig_role ${_r})
|
||||
endif()
|
||||
endforeach()
|
||||
if(NOT _kconfig_role STREQUAL ZEPHCORE_BUILD_ROLE)
|
||||
message(FATAL_ERROR
|
||||
"ZephCore: Kconfig resolved role '${_kconfig_role}' but the build was "
|
||||
"classified as '${ZEPHCORE_BUILD_ROLE}' from EXTRA_CONF_FILE. Select the "
|
||||
"role with boards/common/${_kconfig_role}.conf (or -DCONFIG_ZEPHCORE_ROLE_*).")
|
||||
endif()
|
||||
|
||||
# Build date and epoch, injected into every app TU. Both are day-granular so a
|
||||
# reconfigure on the same day leaves every compile command unchanged and ccache's
|
||||
# direct mode keeps hitting. The epoch is mesh time sync's "provably dead clock"
|
||||
# floor (a local time below it is impossible for a running build); midnight UTC
|
||||
# of the build day keeps that true. string(TIMESTAMP) honors SOURCE_DATE_EPOCH
|
||||
# for reproducible builds.
|
||||
string(TIMESTAMP ZEPHCORE_BUILD_DATE "%Y %b %d" UTC)
|
||||
add_definitions(-DFIRMWARE_BUILD_DATE="${ZEPHCORE_BUILD_DATE}")
|
||||
|
||||
# Build-time UNIX epoch: mesh time sync's "provably dead clock" floor (a local
|
||||
# time below this is impossible for a running build). string(TIMESTAMP) honors
|
||||
# SOURCE_DATE_EPOCH for reproducible builds.
|
||||
string(TIMESTAMP ZEPHCORE_BUILD_EPOCH "%s" UTC)
|
||||
string(TIMESTAMP _now "%s" UTC)
|
||||
math(EXPR ZEPHCORE_BUILD_EPOCH "${_now} - (${_now} % 86400)")
|
||||
add_definitions(-DFIRMWARE_BUILD_EPOCH=${ZEPHCORE_BUILD_EPOCH}u)
|
||||
|
||||
# Single source of truth for the firmware version string (vMAJOR.MINOR.PATCH-zephyr).
|
||||
# Injected globally like FIRMWARE_BUILD_DATE above, so every app TU sees the same
|
||||
# value; the per-app `#ifndef FIRMWARE_VERSION` fallbacks only apply to builds that
|
||||
# bypass this injection. NOTE: the BLE DIS value in boards/common/zephcore_common.conf
|
||||
# (CONFIG_BT_DIS_FW_REV_STR) is Kconfig, not C, so it must be bumped here AND there.
|
||||
# The single source of truth for the firmware version. This exact string is what
|
||||
# the device reports, what CONFIG_BT_DIS_FW_REV_STR advertises, what the GitHub
|
||||
# release is tagged/named, and what the Mesh America catalog uses as its version
|
||||
# key — so the configurator can match a running device against the catalog. Keep
|
||||
# all four identical; the release workflow reads this value directly.
|
||||
set(ZEPHCORE_FIRMWARE_VERSION "1.17.5c-zephcore")
|
||||
# Version string: set once near the top of this file (the release workflow reads
|
||||
# that line). The per-app `#ifndef FIRMWARE_VERSION` fallbacks only apply to
|
||||
# builds that bypass this injection.
|
||||
add_definitions(-DFIRMWARE_VERSION="${ZEPHCORE_FIRMWARE_VERSION}")
|
||||
|
||||
add_subdirectory(lib/monocypher)
|
||||
@@ -551,10 +388,8 @@ target_sources_ifdef(CONFIG_SENSOR app PRIVATE adapters/sensors/t1000e_analog.c)
|
||||
# and the weak battery_curve_default. A board-specific battery_curve.c is compiled
|
||||
# alongside it to override the weak symbol with measured cell data.
|
||||
target_sources(app PRIVATE helpers/battery_curve.c)
|
||||
file(GLOB_RECURSE _BOARD_BATTERY_CURVE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/boards/*/${BOARD_BASE}/battery_curve.c")
|
||||
zephcore_board_file(_BOARD_BATTERY_CURVE battery_curve.c)
|
||||
if(_BOARD_BATTERY_CURVE)
|
||||
list(GET _BOARD_BATTERY_CURVE 0 _BOARD_BATTERY_CURVE)
|
||||
message(STATUS "ZephCore Battery: board-specific curve (${_BOARD_BATTERY_CURVE})")
|
||||
target_sources(app PRIVATE "${_BOARD_BATTERY_CURVE}")
|
||||
else()
|
||||
@@ -572,10 +407,8 @@ endif()
|
||||
# bus put any inserted card into SPI idle before the radio driver initialises,
|
||||
# so it cannot disturb the bus. Registers itself with SYS_INIT; nothing calls
|
||||
# into it, so it is simply compiled in when the board provides one.
|
||||
file(GLOB_RECURSE _BOARD_SD_QUIESCE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/boards/*/${BOARD_BASE}/sd_quiesce.c")
|
||||
zephcore_board_file(_BOARD_SD_QUIESCE sd_quiesce.c)
|
||||
if(_BOARD_SD_QUIESCE)
|
||||
list(GET _BOARD_SD_QUIESCE 0 _BOARD_SD_QUIESCE)
|
||||
message(STATUS "ZephCore SD: shared-bus quiesce (${_BOARD_SD_QUIESCE})")
|
||||
target_sources(app PRIVATE "${_BOARD_SD_QUIESCE}")
|
||||
endif()
|
||||
@@ -637,7 +470,6 @@ if(CONFIG_ZEPHCORE_RADIO_LR1110)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/radio/lr11xx
|
||||
)
|
||||
# Driver header (lr11xx_lora.h) is in the Zephyr tree after patching
|
||||
get_filename_component(ZEPHYR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE)
|
||||
target_include_directories(app PRIVATE
|
||||
${ZEPHYR_DIR}/drivers/lora/lr11xx
|
||||
)
|
||||
@@ -649,9 +481,8 @@ elseif(CONFIG_ZEPHCORE_RADIO_LR2021)
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/radio/lr20xx
|
||||
)
|
||||
get_filename_component(ZEPHYR_DIR_LR20 ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE)
|
||||
target_include_directories(app PRIVATE
|
||||
${ZEPHYR_DIR_LR20}/drivers/lora/lr20xx
|
||||
${ZEPHYR_DIR}/drivers/lora/lr20xx
|
||||
)
|
||||
elseif(CONFIG_ZEPHCORE_RADIO_SX127X)
|
||||
message(STATUS "ZephCore Radio: SX127x (Zephyr loramac-node driver)")
|
||||
@@ -665,29 +496,42 @@ else()
|
||||
adapters/radio/SX126xRadio.cpp
|
||||
)
|
||||
# Extension API header (sx126x_ext.h) is in the Zephyr driver tree
|
||||
get_filename_component(ZEPHYR_DIR_SX ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE)
|
||||
target_include_directories(app PRIVATE
|
||||
${ZEPHYR_DIR_SX}/drivers/lora/native/sx126x
|
||||
${ZEPHYR_DIR}/drivers/lora/native/sx126x
|
||||
)
|
||||
endif()
|
||||
|
||||
# ========== Role-Specific Sources ==========
|
||||
if(CONFIG_ZEPHCORE_ROLE_REPEATER)
|
||||
message(STATUS "ZephCore Role: REPEATER")
|
||||
# Source order is link order and so affects the image; keep it stable.
|
||||
if(CONFIG_ZEPHCORE_ROLE_REPEATER OR CONFIG_ZEPHCORE_ROLE_ROOM_SERVER)
|
||||
if(CONFIG_ZEPHCORE_ROLE_REPEATER)
|
||||
message(STATUS "ZephCore Role: REPEATER")
|
||||
target_sources(app PRIVATE
|
||||
src/main_repeater.cpp
|
||||
app/RepeaterMesh.cpp
|
||||
app/RepeaterRegionCLI.cpp
|
||||
)
|
||||
else()
|
||||
message(STATUS "ZephCore Role: ROOM SERVER")
|
||||
target_sources(app PRIVATE
|
||||
src/main_room_server.cpp
|
||||
app/RoomServerMesh.cpp
|
||||
app/RoomServerRegionCLI.cpp
|
||||
)
|
||||
endif()
|
||||
# Shared by both server roles: persistence under /lfs/repeater/, client ACL,
|
||||
# region filtering, transport keys, and the text CLI.
|
||||
target_sources(app PRIVATE
|
||||
src/main_repeater.cpp
|
||||
app/RepeaterMesh.cpp
|
||||
app/RepeaterRegionCLI.cpp
|
||||
app/RepeaterDataStore.cpp
|
||||
helpers/ClientACL.cpp
|
||||
helpers/RegionMap.cpp
|
||||
helpers/TransportKeyStore.cpp
|
||||
helpers/CommonCLI.cpp
|
||||
)
|
||||
# Headless repeater (e.g. native-Linux SBC): no display/buttons/buzzer, so the
|
||||
# Headless build (e.g. native-Linux SBC): no display/buttons/buzzer, so the
|
||||
# real ui_* implementation (helpers/ui-button or helpers/ui-joystick) isn't
|
||||
# compiled. main_repeater.cpp calls ui_* unconditionally — pull in the weak
|
||||
# no-op stubs to satisfy the link. Mirrors the companion TCP branch above.
|
||||
# compiled, but the server mains call ui_* unconditionally — the weak no-op
|
||||
# stubs satisfy the link.
|
||||
if(NOT CONFIG_ZEPHCORE_UI_DESIGN_BUTTON AND NOT CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
target_sources(app PRIVATE helpers/ui/ui_headless_stubs.c)
|
||||
target_include_directories(app PRIVATE
|
||||
@@ -704,54 +548,26 @@ if(CONFIG_ZEPHCORE_ROLE_REPEATER)
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/usb
|
||||
)
|
||||
if(CONFIG_ZEPHCORE_REPEATER_UPLINK AND CONFIG_MQTT_LIB)
|
||||
target_sources(app PRIVATE
|
||||
app/RepeaterUplink.cpp
|
||||
app/observer_creds.cpp
|
||||
adapters/wifi/ZephyrWiFiStation.c
|
||||
adapters/mqtt/ZephyrMQTTPublisher.c
|
||||
)
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/wifi
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/mqtt
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
)
|
||||
elseif(CONFIG_ZEPHCORE_REPEATER_UPLINK)
|
||||
message(WARNING "ZephCore repeater uplink requested, but MQTT is disabled in this configuration. Build will exclude uplink runtime.")
|
||||
if(CONFIG_ZEPHCORE_ROLE_REPEATER)
|
||||
if(CONFIG_ZEPHCORE_REPEATER_UPLINK AND CONFIG_MQTT_LIB)
|
||||
target_sources(app PRIVATE
|
||||
app/RepeaterUplink.cpp
|
||||
app/observer_creds.cpp
|
||||
adapters/wifi/ZephyrWiFiStation.c
|
||||
adapters/mqtt/ZephyrMQTTPublisher.c
|
||||
)
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/wifi
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/mqtt
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/app
|
||||
)
|
||||
elseif(CONFIG_ZEPHCORE_REPEATER_UPLINK)
|
||||
message(WARNING "ZephCore repeater uplink requested, but MQTT is disabled in this configuration. Build will exclude uplink runtime.")
|
||||
endif()
|
||||
target_compile_definitions(app PRIVATE ZEPHCORE_REPEATER=1)
|
||||
else()
|
||||
target_compile_definitions(app PRIVATE ZEPHCORE_ROOM_SERVER=1)
|
||||
endif()
|
||||
target_compile_definitions(app PRIVATE ZEPHCORE_REPEATER=1)
|
||||
elseif(CONFIG_ZEPHCORE_ROLE_ROOM_SERVER)
|
||||
message(STATUS "ZephCore Role: ROOM SERVER")
|
||||
target_sources(app PRIVATE
|
||||
src/main_room_server.cpp
|
||||
app/RoomServerMesh.cpp
|
||||
app/RoomServerRegionCLI.cpp
|
||||
app/RepeaterDataStore.cpp
|
||||
helpers/ClientACL.cpp
|
||||
helpers/RegionMap.cpp
|
||||
helpers/TransportKeyStore.cpp
|
||||
helpers/CommonCLI.cpp
|
||||
)
|
||||
# Headless build (e.g. native-Linux SBC): no display/buttons/buzzer, so the
|
||||
# real ui_* implementation (helpers/ui-button or helpers/ui-joystick) isn't
|
||||
# compiled. main_room_server.cpp calls ui_* unconditionally — pull in the weak
|
||||
# no-op stubs to satisfy the link. Mirrors the repeater/observer branches.
|
||||
if(NOT CONFIG_ZEPHCORE_UI_DESIGN_BUTTON AND NOT CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
target_sources(app PRIVATE helpers/ui/ui_headless_stubs.c)
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/helpers/ui
|
||||
)
|
||||
endif()
|
||||
# Shared USBD CDC ACM init + 1200-baud DFU + DTR event/callback module.
|
||||
if(NOT CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT AND (CONFIG_USB_CDC_ACM OR CONFIG_USBD_CDC_ACM_CLASS))
|
||||
target_sources(app PRIVATE
|
||||
adapters/usb/ZephyrUSBCDC.cpp
|
||||
)
|
||||
endif()
|
||||
target_include_directories(app PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/adapters/usb
|
||||
)
|
||||
target_compile_definitions(app PRIVATE ZEPHCORE_ROOM_SERVER=1)
|
||||
elseif(CONFIG_ZEPHCORE_ROLE_OBSERVER)
|
||||
message(STATUS "ZephCore Role: OBSERVER")
|
||||
target_sources(app PRIVATE
|
||||
@@ -928,35 +744,11 @@ if(CONFIG_ZEPHCORE_WIFI_OTA)
|
||||
zephyr_linker_sources(SECTIONS ${CMAKE_CURRENT_SOURCE_DIR}/adapters/ota/sections-rom.ld)
|
||||
endif()
|
||||
|
||||
# ========== Post-build: ESP32 flash instructions ==========
|
||||
if(ZEPHCORE_PLATFORM_CONF MATCHES "esp32_common")
|
||||
if(CONFIG_ZEPHCORE_WIFI_OTA)
|
||||
# MCUboot build (via --sysbuild): merged image or west flash
|
||||
message(STATUS "")
|
||||
message(STATUS "ESP32 Flash Commands (MCUboot + WiFi OTA):")
|
||||
message(STATUS " Flash all (MCUboot + app): west flash --build-dir build_dir")
|
||||
message(STATUS " OTA update binary: build_dir/zephcore/zephyr/zephyr.signed.bin")
|
||||
message(STATUS " Full erase first: esptool --port COMx erase_flash")
|
||||
message(STATUS "")
|
||||
else()
|
||||
# Simple boot: zephyr.bin is a self-contained image at 0x0.
|
||||
message(STATUS "")
|
||||
message(STATUS "ESP32 Flash Commands:")
|
||||
message(STATUS " Normal update (preserves NVS + user data):")
|
||||
message(STATUS " esptool --port COMx --baud 921600 write_flash 0x0 build_dir/zephyr/zephyr.bin")
|
||||
message(STATUS " Full flash (after erase_flash):")
|
||||
message(STATUS " esptool --port COMx --baud 921600 erase_flash")
|
||||
message(STATUS " esptool --port COMx --baud 921600 write_flash 0x0 build_dir/zephyr/zephyr.bin")
|
||||
message(STATUS " Or use: west flash --build-dir build_dir")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ========== Post-build: Generate DFU zip for OTA updates (nRF52 only) ==========
|
||||
# This runs after ALL build steps (linking, objcopy) via a custom target
|
||||
# Only applicable on nRF52 boards with Adafruit bootloader
|
||||
find_program(ADAFRUIT_NRFUTIL adafruit-nrfutil)
|
||||
if(ADAFRUIT_NRFUTIL AND ZEPHCORE_PLATFORM_CONF MATCHES "nrf52_common")
|
||||
if(ADAFRUIT_NRFUTIL AND ZEPHCORE_PLATFORM STREQUAL "nrf52")
|
||||
set(DFU_ZIP "${CMAKE_BINARY_DIR}/zephyr/zephyr.zip")
|
||||
set(DFU_HEX "${CMAKE_BINARY_DIR}/zephyr/zephyr.hex")
|
||||
add_custom_target(dfu_zip ALL
|
||||
|
||||
+4
-1
@@ -1120,7 +1120,10 @@ config ZEPHCORE_DISPLAY_MONO_TFT
|
||||
|
||||
config ZEPHCORE_EASTER_EGG_DOOM
|
||||
bool "Enable Doom raycaster easter egg"
|
||||
default n
|
||||
# Shipped on the Wio Tracker L1 companion. A Kconfig default rather than a
|
||||
# build.sh flag, so a local build is the same firmware as the release; not
|
||||
# in board.conf, which would also enable it in that board's repeater build.
|
||||
default y if BOARD_WIO_TRACKER_L1 && ZEPHCORE_ROLE_COMPANION
|
||||
depends on ZEPHCORE_UI_DISPLAY && ZEPHCORE_UI_BUTTONS
|
||||
help
|
||||
Hidden easter egg: Wolf3D-style raycaster on the OLED.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Packet Logging Only - Common to Repeater boards
|
||||
# Build: west build -b <board> zephcore --pristine -- \
|
||||
# -DCONFIG_ZEPHCORE_ROLE_REPEATER=y \
|
||||
# -DEXTRA_CONF_FILE="boards/common/packet_logging.conf"
|
||||
# -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/packet_logging.conf"
|
||||
#
|
||||
# Output: Clean packet logs only (RAW, RX, TX lines)
|
||||
# No debug spam from zephcore_lora, zephcore_dispatcher, etc.
|
||||
|
||||
@@ -166,11 +166,9 @@ CONFIG_BT_DIS_MANUF_NAME_STR="ZephCore"
|
||||
CONFIG_BT_DIS_MODEL_NUMBER=y
|
||||
CONFIG_BT_DIS_MODEL_NUMBER_STR="ZephCore"
|
||||
CONFIG_BT_DIS_FW_REV=y
|
||||
# Keep in sync with ZEPHCORE_FIRMWARE_VERSION in CMakeLists.txt (the single source
|
||||
# of truth for the C side, injected as -DFIRMWARE_VERSION). This Kconfig value can't
|
||||
# read a C macro, so it must be bumped here too. Phones that read DIS and phones
|
||||
# that query CMD_DEVICE_QUERY should see the same version.
|
||||
CONFIG_BT_DIS_FW_REV_STR="1.17.5c-zephcore"
|
||||
# CONFIG_BT_DIS_FW_REV_STR is generated by CMakeLists.txt from
|
||||
# ZEPHCORE_FIRMWARE_VERSION (build dir zephcore_version.conf), so phones that read
|
||||
# DIS and phones that query CMD_DEVICE_QUERY always see the same version.
|
||||
CONFIG_BT_DIS_SW_REV=y
|
||||
CONFIG_BT_DIS_SW_REV_STR="Zephyr"
|
||||
CONFIG_BT_DIS_PNP=n
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wifi_lora32_v3/esp32s3/procpu
|
||||
capabilities:
|
||||
light_sleep: true
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec v3"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wifi_lora32_v4/esp32s3/procpu
|
||||
capabilities:
|
||||
light_sleep: true
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec v4"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,11 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wifi_lora32_v43/esp32s3/procpu
|
||||
capabilities:
|
||||
light_sleep: true
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec v4.3"
|
||||
maker: heltec
|
||||
new: true
|
||||
img: heltec_v4.svg
|
||||
@@ -0,0 +1,14 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wifi_lora32_v4_r8/esp32s3/procpu
|
||||
# No light_sleep capability on purpose: the wiring qualifies (DIO1=GPIO14,
|
||||
# NSS=GPIO8, RESET=GPIO12, button on gpio0) but CONFIG_PM was never validated on
|
||||
# this board. Declare it once pm_esp32.conf's three checks pass on hardware.
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: MeshCore folds the R8 (ESP32-S3R8, 8 MB octal PSRAM) into its
|
||||
# "Heltec v4" tile as "[R8] ..." role rows rather than a device of its own, so it
|
||||
# folds here too, told apart by the subtitle.
|
||||
catalog:
|
||||
device: "Heltec v4"
|
||||
maker: heltec
|
||||
subtitle: R8
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wireless_tracker/esp32s3/procpu
|
||||
capabilities:
|
||||
light_sleep: true
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec Wireless Tracker"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_wireless_tracker_v2/esp32s3/procpu
|
||||
capabilities:
|
||||
light_sleep: true
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec Wireless Tracker v2"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,10 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: lilygo_t3s3/esp32s3/procpu
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: MeshCore splits this board by radio -- "(SX126x)" and "(SX127x)" are
|
||||
# two tiles sharing the "LilyGo T3 S3" group. We only build the SX1262 variant, so
|
||||
# fold into the SX126x tile; an SX127x port would be its own entry.
|
||||
catalog:
|
||||
device: "LilyGo T3 S3 (SX126x)"
|
||||
maker: lilygo
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: lilygo_tlora_c6/esp32c6/hpcore
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "LilyGo T-Lora C6"
|
||||
maker: lilygo
|
||||
new: true
|
||||
img: lilygo_tlora_c6.svg
|
||||
@@ -0,0 +1,13 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: meshnology_w12/esp32s3/procpu
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: MeshCore firmware has a meshnology_w12 variant but their flasher config
|
||||
# has no W12 device and no meshnology maker, so there is nothing to fold into yet.
|
||||
# The name follows their maker-plus-product convention, so it folds by itself if
|
||||
# they add one. WHEN IT APPEARS: drop `new` (and `own_img`, if one is set).
|
||||
catalog:
|
||||
device: "Meshnology W12"
|
||||
maker: meshnology
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: station_g2/esp32s3/procpu
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "UnitEng Station G2"
|
||||
maker: uniteng
|
||||
@@ -0,0 +1,6 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
# Pulled from the release matrix 2026-07-22: bring-up still in progress (keypad,
|
||||
# GPS, battery unverified on hardware). To publish, add:
|
||||
# release: {roles: [companion, repeater]}
|
||||
# catalog: {device: "Elecrow ThinkNode M9", maker: elecrow}
|
||||
target: thinknode_m9/esp32s3/procpu
|
||||
@@ -0,0 +1,3 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
# Not in the release matrix (bring-up / unvalidated).
|
||||
target: ttgo_lora32/esp32/procpu
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: ttgo_tbeam/esp32/procpu
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "LilyGo T-Beam (SX1262)"
|
||||
maker: lilygo
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_esp32c3
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao C3"
|
||||
maker: seeed
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_esp32c6/esp32c6/hpcore
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao ESP32-C6"
|
||||
maker: seeed
|
||||
new: true
|
||||
img: xiao_esp32c6.svg
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_esp32s3/esp32s3/procpu
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao S3 WIO"
|
||||
maker: seeed
|
||||
@@ -276,8 +276,11 @@ Steps:
|
||||
2. Copy board.conf and board.overlay from THIS directory
|
||||
3. Uncomment the sections matching your platform
|
||||
4. Fill in YOUR pin numbers and partition layout
|
||||
5. Add board detection to CMakeLists.txt (platform detection block, ~line 270):
|
||||
Add `BOARD MATCHES "your_board"` to the correct platform line
|
||||
5. Add a zephcore.yml manifest (copy one from a similar board; schema in
|
||||
zephcore/scripts/board_manifest.py). No CMake edit is needed: the
|
||||
boards/<platform>/ directory the board lives in selects its platform.
|
||||
Leave out `release:` until the board is validated on hardware, then run
|
||||
`python zephcore/scripts/board_manifest.py check`.
|
||||
6. Build and iterate!
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore native-Linux preset manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: native_sim
|
||||
linux:
|
||||
host: arm
|
||||
cross_compile: /usr/bin/arm-linux-gnueabihf-
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Femtofox (Luckfox Pico Mini)"
|
||||
maker: femtofox
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore native-Linux preset manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: native_sim/native/64
|
||||
linux:
|
||||
host: aarch64
|
||||
cross_compile: /usr/bin/aarch64-linux-gnu-
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "RAK6421 WisMesh (Raspberry Pi)"
|
||||
maker: rak
|
||||
new: true
|
||||
img: rpi.svg
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore native-Linux preset manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: native_sim/native/64
|
||||
linux:
|
||||
host: aarch64
|
||||
cross_compile: /usr/bin/aarch64-linux-gnu-
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "RAK6421 WisMesh (Raspberry Pi 5)"
|
||||
maker: rak
|
||||
new: true
|
||||
img: rpi.svg
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_mg24
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: no USB device peripheral, so no bootloader and no browser-flashable
|
||||
# path; the .hex is flashed with an external probe (the XIAO's USB-C is a debug
|
||||
# bridge). MeshCore has no MG24 support, hence a ZephCore-only tile.
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao MG24"
|
||||
maker: seeed
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: gat562_30s
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "GAT-IoT GAT562 30s"
|
||||
maker: gat-iot
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_t096
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec Mesh Node T096"
|
||||
maker: heltec
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_t1
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Heltec Mesh Node T1"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,11 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: heltec_t114
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
variants:
|
||||
- id: noscreen
|
||||
conf: no_display.conf
|
||||
subtitle: "No screen"
|
||||
catalog:
|
||||
device: "Heltec T114"
|
||||
maker: heltec
|
||||
@@ -0,0 +1,8 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: ikoka_nano_30dbm
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Ikoka Nano"
|
||||
maker: Ikoka
|
||||
subtitle: "30 dBm"
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: lilygo_techo
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "LilyGo T-Echo"
|
||||
maker: lilygo
|
||||
@@ -0,0 +1,9 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: lilygo_timpulse_plus
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "LilyGo T-Impulse Plus"
|
||||
maker: lilygo
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,14 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: meshtracker_x1
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: name pre-matched to MeshCore's flasher convention ("Seeed Studio" +
|
||||
# full product name), so this folds into their tile automatically the moment
|
||||
# they add one -- the merge is by exact name. Their flasher config has no X1 entry
|
||||
# yet even though MeshCore firmware supports it, hence new + our own photo.
|
||||
# WHEN IT APPEARS: drop `new` and `own_img` so their art and tile win.
|
||||
catalog:
|
||||
device: "Seeed Studio SenseCAP MeshTracker X1"
|
||||
maker: seeed
|
||||
new: true
|
||||
own_img: meshtracker_x1.jpg
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: muziworks_r1neo
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "muzi works R1 Neo"
|
||||
maker: muziworks
|
||||
@@ -0,0 +1,3 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
# Not in the release matrix (bring-up / unvalidated).
|
||||
target: promicro_lr2021
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: promicro_sx1262
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "ProMicro nrf52 (faketec)"
|
||||
maker: promicro
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: rak3401_1watt
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "RAK WisMesh 1W Booster (3401 + 13302)"
|
||||
maker: rak
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: rak4631
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "RAK WisBlock / WisMesh (RAK 4631)"
|
||||
maker: rak
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: rak_wismesh_tag
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "RAK WisMesh Tag"
|
||||
maker: rak
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: sensecap_solar
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio SenseCAP Solar"
|
||||
maker: seeed
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: t1000_e
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio SenseCAP T1000-E"
|
||||
maker: seeed
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: thinknode_m1
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Elecrow ThinkNode M1"
|
||||
maker: elecrow
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: thinknode_m3
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Elecrow ThinkNode M3"
|
||||
maker: elecrow
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: thinknode_m6
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Elecrow ThinkNode M6"
|
||||
maker: elecrow
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: wio_tracker_l1
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio Wio Tracker L1 Pro"
|
||||
maker: seeed
|
||||
@@ -0,0 +1,4 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: wio_tracker_l1_1w
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
@@ -0,0 +1,7 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_nrf52840
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao nRF52 WIO"
|
||||
maker: seeed
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: me25ls02/nrf54l15/cpuapp
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: the nRF54L15 has no USB peripheral, so there is no bootloader and no
|
||||
# WebUSB/DFU path -- the Type-C port on the carrier is a CH340x UART bridge. The
|
||||
# tile publishes the .hex as a plain download, flashed over SWD.
|
||||
catalog:
|
||||
device: "MinewSemi ME25LS02"
|
||||
maker: minewsemi
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,14 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: seeed_lr2021_evk/nrf54l15/cpuapp
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: same XIAO nRF54L15 host and CMSIS-DAP story as xiao_nrf54l15, but a
|
||||
# different kit (Semtech LoRa Plus expansion board + Wio-LR2021 module), so a
|
||||
# separate tile: the radio and the whole D0..D10 wiring differ. Maker is seeed:
|
||||
# there is no Semtech entry in MAKERS, and before the manifests this device had no
|
||||
# maker at all, which made the catalog generator fail once its firmware was built.
|
||||
catalog:
|
||||
device: "Semtech LR2021 LoRa Plus EVK"
|
||||
maker: seeed
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: xiao_nrf54l15/nrf54l15/cpuapp
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: no USB bootloader (nRF54L15); the XIAO carries a SAMD11 CMSIS-DAP
|
||||
# bridge, so its own USB cable is enough (openocd/pyocd) -- still not a browser
|
||||
# flash flow. Download-only tile.
|
||||
catalog:
|
||||
device: "Seeed Studio Xiao nRF54L15"
|
||||
maker: seeed
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,12 @@
|
||||
# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py
|
||||
target: lora_e5_mini
|
||||
release:
|
||||
roles: [companion, repeater]
|
||||
# Catalog: no Bluetooth on this SoC -- the companion speaks MeshCore serial
|
||||
# framing over USART1, bridged to USB-C by the onboard USB-UART chip, so it is
|
||||
# offered as companionUsb rather than companionBle. SWD-flashed, download-only.
|
||||
catalog:
|
||||
device: "Seeed Studio LoRa-E5 mini"
|
||||
maker: seeed
|
||||
new: true
|
||||
img: lora.svg
|
||||
@@ -0,0 +1,91 @@
|
||||
# Resolve the ZephCore board directory, the platform, and the build class.
|
||||
#
|
||||
# Shared by zephcore/CMakeLists.txt and zephcore/sysbuild/CMakeLists.txt so the
|
||||
# app and the MCUboot image always agree on which board directory they read.
|
||||
# Runs before find_package(Zephyr): nothing here may depend on Kconfig or DT.
|
||||
#
|
||||
# Inputs: BOARD name[@revision][/soc[/cpucluster]]
|
||||
# ZEPHCORE_SOURCE_DIR the application source dir
|
||||
# Outputs: ZEPHCORE_BOARD_BASE board name without revision or qualifiers
|
||||
# ZEPHCORE_BOARD_DIR boards/<platform-dir>/<board>, or "" if none
|
||||
# ZEPHCORE_PLATFORM nrf52 | nrf54l | esp32 | mg24 | stm32wl | linux | ""
|
||||
# ZEPHCORE_BOARD_LIGHT_SLEEP TRUE if the board manifest declares it
|
||||
# zephcore_board_file(<out-var> <file-name>)
|
||||
# absolute path of <file-name> inside the board dir, or "".
|
||||
|
||||
string(REGEX REPLACE "[@/].*$" "" ZEPHCORE_BOARD_BASE "${BOARD}")
|
||||
|
||||
# ---- Board directory -------------------------------------------------------
|
||||
# boards/<platform-dir>/<board>/. Exactly one may exist: two would make every
|
||||
# per-board file (conf, overlay, partitions, battery curve) ambiguous, and the
|
||||
# old "take the first glob match" behaviour picked one silently.
|
||||
set(ZEPHCORE_BOARD_DIR "")
|
||||
if(ZEPHCORE_BOARD_BASE)
|
||||
file(GLOB _zb_candidates LIST_DIRECTORIES true
|
||||
"${ZEPHCORE_SOURCE_DIR}/boards/*/${ZEPHCORE_BOARD_BASE}")
|
||||
set(_zb_dirs "")
|
||||
foreach(_c IN LISTS _zb_candidates)
|
||||
if(IS_DIRECTORY "${_c}")
|
||||
list(APPEND _zb_dirs "${_c}")
|
||||
endif()
|
||||
endforeach()
|
||||
list(LENGTH _zb_dirs _zb_count)
|
||||
if(_zb_count GREATER 1)
|
||||
message(FATAL_ERROR
|
||||
"ZephCore: board '${ZEPHCORE_BOARD_BASE}' has ${_zb_count} directories:\n"
|
||||
" ${_zb_dirs}\nKeep exactly one under boards/<platform>/.")
|
||||
elseif(_zb_count EQUAL 1)
|
||||
set(ZEPHCORE_BOARD_DIR "${_zb_dirs}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(zephcore_board_file OUT NAME)
|
||||
if(ZEPHCORE_BOARD_DIR AND EXISTS "${ZEPHCORE_BOARD_DIR}/${NAME}")
|
||||
set(${OUT} "${ZEPHCORE_BOARD_DIR}/${NAME}" PARENT_SCOPE)
|
||||
else()
|
||||
set(${OUT} "" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# ---- Platform --------------------------------------------------------------
|
||||
# The board's directory is its platform. Boards without a ZephCore directory
|
||||
# (a plain upstream board built for experiments) fall back to the SoC named in
|
||||
# the board qualifier; native_sim is the Linux port, whose presets are
|
||||
# boards/linux_native/<preset>.conf rather than board directories.
|
||||
set(ZEPHCORE_PLATFORM "")
|
||||
if(ZEPHCORE_BOARD_DIR)
|
||||
get_filename_component(_zb_parent "${ZEPHCORE_BOARD_DIR}" DIRECTORY)
|
||||
get_filename_component(_zb_platdir "${_zb_parent}" NAME)
|
||||
if(_zb_platdir STREQUAL "nrf52840")
|
||||
set(ZEPHCORE_PLATFORM nrf52)
|
||||
elseif(_zb_platdir MATCHES "^(nrf54l|esp32|mg24|stm32wl)$")
|
||||
set(ZEPHCORE_PLATFORM "${_zb_platdir}")
|
||||
else()
|
||||
message(FATAL_ERROR "ZephCore: unknown platform directory boards/${_zb_platdir}/")
|
||||
endif()
|
||||
elseif(BOARD MATCHES "nrf52")
|
||||
set(ZEPHCORE_PLATFORM nrf52)
|
||||
elseif(BOARD MATCHES "esp32")
|
||||
set(ZEPHCORE_PLATFORM esp32)
|
||||
elseif(BOARD MATCHES "nrf54l")
|
||||
set(ZEPHCORE_PLATFORM nrf54l)
|
||||
elseif(BOARD MATCHES "mg24|efr32")
|
||||
set(ZEPHCORE_PLATFORM mg24)
|
||||
elseif(BOARD MATCHES "stm32wl|lora_e5")
|
||||
set(ZEPHCORE_PLATFORM stm32wl)
|
||||
elseif(BOARD MATCHES "^native_(sim|posix)")
|
||||
set(ZEPHCORE_PLATFORM linux)
|
||||
endif()
|
||||
|
||||
# ---- Capabilities from the board manifest ----------------------------------
|
||||
# zephcore.yml (schema: scripts/board_manifest.py). Read by line match because
|
||||
# this runs before Zephyr has set up Python; `board_manifest.py check` enforces
|
||||
# the exact two-space-indented ` <capability>: true|false` form this relies on.
|
||||
set(ZEPHCORE_BOARD_LIGHT_SLEEP FALSE)
|
||||
zephcore_board_file(_zb_manifest zephcore.yml)
|
||||
if(_zb_manifest)
|
||||
file(STRINGS "${_zb_manifest}" _zb_ls REGEX "^ light_sleep: true[ \t]*$")
|
||||
if(_zb_ls)
|
||||
set(ZEPHCORE_BOARD_LIGHT_SLEEP TRUE)
|
||||
endif()
|
||||
endif()
|
||||
@@ -34,4 +34,31 @@ if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/zephyr-new)
|
||||
${ZEPHCORE_ZEPHYR_DIR}/${REL_PATH} COPYONLY)
|
||||
message(STATUS " [zephyr-new] ${REL_PATH}")
|
||||
endforeach()
|
||||
|
||||
# Remove copies whose source was deleted from patches/zephyr-new. The list
|
||||
# of what we copied last time lives next to the patch stamps. A file is only
|
||||
# removed while git reports it untracked in the Zephyr tree: once upstream
|
||||
# ships a file at the same path (LR11xx is heading that way), it is theirs
|
||||
# and must be left alone.
|
||||
set(_zn_manifest "${ZEPHCORE_ZEPHYR_DIR}/.zephcore_new_files.list")
|
||||
if(EXISTS "${_zn_manifest}")
|
||||
file(STRINGS "${_zn_manifest}" _zn_previous)
|
||||
foreach(REL_PATH IN LISTS _zn_previous)
|
||||
if(REL_PATH IN_LIST ZEPHCORE_NEW_FILES
|
||||
OR NOT EXISTS "${ZEPHCORE_ZEPHYR_DIR}/${REL_PATH}")
|
||||
continue()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND git ls-files --error-unmatch -- "${REL_PATH}"
|
||||
WORKING_DIRECTORY "${ZEPHCORE_ZEPHYR_DIR}"
|
||||
RESULT_VARIABLE _zn_tracked
|
||||
OUTPUT_QUIET ERROR_QUIET)
|
||||
if(NOT _zn_tracked EQUAL 0)
|
||||
file(REMOVE "${ZEPHCORE_ZEPHYR_DIR}/${REL_PATH}")
|
||||
message(STATUS " [zephyr-new] removed stale ${REL_PATH}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
string(REPLACE ";" "\n" _zn_list "${ZEPHCORE_NEW_FILES}")
|
||||
file(WRITE "${_zn_manifest}" "${_zn_list}\n")
|
||||
endif()
|
||||
|
||||
@@ -174,7 +174,12 @@ function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL)
|
||||
list(APPEND PATCH_PATHS "${_path}")
|
||||
endforeach()
|
||||
if(PATCH_PATHS)
|
||||
message(STATUS " [${LABEL}] Resetting stale files for: ${PATCH_NAME}")
|
||||
# This discards any uncommitted edits in these files of the
|
||||
# target tree (e.g. an in-progress driver change not yet
|
||||
# regenerated into the patch) -- say so loudly.
|
||||
message(WARNING " [${LABEL}] ${PATCH_NAME} does not apply; "
|
||||
"resetting these files in ${TARGET_DIR} to HEAD, discarding "
|
||||
"any local edits in them: ${PATCH_PATHS}")
|
||||
execute_process(
|
||||
COMMAND git checkout -- ${PATCH_PATHS}
|
||||
WORKING_DIRECTORY "${TARGET_DIR}"
|
||||
@@ -233,13 +238,3 @@ if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/modules/loramac-node)
|
||||
"loramac-node"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Apply patches to hal_espressif module (ESP32 BLE controller glue)
|
||||
if(EXISTS ${ZEPHCORE_SOURCE_DIR}/patches/modules/hal-espressif)
|
||||
message(STATUS "Applying ZephCore patches to hal_espressif...")
|
||||
zephcore_apply_patches(
|
||||
"${ZEPHCORE_SOURCE_DIR}/patches/modules/hal-espressif"
|
||||
"${ZEPHCORE_MODULES_DIR}/hal/espressif"
|
||||
"hal-espressif"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""ZephCore board manifests: the one list of boards and what we publish for them.
|
||||
|
||||
Every board directory boards/<platform>/<board>/ carries a zephcore.yml; each
|
||||
native-Linux preset boards/linux_native/<preset>.conf carries a sibling
|
||||
<preset>.zephcore.yml. Schema (all keys optional unless marked):
|
||||
|
||||
target: rak4631 # REQUIRED west -b string, with qualifiers
|
||||
capabilities:
|
||||
light_sleep: true # DIO1 on an RTC-wake-capable GPIO AND
|
||||
# validated on hardware (ESP32 repeaters)
|
||||
release: # absent = not published (bring-up)
|
||||
roles: [companion, repeater] # REQUIRED in release
|
||||
variants: # extra published builds of this board
|
||||
- id: noscreen # artifact infix
|
||||
conf: no_display.conf # relative to the board dir
|
||||
subtitle: No screen # catalog role-row subtitle
|
||||
catalog: # absent = published but not in the catalog
|
||||
device: RAK WisBlock / WisMesh (RAK 4631) # REQUIRED in catalog
|
||||
maker: rak # REQUIRED in catalog, key of MAKERS
|
||||
new: true # own tile instead of folding into MeshCore's
|
||||
img: lora.svg | own_img: x.jpg
|
||||
subtitle: 30 dBm
|
||||
linux: # native-Linux presets only
|
||||
host: arm | aarch64
|
||||
cross_compile: /usr/bin/arm-linux-gnueabihf-
|
||||
|
||||
Readers:
|
||||
- zephcore/CMakeLists.txt reads capabilities.light_sleep with a line match
|
||||
(it runs before Zephyr has set up Python), so `check` enforces the literal
|
||||
two-space-indented ` light_sleep: true|false` form.
|
||||
- build.sh: board_manifest.py matrix <nrf|nrf54l|mg24|stm32wl|esp32|linux> [companions|repeaters]
|
||||
- gen_provider_catalog.py imports load_boards().
|
||||
- docs: board_manifest.py docs (rewrites the generated lists in docs/supported_boards.md)
|
||||
|
||||
`board_manifest.py check` validates every manifest; CI should run it.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
APP_DIR = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
BOARDS_DIR = os.path.join(APP_DIR, "boards")
|
||||
|
||||
PLATFORM_DIRS = {"nrf52840": "nrf52", "nrf54l": "nrf54l", "esp32": "esp32",
|
||||
"mg24": "mg24", "stm32wl": "stm32wl"}
|
||||
ROLES = ("companion", "repeater", "room_server", "observer")
|
||||
KEYS = {"target", "capabilities", "release", "catalog", "linux"}
|
||||
CAPS = {"light_sleep"}
|
||||
CATALOG_KEYS = {"device", "maker", "new", "img", "own_img", "subtitle"}
|
||||
CAP_LINE = re.compile(r"^ (\w+): (true|false)\s*$", re.M)
|
||||
|
||||
|
||||
class Board:
|
||||
def __init__(self, name, platform, manifest_path, data, board_dir=None, conf=None):
|
||||
self.name = name # board dir name or linux preset name
|
||||
self.platform = platform # nrf52 | nrf54l | esp32 | mg24 | stm32wl | linux
|
||||
self.manifest_path = manifest_path
|
||||
self.board_dir = board_dir # None for linux presets
|
||||
self.conf = conf # linux preset conf, relative to APP_DIR
|
||||
self.target = data.get("target")
|
||||
self.capabilities = data.get("capabilities") or {}
|
||||
self.release = data.get("release")
|
||||
self.catalog = data.get("catalog")
|
||||
self.linux = data.get("linux") or {}
|
||||
|
||||
@property
|
||||
def chip(self):
|
||||
"""ESP32 chip name from the target (esp32, esp32s3, esp32c3, esp32c6)."""
|
||||
m = re.search(r"(esp32[^/]*)", self.target or "")
|
||||
return m.group(1) if m else None
|
||||
|
||||
@property
|
||||
def stem(self):
|
||||
"""Artifact filename stem, exactly as build.sh has always named files."""
|
||||
if self.platform == "linux":
|
||||
return f"zephcore_linux_{self.name}"
|
||||
return self.target.replace("/", "-")
|
||||
|
||||
@property
|
||||
def sysbuild(self):
|
||||
"""--sysbuild / --no-sysbuild / None (west default) for release builds."""
|
||||
if self.platform == "esp32":
|
||||
return True if self.chip != "esp32" else None # classic ESP32: simple boot
|
||||
if self.platform == "nrf54l":
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def _load(path):
|
||||
with open(path, encoding="utf-8") as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
|
||||
|
||||
def load_boards():
|
||||
boards = []
|
||||
for plat_dir, platform in PLATFORM_DIRS.items():
|
||||
root = os.path.join(BOARDS_DIR, plat_dir)
|
||||
if not os.path.isdir(root):
|
||||
continue
|
||||
for name in sorted(os.listdir(root)):
|
||||
bdir = os.path.join(root, name)
|
||||
path = os.path.join(bdir, "zephcore.yml")
|
||||
if os.path.isfile(path):
|
||||
boards.append(Board(name, platform, path, _load(path), board_dir=bdir))
|
||||
lroot = os.path.join(BOARDS_DIR, "linux_native")
|
||||
if os.path.isdir(lroot):
|
||||
for fn in sorted(os.listdir(lroot)):
|
||||
if fn.endswith(".zephcore.yml"):
|
||||
name = fn[: -len(".zephcore.yml")]
|
||||
boards.append(Board(name, "linux", os.path.join(lroot, fn),
|
||||
_load(os.path.join(lroot, fn)),
|
||||
conf=f"boards/linux_native/{name}.conf"))
|
||||
return boards
|
||||
|
||||
|
||||
def check(boards, makers=None):
|
||||
errors = []
|
||||
for b in boards:
|
||||
where = os.path.relpath(b.manifest_path, APP_DIR)
|
||||
data = _load(b.manifest_path)
|
||||
for k in set(data) - KEYS:
|
||||
errors.append(f"{where}: unknown key '{k}'")
|
||||
if not b.target:
|
||||
errors.append(f"{where}: missing target")
|
||||
elif b.platform != "linux" and not re.match(rf"^{re.escape(b.name)}(@|/|$)", b.target):
|
||||
errors.append(f"{where}: target '{b.target}' does not name board '{b.name}'")
|
||||
for k, v in b.capabilities.items():
|
||||
if k not in CAPS:
|
||||
errors.append(f"{where}: unknown capability '{k}'")
|
||||
if not isinstance(v, bool):
|
||||
errors.append(f"{where}: capability '{k}' must be true/false")
|
||||
if b.capabilities:
|
||||
text = open(b.manifest_path, encoding="utf-8").read()
|
||||
block = re.search(r"^capabilities:\n((?: .*\n?)*)", text, re.M)
|
||||
found = {m.group(1) for m in CAP_LINE.finditer(block.group(1))} if block else set()
|
||||
if found != set(b.capabilities):
|
||||
errors.append(f"{where}: write capabilities as two-space-indented "
|
||||
f"' <name>: true|false' lines (CMake reads them by line)")
|
||||
if b.release is not None:
|
||||
roles = b.release.get("roles") or []
|
||||
if not roles or any(r not in ROLES for r in roles):
|
||||
errors.append(f"{where}: release.roles must be a non-empty subset of {ROLES}")
|
||||
for v in b.release.get("variants") or []:
|
||||
if not v.get("id") or not v.get("conf"):
|
||||
errors.append(f"{where}: variant needs id and conf")
|
||||
elif b.board_dir and not os.path.isfile(os.path.join(b.board_dir, v["conf"])):
|
||||
errors.append(f"{where}: variant conf '{v['conf']}' not found")
|
||||
if b.catalog is not None:
|
||||
for k in set(b.catalog) - CATALOG_KEYS:
|
||||
errors.append(f"{where}: unknown catalog key '{k}'")
|
||||
if not b.catalog.get("device") or not b.catalog.get("maker"):
|
||||
errors.append(f"{where}: catalog needs device and maker")
|
||||
if makers is not None and b.catalog.get("maker") not in makers:
|
||||
errors.append(f"{where}: catalog maker '{b.catalog.get('maker')}' not in MAKERS")
|
||||
if b.release is None:
|
||||
errors.append(f"{where}: catalog entry without a release")
|
||||
if b.platform == "linux" and not {"host", "cross_compile"} <= set(b.linux):
|
||||
errors.append(f"{where}: linux presets need linux.host and linux.cross_compile")
|
||||
return errors
|
||||
|
||||
|
||||
def matrix(boards, group, role_filter=None):
|
||||
"""Release builds for one build.sh group: target|role|variant|confs|stem|sysbuild|host|cross."""
|
||||
want = {"nrf": "nrf52"}.get(group, group)
|
||||
if role_filter:
|
||||
role_filter = {"companions": "companion", "repeaters": "repeater"}[role_filter]
|
||||
rows = []
|
||||
for b in boards:
|
||||
if b.platform != want or b.release is None:
|
||||
continue
|
||||
sb = {True: "sysbuild", False: "no-sysbuild", None: ""}[b.sysbuild]
|
||||
base_confs = [b.conf] if b.conf else []
|
||||
builds = [("", base_confs)]
|
||||
for v in b.release.get("variants") or []:
|
||||
rel = os.path.relpath(os.path.join(b.board_dir, v["conf"]), APP_DIR).replace(os.sep, "/")
|
||||
builds.append((v["id"], base_confs + [rel]))
|
||||
for role in b.release["roles"]:
|
||||
if role_filter and role != role_filter:
|
||||
continue
|
||||
for variant, confs in builds:
|
||||
c = list(confs)
|
||||
if role != "companion":
|
||||
# Role conf before the variant conf, as build.sh always ordered it.
|
||||
c = ([c[0]] if b.conf else []) + [f"boards/common/{role}.conf"] + c[1 if b.conf else 0:]
|
||||
rows.append("|".join([b.target, role, variant, ";".join(c), b.stem, sb,
|
||||
b.linux.get("host", ""), b.linux.get("cross_compile", "")]))
|
||||
return rows
|
||||
|
||||
|
||||
DOCS_GROUPS = [("nrf52", "nRF52840"), ("esp32", "ESP32"), ("stm32wl", "STM32WL"),
|
||||
("mg24", "MG24"), ("nrf54l", "nRF54L")]
|
||||
|
||||
|
||||
def docs(boards, path):
|
||||
"""Rewrite each '<!-- boards:<platform> -->' ... '<!-- /boards -->' block."""
|
||||
text = open(path, encoding="utf-8").read()
|
||||
for platform, _ in DOCS_GROUPS:
|
||||
group = [b for b in boards if b.platform == platform]
|
||||
width = max((len(b.target) for b in group), default=0)
|
||||
lines = [b.target if b.release is not None
|
||||
else f"{b.target:<{width}} # source-only, no published firmware"
|
||||
for b in group]
|
||||
block = "<!-- boards:%s -->\n```\n%s\n```\n<!-- /boards -->" % (platform, "\n".join(lines))
|
||||
text = re.sub(r"<!-- boards:%s -->.*?<!-- /boards -->" % platform, lambda _m: block,
|
||||
text, flags=re.S)
|
||||
open(path, "w", encoding="utf-8", newline="\n").write(text)
|
||||
|
||||
|
||||
def main(argv):
|
||||
boards = load_boards()
|
||||
cmd = argv[1] if len(argv) > 1 else "check"
|
||||
if cmd == "check":
|
||||
errs = check(boards)
|
||||
for e in errs:
|
||||
print(e, file=sys.stderr)
|
||||
print(f"{len(boards)} manifests, {len(errs)} errors")
|
||||
return 1 if errs else 0
|
||||
if cmd == "matrix":
|
||||
for row in matrix(boards, argv[2], argv[3] if len(argv) > 3 else None):
|
||||
print(row)
|
||||
return 0
|
||||
if cmd == "docs":
|
||||
docs(boards, argv[2] if len(argv) > 2 else os.path.join(APP_DIR, "..", "docs", "supported_boards.md"))
|
||||
return 0
|
||||
print(__doc__)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""Print the flash size of a built image's SoC flash node as '<N>MB'.
|
||||
|
||||
Used by build.sh to pass esptool merge-bin a --flash-size that matches the
|
||||
board, read from the final devicetree rather than duplicated per board.
|
||||
|
||||
dts_flash_size.py [path/to/zephyr.dts] (default: build/zephcore/zephyr/zephyr.dts)
|
||||
"""
|
||||
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")
|
||||
@@ -1,7 +1,8 @@
|
||||
# ZephCore Sysbuild Configuration
|
||||
# Enables MCUboot bootloader for ESP32 OTA firmware updates.
|
||||
# Only used when building with --sysbuild flag (ESP32 repeater builds).
|
||||
# nRF52/nRF54L/MG24 builds do NOT use --sysbuild and are unaffected.
|
||||
# Enables the MCUboot bootloader whenever --sysbuild is passed. Used by the
|
||||
# ESP32-S3/C-series release builds (companion and repeater: MCUboot @ 0x0, app
|
||||
# @ 0x10000) and by any build with wifi_ota.conf. Classic ESP32, nRF52, nRF54L,
|
||||
# MG24 and STM32WL builds do not use --sysbuild.
|
||||
|
||||
SB_CONFIG_BOOTLOADER_MCUBOOT=y
|
||||
SB_CONFIG_MCUBOOT_MODE_OVERWRITE_ONLY=y
|
||||
|
||||
@@ -31,18 +31,12 @@ include("${APP_DIR}/cmake/zephyr_new_files.cmake")
|
||||
#
|
||||
# Static conf files (mcuboot_flashsize_*.conf) are used instead of generating
|
||||
# a temp file to avoid Windows line-ending issues with cmake file(WRITE).
|
||||
string(REPLACE "/" ";" _board_parts "${BOARD}")
|
||||
list(GET _board_parts 0 _board_base)
|
||||
|
||||
file(GLOB_RECURSE _board_conf_candidates
|
||||
"${APP_DIR}/boards/*/${_board_base}/board.conf")
|
||||
if(NOT _board_conf_candidates AND
|
||||
EXISTS "${APP_DIR}/boards/${_board_base}/board.conf")
|
||||
set(_board_conf_candidates "${APP_DIR}/boards/${_board_base}/board.conf")
|
||||
endif()
|
||||
|
||||
if(_board_conf_candidates)
|
||||
list(GET _board_conf_candidates 0 _board_conf)
|
||||
#
|
||||
# The board directory comes from the same resolver the app uses, so both images
|
||||
# always read the same board.
|
||||
include("${APP_DIR}/cmake/zephcore_board.cmake")
|
||||
zephcore_board_file(_board_conf board.conf)
|
||||
if(_board_conf)
|
||||
file(READ "${_board_conf}" _board_conf_content)
|
||||
|
||||
set(_flashsize_fragment "")
|
||||
@@ -72,14 +66,8 @@ endif()
|
||||
# uploads report success and then silently boot the old image on reset.
|
||||
#
|
||||
# Feeding the same partitions.overlay to MCUboot keeps both flash maps identical.
|
||||
file(GLOB_RECURSE _board_partitions
|
||||
"${APP_DIR}/boards/*/${_board_base}/partitions.overlay")
|
||||
if(NOT _board_partitions AND
|
||||
EXISTS "${APP_DIR}/boards/${_board_base}/partitions.overlay")
|
||||
set(_board_partitions "${APP_DIR}/boards/${_board_base}/partitions.overlay")
|
||||
endif()
|
||||
if(_board_partitions)
|
||||
list(GET _board_partitions 0 _board_partitions_file)
|
||||
zephcore_board_file(_board_partitions_file partitions.overlay)
|
||||
if(_board_partitions_file)
|
||||
set(mcuboot_EXTRA_DTC_OVERLAY_FILE "${_board_partitions_file}"
|
||||
CACHE INTERNAL "MCUboot partitions overlay: shared with the app image")
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user