#!/usr/bin/env python3 """ ESP32 WiFi Configuration Tool - With verbose mode """ import serial import time import sys import argparse def log_verbose(message, verbose=False): """Print message only if verbose is enabled""" if verbose: print(f"[VERBOSE] {message}") def config_device(port, ip, ssid="ClubHouse2G", password="ez2remember", gateway="192.168.1.1", netmask="255.255.255.0", verbose=False): """Configure ESP32 device via serial""" print(f"\n{'='*70}") print(f"ESP32 WiFi Configuration") print(f"{'='*70}") print(f"Port: {port}") print(f"SSID: {ssid}") print(f"Password: {'*' * len(password)}") print(f"IP: {ip}") print(f"Gateway: {gateway}") print(f"Netmask: {netmask}") print(f"Verbose: {verbose}") print(f"{'='*70}\n") try: # Open serial connection log_verbose(f"Opening serial port {port} at 115200 baud...", verbose) ser = serial.Serial(port, 115200, timeout=0.5, write_timeout=0.5) log_verbose(f"Serial port opened successfully", verbose) log_verbose(f"Port settings: {ser}", verbose) time.sleep(0.2) # Check if there's any data waiting if ser.in_waiting: log_verbose(f"{ser.in_waiting} bytes waiting in buffer", verbose) existing = ser.read(ser.in_waiting).decode('utf-8', errors='ignore') log_verbose(f"Existing data: {existing[:100]}", verbose) # Build config message config_lines = [ "CFG", f"SSID:{ssid}", f"PASS:{password}", f"IP:{ip}", f"MASK:{netmask}", f"GW:{gateway}", "DHCP:0", "END" ] config = '\n'.join(config_lines) + '\n' log_verbose(f"Config message size: {len(config)} bytes", verbose) if verbose: print("[VERBOSE] Config message:") for line in config_lines: display_line = line if not line.startswith("PASS:") else "PASS:********" print(f"[VERBOSE] {display_line}") # Send config print("Sending configuration...") start_time = time.time() bytes_written = ser.write(config.encode('utf-8')) ser.flush() send_time = time.time() - start_time log_verbose(f"Wrote {bytes_written} bytes in {send_time:.3f}s", verbose) print("Sent! Waiting for response...") time.sleep(2) # Read response if ser.in_waiting: response_size = ser.in_waiting log_verbose(f"Response available: {response_size} bytes", verbose) response = ser.read(response_size).decode('utf-8', errors='ignore') if verbose: print("[VERBOSE] Raw response:") for line in response.split('\n')[:20]: # Show first 20 lines if line.strip(): print(f"[VERBOSE] {line}") # Check for key indicators if "OK" in response: print("✓ Device acknowledged configuration (OK)") if "got ip:" in response.lower(): print("✓ Device connected to WiFi!") # Extract IP from response import re ip_match = re.search(r'got ip:(\d+\.\d+\.\d+\.\d+)', response, re.IGNORECASE) if ip_match: print(f" Assigned IP: {ip_match.group(1)}") if "connected" in response.lower(): print("✓ WiFi connection established") if "failed" in response.lower() or "disconnect" in response.lower(): print("✗ WiFi connection may have failed") if verbose: print("[VERBOSE] Check response above for error details") else: log_verbose("No immediate response from device", verbose) print("⚠ No response (device may still be processing)") # Get final port stats if verbose: log_verbose(f"Input buffer: {ser.in_waiting} bytes", verbose) log_verbose(f"Output buffer empty: {ser.out_waiting == 0}", verbose) ser.close() log_verbose("Serial port closed", verbose) print(f"\nConfiguration sent to {port}") print(f"Expected IP: {ip}") print(f"Test with: ping {ip}") print(f" iperf -c {ip}") return True except serial.SerialException as e: print(f"\n✗ Serial error: {e}") log_verbose(f"Serial exception details: {type(e).__name__}", verbose) print(" Is another program using this port? (idf.py monitor, screen, etc.)") return False except KeyboardInterrupt: print("\n\nConfiguration cancelled by user") if 'ser' in locals() and ser.is_open: ser.close() log_verbose("Serial port closed after interrupt", verbose) return False except Exception as e: print(f"\n✗ Error: {e}") if verbose: import traceback print("\n[VERBOSE] Full traceback:") traceback.print_exc() return False def main(): parser = argparse.ArgumentParser( description='Configure ESP32 WiFi via serial', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Basic configuration %(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 # With verbose output %(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 -v # Custom WiFi credentials %(prog)s -p /dev/ttyUSB0 -i 192.168.1.52 -s MyWiFi -P mypass -v # Custom gateway %(prog)s -p /dev/ttyUSB0 -i 10.0.0.100 -g 10.0.0.1 """ ) parser.add_argument('-p', '--port', required=True, help='Serial port (e.g., /dev/ttyUSB0)') parser.add_argument('-i', '--ip', required=True, help='Static IP address') 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('-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='Netmask (default: 255.255.255.0)') parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output (show detailed debug info)') args = parser.parse_args() success = config_device( port=args.port, ip=args.ip, ssid=args.ssid, password=args.password, gateway=args.gateway, netmask=args.netmask, verbose=args.verbose ) sys.exit(0 if success else 1) if __name__ == '__main__': main()