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
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:
116
flasher/pio_create_dfu_zip.py
Normal file
116
flasher/pio_create_dfu_zip.py
Normal 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
|
||||
Reference in New Issue
Block a user