#!/usr/bin/env python3 """ Simple ESP32 WiFi Reconfiguration Tool Sends WiFi config to all connected ESP32 devices via serial """ import serial import time import glob import argparse import sys def reconfig_devices(ssid, password, start_ip, gateway="192.168.1.1", netmask="255.255.255.0", verbose=False): """Reconfigure all connected devices""" devices = sorted(glob.glob('/dev/ttyUSB*')) num_devices = len(devices) if num_devices == 0: print("ERROR: No devices found!") return 0 # Parse start IP ip_parts = start_ip.split('.') ip_base = '.'.join(ip_parts[:3]) ip_start = int(ip_parts[3]) ok_devices = 0 print(f"Found {num_devices} devices") print(f"SSID: {ssid}") print(f"Password: {'*' * len(password)}") print(f"IP Range: {ip_base}.{ip_start} - {ip_base}.{ip_start + num_devices - 1}") print() for idx, dev in enumerate(devices): ip = f"{ip_base}.{ip_start + idx}" print(f"[{idx:2d}] Configuring {dev:14s} → {ip}", end='') try: ser = serial.Serial(dev, 115200, timeout=1) time.sleep(0.5) # Let serial port stabilize # Send configuration ser.write(b"CFG\n") time.sleep(0.1) ser.write(f"SSID:{ssid}\n".encode()) time.sleep(0.1) ser.write(f"PASS:{password}\n".encode()) time.sleep(0.1) ser.write(f"IP:{ip}\n".encode()) time.sleep(0.1) ser.write(f"MASK:{netmask}\n".encode()) time.sleep(0.1) ser.write(f"GW:{gateway}\n".encode()) time.sleep(0.1) ser.write(b"DHCP:0\n") time.sleep(0.1) ser.write(b"END\n") # Wait for OK response time.sleep(0.5) response = ser.read(100).decode('utf-8', errors='ignore') if verbose and response.strip(): print(f"\n Response: {response[:80]}") if 'OK' in response: print(" ✓") ok_devices += 1 else: print(" ⚠ (no OK)") ser.close() except Exception as e: print(f" ✗ Error: {e}") time.sleep(0.5) print() print(f"{'='*60}") print(f"Success: {ok_devices}/{num_devices}") print(f"Failed: {num_devices - ok_devices}/{num_devices}") print(f"{'='*60}") return ok_devices def main(): parser = argparse.ArgumentParser( description='Reconfigure WiFi settings on all connected ESP32 devices', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Basic usage with defaults %(prog)s # Custom IP range %(prog)s --start-ip 192.168.1.100 # Custom WiFi credentials %(prog)s -s MyNetwork -p mypassword # Different subnet %(prog)s --start-ip 10.0.0.50 -g 10.0.0.1 # Verbose mode %(prog)s -v """ ) parser.add_argument('-s', '--ssid', default='ClubHouse2G', help='WiFi SSID (default: ClubHouse2G)') parser.add_argument('-p', '--password', default='ez2remember', help='WiFi password (default: ez2remember)') parser.add_argument('--start-ip', default='192.168.1.51', help='Starting IP address (default: 192.168.1.51)') parser.add_argument('-g', '--gateway', default='192.168.1.1', help='Gateway IP (default: 192.168.1.1)') parser.add_argument('-m', '--netmask', default='255.255.255.0', help='Network mask (default: 255.255.255.0)') parser.add_argument('-v', '--verbose', action='store_true', help='Show device responses') parser.add_argument('-w', '--wait', type=int, default=30, help='Seconds to wait for connections (default: 30)') args = parser.parse_args() # Reconfigure all devices ok_count = reconfig_devices( ssid=args.ssid, password=args.password, start_ip=args.start_ip, gateway=args.gateway, netmask=args.netmask, verbose=args.verbose ) # Wait for connections if ok_count > 0: print(f"\nWaiting {args.wait}s for WiFi connections...") time.sleep(args.wait) print("Done!") print() print("Test commands:") # Extract IP info ip_parts = args.start_ip.split('.') ip_base = '.'.join(ip_parts[:3]) ip_start = int(ip_parts[3]) num_devices = len(sorted(glob.glob('/dev/ttyUSB*'))) print(f" # Ping all devices") print(f" for i in {{{ip_start}..{ip_start + num_devices - 1}}}; do ping -c 1 {ip_base}.$i & done; wait") print() print(f" # Check device status") print(f" ./check_device_status.py --reset") print() print(f" # Test first device") print(f" iperf -c {ip_base}.{ip_start}") print() sys.exit(0 if ok_count > 0 else 1) if __name__ == '__main__': main()