199 lines
5.8 KiB
Python
Executable File
199 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Flash uniform firmware to all ESP32 devices, then reconfigure via serial
|
|
This is much faster than building unique firmwares for each device
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import argparse
|
|
import glob
|
|
from pathlib import Path
|
|
import time
|
|
|
|
def detect_devices():
|
|
"""Detect all ESP32 devices"""
|
|
devices = sorted(glob.glob('/dev/ttyUSB*'))
|
|
return devices
|
|
|
|
def build_firmware(project_dir):
|
|
"""Build the firmware once"""
|
|
print("=" * 60)
|
|
print("Building firmware (one time)...")
|
|
print("=" * 60)
|
|
|
|
result = subprocess.run(
|
|
['idf.py', 'build'],
|
|
cwd=project_dir,
|
|
capture_output=False
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
print("✗ Build failed!")
|
|
return False
|
|
|
|
print("✓ Build complete")
|
|
return True
|
|
|
|
def flash_device(port, project_dir):
|
|
"""Flash a single device"""
|
|
try:
|
|
result = subprocess.run(
|
|
['idf.py', '-p', port, 'flash'],
|
|
cwd=project_dir,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=120
|
|
)
|
|
return result.returncode == 0
|
|
except Exception as e:
|
|
return False
|
|
|
|
def flash_all_devices(devices, project_dir):
|
|
"""Flash all devices with the same firmware"""
|
|
print(f"\nFlashing {len(devices)} devices...")
|
|
|
|
success_count = 0
|
|
for idx, dev in enumerate(devices, 1):
|
|
print(f"[{idx:2d}/{len(devices)}] Flashing {dev}...", end='', flush=True)
|
|
|
|
if flash_device(dev, project_dir):
|
|
print(" ✓")
|
|
success_count += 1
|
|
else:
|
|
print(" ✗")
|
|
|
|
print(f"\nFlashed: {success_count}/{len(devices)}")
|
|
return success_count
|
|
|
|
def reconfigure_devices(ssid, password, start_ip, gateway="192.168.1.1"):
|
|
"""Reconfigure devices using the reconfig script"""
|
|
script_path = os.path.join(os.path.dirname(__file__), 'reconfig_simple.py')
|
|
|
|
if not os.path.exists(script_path):
|
|
print(f"Error: {script_path} not found!")
|
|
return False
|
|
|
|
print("\n" + "=" * 60)
|
|
print("Reconfiguring WiFi settings via serial...")
|
|
print("=" * 60)
|
|
|
|
cmd = [
|
|
'python3', script_path,
|
|
'-s', ssid,
|
|
'-p', password,
|
|
'--start-ip', start_ip,
|
|
'-g', gateway
|
|
]
|
|
|
|
result = subprocess.run(cmd)
|
|
return result.returncode == 0
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description='Flash and configure all ESP32 devices',
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
This script:
|
|
1. Builds firmware ONCE
|
|
2. Flashes the SAME firmware to all devices (fast!)
|
|
3. Reconfigures each device via serial with unique IP
|
|
|
|
Much faster than building 32 different firmwares!
|
|
|
|
Examples:
|
|
# Basic usage
|
|
%(prog)s --ssid MyWiFi --password mypass
|
|
|
|
# Custom IP range
|
|
%(prog)s --ssid MyWiFi --password mypass --start-ip 192.168.1.100
|
|
|
|
# Build only (no flash)
|
|
%(prog)s --build-only
|
|
|
|
# Reconfigure only (no flash)
|
|
%(prog)s --reconfig-only --ssid MyWiFi --password mypass
|
|
"""
|
|
)
|
|
|
|
parser.add_argument('--ssid', help='WiFi SSID')
|
|
parser.add_argument('--password', help='WiFi password')
|
|
parser.add_argument('--start-ip', default='192.168.1.50',
|
|
help='Starting IP address (default: 192.168.1.50)')
|
|
parser.add_argument('--gateway', default='192.168.1.1',
|
|
help='Gateway IP (default: 192.168.1.1)')
|
|
parser.add_argument('--project-dir', default=None,
|
|
help='ESP32 project directory (default: current dir)')
|
|
parser.add_argument('--build-only', action='store_true',
|
|
help='Only build, do not flash')
|
|
parser.add_argument('--reconfig-only', action='store_true',
|
|
help='Only reconfigure, do not build/flash')
|
|
parser.add_argument('--skip-build', action='store_true',
|
|
help='Skip build, use existing firmware')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Determine project directory
|
|
if args.project_dir:
|
|
project_dir = args.project_dir
|
|
else:
|
|
project_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
if not os.path.exists(os.path.join(project_dir, 'CMakeLists.txt')):
|
|
print(f"Error: Not an ESP-IDF project directory: {project_dir}")
|
|
return 1
|
|
|
|
# Detect devices
|
|
devices = detect_devices()
|
|
if not devices and not args.build_only:
|
|
print("Error: No devices found!")
|
|
return 1
|
|
|
|
print(f"Found {len(devices)} device(s)")
|
|
|
|
# Reconfigure only mode
|
|
if args.reconfig_only:
|
|
if not args.ssid or not args.password:
|
|
print("Error: --ssid and --password required for reconfigure mode")
|
|
return 1
|
|
|
|
return 0 if reconfigure_devices(args.ssid, args.password, args.start_ip, args.gateway) else 1
|
|
|
|
# Build firmware
|
|
if not args.skip_build:
|
|
if not build_firmware(project_dir):
|
|
return 1
|
|
|
|
if args.build_only:
|
|
print("\n Build complete. Use --skip-build to flash later.")
|
|
return 0
|
|
|
|
# Flash all devices
|
|
flash_count = flash_all_devices(devices, project_dir)
|
|
|
|
if flash_count == 0:
|
|
print("✗ No devices flashed successfully")
|
|
return 1
|
|
|
|
# Reconfigure if credentials provided
|
|
if args.ssid and args.password:
|
|
print("\nWaiting for devices to boot...")
|
|
time.sleep(5)
|
|
|
|
if not reconfigure_devices(args.ssid, args.password, args.start_ip, args.gateway):
|
|
print("✗ Reconfiguration failed")
|
|
return 1
|
|
else:
|
|
print("\n" + "=" * 60)
|
|
print("Flashing complete!")
|
|
print("=" * 60)
|
|
print("\nTo configure WiFi settings, run:")
|
|
print(f" python3 reconfig_simple.py -s YourSSID -p YourPassword --start-ip {args.start_ip}")
|
|
|
|
print("\n✓ Done!")
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|