diff --git a/esp32_deploy.py b/esp32_deploy.py index 12d3e66..846f8ff 100755 --- a/esp32_deploy.py +++ b/esp32_deploy.py @@ -3,6 +3,7 @@ ESP32 Unified Deployment Tool (esp32_deploy) Combines firmware flashing and device configuration with full control. Updates: + - FIXED: Indentation errors in class methods - FIXED: Overlap error caused by swapping ota_data_initial.bin with main app - '--target auto' support for mixed-device flashing - '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) firmware_dir = self.project_dir / "firmware" - # Find unique binary + # Find unique binary for this specific target config unique_app = None if firmware_dir.exists(): 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 break 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 unique_boot = f"bootloader_{suffix}.bin" @@ -164,21 +165,25 @@ class UnifiedDeployWorker: raw_args = [x for x in content.split(' ') if x] final_args = [] - # 4. Construct Flash Command (Swap paths safely) + # 4. Construct Flash Command (Safe Swapping) for arg in raw_args: if arg.endswith('bootloader.bin'): final_args.append(str(firmware_dir / unique_boot)) elif arg.endswith('partition-table.bin'): final_args.append(str(firmware_dir / unique_part)) 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(): final_args.append(str(firmware_dir / unique_ota)) else: - # Fallback to standard if unique doesn't exist (though it should) - final_args.append(arg) + # 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) 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)) else: 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}") project_dir = Path(args.dir).resolve() + # --- Target 'ALL' Mode --- 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'] booleans = [False, True] results = [] - total_steps = len(targets) * 4 + + total_steps = len(targets) * len(booleans) * len(booleans) current_step = 0 + for target in targets: for csi in booleans: for ampdu in booleans: current_step += 1 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}) + print(f"\n{Colors.BLUE}Batch Summary:{Colors.RESET}") for r in results: 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)") 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': target = args.target if args.target else 'esp32s3' csi = args.csi_enable ampdu = args.ampdu + if args.interactive: print(f"\n{Colors.YELLOW}--- Build Configuration ---{Colors.RESET}") 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.target = target args.ampdu = ampdu + success, msg, _ = await build_task(project_dir, target, csi, ampdu, 1, 1) if not success: 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: print(f"{Colors.YELLOW}Target 'auto' selected. Skipping build step (assuming artifacts in firmware/).{Colors.RESET}") + # --- Device Detection & Flash --- if args.devices: devs = [type('obj', (object,), {'device': d.strip()}) for d in args.devices.split(',')] else: