feat: add web flasher (Vue 3 + Web Serial DFU) and DFU zip generation
Some checks failed
Build and deploy Docs site to GitHub Pages / github-pages (push) Has been cancelled
PR Build Check / build (Heltec_v3_companion_radio_ble) (push) Has been cancelled
PR Build Check / build (Heltec_v3_repeater) (push) Has been cancelled
PR Build Check / build (Heltec_v3_room_server) (push) Has been cancelled
PR Build Check / build (LilyGo_Tlora_C6_repeater_) (push) Has been cancelled
PR Build Check / build (PicoW_repeater) (push) Has been cancelled
PR Build Check / build (RAK_4631_companion_radio_ble) (push) Has been cancelled
PR Build Check / build (RAK_4631_repeater) (push) Has been cancelled
PR Build Check / build (RAK_4631_room_server) (push) Has been cancelled
PR Build Check / build (wio-e5-mini_repeater) (push) Has been cancelled

This commit is contained in:
UA1ZBE
2026-06-05 09:48:14 +03:00
parent 40c9633238
commit 339d87bf86
8 changed files with 1223 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
PlatformIO extra script: generate DFU .zip after build.
Add to platformio.ini:
extra_scripts = post:flasher/pio_create_dfu_zip.py
"""
import sys
import os
import json
import struct
import zipfile
# Add flasher dir to path for importing the main module
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
try:
from create_dfu_zip import hex_to_bin, uf2_to_bin, create_dfu_zip
except ImportError:
# Fallback: define minimal conversion inline
def hex_to_bin(hex_path):
from intelhex import IntelHex
ih = IntelHex(hex_path)
min_addr = ih.minaddr() or 0
max_addr = ih.maxaddr() or 0
size = max_addr - min_addr + 1
page_size = 0x1000
aligned = ((size + page_size - 1) // page_size) * page_size
return bytes(ih.tobinarray(start=min_addr, size=aligned)), min_addr
def create_dfu_zip(bin_data, base_addr, fw_version=1, hw_version=52):
init_packet = bytearray()
init_packet.append(0x01)
init_packet.append(0x04)
init_packet += struct.pack('<I', 0xFFFE)
init_packet += struct.pack('<I', 0xFFFFFFFF)
init_packet += struct.pack('<I', hw_version)
init_packet += struct.pack('<I', fw_version)
init_packet += struct.pack('<II', 0, 0)
manifest = {
"manifest": {
"application": {
"bin_file": "firmware.bin",
"dat_file": "firmware.dat",
"init_packet_data": {
"fw_version": fw_version,
"hw_version": hw_version,
"softdevice_req": [0xFFFE],
"components": [{"data": list(init_packet)}]
}
}
}
}
return manifest, bin_data, bytes(init_packet)
def create_dfu_zip_from_build(target, source, env):
"""PlatformIO post-build hook."""
build_dir = env.subst("$BUILD_DIR")
prog_name = env.subst("$PROGNAME")
hex_file = os.path.join(build_dir, prog_name + ".hex")
if not os.path.isfile(hex_file):
print(f" [DFU] {hex_file} not found, skipping")
return
try:
bin_data, base_addr = hex_to_bin(hex_file)
except Exception as e:
print(f" [DFU] Error: {e}")
return
manifest, fw_bin, fw_dat = create_dfu_zip(bin_data, base_addr)
# Name the zip after the build target (environment name)
env_name = env.subst("$PIOENV")
# Extract firmware name from env: e.g. Heltec_t114_without_display_beacon_sensor_ble -> Heltec_T114_Beacon_BLE
parts = env_name.split("_")
if "beacon" in parts:
fw_name = "Heltec_T114_Beacon"
elif "companion" in parts:
fw_name = "Heltec_T114_Companion_Radio"
elif "repeater" in parts:
fw_name = "Heltec_T114_Repeater"
elif "room" in parts:
fw_name = "Heltec_T114_Room_Server"
else:
fw_name = "firmware"
if "ble" in parts:
fw_name += "_BLE"
elif "usb" in parts:
fw_name += "_USB"
zip_name = f"{fw_name}.dfu.zip"
zip_path = os.path.join(build_dir, zip_name)
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.writestr("manifest.json", json.dumps(manifest, indent=2))
zf.writestr("firmware.bin", fw_bin)
zf.writestr("firmware.dat", fw_dat)
size = os.path.getsize(zip_path)
print(f" [DFU] Created: {zip_path} ({size / 1024:.0f} KB)")
# Register hook
Import("env")
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", create_dfu_zip_from_build)
# Also trigger after .uf2 creation if that action exists
try:
env.AddPostAction("$BUILD_DIR/${PROGNAME}.uf2", create_dfu_zip_from_build)
except:
pass