mirror of
https://github.com/vk496/MeshCore.git
synced 2026-09-12 06:55:47 +00:00
93 lines
4.0 KiB
Python
93 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Package a full + same-image-delta .mota for ONE built firmware (dev rolling release).
|
|
|
|
Extracts the OTA image (BODY||EndF) from a PlatformIO build and runs `mota build` twice:
|
|
- full .mota (codec full) — the flashable image, universally applicable
|
|
- delta .mota (same-image) — base == target, so the patch is tiny (a format/transport demo).
|
|
ESP32 -> sequential+crle ; nRF52 -> in-place (RAK4631 flash layout).
|
|
|
|
Image source per platform:
|
|
ESP32 : <build_dir>/firmware.bin (pio_endf has already appended EndF)
|
|
nRF52 : app region of <build_dir>/firmware.hex, extracted via intelhex (EndF already appended to the .hex)
|
|
|
|
Usage:
|
|
dev_motas.py --env RAK_4631_repeater --platform NRF52 --build-dir .pio/build/RAK_4631_repeater \
|
|
--target-env RAK_4631_repeater --out-prefix out/RAK_4631_repeater-dev-abc1234
|
|
Writes <out-prefix>.full.mota and (best-effort) <out-prefix>.delta.mota.
|
|
"""
|
|
import argparse, os, subprocess, sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
MOTA = os.path.join(HERE, "mota.py")
|
|
|
|
# nRF52 (RAK4631) flash layout — keep in sync with src/helpers/ota/OtaFlashLayout_nrf52.h
|
|
NRF52_APP_BASE = 0x26000
|
|
NRF52_FS_START = 0xD4000
|
|
NRF52_INPLACE_MEMORY = NRF52_FS_START - NRF52_APP_BASE # 0xAE000 working size for in-place apply
|
|
NRF52_INPLACE_SEGMENT = 4096
|
|
|
|
|
|
def extract_image(platform, build_dir, work):
|
|
"""Return the path to the OTA image (BODY||EndF) as a raw .bin, or None if not found."""
|
|
if platform == "ESP32":
|
|
bin_path = os.path.join(build_dir, "firmware.bin")
|
|
return bin_path if os.path.isfile(bin_path) else None
|
|
if platform == "NRF52":
|
|
hex_path = os.path.join(build_dir, "firmware.hex")
|
|
if not os.path.isfile(hex_path):
|
|
return None
|
|
from intelhex import IntelHex
|
|
ih = IntelHex(hex_path)
|
|
# the app .hex is a single contiguous segment [APP_BASE .. app_end]; take it verbatim
|
|
start = ih.minaddr()
|
|
data = ih.tobinarray(start=start, end=ih.maxaddr())
|
|
out = os.path.join(work, "app_image.bin")
|
|
with open(out, "wb") as f:
|
|
f.write(bytes(data))
|
|
return out
|
|
return None
|
|
|
|
|
|
def run_mota(args):
|
|
print(" +", "mota.py", " ".join(args))
|
|
subprocess.run([sys.executable, MOTA, "build", *args], check=True)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--env", required=True)
|
|
ap.add_argument("--platform", required=True, choices=["ESP32", "NRF52"])
|
|
ap.add_argument("--build-dir", required=True)
|
|
ap.add_argument("--target-env", required=True)
|
|
ap.add_argument("--out-prefix", required=True)
|
|
ap.add_argument("--work", default=".")
|
|
a = ap.parse_args()
|
|
|
|
img = extract_image(a.platform, a.build_dir, a.work)
|
|
if not img:
|
|
print(f"::warning::no OTA image for {a.env} ({a.platform}) — skipping .mota")
|
|
return 0
|
|
print(f"OTA image for {a.env}: {img} ({os.path.getsize(img)} bytes)")
|
|
|
|
# full .mota (always)
|
|
run_mota(["--fw", img, "--target-env", a.target_env, "--fw-version", "0.0.0",
|
|
"--codec", "full", "--out", a.out_prefix + ".full.mota"])
|
|
|
|
# same-image delta .mota (best-effort; codec per platform's applier)
|
|
try:
|
|
if a.platform == "ESP32":
|
|
run_mota(["--fw", img, "--base", img, "--target-env", a.target_env, "--fw-version", "0.0.1",
|
|
"--codec", "sequential", "--compression", "crle", "--out", a.out_prefix + ".delta.mota"])
|
|
else: # NRF52 in-place
|
|
run_mota(["--fw", img, "--base", img, "--target-env", a.target_env, "--fw-version", "0.0.1",
|
|
"--codec", "inplace", "--compression", "crle",
|
|
"--inplace-memory", str(NRF52_INPLACE_MEMORY), "--inplace-segment", str(NRF52_INPLACE_SEGMENT),
|
|
"--out", a.out_prefix + ".delta.mota"])
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"::warning::delta .mota for {a.env} failed ({e}); full .mota still produced")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|