more on esp_deploy

This commit is contained in:
Bob 2025-12-11 11:20:53 -08:00
parent ddc0ab185f
commit 6ce2de5088
1 changed files with 39 additions and 10 deletions

View File

@ -3,6 +3,7 @@
ESP32 Unified Deployment Tool (esp32_deploy) ESP32 Unified Deployment Tool (esp32_deploy)
Combines firmware flashing and device configuration with full control. Combines firmware flashing and device configuration with full control.
Updates: Updates:
- FIXED: Indentation errors in class methods
- FIXED: Overlap error caused by swapping ota_data_initial.bin with main app - FIXED: Overlap error caused by swapping ota_data_initial.bin with main app
- '--target auto' support for mixed-device flashing - '--target auto' support for mixed-device flashing
- 'target all' support (Build 12 configurations) - 'target all' support (Build 12 configurations)
@ -134,16 +135,16 @@ class UnifiedDeployWorker:
suffix = generate_config_suffix(target_to_use, self.args.csi_enable, self.args.ampdu) suffix = generate_config_suffix(target_to_use, self.args.csi_enable, self.args.ampdu)
firmware_dir = self.project_dir / "firmware" firmware_dir = self.project_dir / "firmware"
# Find unique binary # Find unique binary for this specific target config
unique_app = None unique_app = None
if firmware_dir.exists(): if firmware_dir.exists():
for f in os.listdir(firmware_dir): for f in os.listdir(firmware_dir):
if f.endswith(f"_{suffix}.bin") and not f.startswith("bootloader") and not f.startswith("partition"): if f.endswith(f"_{suffix}.bin") and not f.startswith("bootloader") and not f.startswith("partition") and not f.startswith("ota_data") and not f.startswith("phy_init"):
unique_app = f unique_app = f
break break
if not unique_app: if not unique_app:
self.log.error(f"Binary for config '{suffix}' not found in firmware/.") self.log.error(f"Binary for config '{suffix}' not found in firmware/. Run --target all first?")
return False return False
unique_boot = f"bootloader_{suffix}.bin" unique_boot = f"bootloader_{suffix}.bin"
@ -164,21 +165,25 @@ class UnifiedDeployWorker:
raw_args = [x for x in content.split(' ') if x] raw_args = [x for x in content.split(' ') if x]
final_args = [] final_args = []
# 4. Construct Flash Command (Swap paths safely) # 4. Construct Flash Command (Safe Swapping)
for arg in raw_args: for arg in raw_args:
if arg.endswith('bootloader.bin'): if arg.endswith('bootloader.bin'):
final_args.append(str(firmware_dir / unique_boot)) final_args.append(str(firmware_dir / unique_boot))
elif arg.endswith('partition-table.bin'): elif arg.endswith('partition-table.bin'):
final_args.append(str(firmware_dir / unique_part)) final_args.append(str(firmware_dir / unique_part))
elif arg.endswith('ota_data_initial.bin'): elif arg.endswith('ota_data_initial.bin'):
# Fix: Handle OTA data specifically so it doesn't get swapped with app # Only use unique if it exists, otherwise assume standard path relative to build (risky if build gone)
if (firmware_dir / unique_ota).exists(): if (firmware_dir / unique_ota).exists():
final_args.append(str(firmware_dir / unique_ota)) final_args.append(str(firmware_dir / unique_ota))
else: else:
# Fallback to standard if unique doesn't exist (though it should) # Skip if missing to avoid error
self.log.warning(f"OTA binary {unique_ota} missing. Skipping arg to prevent crash.")
continue
elif arg.endswith('phy_init_data.bin'):
# System binary: Do NOT replace with App.
final_args.append(arg) final_args.append(arg)
elif arg.endswith('.bin'): elif arg.endswith('.bin'):
# This catch-all must exclude partition/bootloader/ota # This catch-all is for the MAIN APP only.
final_args.append(str(firmware_dir / unique_app)) final_args.append(str(firmware_dir / unique_app))
else: else:
final_args.append(arg) final_args.append(arg)
@ -431,29 +436,51 @@ async def run_deployment(args):
print(f"\n{Colors.BLUE}{'='*60}{Colors.RESET}\n ESP32 Unified Deployment Tool\n{Colors.BLUE}{'='*60}{Colors.RESET}") print(f"\n{Colors.BLUE}{'='*60}{Colors.RESET}\n ESP32 Unified Deployment Tool\n{Colors.BLUE}{'='*60}{Colors.RESET}")
project_dir = Path(args.dir).resolve() project_dir = Path(args.dir).resolve()
# --- Target 'ALL' Mode ---
if args.target == 'all': if args.target == 'all':
print(f"{Colors.YELLOW}Starting Batch Build Verification (12 Combinations){Colors.RESET}\n") print(f"{Colors.YELLOW}Starting Batch Build Verification (12 Combinations){Colors.RESET}")
# SAFETY: Wipe firmware dir to ensure no stale binaries exist
firmware_dir = project_dir / "firmware"
if firmware_dir.exists():
try:
shutil.rmtree(firmware_dir)
print(f"{Colors.YELLOW} [Clean] Removed old firmware/ directory.{Colors.RESET}")
except Exception as e:
print(f"{Colors.RED} [Error] Could not clean firmware dir: {e}{Colors.RESET}")
return
# Re-create it fresh
firmware_dir.mkdir(exist_ok=True)
print("") # Spacer
targets = ['esp32', 'esp32s3', 'esp32c5'] targets = ['esp32', 'esp32s3', 'esp32c5']
booleans = [False, True] booleans = [False, True]
results = [] results = []
total_steps = len(targets) * 4
total_steps = len(targets) * len(booleans) * len(booleans)
current_step = 0 current_step = 0
for target in targets: for target in targets:
for csi in booleans: for csi in booleans:
for ampdu in booleans: for ampdu in booleans:
current_step += 1 current_step += 1
success, msg, dur = await build_task(project_dir, target, csi, ampdu, current_step, total_steps) success, msg, dur = await build_task(project_dir, target, csi, ampdu, current_step, total_steps)
results.append({"cfg": f"{target.ljust(9)} CSI:{'ON ' if csi else 'OFF'} AMPDU:{'ON ' if ampdu else 'OFF'}", "ok": success, "dur": dur}) results.append({"cfg": f"{target.ljust(9)} CSI:{'ON ' if csi else 'OFF'} AMPDU:{'ON ' if ampdu else 'OFF'}", "ok": success, "dur": dur})
print(f"\n{Colors.BLUE}Batch Summary:{Colors.RESET}") print(f"\n{Colors.BLUE}Batch Summary:{Colors.RESET}")
for r in results: for r in results:
status = f"{Colors.GREEN}PASS{Colors.RESET}" if r['ok'] else f"{Colors.RED}FAIL{Colors.RESET}" status = f"{Colors.GREEN}PASS{Colors.RESET}" if r['ok'] else f"{Colors.RED}FAIL{Colors.RESET}"
print(f" {r['cfg']} : {status} ({r['dur']:.1f}s)") print(f" {r['cfg']} : {status} ({r['dur']:.1f}s)")
return return
# --- Single Build Configuration ---
# Skip build if we are in AUTO mode (we assume binaries exist in firmware/)
if not args.config_only and args.target != 'auto': if not args.config_only and args.target != 'auto':
target = args.target if args.target else 'esp32s3' target = args.target if args.target else 'esp32s3'
csi = args.csi_enable csi = args.csi_enable
ampdu = args.ampdu ampdu = args.ampdu
if args.interactive: if args.interactive:
print(f"\n{Colors.YELLOW}--- Build Configuration ---{Colors.RESET}") print(f"\n{Colors.YELLOW}--- Build Configuration ---{Colors.RESET}")
target = ask_user("Target Chip", default=target, choices=['esp32', 'esp32s3', 'esp32c5']) target = ask_user("Target Chip", default=target, choices=['esp32', 'esp32s3', 'esp32c5'])
@ -462,6 +489,7 @@ async def run_deployment(args):
args.csi_enable = csi args.csi_enable = csi
args.target = target args.target = target
args.ampdu = ampdu args.ampdu = ampdu
success, msg, _ = await build_task(project_dir, target, csi, ampdu, 1, 1) success, msg, _ = await build_task(project_dir, target, csi, ampdu, 1, 1)
if not success: if not success:
print(f"{Colors.RED}{msg}{Colors.RESET}") print(f"{Colors.RED}{msg}{Colors.RESET}")
@ -469,6 +497,7 @@ async def run_deployment(args):
elif args.target == 'auto' and not args.config_only: elif args.target == 'auto' and not args.config_only:
print(f"{Colors.YELLOW}Target 'auto' selected. Skipping build step (assuming artifacts in firmware/).{Colors.RESET}") print(f"{Colors.YELLOW}Target 'auto' selected. Skipping build step (assuming artifacts in firmware/).{Colors.RESET}")
# --- Device Detection & Flash ---
if args.devices: if args.devices:
devs = [type('obj', (object,), {'device': d.strip()}) for d in args.devices.split(',')] devs = [type('obj', (object,), {'device': d.strip()}) for d in args.devices.split(',')]
else: else: