From 289efa97a658b420297f431d09f9b0463b1947a6 Mon Sep 17 00:00:00 2001 From: Bob Date: Tue, 11 Nov 2025 14:57:56 -0800 Subject: [PATCH] support scripts --- DEPLOYMENT_GUIDE.md | 66 +++++++++++ QUICK_START.md | 70 ++++++++++++ mass_deploy.sh | 126 +++++++++++++++++++++ mass_deploy_enhanced.sh | 244 ++++++++++++++++++++++++++++++++++++++++ reconfig_simple.py | 52 +++++++++ test_devices.sh | 135 ++++++++++++++++++++++ 6 files changed, 693 insertions(+) create mode 100644 DEPLOYMENT_GUIDE.md create mode 100644 QUICK_START.md create mode 100755 mass_deploy.sh create mode 100755 mass_deploy_enhanced.sh create mode 100755 reconfig_simple.py create mode 100755 test_devices.sh diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md new file mode 100644 index 0000000..4729248 --- /dev/null +++ b/DEPLOYMENT_GUIDE.md @@ -0,0 +1,66 @@ +# ESP32 Mass Deployment Scripts + +Scripts for deploying, configuring, and testing multiple ESP32 devices in parallel. + +## Scripts + +### 1. `mass_deploy.sh` - Basic Deployment +Simple parallel flashing and configuration. + +**Usage:** +```bash +chmod +x mass_deploy.sh +PASSWORD='your_wifi_password' ./mass_deploy.sh ~/Code/esp32/esp32-iperf +``` + +### 2. `mass_deploy_enhanced.sh` - Production Deployment (RECOMMENDED) +Enhanced version with retry logic and verification. + +**Usage:** +```bash +chmod +x mass_deploy_enhanced.sh + +# Basic usage +PASSWORD='your_wifi_password' ./mass_deploy_enhanced.sh ~/Code/esp32/esp32-iperf + +# With custom settings +PASSWORD='mypass' \ +SSID='MyNetwork' \ +START_IP='192.168.1.100' \ +./mass_deploy_enhanced.sh +``` + +**Environment Variables:** +- `PASSWORD` - WiFi password (required) +- `SSID` - WiFi SSID (default: ClubHouse2G) +- `START_IP` - Starting IP (default: 192.168.1.51) +- `MAX_RETRIES` - Retry attempts (default: 2) +- `VERIFY_PING` - Test after config (default: true) + +### 3. `test_devices.sh` - Device Testing +Tests all deployed devices with iperf. + +**Usage:** +```bash +chmod +x test_devices.sh +NUM_DEVICES=32 ./test_devices.sh +``` + +## Quick Start + +```bash +# 1. Connect all ESP32 devices via USB + +# 2. Deploy with one command +PASSWORD='your_wifi_pass' ./mass_deploy_enhanced.sh ~/Code/esp32/esp32-iperf + +# 3. Test all devices +NUM_DEVICES=32 ./test_devices.sh +``` + +## Performance + +**Before:** 60-90 minutes (sequential) +**After:** 15-20 minutes (parallel) ⚡ + +See DEPLOYMENT_GUIDE.md for full documentation. diff --git a/QUICK_START.md b/QUICK_START.md new file mode 100644 index 0000000..10f5aec --- /dev/null +++ b/QUICK_START.md @@ -0,0 +1,70 @@ +# ESP32 Mass Deployment - Quick Reference + +## Files You Need + +1. **mass_deploy_enhanced.sh** - Main deployment script (RECOMMENDED) +2. **test_devices.sh** - Testing script +3. **DEPLOYMENT_GUIDE.md** - Full documentation + +## One-Line Deployment + +```bash +PASSWORD='your_wifi_pass' ./mass_deploy_enhanced.sh ~/Code/esp32/esp32-iperf +``` + +## Common Commands + +### Deploy 32 devices +```bash +PASSWORD='mypass' ./mass_deploy_enhanced.sh +``` + +### Test all devices +```bash +NUM_DEVICES=32 ./test_devices.sh +``` + +### Custom IP range +```bash +PASSWORD='mypass' START_IP='192.168.1.100' ./mass_deploy_enhanced.sh +``` + +### Different WiFi network +```bash +PASSWORD='newpass' SSID='NewNetwork' GATEWAY='192.168.2.1' START_IP='192.168.2.50' ./mass_deploy_enhanced.sh +``` + +## IP Address Scheme + +Default: `192.168.1.51 + device_index` +- Device 0 → 192.168.1.51 +- Device 1 → 192.168.1.52 +- Device 31 → 192.168.1.82 + +## Time Savings + +- **Old way:** 60-90 minutes for 32 devices +- **New way:** 15-20 minutes for 32 devices +- **Speedup:** 4-5x faster! ⚡ + +## Troubleshooting + +**No devices found?** +```bash +ls /dev/ttyUSB* /dev/ttyACM* +sudo usermod -a -G dialout $USER # then logout/login +``` + +**Flash failed?** +```bash +BAUD_RATE=115200 PASSWORD='pass' ./mass_deploy_enhanced.sh +``` + +**Can't ping devices?** +- Check WiFi password +- Wait 30 seconds after deployment +- Verify network supports static IPs + +**iperf connection refused?** +- Device still booting (wait 30s) +- Check logs: `cat /tmp/esp32_deploy_*.log` diff --git a/mass_deploy.sh b/mass_deploy.sh new file mode 100755 index 0000000..8e8270e --- /dev/null +++ b/mass_deploy.sh @@ -0,0 +1,126 @@ +#!/bin/bash +# ESP32 Mass Deployment Script +# Builds firmware once, then flashes and configures multiple devices in parallel + +set -e + +# Configuration +PROJECT_DIR="${1:-$PWD}" +SSID="${SSID:-ClubHouse2G}" +PASSWORD="${PASSWORD:-your_password_here}" +START_IP="${START_IP:-192.168.1.51}" +NETMASK="${NETMASK:-255.255.255.0}" +GATEWAY="${GATEWAY:-192.168.1.1}" +BAUD_RATE="${BAUD_RATE:-460800}" + +# Parse starting IP +IFS='.' read -r -a IP_PARTS <<< "$START_IP" +IP_BASE="${IP_PARTS[0]}.${IP_PARTS[1]}.${IP_PARTS[2]}" +IP_START=${IP_PARTS[3]} + +echo "==========================================" +echo "ESP32 Mass Deployment" +echo "==========================================" +echo "Project: $PROJECT_DIR" +echo "SSID: $SSID" +echo "IP Range: ${IP_BASE}.${IP_START}+" +echo "Gateway: $GATEWAY" +echo "Netmask: $NETMASK" +echo "==========================================" + +# Step 1: Build firmware once +echo "" +echo "[1/3] Building firmware..." +cd "$PROJECT_DIR" +idf.py build +echo "✓ Build complete" + +# Step 2: Find all connected ESP32 devices +echo "" +echo "[2/3] Detecting ESP32 devices..." +DEVICES=($(ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null || true)) + +if [ ${#DEVICES[@]} -eq 0 ]; then + echo "ERROR: No devices found!" + exit 1 +fi + +echo "Found ${#DEVICES[@]} device(s):" +for i in "${!DEVICES[@]}"; do + echo " [$i] ${DEVICES[$i]}" +done + +# Step 3: Flash and configure in parallel +echo "" +echo "[3/3] Flashing and configuring devices..." +echo "" + +flash_and_configure() { + local INDEX=$1 + local DEVICE=$2 + local IP_ADDR="${IP_BASE}.$((IP_START + INDEX))" + local LOG_FILE="/tmp/esp32_deploy_${INDEX}.log" + + { + echo "=== Device $INDEX: $DEVICE ===" + echo "Target IP: $IP_ADDR" + + # Flash + echo "Flashing..." + idf.py -p "$DEVICE" -b "$BAUD_RATE" flash 2>&1 | grep -E "(Connecting|Wrote|Hash|Hard resetting|ERROR)" || true + + # Wait for boot + sleep 3 + + # Configure WiFi + echo "Configuring WiFi..." + { + echo "CFG" + echo "SSID:$SSID" + echo "PASS:$PASSWORD" + echo "IP:$IP_ADDR" + echo "MASK:$NETMASK" + echo "GW:$GATEWAY" + echo "DHCP:0" + echo "END" + } > "$DEVICE" + + # Wait for OK response + sleep 2 + + echo "✓ Device $INDEX complete: $IP_ADDR" + + } > "$LOG_FILE" 2>&1 + + # Show summary + echo "[Device $INDEX] ${DEVICES[$INDEX]} → $IP_ADDR [DONE]" +} + +# Export function and variables for parallel execution +export -f flash_and_configure +export PROJECT_DIR SSID PASSWORD IP_BASE IP_START NETMASK GATEWAY BAUD_RATE + +# Launch parallel jobs +for i in "${!DEVICES[@]}"; do + flash_and_configure "$i" "${DEVICES[$i]}" & +done + +# Wait for all background jobs +wait + +echo "" +echo "==========================================" +echo "Deployment Complete!" +echo "==========================================" +echo "" +echo "Configured devices:" +for i in "${!DEVICES[@]}"; do + IP_ADDR="${IP_BASE}.$((IP_START + i))" + echo " ${DEVICES[$i]} → $IP_ADDR" +done + +echo "" +echo "Logs saved to: /tmp/esp32_deploy_*.log" +echo "" +echo "Test with: iperf -c ${IP_BASE}.${IP_START}" +echo "" diff --git a/mass_deploy_enhanced.sh b/mass_deploy_enhanced.sh new file mode 100755 index 0000000..b9694e5 --- /dev/null +++ b/mass_deploy_enhanced.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# ESP32 Mass Deployment Script (Enhanced) +# Features: parallel flashing, auto-retry, verification, progress tracking + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +PROJECT_DIR="${1:-$PWD}" +SSID="${SSID:-ClubHouse2G}" +PASSWORD="${PASSWORD:-}" +START_IP="${START_IP:-192.168.1.51}" +NETMASK="${NETMASK:-255.255.255.0}" +GATEWAY="${GATEWAY:-192.168.1.1}" +BAUD_RATE="${BAUD_RATE:-460800}" +MAX_RETRIES="${MAX_RETRIES:-2}" +VERIFY_PING="${VERIFY_PING:-true}" + +# Parse starting IP +IFS='.' read -r -a IP_PARTS <<< "$START_IP" +IP_BASE="${IP_PARTS[0]}.${IP_PARTS[1]}.${IP_PARTS[2]}" +IP_START=${IP_PARTS[3]} + +# Check for password +if [ -z "$PASSWORD" ]; then + echo -e "${RED}ERROR: WiFi password not set!${NC}" + echo "Usage: PASSWORD='your_wifi_pass' $0 [project_dir]" + echo "" + echo "Or set via environment:" + echo " export PASSWORD='your_wifi_pass'" + echo " export SSID='YourSSID' # Default: ClubHouse2G" + echo " export START_IP='192.168.1.51' # Default: 192.168.1.51" + echo " $0" + exit 1 +fi + +print_banner() { + echo "" + echo -e "${BLUE}==========================================" + echo "ESP32 Mass Deployment Tool" + echo -e "==========================================${NC}" + echo "Project: $PROJECT_DIR" + echo "SSID: $SSID" + echo "IP Range: ${IP_BASE}.${IP_START}+" + echo "Gateway: $GATEWAY" + echo "Netmask: $NETMASK" + echo "Verify: $VERIFY_PING" + echo -e "${BLUE}==========================================${NC}" +} + +print_banner + +# Step 1: Build firmware +echo "" +echo -e "${YELLOW}[1/4] Building firmware...${NC}" +cd "$PROJECT_DIR" +if ! idf.py build; then + echo -e "${RED}✗ Build failed!${NC}" + exit 1 +fi +echo -e "${GREEN}✓ Build complete${NC}" + +# Step 2: Detect devices +echo "" +echo -e "${YELLOW}[2/4] Detecting ESP32 devices...${NC}" +DEVICES=($(ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null || true)) + +if [ ${#DEVICES[@]} -eq 0 ]; then + echo -e "${RED}ERROR: No devices found!${NC}" + echo "Connect ESP32 devices via USB and try again." + exit 1 +fi + +echo -e "${GREEN}Found ${#DEVICES[@]} device(s):${NC}" +for i in "${!DEVICES[@]}"; do + IP_ADDR="${IP_BASE}.$((IP_START + i))" + echo " [$i] ${DEVICES[$i]} → $IP_ADDR" +done + +# Step 3: Flash and configure +echo "" +echo -e "${YELLOW}[3/4] Flashing and configuring...${NC}" +echo "" + +flash_and_configure() { + local INDEX=$1 + local DEVICE=$2 + local IP_ADDR="${IP_BASE}.$((IP_START + INDEX))" + local LOG_FILE="/tmp/esp32_deploy_${INDEX}.log" + local STATUS_FILE="/tmp/esp32_status_${INDEX}" + + { + for ATTEMPT in $(seq 1 $MAX_RETRIES); do + echo "=== Device $INDEX: $DEVICE (Attempt $ATTEMPT/$MAX_RETRIES) ===" + echo "Target IP: $IP_ADDR" + + # Flash + echo "Flashing..." + if idf.py -p "$DEVICE" -b "$BAUD_RATE" flash 2>&1; then + echo "✓ Flash successful" + else + echo "✗ Flash failed on attempt $ATTEMPT" + if [ $ATTEMPT -eq $MAX_RETRIES ]; then + echo "FAILED" > "$STATUS_FILE" + exit 1 + fi + sleep 2 + continue + fi + + # Wait for boot + sleep 3 + + # Configure WiFi + echo "Configuring WiFi..." + { + echo "CFG" + echo "SSID:$SSID" + echo "PASS:$PASSWORD" + echo "IP:$IP_ADDR" + echo "MASK:$NETMASK" + echo "GW:$GATEWAY" + echo "DHCP:0" + echo "END" + } > "$DEVICE" 2>/dev/null || true + + # Wait for network + sleep 5 + + # Verify if requested + if [ "$VERIFY_PING" = "true" ]; then + echo "Verifying connectivity..." + if ping -c 2 -W 3 "$IP_ADDR" > /dev/null 2>&1; then + echo "✓ Ping successful" + echo "SUCCESS" > "$STATUS_FILE" + break + else + echo "✗ Ping failed on attempt $ATTEMPT" + if [ $ATTEMPT -eq $MAX_RETRIES ]; then + echo "PING_FAIL" > "$STATUS_FILE" + fi + fi + else + echo "SUCCESS" > "$STATUS_FILE" + break + fi + + sleep 2 + done + + } > "$LOG_FILE" 2>&1 + + # Show result + if [ -f "$STATUS_FILE" ]; then + STATUS=$(cat "$STATUS_FILE") + if [ "$STATUS" = "SUCCESS" ]; then + echo -e "${GREEN}[Device $INDEX] ${DEVICES[$INDEX]} → $IP_ADDR [OK]${NC}" + elif [ "$STATUS" = "PING_FAIL" ]; then + echo -e "${YELLOW}[Device $INDEX] ${DEVICES[$INDEX]} → $IP_ADDR [FLASHED, NO PING]${NC}" + else + echo -e "${RED}[Device $INDEX] ${DEVICES[$INDEX]} → $IP_ADDR [FAILED]${NC}" + fi + fi +} + +export -f flash_and_configure +export PROJECT_DIR SSID PASSWORD IP_BASE IP_START NETMASK GATEWAY BAUD_RATE MAX_RETRIES VERIFY_PING +export RED GREEN YELLOW BLUE NC + +# Clean old status files +rm -f /tmp/esp32_status_* /tmp/esp32_deploy_*.log + +# Launch parallel jobs +for i in "${!DEVICES[@]}"; do + flash_and_configure "$i" "${DEVICES[$i]}" & +done + +# Wait for completion +wait + +# Step 4: Summary +echo "" +echo -e "${YELLOW}[4/4] Deployment Summary${NC}" +echo -e "${BLUE}==========================================${NC}" + +SUCCESS_COUNT=0 +FAILED_COUNT=0 +PING_FAIL_COUNT=0 + +for i in "${!DEVICES[@]}"; do + IP_ADDR="${IP_BASE}.$((IP_START + i))" + STATUS_FILE="/tmp/esp32_status_${i}" + + if [ -f "$STATUS_FILE" ]; then + STATUS=$(cat "$STATUS_FILE") + case "$STATUS" in + SUCCESS) + echo -e "${GREEN}✓${NC} ${DEVICES[$i]} → $IP_ADDR" + ((SUCCESS_COUNT++)) + ;; + PING_FAIL) + echo -e "${YELLOW}⚠${NC} ${DEVICES[$i]} → $IP_ADDR (no ping response)" + ((PING_FAIL_COUNT++)) + ;; + *) + echo -e "${RED}✗${NC} ${DEVICES[$i]} → $IP_ADDR (failed)" + ((FAILED_COUNT++)) + ;; + esac + else + echo -e "${RED}✗${NC} ${DEVICES[$i]} → $IP_ADDR (no status)" + ((FAILED_COUNT++)) + fi +done + +echo -e "${BLUE}==========================================${NC}" +echo -e "Total: ${#DEVICES[@]} devices" +echo -e "${GREEN}Success: $SUCCESS_COUNT${NC}" +if [ $PING_FAIL_COUNT -gt 0 ]; then + echo -e "${YELLOW}Warning: $PING_FAIL_COUNT (flashed but no ping)${NC}" +fi +if [ $FAILED_COUNT -gt 0 ]; then + echo -e "${RED}Failed: $FAILED_COUNT${NC}" +fi +echo -e "${BLUE}==========================================${NC}" + +echo "" +echo "Logs: /tmp/esp32_deploy_*.log" +echo "" +echo "Test commands:" +echo " iperf -c ${IP_BASE}.${IP_START}" +echo " for i in {${IP_START}..$((IP_START + ${#DEVICES[@]} - 1))}; do ping -c 1 ${IP_BASE}.\$i & done; wait" +echo "" + +# Cleanup status files +rm -f /tmp/esp32_status_* + +exit $FAILED_COUNT diff --git a/reconfig_simple.py b/reconfig_simple.py new file mode 100755 index 0000000..febe0f0 --- /dev/null +++ b/reconfig_simple.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +import serial +import time +import glob + +SSID = "ClubHouse2G" +PASSWORD = "ez2remember" +START_IP = 51 + +devices = sorted(glob.glob('/dev/ttyUSB*')) +print(f"Found {len(devices)} devices\n") + +for idx, dev in enumerate(devices): + ip = f"192.168.1.{START_IP + idx}" + print(f"[{idx}] Configuring {dev} → {ip}") + + 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(b"MASK:255.255.255.0\n") + time.sleep(0.1) + ser.write(b"GW:192.168.1.1\n") + 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 'OK' in response: + print(f" ✓ Got OK response") + + ser.close() + except Exception as e: + print(f" ✗ Error: {e}") + + time.sleep(0.5) + +print("\nWaiting 30s for connections...") +time.sleep(30) +print("Done! Test with: NUM_DEVICES=31 ./test_devices.sh") diff --git a/test_devices.sh b/test_devices.sh new file mode 100755 index 0000000..d8a6eb0 --- /dev/null +++ b/test_devices.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# ESP32 Device Testing Script +# Tests all deployed ESP32 devices with iperf + +# Configuration +START_IP="${START_IP:-192.168.1.51}" +NUM_DEVICES="${NUM_DEVICES:-10}" +IPERF_TIME="${IPERF_TIME:-5}" +PROTOCOL="${PROTOCOL:-tcp}" # tcp or udp + +# Parse starting IP +IFS='.' read -r -a IP_PARTS <<< "$START_IP" +IP_BASE="${IP_PARTS[0]}.${IP_PARTS[1]}.${IP_PARTS[2]}" +IP_START=${IP_PARTS[3]} + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +echo -e "${BLUE}==========================================" +echo "ESP32 Device Testing" +echo -e "==========================================${NC}" +echo "IP Range: ${IP_BASE}.${IP_START} - ${IP_BASE}.$((IP_START + NUM_DEVICES - 1))" +echo "Protocol: $PROTOCOL" +echo "Duration: ${IPERF_TIME}s per device" +echo -e "${BLUE}==========================================${NC}" +echo "" + +test_device() { + local INDEX=$1 + local IP_ADDR="${IP_BASE}.$((IP_START + INDEX))" + local RESULT_FILE="/tmp/iperf_result_${INDEX}.txt" + + # Test ping + if ! ping -c 1 -W 2 "$IP_ADDR" > /dev/null 2>&1; then + echo -e "${RED}✗ Device $INDEX ($IP_ADDR): No ping response${NC}" + echo "FAIL" > "$RESULT_FILE" + return 1 + fi + + # Test iperf + if [ "$PROTOCOL" = "udp" ]; then + IPERF_CMD="iperf -c $IP_ADDR -u -b 100M -t $IPERF_TIME" + else + IPERF_CMD="iperf -c $IP_ADDR -t $IPERF_TIME" + fi + + if OUTPUT=$($IPERF_CMD 2>&1); then + # Extract bandwidth + BANDWIDTH=$(echo "$OUTPUT" | grep -oP '\d+(\.\d+)?\s+(K|M|G)bits/sec' | tail -1) + echo -e "${GREEN}✓ Device $INDEX ($IP_ADDR): $BANDWIDTH${NC}" + echo "SUCCESS:$BANDWIDTH" > "$RESULT_FILE" + else + echo -e "${RED}✗ Device $INDEX ($IP_ADDR): iperf failed${NC}" + echo "FAIL" > "$RESULT_FILE" + return 1 + fi +} + +export -f test_device +export IP_BASE IP_START IPERF_TIME PROTOCOL GREEN RED YELLOW BLUE NC + +# Clean old results +rm -f /tmp/iperf_result_*.txt + +# Quick ping test first +echo -e "${YELLOW}Quick connectivity check...${NC}" +for i in $(seq 0 $((NUM_DEVICES - 1))); do + IP_ADDR="${IP_BASE}.$((IP_START + i))" + if ping -c 1 -W 1 "$IP_ADDR" > /dev/null 2>&1; then + echo -n "." + else + echo -n "x" + fi +done +echo "" +echo "" + +# Run iperf tests in parallel (batches of 4 to avoid overwhelming network) +echo -e "${YELLOW}Running iperf tests...${NC}" +BATCH_SIZE=4 + +for BATCH_START in $(seq 0 $BATCH_SIZE $((NUM_DEVICES - 1))); do + for i in $(seq $BATCH_START $(( BATCH_START + BATCH_SIZE - 1 ))); do + if [ $i -lt $NUM_DEVICES ]; then + test_device "$i" & + fi + done + wait +done + +# Summary +echo "" +echo -e "${BLUE}==========================================" +echo "Test Summary" +echo -e "==========================================${NC}" + +SUCCESS=0 +FAILED=0 +TOTAL_BANDWIDTH=0 + +for i in $(seq 0 $((NUM_DEVICES - 1))); do + RESULT_FILE="/tmp/iperf_result_${i}.txt" + if [ -f "$RESULT_FILE" ]; then + RESULT=$(cat "$RESULT_FILE") + if [[ "$RESULT" == SUCCESS:* ]]; then + ((SUCCESS++)) + else + ((FAILED++)) + fi + else + ((FAILED++)) + fi +done + +echo "Tested: $NUM_DEVICES devices" +echo -e "${GREEN}Success: $SUCCESS${NC}" +if [ $FAILED -gt 0 ]; then + echo -e "${RED}Failed: $FAILED${NC}" +fi + +SUCCESS_RATE=$(awk "BEGIN {printf \"%.1f\", ($SUCCESS / $NUM_DEVICES) * 100}") +echo "Rate: ${SUCCESS_RATE}%" + +echo -e "${BLUE}==========================================${NC}" + +# Cleanup +rm -f /tmp/iperf_result_*.txt + +if [ $FAILED -gt 0 ]; then + exit 1 +fi