mirror of
https://github.com/ratspeak/ratdeck.git
synced 2026-10-22 17:31:14 +00:00
rsdeck: dual-boot launcher, RNode mode, build pipeline
This commit is contained in:
1 parent
16dedbfa3e
commit
a2dee44b07
112 files changed
+20450
-3
No files matched your search
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check that launcher, Standalone, and RNode app images fit their OTA slots."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def parse_int(value: str) -> int:
|
||||
return int(value, 0)
|
||||
|
||||
|
||||
def image_size(path: Path) -> int:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(path)
|
||||
return path.stat().st_size
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--launcher", required=True, type=Path)
|
||||
parser.add_argument("--launcher-slot-size", default="0x100000", type=parse_int)
|
||||
parser.add_argument("--standalone", required=True, type=Path)
|
||||
parser.add_argument("--standalone-slot-size", default="0x400000", type=parse_int)
|
||||
parser.add_argument("--rnode", required=True, type=Path)
|
||||
parser.add_argument("--rnode-slot-size", default="0x300000", type=parse_int)
|
||||
args = parser.parse_args()
|
||||
|
||||
failed = False
|
||||
checks = (
|
||||
("Launcher", args.launcher, args.launcher_slot_size),
|
||||
("Standalone", args.standalone, args.standalone_slot_size),
|
||||
("RNode", args.rnode, args.rnode_slot_size),
|
||||
)
|
||||
|
||||
for name, path, slot_size in checks:
|
||||
size = image_size(path)
|
||||
margin = slot_size - size
|
||||
print(f"{name}: {size} bytes, slot {slot_size} bytes, margin {margin} bytes")
|
||||
if margin < 0:
|
||||
failed = True
|
||||
|
||||
if failed:
|
||||
print("error: at least one image is too large for its OTA slot")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a merged rsDeck image with launcher, Standalone, and RNode."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LAUNCHER_OFFSET = "0x10000"
|
||||
STANDALONE_OFFSET = "0x110000"
|
||||
RNODE_OFFSET = "0x510000"
|
||||
|
||||
|
||||
def parse_int(value: str) -> int:
|
||||
return int(value, 0)
|
||||
|
||||
|
||||
def require_file(path: Path, label: str) -> None:
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"{label} not found: {path}")
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"{label} is not a file: {path}")
|
||||
|
||||
|
||||
def check_slot(path: Path, label: str, slot_size: int) -> None:
|
||||
size = path.stat().st_size
|
||||
if size > slot_size:
|
||||
raise ValueError(f"{label} image is {size} bytes, exceeds slot size {slot_size}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--bootloader", required=True, type=Path)
|
||||
parser.add_argument("--partitions", required=True, type=Path)
|
||||
parser.add_argument("--boot-app0", required=True, type=Path)
|
||||
parser.add_argument("--launcher", required=True, type=Path)
|
||||
parser.add_argument("--standalone", required=True, type=Path)
|
||||
parser.add_argument("--rnode", required=True, type=Path)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
parser.add_argument("--launcher-slot-size", default="0x100000", type=parse_int)
|
||||
parser.add_argument("--standalone-slot-size", default="0x400000", type=parse_int)
|
||||
parser.add_argument("--rnode-slot-size", default="0x300000", type=parse_int)
|
||||
args = parser.parse_args()
|
||||
|
||||
for label, path in (
|
||||
("bootloader", args.bootloader),
|
||||
("partition table", args.partitions),
|
||||
("boot_app0", args.boot_app0),
|
||||
("launcher app", args.launcher),
|
||||
("Standalone app", args.standalone),
|
||||
("RNode app", args.rnode),
|
||||
):
|
||||
require_file(path, label)
|
||||
|
||||
check_slot(args.launcher, "Launcher", args.launcher_slot_size)
|
||||
check_slot(args.standalone, "Standalone", args.standalone_slot_size)
|
||||
check_slot(args.rnode, "RNode", args.rnode_slot_size)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"esptool",
|
||||
"--chip",
|
||||
"esp32s3",
|
||||
"merge-bin",
|
||||
"--flash-mode",
|
||||
"qio",
|
||||
"--flash-size",
|
||||
"16MB",
|
||||
"--output",
|
||||
str(args.output),
|
||||
"0x0000",
|
||||
str(args.bootloader),
|
||||
"0x8000",
|
||||
str(args.partitions),
|
||||
"0xe000",
|
||||
str(args.boot_app0),
|
||||
LAUNCHER_OFFSET,
|
||||
str(args.launcher),
|
||||
STANDALONE_OFFSET,
|
||||
str(args.standalone),
|
||||
RNODE_OFFSET,
|
||||
str(args.rnode),
|
||||
]
|
||||
subprocess.run(cmd, check=True)
|
||||
print(f"dual firmware image written to {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package a merged ESP32-S3 image with a web-flasher manifest."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--image", required=True, type=Path)
|
||||
parser.add_argument("--name", required=True)
|
||||
parser.add_argument("--output", required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.image.is_file():
|
||||
raise FileNotFoundError(args.image)
|
||||
|
||||
image_name = f"{args.name}.bin"
|
||||
manifest = {
|
||||
"chipFamily": "ESP32-S3",
|
||||
"flashSize": "16MB",
|
||||
"flashMode": "qio",
|
||||
"flashFreq": "80m",
|
||||
"parts": [
|
||||
{"path": image_name, "offset": "0x0000"},
|
||||
],
|
||||
}
|
||||
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zipfile.ZipFile(args.output, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
||||
zf.write(args.image, image_name)
|
||||
zf.writestr("manifest.json", json.dumps(manifest, indent=2) + "\n")
|
||||
|
||||
print(f"firmware package written to {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user