From 49dc6962bacb6ef8cfbe73f4d0c705037d9ee3d0 Mon Sep 17 00:00:00 2001 From: Bob Date: Sat, 8 Nov 2025 23:10:07 +0000 Subject: [PATCH] Add mass deployment system with static IP and WiFi configuration - Add static IP configuration via Kconfig - Add WiFi console command for runtime reconfiguration - Add mass flash script for automated deployment - Add device detection script - Add comprehensive deployment documentation --- MASS_DEPLOY.md | 363 +++++++++++++++++++++++++++++++++++++++++ detect_esp32.py | 115 +++++++++++++ flash_all.py | 308 ++++++++++++++++++++++++++++++++++ main/Kconfig.projbuild | 27 +++ main/main.c | 88 +++++++++- 5 files changed, 900 insertions(+), 1 deletion(-) create mode 100644 MASS_DEPLOY.md create mode 100755 detect_esp32.py create mode 100755 flash_all.py diff --git a/MASS_DEPLOY.md b/MASS_DEPLOY.md new file mode 100644 index 0000000..bed189c --- /dev/null +++ b/MASS_DEPLOY.md @@ -0,0 +1,363 @@ +# Mass ESP32 Deployment Guide + +Complete guide for flashing 32+ ESP32 devices with unique static IPs. + +## Overview + +This system allows you to: +- Flash multiple ESP32/ESP32-S2/ESP32-S3 devices automatically +- Assign unique static IP addresses to each device (192.168.1.50-192.168.1.81) +- Configure WiFi credentials during build +- Reconfigure WiFi after flashing via console + +## Prerequisites + +```bash +# Install Python dependencies +pip install pyserial + +# Ensure ESP-IDF is installed with all targets +cd ~/Code/esp32/esp-idf +./install.sh esp32,esp32s2,esp32s3 + +# Activate ESP-IDF environment +. ~/Code/esp32/esp-idf/export.sh +``` + +## Method 1: Automated Mass Flash (Recommended) + +### Prepare Your Devices + +1. Connect all ESP32 devices to USB hubs +2. Verify detection: + ```bash + cd ~/Code/esp32/esp32-iperf + python3 detect_esp32.py + ``` + +### Flash All Devices + +```bash +cd ~/Code/esp32/esp32-iperf + +# Dry run to see the plan +python3 flash_all.py \ + --ssid "YourWiFiSSID" \ + --password "YourPassword" \ + --start-ip 192.168.1.50 \ + --gateway 192.168.1.1 \ + --dry-run + +# Actually flash (this will take a while!) +python3 flash_all.py \ + --ssid "YourWiFiSSID" \ + --password "YourPassword" \ + --start-ip 192.168.1.50 \ + --gateway 192.168.1.1 + +# With chip probing (slower but more accurate) +python3 flash_all.py \ + --ssid "YourWiFiSSID" \ + --password "YourPassword" \ + --start-ip 192.168.1.50 \ + --probe +``` + +### Script Options + +- `--ssid`: WiFi network name (required) +- `--password`: WiFi password (required) +- `--start-ip`: Starting IP address (default: 192.168.1.50) +- `--gateway`: Gateway IP (default: 192.168.1.1) +- `--probe`: Probe each device to detect exact chip type (slower) +- `--dry-run`: Show plan without flashing +- `--project-dir`: Custom project directory + +### What the Script Does + +For each device: +1. Detects chip type (ESP32/ESP32-S2/ESP32-S3) +2. Calculates unique IP address (increments from start IP) +3. Creates custom sdkconfig.defaults with WiFi and IP settings +4. Sets the correct target (esp32/esp32s2/esp32s3) +5. Builds firmware with custom configuration +6. Flashes the device + +## Method 2: Manual Configuration Per Device + +If you want to flash devices one at a time or need custom settings: + +### Create sdkconfig.defaults + +```bash +cd ~/Code/esp32/esp32-iperf +cat > sdkconfig.defaults << EOF +# WiFi Configuration +CONFIG_WIFI_SSID="YourSSID" +CONFIG_WIFI_PASSWORD="YourPassword" +CONFIG_WIFI_MAXIMUM_RETRY=5 + +# Static IP Configuration +CONFIG_USE_STATIC_IP=y +CONFIG_STATIC_IP_ADDR="192.168.1.50" +CONFIG_STATIC_GATEWAY_ADDR="192.168.1.1" +CONFIG_STATIC_NETMASK_ADDR="255.255.255.0" +EOF +``` + +### Build and Flash + +```bash +# Set target (choose one) +idf.py set-target esp32 # for ESP32 +idf.py set-target esp32s2 # for ESP32-S2 +idf.py set-target esp32s3 # for ESP32-S3 + +# Build +idf.py build + +# Flash to specific device +idf.py -p /dev/ttyUSB0 flash monitor + +# For next device, edit sdkconfig.defaults with new IP +# Then clean and rebuild +idf.py fullclean +# ... edit sdkconfig.defaults ... +idf.py build +idf.py -p /dev/ttyUSB1 flash +``` + +## Method 3: Reconfigure After Flashing + +If devices are already flashed but need different WiFi credentials: + +### Via Console + +```bash +# Connect to device +idf.py -p /dev/ttyUSB0 monitor + +# At the prompt +iperf> wifi -s "NewSSID" -p "NewPassword" +``` + +### Via Script (Multiple Devices) + +Create a script to update all devices: + +```bash +#!/bin/bash +for port in /dev/ttyUSB{0..31}; do + echo "Updating $port..." + # Send commands via screen or minicom + screen -S esp_config $port 115200 -X stuff "wifi -s \"NewSSID\" -p \"NewPassword\"\n" +done +``` + +## IP Address Assignment + +Based on your 32 detected devices: + +``` +Device 1 -> /dev/ttyUSB0 -> 192.168.1.50 +Device 2 -> /dev/ttyUSB1 -> 192.168.1.51 +Device 3 -> /dev/ttyUSB2 -> 192.168.1.52 +... +Device 32 -> /dev/ttyUSB31 -> 192.168.1.81 +``` + +## Testing Your Deployment + +### Check Device Connectivity + +```bash +# Ping all devices +for i in {50..81}; do + ping -c 1 -W 1 192.168.1.$i && echo "192.168.1.$i is UP" || echo "192.168.1.$i is DOWN" +done +``` + +### Run iperf Tests + +```bash +# Start all devices as iperf servers +# (via console on each device) +iperf> iperf -s + +# From your PC, test each device +for i in {50..81}; do + echo "Testing 192.168.1.$i" + iperf -c 192.168.1.$i -t 5 +done +``` + +### Batch iperf Test Script + +```python +#!/usr/bin/env python3 +import subprocess +import sys + +start_ip = "192.168.1.50" +end_ip = "192.168.1.81" + +base = start_ip.rsplit('.', 1)[0] +start = int(start_ip.rsplit('.', 1)[1]) +end = int(end_ip.rsplit('.', 1)[1]) + +results = [] +for i in range(start, end + 1): + ip = f"{base}.{i}" + print(f"Testing {ip}...", end=' ', flush=True) + + result = subprocess.run( + ['iperf', '-c', ip, '-t', '5', '-f', 'm'], + capture_output=True, + text=True, + timeout=10 + ) + + if result.returncode == 0: + # Parse bandwidth from output + for line in result.stdout.split('\n'): + if 'Mbits/sec' in line: + bandwidth = line.split()[-2] + print(f"✓ {bandwidth} Mbits/sec") + results.append((ip, bandwidth)) + break + else: + print("✗ FAILED") + +print(f"\nTested {len(results)}/{end-start+1} devices successfully") +``` + +## Troubleshooting + +### Device Not Detected + +```bash +# Check USB connection +lsusb + +# Check permissions +sudo usermod -a -G dialout $USER +# Log out and back in + +# Check if port exists +ls -la /dev/ttyUSB* +``` + +### Flash Failed + +```bash +# Try holding BOOT button during flash +idf.py -p /dev/ttyUSB0 flash + +# Lower baud rate +idf.py -p /dev/ttyUSB0 -b 115200 flash + +# Erase flash first +idf.py -p /dev/ttyUSB0 erase-flash +idf.py -p /dev/ttyUSB0 flash +``` + +### WiFi Not Connecting + +```bash +# Monitor the device +idf.py -p /dev/ttyUSB0 monitor + +# Check logs for WiFi errors +# Try reconfiguring via console: +iperf> wifi -s "YourSSID" -p "YourPassword" +``` + +### IP Address Conflict + +```bash +# Check what's using the IP +ping 192.168.1.50 +arp -a | grep 192.168.1.50 + +# Reflash with different IP range +python3 flash_all.py \ + --ssid "YourSSID" \ + --password "YourPassword" \ + --start-ip 192.168.1.100 +``` + +## Console Commands Reference + +### WiFi Configuration +``` +wifi -s -p +``` + +### iperf Commands +```bash +# TCP server +iperf -s + +# TCP client +iperf -c + +# UDP server +iperf -s -u + +# UDP client +iperf -c -u + +# Custom port +iperf -s -p 5002 + +# Custom duration +iperf -c -t 30 + +# Stop running test +iperf -a +``` + +## Advanced: Parallel Flashing + +To flash multiple devices simultaneously: + +```bash +#!/bin/bash +# flash_parallel.sh + +# Flash 4 devices at once +idf.py -p /dev/ttyUSB0 flash & +idf.py -p /dev/ttyUSB1 flash & +idf.py -p /dev/ttyUSB2 flash & +idf.py -p /dev/ttyUSB3 flash & + +wait +echo "Batch complete" +``` + +Note: Each device still needs its own build with unique IP. + +## Production Deployment Workflow + +1. **Prepare**: Connect all devices, verify detection +2. **Flash**: Run mass flash script with dry-run first +3. **Verify**: Ping all IPs to confirm connectivity +4. **Test**: Run iperf from PC to each device +5. **Deploy**: Mount devices in test locations +6. **Monitor**: Use iperf console to run tests + +## File Structure + +``` +esp32-iperf/ +├── main/ +│ ├── main.c # Main app with WiFi +│ ├── iperf.c # iperf implementation +│ ├── iperf.h # iperf header +│ └── Kconfig.projbuild # Configuration options +├── CMakeLists.txt +├── README.md +├── flash_all.py # Mass flash script +├── detect_esp32.py # Device detection +└── sdkconfig.defaults # Auto-generated config +``` diff --git a/detect_esp32.py b/detect_esp32.py new file mode 100755 index 0000000..7b484e0 --- /dev/null +++ b/detect_esp32.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +""" +ESP32 USB Device Detection Script +Detects and lists ESP32 devices connected via USB +""" + +import serial.tools.list_ports + + +def detect_esp32_devices(): + """ + Detect ESP32 devices connected via USB. + Returns a list of tuples containing (port, description, hwid) + """ + esp32_devices = [] + + # Common USB vendor IDs used by ESP32 boards + ESP32_VID_PIDS = [ + ('10C4', 'EA60'), # Silicon Labs CP210x + ('1A86', '7523'), # CH340 + ('1A86', '55D4'), # CH9102 + ('0403', '6001'), # FTDI FT232 + ('0403', '6010'), # FTDI FT2232 + ('303A', '1001'), # Espressif USB JTAG/serial debug unit (ESP32-C3, ESP32-S3) + ('303A', '1002'), # Espressif USB JTAG/serial debug unit + ] + + # Keywords that might appear in ESP32 device descriptions + ESP32_KEYWORDS = [ + 'CP210', + 'CH340', + 'CH9102', + 'UART', + 'USB-SERIAL', + 'USB SERIAL', + 'JTAG', + 'ESP32', + ] + + # Get all available ports + ports = serial.tools.list_ports.comports() + + for port in ports: + # Check by VID:PID + if port.vid is not None and port.pid is not None: + vid = f"{port.vid:04X}" + pid = f"{port.pid:04X}" + + if any((vid == v and pid == p) for v, p in ESP32_VID_PIDS): + esp32_devices.append(port) + continue + + # Check by description keywords + description_upper = port.description.upper() + if any(keyword in description_upper for keyword in ESP32_KEYWORDS): + esp32_devices.append(port) + + return esp32_devices + + +def main(): + print("=" * 60) + print("ESP32 USB Device Detection") + print("=" * 60) + print() + + # Detect ESP32 devices + esp32_devices = detect_esp32_devices() + + if esp32_devices: + print(f"Found {len(esp32_devices)} ESP32 device(s):\n") + + for idx, device in enumerate(esp32_devices, 1): + print(f"Device {idx}:") + print(f" Port: {device.device}") + print(f" Description: {device.description}") + print(f" Hardware ID: {device.hwid}") + + if device.vid is not None and device.pid is not None: + print(f" VID:PID: {device.vid:04X}:{device.pid:04X}") + + if device.manufacturer: + print(f" Manufacturer: {device.manufacturer}") + + if device.serial_number: + print(f" Serial: {device.serial_number}") + + print() + + print("=" * 60) + print(f"Total ESP32 devices detected: {len(esp32_devices)}") + print("=" * 60) + else: + print("No ESP32 devices detected.") + print() + print("Available USB serial devices:") + all_ports = serial.tools.list_ports.comports() + + if all_ports: + for port in all_ports: + print(f" - {port.device}: {port.description}") + if port.vid is not None and port.pid is not None: + print(f" VID:PID = {port.vid:04X}:{port.pid:04X}") + else: + print(" No USB serial devices found.") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nDetection interrupted by user.") + except Exception as e: + print(f"\nError: {e}") + print("\nMake sure pyserial is installed: pip install pyserial") diff --git a/flash_all.py b/flash_all.py new file mode 100755 index 0000000..4fca28f --- /dev/null +++ b/flash_all.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python3 +""" +ESP32 Mass Flash Script +Automatically detects, configures, and flashes multiple ESP32 devices with unique IPs +""" + +import subprocess +import sys +import os +import time +import argparse +from pathlib import Path + +# Import the detection script +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +try: + import detect_esp32 +except ImportError: + print("Error: detect_esp32.py must be in the same directory") + sys.exit(1) + + +# Device type detection based on USB chip +def detect_device_type(port_info): + """ + Try to detect ESP32 variant based on USB chip and other heuristics. + Returns: 'esp32', 'esp32s2', 'esp32s3', or 'unknown' + """ + # Espressif's own USB JTAG is used in ESP32-C3 and ESP32-S3 + if port_info.vid == 0x303A: + if port_info.pid == 0x1001: + return 'esp32s3' # Most likely S3 + return 'esp32s3' # Default to S3 for Espressif USB + + # For FTDI and CP210x, we need to probe the chip + # Default assumption based on quantity in your setup + # You may need to adjust this logic + return 'esp32' # Default to ESP32 for FTDI/CP210x + + +def probe_chip_type(port): + """ + Probe the actual chip type using esptool.py + """ + try: + result = subprocess.run( + ['esptool.py', '--port', port, 'chip_id'], + capture_output=True, + text=True, + timeout=10 + ) + + output = result.stdout + result.stderr + + if 'ESP32-S3' in output: + return 'esp32s3' + elif 'ESP32-S2' in output: + return 'esp32s2' + elif 'ESP32-C3' in output: + return 'esp32c3' + elif 'ESP32' in output: + return 'esp32' + + except Exception as e: + print(f" Warning: Could not probe {port}: {e}") + + return 'unknown' + + +def create_sdkconfig(project_dir, ssid, password, ip_addr, gateway='192.168.1.1', netmask='255.255.255.0'): + """ + Create sdkconfig.defaults file with WiFi and IP configuration + """ + sdkconfig_path = os.path.join(project_dir, 'sdkconfig.defaults') + + config_content = f"""# WiFi Configuration +CONFIG_WIFI_SSID="{ssid}" +CONFIG_WIFI_PASSWORD="{password}" +CONFIG_WIFI_MAXIMUM_RETRY=5 + +# Static IP Configuration +CONFIG_USE_STATIC_IP=y +CONFIG_STATIC_IP_ADDR="{ip_addr}" +CONFIG_STATIC_GATEWAY_ADDR="{gateway}" +CONFIG_STATIC_NETMASK_ADDR="{netmask}" +""" + + with open(sdkconfig_path, 'w') as f: + f.write(config_content) + + print(f" Created sdkconfig.defaults with IP {ip_addr}") + + +def flash_device(port, chip_type, device_num, ssid, password, base_ip, project_dir): + """ + Configure and flash a single device + """ + print(f"\n{'='*60}") + print(f"Device {device_num}: {port} ({chip_type})") + print(f"{'='*60}") + + # Calculate IP address + base_parts = base_ip.split('.') + ip_last_octet = int(base_parts[3]) + device_num - 1 + + if ip_last_octet > 254: + print(f" ERROR: IP address overflow! Device {device_num} would exceed .254") + return False + + ip_addr = f"{base_parts[0]}.{base_parts[1]}.{base_parts[2]}.{ip_last_octet}" + + print(f" Assigned IP: {ip_addr}") + + # Create sdkconfig.defaults + create_sdkconfig(project_dir, ssid, password, ip_addr) + + # Clean previous build if target changed + print(" Cleaning previous build...") + subprocess.run(['idf.py', 'fullclean'], cwd=project_dir, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + # Set target + print(f" Setting target to {chip_type}...") + result = subprocess.run( + ['idf.py', 'set-target', chip_type], + cwd=project_dir, + capture_output=True, + text=True + ) + + if result.returncode != 0: + print(f" ERROR: Failed to set target: {result.stderr}") + return False + + # Build + print(f" Building for {chip_type}...") + result = subprocess.run( + ['idf.py', 'build'], + cwd=project_dir, + capture_output=True, + text=True + ) + + if result.returncode != 0: + print(f" ERROR: Build failed!") + print(result.stderr[-1000:]) # Print last 1000 chars of error + return False + + print(f" Build successful!") + + # Flash + print(f" Flashing to {port}...") + result = subprocess.run( + ['idf.py', '-p', port, 'flash'], + cwd=project_dir, + capture_output=True, + text=True + ) + + if result.returncode != 0: + print(f" ERROR: Flash failed!") + print(result.stderr[-1000:]) + return False + + print(f" ✓ Successfully flashed device {device_num} at {ip_addr}") + return True + + +def main(): + parser = argparse.ArgumentParser(description='Mass flash ESP32 devices with unique IPs') + parser.add_argument('--ssid', required=True, help='WiFi SSID') + parser.add_argument('--password', required=True, 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 iperf project directory') + parser.add_argument('--probe', action='store_true', + help='Probe each device to detect exact chip type (slower)') + parser.add_argument('--dry-run', action='store_true', + help='Show what would be done without flashing') + + args = parser.parse_args() + + # Find project directory + if args.project_dir: + project_dir = args.project_dir + else: + # Try to find it relative to script location + script_dir = os.path.dirname(os.path.abspath(__file__)) + project_dir = os.path.join(script_dir, 'esp32-iperf') + + if not os.path.exists(project_dir): + project_dir = os.path.join(os.path.expanduser('~/Code/esp32'), 'esp32-iperf') + + if not os.path.exists(project_dir): + print(f"ERROR: Project directory not found: {project_dir}") + print("Please specify --project-dir") + sys.exit(1) + + print(f"Using project directory: {project_dir}") + + # Detect devices + print("\nDetecting ESP32 devices...") + devices = detect_esp32.detect_esp32_devices() + + if not devices: + print("No ESP32 devices detected!") + sys.exit(1) + + print(f"Found {len(devices)} device(s)") + + # Detect chip types + device_list = [] + for idx, device in enumerate(devices, 1): + if args.probe: + print(f"Probing {device.device}...") + chip_type = probe_chip_type(device.device) + else: + chip_type = detect_device_type(device) + + device_list.append({ + 'number': idx, + 'port': device.device, + 'chip': chip_type, + 'info': device + }) + + # Display plan + print(f"\n{'='*60}") + print("FLASH PLAN") + print(f"{'='*60}") + print(f"SSID: {args.ssid}") + print(f"Starting IP: {args.start_ip}") + print(f"Gateway: {args.gateway}") + print() + + base_parts = args.start_ip.split('.') + for dev in device_list: + ip_last = int(base_parts[3]) + dev['number'] - 1 + ip = f"{base_parts[0]}.{base_parts[1]}.{base_parts[2]}.{ip_last}" + print(f"Device {dev['number']:2d}: {dev['port']} -> {dev['chip']:8s} -> {ip}") + + if args.dry_run: + print("\nDry run - no devices will be flashed") + return + + # Confirm + print(f"\n{'='*60}") + response = input("Proceed with flashing? (yes/no): ").strip().lower() + if response != 'yes': + print("Aborted.") + return + + # Flash devices + success_count = 0 + failed_devices = [] + + for dev in device_list: + try: + success = flash_device( + dev['port'], + dev['chip'], + dev['number'], + args.ssid, + args.password, + args.start_ip, + project_dir + ) + + if success: + success_count += 1 + else: + failed_devices.append(dev['number']) + + time.sleep(1) # Brief pause between devices + + except KeyboardInterrupt: + print("\n\nFlashing interrupted by user!") + break + except Exception as e: + print(f"\n ERROR: Exception during flash: {e}") + failed_devices.append(dev['number']) + + # Summary + print(f"\n{'='*60}") + print("FLASH SUMMARY") + print(f"{'='*60}") + print(f"Successfully flashed: {success_count}/{len(device_list)} devices") + + if failed_devices: + print(f"Failed devices: {', '.join(map(str, failed_devices))}") + + print(f"{'='*60}") + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print("\n\nInterrupted by user") + sys.exit(1) + except Exception as e: + print(f"\nFATAL ERROR: {e}") + import traceback + traceback.print_exc() + sys.exit(1) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 24bcd34..4b6ca60 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -18,4 +18,31 @@ menu "ESP32 iperf Configuration" help Set the Maximum retry to prevent station reconnecting to the AP unlimited when the AP doesn't exist. + config USE_STATIC_IP + bool "Use static IP address" + default n + help + Enable static IP address configuration instead of DHCP. + + config STATIC_IP_ADDR + string "Static IP address" + default "192.168.1.50" + depends on USE_STATIC_IP + help + Static IP address for this device. + + config STATIC_GATEWAY_ADDR + string "Gateway address" + default "192.168.1.1" + depends on USE_STATIC_IP + help + Gateway IP address. + + config STATIC_NETMASK_ADDR + string "Netmask" + default "255.255.255.0" + depends on USE_STATIC_IP + help + Netmask for the network. + endmenu diff --git a/main/main.c b/main/main.c index e3f19f8..76c1964 100644 --- a/main/main.c +++ b/main/main.c @@ -54,7 +54,24 @@ void wifi_init_sta(void) ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_create_default_wifi_sta(); + + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + +#ifdef CONFIG_USE_STATIC_IP + // Configure static IP + ESP_LOGI(TAG, "Configuring static IP: %s", CONFIG_STATIC_IP_ADDR); + + esp_netif_dhcpc_stop(sta_netif); + + esp_netif_ip_info_t ip_info; + memset(&ip_info, 0, sizeof(esp_netif_ip_info_t)); + + ip_info.ip.addr = ipaddr_addr(CONFIG_STATIC_IP_ADDR); + ip_info.gw.addr = ipaddr_addr(CONFIG_STATIC_GATEWAY_ADDR); + ip_info.netmask.addr = ipaddr_addr(CONFIG_STATIC_NETMASK_ADDR); + + ESP_ERROR_CHECK(esp_netif_set_ip_info(sta_netif, &ip_info)); +#endif wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); @@ -114,6 +131,74 @@ static struct { struct arg_end *end; } iperf_args; +/* Console command structure for wifi config */ +static struct { + struct arg_str *ssid; + struct arg_str *password; + struct arg_end *end; +} wifi_args; + +static int cmd_wifi(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&wifi_args); + if (nerrors != 0) { + arg_print_errors(stderr, wifi_args.end, argv[0]); + return 1; + } + + if (wifi_args.ssid->count == 0) { + ESP_LOGE(TAG, "Please provide SSID with -s option"); + return 1; + } + + wifi_config_t wifi_config; + memset(&wifi_config, 0, sizeof(wifi_config_t)); + + // Set SSID + strncpy((char *)wifi_config.sta.ssid, wifi_args.ssid->sval[0], sizeof(wifi_config.sta.ssid) - 1); + + // Set password if provided + if (wifi_args.password->count > 0) { + strncpy((char *)wifi_config.sta.password, wifi_args.password->sval[0], sizeof(wifi_config.sta.password) - 1); + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + } + + ESP_LOGI(TAG, "Updating WiFi configuration..."); + ESP_LOGI(TAG, "SSID: %s", wifi_config.sta.ssid); + + // Stop WiFi + esp_wifi_disconnect(); + esp_wifi_stop(); + + // Update config + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + + // Restart WiFi + ESP_ERROR_CHECK(esp_wifi_start()); + esp_wifi_connect(); + + ESP_LOGI(TAG, "WiFi configuration updated. Attempting to connect..."); + + return 0; +} + +static void register_wifi(void) +{ + wifi_args.ssid = arg_str1("s", "ssid", "", "WiFi SSID"); + wifi_args.password = arg_str0("p", "password", "", "WiFi password"); + wifi_args.end = arg_end(1); + + const esp_console_cmd_t wifi_cmd = { + .command = "wifi", + .help = "Configure WiFi credentials (wifi -s -p )", + .hint = NULL, + .func = &cmd_wifi, + .argtable = &wifi_args + }; + + ESP_ERROR_CHECK(esp_console_cmd_register(&wifi_cmd)); +} + static int cmd_iperf(int argc, char **argv) { int nerrors = arg_parse(argc, argv, (void **)&iperf_args); @@ -215,6 +300,7 @@ static void initialize_console(void) ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); register_iperf(); + register_wifi(); ESP_ERROR_CHECK(esp_console_start_repl(repl)); }