diff --git a/components/iperf/iperf.c b/components/iperf/iperf.c index b9532d1..03b59f3 100644 --- a/components/iperf/iperf.c +++ b/components/iperf/iperf.c @@ -27,7 +27,7 @@ static EventGroupHandle_t s_iperf_event_group = NULL; #define IPERF_IP_READY_BIT (1 << 0) #define IPERF_STOP_REQ_BIT (1 << 1) -#define RATE_CHECK_INTERVAL_US 500000 +#define MIN_RATE_CHECK_INTERVAL_US 250000 #define MIN_PACING_INTERVAL_US 100 typedef struct { @@ -105,32 +105,36 @@ void iperf_print_status(void) { } // 3. Compute Session Bandwidth - // Logic: If running, use current time. If stopped, use stored end time. float avg_bw_mbps = 0.0f; if (s_session_start_time > 0) { int64_t end_t = (s_stats.running) ? esp_timer_get_time() : s_session_end_time; - if (end_t > s_session_start_time) { double duration_sec = (double)(end_t - s_session_start_time) / 1000000.0; - - // Only calc if duration is significant to avoid divide-by-tiny-number if (duration_sec > 0.001) { - // Total bits = packets * packet_size * 8 double total_bits = (double)s_session_packets * (double)s_iperf_ctrl.cfg.send_len * 8.0; avg_bw_mbps = (float)(total_bits / duration_sec / 1000000.0); } } } - // New Format: Standard Stats + // 4. Calculate State Percentages + double total_us = (double)(s_time_tx_us + s_time_slow_us + s_time_stalled_us); + if (total_us < 1.0) total_us = 1.0; // Prevent div/0 + + double pct_tx = ((double)s_time_tx_us / total_us) * 100.0; + double pct_slow = ((double)s_time_slow_us / total_us) * 100.0; + double pct_stalled = ((double)s_time_stalled_us / total_us) * 100.0; + + // Standard Stats printf("IPERF_STATUS: Src=%s, Dst=%s, Running=%d, Config=%" PRIu32 ", Actual=%" PRIu32 ", Err=%.1f%%, Pkts=%" PRIu64 ", AvgBW=%.2f Mbps\n", src_ip, dst_ip, s_stats.running, s_stats.config_pps, s_stats.actual_pps, err, s_session_packets, avg_bw_mbps); - // New Format: State Durations & Edges - printf("IPERF_STATES: TX=%.2fs (%lu), SLOW=%.2fs (%lu), STALLED=%.2fs (%lu)\n", - (double)s_time_tx_us/1000000.0, (unsigned long)s_edge_tx, - (double)s_time_slow_us/1000000.0, (unsigned long)s_edge_slow, - (double)s_time_stalled_us/1000000.0, (unsigned long)s_edge_stalled); + // New Format: Time + Percentage + Edges + // Example: TX=15.15s/28.5% (15) + printf("IPERF_STATES: TX=%.2fs/%.1f%% (%lu), SLOW=%.2fs/%.1f%% (%lu), STALLED=%.2fs/%.1f%% (%lu)\n", + (double)s_time_tx_us/1000000.0, pct_tx, (unsigned long)s_edge_tx, + (double)s_time_slow_us/1000000.0, pct_slow, (unsigned long)s_edge_slow, + (double)s_time_stalled_us/1000000.0, pct_stalled, (unsigned long)s_edge_stalled); } // --- Network Events --- diff --git a/control_iperf.py b/control_iperf.py index 2d1a8e2..17fff15 100755 --- a/control_iperf.py +++ b/control_iperf.py @@ -59,23 +59,26 @@ class SerialController(asyncio.Protocol): } elif "IPERF_STATES" in line: - m = re.search(r'TX=([\d\.]+)s \((\d+)\), SLOW=([\d\.]+)s \((\d+)\), STALLED=([\d\.]+)s \((\d+)\)', line) + # NEW REGEX: Matches "TX=15.15s/28.5% (15)" + # Groups: 1=Time, 2=Pct, 3=Count (Repeated for SLOW and STALLED) + m = re.search(r'TX=([\d\.]+)s/([\d\.]+)% \((\d+)\), SLOW=([\d\.]+)s/([\d\.]+)% \((\d+)\), STALLED=([\d\.]+)s/([\d\.]+)% \((\d+)\)', line) if m: self.status_data['states'] = { - 'tx_t': m.group(1), 'tx_c': m.group(2), - 'sl_t': m.group(3), 'sl_c': m.group(4), - 'st_t': m.group(5), 'st_c': m.group(6) + 'tx_t': m.group(1), 'tx_p': m.group(2), 'tx_c': m.group(3), + 'sl_t': m.group(4), 'sl_p': m.group(5), 'sl_c': m.group(6), + 'st_t': m.group(7), 'st_p': m.group(8), 'st_c': m.group(9) } if 'main' in self.status_data and 'states' in self.status_data: if not self.completion_future.done(): d = self.status_data['main'] s = self.status_data['states'] + # Updated Output Format output = (f"{d['src']} -> {d['dst']} | {d['run']}, " f"Cfg:{d['cfg']}, Act:{d['act']}, Err:{d['err']}%, Pkts:{d['pkts']}, BW:{d['bw']}M | " - f"TX:{s['tx_t']}s({s['tx_c']}) " - f"SL:{s['sl_t']}s({s['sl_c']}) " - f"ST:{s['st_t']}s({s['st_c']})") + f"TX:{s['tx_t']}s/{s['tx_p']}%({s['tx_c']}) " + f"SL:{s['sl_t']}s/{s['sl_p']}%({s['sl_c']}) " + f"ST:{s['st_t']}s/{s['st_p']}%({s['st_c']})") self.completion_future.set_result(output) self.transport.close() return diff --git a/esp32_deploy.py b/esp32_deploy.py index 16b60e2..ee78acb 100755 --- a/esp32_deploy.py +++ b/esp32_deploy.py @@ -588,19 +588,30 @@ async def run_deployment(args): max_c = args.max_concurrent if args.max_concurrent else (1 if args.devices and not args.config_only else DEFAULT_MAX_CONCURRENT_FLASH) flash_sem = asyncio.Semaphore(max_c) + tasks = [] for i, dev in enumerate(devs): # --- ROBUST IP CALCULATION LOGIC --- if args.ip_device_based: - # Mode A: Offset based on physical port number (e.g. 14 for ttyUSB14) - offset = extract_device_number(dev.device) - print(f" [{dev.device}] Using device-based IP offset: +{offset}") - else: - # Mode B: Sequential offset based on loop index - offset = i - print(f" [{dev.device}] Using sequential IP offset: +{offset}") + # Mode A: Offset based on physical port number (e.g. 14 for esp_port_14) + raw_port_number = extract_device_number(dev.device) + + # FIX: Subtract 1 from the raw port number to make the offset zero-based. + # This ensures esp_port_1 gets the exact --start-ip (offset 0). + offset = raw_port_number - 1 + + target_ip = str(start_ip + offset) + + # Display the result using the clear 'DEVICE IP' label + print(f" [{dev.device}] Using device-based IP offset: +{offset} (Raw Port: {raw_port_number}). DEVICE IP: {target_ip}") + else: + # Mode B: Sequential offset based on loop index (zero-based) + offset = i + target_ip = str(start_ip + offset) + + # Display the result using the clear 'DEVICE IP' label + print(f" [{dev.device}] Using sequential IP offset: +{offset}. DEVICE IP: {target_ip}") - target_ip = str(start_ip + offset) tasks.append(UnifiedDeployWorker(dev.device, target_ip, args, project_dir, flash_sem).run()) results = await asyncio.gather(*tasks)