diff --git a/.github/workflows/custom_build.yml b/.github/workflows/custom_build.yml index a8bc773..d426755 100644 --- a/.github/workflows/custom_build.yml +++ b/.github/workflows/custom_build.yml @@ -135,7 +135,7 @@ jobs: ROOT=$(cat /tmp/fw-src-root.txt) cd "$ROOT" python -m pip install -q --upgrade pip - pip install -q platformio + pip install -q platformio adafruit-nrfutil - name: Install PlatformIO dependencies shell: bash @@ -181,9 +181,15 @@ jobs: ROOT=$(cat /tmp/fw-src-root.txt) BUILD_DIR="$ROOT/.pio/build/${{ inputs.target_env }}" - # MeshCore layout: PlatformIO puts bin/dat/manifest.json in a firmware/ subdirectory. - # Flatten them into the build root so the bundle packaging picks them up. - if [ -d "$BUILD_DIR/firmware" ]; then + # Path 1: MeshCore produces firmware.zip (Nordic DFU package) when adafruit-nrfutil + # is available during pio run. Unzip it to get bin/dat/manifest.json at build root. + if [ -f "$BUILD_DIR/firmware.zip" ]; then + echo "Found firmware.zip; extracting Nordic DFU files to build root" + unzip -j "$BUILD_DIR/firmware.zip" "*.bin" "*.dat" "manifest.json" -d "$BUILD_DIR/" || true + fi + + # Path 2: firmware/ subdirectory fallback (local builds or alternate PIO layouts). + if [ -d "$BUILD_DIR/firmware" ] && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then echo "Found firmware/ subdirectory; staging nRF52 DFU files to build root" shopt -s nullglob for f in "$BUILD_DIR/firmware/"*.bin "$BUILD_DIR/firmware/"*.dat; do @@ -193,21 +199,16 @@ jobs: [ -f "$BUILD_DIR/firmware/manifest.json" ] && cp -a "$BUILD_DIR/firmware/manifest.json" "$BUILD_DIR/" fi - # Meshtastic / generic UF2 layout: generate .dat init packet if .uf2 exists but no .dat yet. - if ls "$BUILD_DIR"/*.uf2 >/dev/null 2>&1 && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then - FW_BIN="$BUILD_DIR/firmware.bin" - if [ ! -f "$FW_BIN" ]; then - echo "firmware.bin not found; skipping DFU init packet generation" - exit 0 - fi - echo "nRF52 UF2 build: generating DFU init packet from firmware.bin" - pip install -q adafruit-nrfutil + # Path 3: firmware.bin present but still no .dat (Meshtastic UF2 builds, or MeshCore + # when the post-build script did not produce firmware.zip). Generate init packet. + if [ -f "$BUILD_DIR/firmware.bin" ] && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then + echo "firmware.bin found but no .dat; generating DFU init packet" adafruit-nrfutil dfu genpkg \ --dev-type 0xFFFF \ --dev-revision 0xFFFF \ --application-version 0xFFFFFFFF \ --sd-req 0xFFFE \ - --application "$FW_BIN" \ + --application "$BUILD_DIR/firmware.bin" \ /tmp/nrf52-dfu.zip unzip -j /tmp/nrf52-dfu.zip "*.dat" -d "$BUILD_DIR/" echo "DFU init packet written: $(ls "$BUILD_DIR"/*.dat 2>/dev/null || echo '(none)')" diff --git a/.github/workflows/custom_build_test.yml b/.github/workflows/custom_build_test.yml index a8bc773..d426755 100644 --- a/.github/workflows/custom_build_test.yml +++ b/.github/workflows/custom_build_test.yml @@ -135,7 +135,7 @@ jobs: ROOT=$(cat /tmp/fw-src-root.txt) cd "$ROOT" python -m pip install -q --upgrade pip - pip install -q platformio + pip install -q platformio adafruit-nrfutil - name: Install PlatformIO dependencies shell: bash @@ -181,9 +181,15 @@ jobs: ROOT=$(cat /tmp/fw-src-root.txt) BUILD_DIR="$ROOT/.pio/build/${{ inputs.target_env }}" - # MeshCore layout: PlatformIO puts bin/dat/manifest.json in a firmware/ subdirectory. - # Flatten them into the build root so the bundle packaging picks them up. - if [ -d "$BUILD_DIR/firmware" ]; then + # Path 1: MeshCore produces firmware.zip (Nordic DFU package) when adafruit-nrfutil + # is available during pio run. Unzip it to get bin/dat/manifest.json at build root. + if [ -f "$BUILD_DIR/firmware.zip" ]; then + echo "Found firmware.zip; extracting Nordic DFU files to build root" + unzip -j "$BUILD_DIR/firmware.zip" "*.bin" "*.dat" "manifest.json" -d "$BUILD_DIR/" || true + fi + + # Path 2: firmware/ subdirectory fallback (local builds or alternate PIO layouts). + if [ -d "$BUILD_DIR/firmware" ] && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then echo "Found firmware/ subdirectory; staging nRF52 DFU files to build root" shopt -s nullglob for f in "$BUILD_DIR/firmware/"*.bin "$BUILD_DIR/firmware/"*.dat; do @@ -193,21 +199,16 @@ jobs: [ -f "$BUILD_DIR/firmware/manifest.json" ] && cp -a "$BUILD_DIR/firmware/manifest.json" "$BUILD_DIR/" fi - # Meshtastic / generic UF2 layout: generate .dat init packet if .uf2 exists but no .dat yet. - if ls "$BUILD_DIR"/*.uf2 >/dev/null 2>&1 && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then - FW_BIN="$BUILD_DIR/firmware.bin" - if [ ! -f "$FW_BIN" ]; then - echo "firmware.bin not found; skipping DFU init packet generation" - exit 0 - fi - echo "nRF52 UF2 build: generating DFU init packet from firmware.bin" - pip install -q adafruit-nrfutil + # Path 3: firmware.bin present but still no .dat (Meshtastic UF2 builds, or MeshCore + # when the post-build script did not produce firmware.zip). Generate init packet. + if [ -f "$BUILD_DIR/firmware.bin" ] && ! ls "$BUILD_DIR"/*.dat >/dev/null 2>&1; then + echo "firmware.bin found but no .dat; generating DFU init packet" adafruit-nrfutil dfu genpkg \ --dev-type 0xFFFF \ --dev-revision 0xFFFF \ --application-version 0xFFFFFFFF \ --sd-req 0xFFFE \ - --application "$FW_BIN" \ + --application "$BUILD_DIR/firmware.bin" \ /tmp/nrf52-dfu.zip unzip -j /tmp/nrf52-dfu.zip "*.dat" -d "$BUILD_DIR/" echo "DFU init packet written: $(ls "$BUILD_DIR"/*.dat 2>/dev/null || echo '(none)')" diff --git a/scripts/emit-flash-manifest.py b/scripts/emit-flash-manifest.py index c0ae4c1..6ba8509 100644 --- a/scripts/emit-flash-manifest.py +++ b/scripts/emit-flash-manifest.py @@ -505,15 +505,35 @@ def main() -> int: print(f"Wrote {out_path} (update + {'factory' if 'factory' in merged_doc else 'no factory'})") return 0 - # Path 3: generic nRF52 (UF2 output) + # Path 3: generic nRF52 — UF2 bootloader builds or Nordic DFU (bin + dat) builds if pio_family == "nrf52": + # 3a: UF2 output (Meshtastic and other UF2-bootloader boards) doc = emit_generic_nrf52(names) if doc: merged_doc = _merge_target_family_meta(doc, pio_family, pio_platform, pio_board) _write_manifest(out_path, merged_doc) print(f"Wrote {out_path} (nRF52 UF2, update + {'factory' if 'factory' in merged_doc else 'no factory'})") return 0 - print("nRF52 target but no *.uf2 found in BUILD_DIR; skipping manifest", file=sys.stderr) + + # 3b: Nordic DFU bin + dat output (MeshCore and similar PlatformIO nRF52 builds). + # The browser uses manifest.json (Nordic format) directly for the DFU protocol; + # flash-manifest.json here just records targetFamily for the UI. + if "firmware.bin" in names and "firmware.dat" in names: + doc = { + "update": { + "images": [ + {"file": "firmware.bin", "offset": 0, "role": "dfu-bin"}, + {"file": "firmware.dat", "offset": 0, "role": "dfu-dat"}, + ], + "eraseFlash": False, + } + } + merged_doc = _merge_target_family_meta(doc, pio_family, pio_platform, pio_board) + _write_manifest(out_path, merged_doc) + print(f"Wrote {out_path} (nRF52 Nordic DFU)") + return 0 + + print("nRF52 target but no flashable files found in BUILD_DIR; skipping manifest", file=sys.stderr) return 0 # Path 4: generic ESP32 (standard PlatformIO bin output)