ESP32/batch_config.py

159 lines
5.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""
ESP32 Batch Configuration Tool
Detects all connected ESP32s and configures them with sequential Static IPs.
Requires: detect_esp32.py and config_device.py in the same directory.
"""
import sys
import os
import argparse
import time
import ipaddress
import re
# Ensure we can import the other scripts
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
try:
import detect_esp32
import config_device
except ImportError as e:
print(f"Error: Could not import required modules ({e}).")
print("Make sure 'detect_esp32.py' and 'config_device.py' are in the same folder.")
sys.exit(1)
def natural_sort_key(device_obj):
"""
Sorts ports naturally (ttyUSB2 comes before ttyUSB10)
"""
s = device_obj.device
# Split string into a list of integers and non-integers
return [int(text) if text.isdigit() else text.lower()
for text in re.split('([0-9]+)', s)]
def main():
parser = argparse.ArgumentParser(
description='Batch Config: Detects all ESP32s and configures sequential IPs',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
# Arguments matching config_device.py options
parser.add_argument('--start-ip', required=True,
help='Starting Static IP (e.g., 192.168.1.101). Will increment for each device.')
parser.add_argument('-s', '--ssid', default='ClubHouse2G',
help='WiFi SSID')
parser.add_argument('-P', '--password', default='ez2remember',
help='WiFi password')
parser.add_argument('-g', '--gateway', default='192.168.1.1',
help='Gateway IP')
parser.add_argument('-m', '--netmask', default='255.255.255.0',
help='Netmask')
parser.add_argument('-b', '--band', default='2.4G', choices=['2.4G', '5G'],
help='WiFi band')
parser.add_argument('-B', '--bandwidth', default='HT20', choices=['HT20', 'HT40', 'VHT80'],
help='Channel bandwidth')
parser.add_argument('-ps', '--powersave', default='NONE',
choices=['NONE', 'MIN', 'MIN_MODEM', 'MAX', 'MAX_MODEM'],
help='Power save mode')
parser.add_argument('-M', '--mode', default='STA', choices=['STA', 'MONITOR'],
help='Operating mode')
parser.add_argument('-mc', '--monitor-channel', type=int, default=36,
help='Monitor mode channel')
parser.add_argument('-r', '--no-reboot', action='store_true',
help='Do NOT reboot devices after configuration')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output')
args = parser.parse_args()
# 1. Detect Devices
print(f"{'='*60}")
print("Step 1: Detecting ESP32 Devices...")
print(f"{'='*60}")
devices = detect_esp32.detect_esp32_devices()
if not devices:
print("No ESP32 devices found! Check USB connections.")
sys.exit(1)
# Sort devices naturally so IPs are assigned in order (USB0, USB1, USB2...)
devices.sort(key=natural_sort_key)
print(f"Found {len(devices)} devices:")
for d in devices:
print(f" - {d.device} ({d.description})")
print()
# 2. Parse Starting IP
try:
start_ip_obj = ipaddress.IPv4Address(args.start_ip)
except ipaddress.AddressValueError:
print(f"Error: Invalid IP address format: {args.start_ip}")
sys.exit(1)
# 3. Configure Each Device
print(f"{'='*60}")
print("Step 2: Configuring Devices Sequentially")
print(f"{'='*60}")
success_count = 0
fail_count = 0
failed_devices = []
for index, device in enumerate(devices):
# Calculate current IP
current_ip = str(start_ip_obj + index)
port = device.device
print(f"\n[{index+1}/{len(devices)}] Configuring {port} with IP {current_ip}...")
# Call the config function from your existing script
result = config_device.config_device(
port=port,
ip=current_ip,
ssid=args.ssid,
password=args.password,
gateway=args.gateway,
netmask=args.netmask,
band=args.band,
bandwidth=args.bandwidth,
powersave=args.powersave,
mode=args.mode,
monitor_channel=args.monitor_channel,
reboot=not args.no_reboot,
verbose=args.verbose
)
if result:
print(f"✓ Success: {port} -> {current_ip}")
success_count += 1
else:
print(f"✗ Failed: {port}")
fail_count += 1
failed_devices.append(port)
# Small delay to prevent USB power spikes if multiple devices reboot simultaneously
if not args.no_reboot and index < len(devices) - 1:
time.sleep(1.0)
# 4. Summary
print(f"\n{'='*60}")
print("Batch Configuration Complete")
print(f"{'='*60}")
print(f"Total Devices: {len(devices)}")
print(f"Successful: {success_count}")
print(f"Failed: {fail_count}")
if failed_devices:
print("\nFailed Ports:")
for p in failed_devices:
print(f" - {p}")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nBatch process interrupted by user.")