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
This commit is contained in:
parent
b9a4a7c45e
commit
49dc6962ba
|
|
@ -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 <ssid> -p <password>
|
||||
```
|
||||
|
||||
### iperf Commands
|
||||
```bash
|
||||
# TCP server
|
||||
iperf -s
|
||||
|
||||
# TCP client
|
||||
iperf -c <ip>
|
||||
|
||||
# UDP server
|
||||
iperf -s -u
|
||||
|
||||
# UDP client
|
||||
iperf -c <ip> -u
|
||||
|
||||
# Custom port
|
||||
iperf -s -p 5002
|
||||
|
||||
# Custom duration
|
||||
iperf -c <ip> -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
|
||||
```
|
||||
|
|
@ -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")
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
88
main/main.c
88
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", "<ssid>", "WiFi SSID");
|
||||
wifi_args.password = arg_str0("p", "password", "<password>", "WiFi password");
|
||||
wifi_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t wifi_cmd = {
|
||||
.command = "wifi",
|
||||
.help = "Configure WiFi credentials (wifi -s <ssid> -p <password>)",
|
||||
.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));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue