ESP32/config_device.py

338 lines
13 KiB
Python
Executable File

#!/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",
band="2.4G", bandwidth="HT20", reboot=True, 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"Band: {band}")
print(f"Bandwidth: {bandwidth}")
print(f"Reboot: {'Yes' if reboot else 'No'}")
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",
f"BAND:{band}",
f"BW:{bandwidth}",
"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...")
print("\nConfiguration being sent:")
for line in config_lines:
display_line = line if not line.startswith("PASS:") else "PASS:********"
print(f" {display_line}")
print()
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(f"Sent {bytes_written} bytes")
print("Waiting for response...")
time.sleep(3) # Give more time for response
# Read response
if ser.in_waiting:
response_size = ser.in_waiting
print(f"\n✓ Response received: {response_size} bytes")
response = ser.read(response_size).decode('utf-8', errors='ignore')
print("\nDevice response:")
print("-" * 70)
for line in response.split('\n')[:30]: # Show first 30 lines
if line.strip():
print(f" {line}")
print("-" * 70)
# Check for key indicators
success_indicators = []
warning_indicators = []
if "OK" in response:
success_indicators.append("✓ Configuration acknowledged (OK)")
if "Config saved" in response or "saved to NVS" in response:
success_indicators.append("✓ Config saved to NVS")
if "got ip:" in response.lower():
success_indicators.append("✓ 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:
success_indicators.append(f" Assigned IP: {ip_match.group(1)}")
if "connected" in response.lower():
success_indicators.append("✓ WiFi connection established")
if "failed" in response.lower() or "disconnect" in response.lower():
warning_indicators.append("⚠ WiFi connection may have failed")
if "error" in response.lower():
warning_indicators.append("⚠ Error detected in response")
if success_indicators:
print("\nStatus indicators:")
for indicator in success_indicators:
print(f" {indicator}")
if warning_indicators:
print("\nWarnings:")
for warning in warning_indicators:
print(f" {warning}")
else:
print("\n⚠ No response from device")
print(" This could mean:")
print(" - Device is not running config handler")
print(" - Wrong serial port")
print(" - Baud rate mismatch")
# Reboot device if requested
if reboot:
print("\n" + "="*70)
print("Rebooting device...")
print("="*70)
log_verbose("Performing hardware reset via DTR/RTS", verbose)
# Standard ESP32 reset sequence
ser.dtr = False # DTR low
ser.rts = True # RTS high (EN low)
time.sleep(0.1)
ser.rts = False # RTS low (EN high)
time.sleep(0.1)
ser.dtr = True # DTR high
print("✓ Reset signal sent - waiting for boot...")
# Wait for boot messages
time.sleep(3) # Longer wait for full boot
if ser.in_waiting:
boot_msg = ser.read(ser.in_waiting).decode('utf-8', errors='ignore')
print("\nBoot messages:")
print("-" * 70)
for line in boot_msg.split('\n')[:40]: # Show more lines
if line.strip():
print(f" {line}")
print("-" * 70)
# Check boot status
boot_success = []
boot_warnings = []
if "WiFi config loaded from NVS" in boot_msg:
boot_success.append("✓ Config successfully loaded from NVS")
elif "No WiFi config" in boot_msg or "YELLOW LED" in boot_msg:
boot_warnings.append("✗ NO CONFIG found in NVS - this is the problem!")
boot_warnings.append(" Device does not see saved config")
if "got ip:" in boot_msg.lower():
boot_success.append("✓ Device connected to WiFi!")
import re
ip_match = re.search(r'got ip:(\d+\.\d+\.\d+\.\d+)', boot_msg, re.IGNORECASE)
if ip_match:
boot_success.append(f" Connected with IP: {ip_match.group(1)}")
if "WiFi CONNECTED" in boot_msg:
boot_success.append("✓ WiFi connection confirmed")
if "BLUE LED solid" in boot_msg or "BLUE solid" in boot_msg:
boot_success.append("✓ LED should be BLUE (connected)")
elif "YELLOW solid" in boot_msg or "YELLOW LED" in boot_msg:
boot_warnings.append("⚠ LED is YELLOW (no config)")
elif "RED" in boot_msg and "blink" in boot_msg:
boot_warnings.append("⚠ LED is RED (connection failed)")
if boot_success:
print("\nBoot Status - SUCCESS:")
for msg in boot_success:
print(f" {msg}")
if boot_warnings:
print("\nBoot Status - ISSUES:")
for msg in boot_warnings:
print(f" {msg}")
else:
print("\n⚠ No boot messages received")
print(" Device may still be booting...")
# 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"\n{'='*70}")
print("Configuration Summary")
print(f"{'='*70}")
print(f"Port: {port}")
print(f"Expected IP: {ip}")
print(f"SSID: {ssid}")
print(f"Band: {band}")
print(f"Bandwidth: {bandwidth}")
print(f"{'='*70}")
print("\nNext steps:")
print(f" 1. Check LED color:")
print(f" - GREEN = Connected ✓")
print(f" - YELLOW = No config saved ✗")
print(f" - RED blinking = Connection failed")
print(f" 2. Test connection:")
print(f" ping {ip}")
print(f" iperf -c {ip}")
print(f"\nIf LED is YELLOW:")
print(f" - Config is NOT being saved to NVS")
print(f" - Check if wifi_cfg.c is handling BAND: and BW: fields")
print(f" - Try running: idf.py monitor")
print(f" to see what the device is actually receiving")
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 (2.4GHz) with auto-reboot
%(prog)s -p /dev/ttyUSB0 -i 192.168.1.51
# 5GHz with HT40 bandwidth and auto-reboot
%(prog)s -p /dev/ttyUSB0 -i 192.168.1.81 -s ClubHouse5G -b 5G -B HT40
# Configure without rebooting (manual reboot required)
%(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 -r
# With verbose output to see boot messages
%(prog)s -p /dev/ttyUSB0 -i 192.168.1.51 -v
# Custom WiFi credentials on 2.4GHz
%(prog)s -p /dev/ttyUSB0 -i 192.168.1.52 -s MyWiFi -P mypass
# 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('-b', '--band', default='2.4G', choices=['2.4G', '5G'],
help='WiFi band: 2.4G or 5G (default: 2.4G)')
parser.add_argument('-B', '--bandwidth', default='HT20',
choices=['HT20', 'HT40'],
help='Channel bandwidth: HT20 or HT40 (default: HT20)')
parser.add_argument('-r', '--no-reboot', action='store_true',
help='Do NOT reboot device after configuration (default: will reboot)')
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,
band=args.band,
bandwidth=args.bandwidth,
reboot=not args.no_reboot, # Invert since flag is --no-reboot
verbose=args.verbose
)
sys.exit(0 if success else 1)
if __name__ == '__main__':
main()