build works

This commit is contained in:
Bob 2025-11-11 13:23:09 -08:00
parent a623e4e1e6
commit c03679358e
9 changed files with 115 additions and 893 deletions

View File

@ -1,6 +1,8 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-iperf)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(hello_world)

142
README.md
View File

@ -1,130 +1,60 @@
# ESP32 iperf
# esp32-iperf (ESP-IDF 6.x)
Network performance testing tool for ESP32 based on iperf2.
Minimal ESP32/ESP32-S3 iPerf firmware with **station mode** and optional **static IP**.
Tested on **ESP-IDF 6.0** (dev snapshot).
## Features
## Repo layout
- TCP and UDP client/server modes
- Bandwidth measurement
- Packet loss detection (UDP)
- Console-based control interface
- WiFi station mode support
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | Linux |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | ----- |
## Hardware Requirements
# Hello World Example
- ESP32, ESP32-S2, or ESP32-S3 development board
- WiFi network
Starts a FreeRTOS task to print "Hello World".
## Software Requirements
(See the README.md file in the upper level 'examples' directory for more information about examples.)
- ESP-IDF v5.0 or later
- iperf2 or iperf3 for testing with PC
## How to use example
## Building
Follow detailed instructions provided specifically for this example.
1. Set up ESP-IDF environment:
```bash
. $HOME/Code/esp32/esp-idf/export.sh
```
Select the instructions depending on Espressif chip installed on your development board:
2. Set the target chip (choose one):
```bash
# For ESP32
idf.py set-target esp32
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
# For ESP32-S2
idf.py set-target esp32s2
# For ESP32-S3
idf.py set-target esp32s3
```
## Example folder contents
3. Configure WiFi credentials:
```bash
idf.py menuconfig
```
Navigate to "ESP32 iperf Configuration" and set your SSID and password.
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
4. Build the project:
```bash
idf.py build
```
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
5. Flash to your device:
```bash
idf.py -p /dev/ttyUSB0 flash monitor
```
## Usage
### TCP Server Mode
On ESP32:
```
iperf> iperf -s
```
On PC:
```bash
iperf -c <ESP32_IP>
```
### TCP Client Mode
On PC:
```bash
iperf -s
```
On ESP32:
```
iperf> iperf -c <PC_IP>
```
### UDP Mode
Add `-u` flag for UDP testing:
```
iperf> iperf -s -u
iperf> iperf -c <IP> -u
```
### Options
- `-s` : Run as server
- `-c <ip>` : Run as client, connecting to server IP
- `-u` : Use UDP instead of TCP
- `-p <port>` : Port number (default: 5001)
- `-t <time>` : Test duration in seconds (default: 30)
- `-b <bw>` : Bandwidth limit for UDP in Mbps
- `-a` : Abort running test
### Examples
```bash
# TCP client test for 10 seconds
iperf -c 192.168.1.100 -t 10
# UDP server on port 5002
iperf -s -u -p 5002
# Stop running test
iperf -a
```
## Project Structure
Below is short explanation of remaining files in the project folder.
```
esp32-iperf/
├── CMakeLists.txt
├── main/
├── pytest_hello_world.py Python script used for automated testing
├── main
│ ├── CMakeLists.txt
│ ├── Kconfig.projbuild
│ ├── main.c # WiFi initialization and console
│ ├── iperf.c # iperf implementation
│ └── iperf.h # iperf header
└── README.md
│ └── hello_world_main.c
└── README.md This is the file you are currently reading
```
## Author
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
Bob - Creator of iperf2
## Troubleshooting
## License
* Program upload failure
Open source
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
## Technical support and feedback
Please use the following feedback channels:
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
We will get back to you as soon as possible.

View File

@ -1,203 +1,72 @@
#!/usr/bin/env python3
import argparse, os, sys, time, fnmatch
import os
import sys
import argparse
import serial
import time
from pathlib import Path
from typing import List, Dict
import serial, serial.tools.list_ports as list_ports
import subprocess
KNOWN_VIDS = {0x0403, 0x10C4, 0x1A86, 0x067B, 0x303A}
def run(cmd, cwd=None, check=True):
print(">>", " ".join(cmd))
p = subprocess.run(cmd, cwd=cwd)
if check and p.returncode != 0:
raise RuntimeError(f"Command failed ({p.returncode}): {' '.join(cmd)}")
return p.returncode
def detect_chip_type(port: str) -> str:
try:
out = subprocess.check_output(["esptool.py", "--port", port, "chip_id"], stderr=subprocess.STDOUT, text=True, timeout=6)
if "ESP32-S3" in out.upper(): return "ESP32-S3"
if "ESP32-S2" in out.upper(): return "ESP32-S2"
if "ESP32-C3" in out.upper(): return "ESP32-C3"
if "ESP32-C6" in out.upper(): return "ESP32-C6"
if "ESP32" in out.upper(): return "ESP32"
except Exception:
pass
return "Unknown"
def map_chip_to_idf_target(chip: str) -> str:
s = chip.upper()
if s.startswith("ESP32-S3"): return "esp32s3"
if s.startswith("ESP32-S2"): return "esp32s2"
if s.startswith("ESP32-C3"): return "esp32c3"
if s.startswith("ESP32-C6"): return "esp32c6"
if s.startswith("ESP32-H2"): return "esp32h2"
if s.startswith("ESP32"): return "esp32"
return "unknown"
def list_ports_filtered(patterns: List[str] = None):
ports = list_ports.comports()
out = []
for p in ports:
dev = p.device
if patterns:
if not any(fnmatch.fnmatch(dev, pat) for pat in patterns):
continue
else:
if not (dev.startswith("/dev/ttyUSB") or dev.startswith("/dev/ttyACM")):
continue
vid = getattr(p, "vid", None)
if vid is not None and vid not in KNOWN_VIDS:
continue
out.append(p)
return out
def ensure_target(project_dir: str, target: str):
if not target or target == "unknown":
raise ValueError("Unknown IDF target; cannot set-target")
run(["idf.py", "set-target", target], cwd=project_dir, check=True)
def flash_device(project_dir: str, port: str, idf_target: str, baud: int = 460800) -> bool:
try:
ensure_target(project_dir, idf_target)
run(["idf.py", "-p", port, "-b", str(baud), "flash"], cwd=project_dir, check=True)
return True
except Exception as e:
print(f" Flash failed on {port}: {e}")
return False
def toggle_reset(ser):
try:
ser.dtr = False
ser.rts = True
time.sleep(0.05)
ser.dtr = True
ser.rts = False
time.sleep(0.05)
except Exception:
pass
def send_wifi_config(port: str, ssid: str, password: str, ip: str, mask: str, gw: str, dhcp: bool, baud: int = 115200, retries: int = 3) -> bool:
for attempt in range(1, retries+1):
try:
print(f" [{port}] opening serial @ {baud} (attempt {attempt}/{retries})")
with serial.Serial(port, baudrate=baud, timeout=2) as ser:
time.sleep(0.3)
toggle_reset(ser)
time.sleep(0.8)
def send_cfg(port, ssid, password, dhcp, start_ip, mask, gw):
print(f"Connecting to {port}...")
with serial.Serial(port, baudrate=115200, timeout=2) as ser:
time.sleep(0.5)
ser.reset_input_buffer()
ser.reset_output_buffer()
ser.write(b"CFG\n")
ser.write(f"SSID:{ssid}\n".encode())
ser.write(f"PASS:{password}\n".encode())
if dhcp == 0:
ser.write(f"IP:{start_ip}\n".encode())
ser.write(f"MASK:{mask}\n".encode())
ser.write(f"GW:{gw}\n".encode())
ser.write(f"DHCP:{dhcp}\n".encode())
ser.write(b"END\n")
time.sleep(0.3)
lines = [
"CFG\n",
f"SSID:{ssid}\n" if ssid else "",
f"PASS:{password}\n" if password else "",
f"IP:{ip}\n" if ip else "",
f"MASK:{mask}\n" if mask else "",
f"GW:{gw}\n" if gw else "",
f"DHCP:{1 if dhcp else 0}\n",
"END\n",
]
payload = "".join([l for l in lines if l])
ser.write(payload.encode("utf-8"))
ser.flush()
t0 = time.time()
buf = b""
while time.time() - t0 < 3.0:
chunk = ser.read(64)
if chunk:
buf += chunk
if b"OK" in buf:
print(f" [{port}] config applied: {buf.decode(errors='ignore').strip()}")
return True
print(f" [{port}] no OK from device, got: {buf.decode(errors='ignore').strip()}")
except Exception as e:
print(f" [{port}] serial error: {e}")
time.sleep(0.6)
return False
print("\nDevice response:")
while ser.in_waiting:
sys.stdout.write(ser.read(ser.in_waiting).decode(errors='ignore'))
sys.stdout.flush()
time.sleep(0.1)
print("\n✅ Configuration sent successfully.")
def main():
ap = argparse.ArgumentParser(description="Mass flash ESP32 devices and push WiFi/IP config over serial.")
ap.add_argument("--project", required=True, help="Path to ESPIDF project")
ap.add_argument("--ssid", required=True, help="WiFi SSID")
ap.add_argument("--password", required=True, help="WiFi password")
ap.add_argument("--start-ip", default="192.168.1.50", help="Base IP (only used if --dhcp=0)")
ap.add_argument("--mask", default="255.255.255.0", help="Netmask for static IP")
ap.add_argument("--gw", default="192.168.1.1", help="Gateway for static IP")
ap.add_argument("--dhcp", type=int, choices=[0,1], default=1, help="1=use DHCP, 0=set static IPs")
ap.add_argument("--baud", type=int, default=460800, help="Flashing baud rate")
ap.add_argument("--cfg-baud", type=int, default=115200, help="Serial baud for config exchange")
ap.add_argument("--ports", help="Comma-separated globs to override port selection, e.g. '/dev/ttyUSB*,/dev/ttyACM*'")
ap.add_argument("--dry-run", action="store_true", help="Plan only; do not flash or configure")
args = ap.parse_args()
parser = argparse.ArgumentParser(description="Configure ESP32 Wi-Fi settings over serial")
parser.add_argument("--project", help="ESP-IDF project path (defaults to current working directory)")
parser.add_argument("--ssid", required=True)
parser.add_argument("--password", required=True)
parser.add_argument("--start-ip", help="Static IP address")
parser.add_argument("--mask", default="255.255.255.0")
parser.add_argument("--gw", help="Gateway address")
parser.add_argument("--dhcp", type=int, choices=[0,1], default=1)
parser.add_argument("--baud", type=int, default=460800)
parser.add_argument("--cfg-baud", type=int, default=115200)
parser.add_argument("--ports", nargs='+', help="Serial port(s), e.g., /dev/ttyUSB0 /dev/ttyUSB1")
parser.add_argument("--port", help="Single serial port (shorthand for --ports PORT)")
parser.add_argument("--dry-run", action="store_true")
project_dir = os.path.abspath(args.project)
if not os.path.isdir(project_dir):
print(f"Project directory not found: {project_dir}")
args = parser.parse_args()
# Default to current working directory
project_path = args.project or os.getcwd()
print(f"Using project directory: {project_path}")
# Resolve ports
ports = []
if args.port:
ports.append(args.port)
elif args.ports:
ports = args.ports
else:
print("❌ No serial port specified. Use --port or --ports.")
sys.exit(1)
patterns = [p.strip() for p in args.ports.split(",")] if args.ports else None
devices = list_ports_filtered(patterns)
if not devices:
print("No candidate USB serial ports found.")
sys.exit(2)
print(f"Found {len(devices)} devices.")
base = args.start_ip.split(".")
try:
base = [int(x) for x in base]
except Exception:
base = [192,168,1,50]
plan = []
for idx, dev in enumerate(devices, 1):
port = dev.device
chip = detect_chip_type(port)
target = map_chip_to_idf_target(chip)
ip = f"{base[0]}.{base[1]}.{base[2]}.{base[3] + idx - 1}" if args.dhcp == 0 else None
plan.append(dict(idx=idx, port=port, chip=chip, target=target, ip=ip))
print("\nPlan:")
for d in plan:
print(f" {d['idx']:2d} {d['port']} {d['chip']} -> target {d['target']} IP:{d['ip'] or 'DHCP'}")
# Apply configuration
for port in ports:
if args.dry_run:
print("\nDry run only.")
return
failed = []
for d in plan:
if d['target'] == 'unknown':
print(f"\nERROR: Unknown IDF target for {d['port']} (chip '{d['chip']}'). Skipping.")
failed.append(d['idx'])
continue
print(f"\nFlashing {d['port']} as {d['target']}...")
if not flash_device(project_dir, d['port'], d['target'], baud=args.baud):
failed.append(d['idx'])
continue
print(f"Configuring WiFi on {d['port']}...")
ok = send_wifi_config(
d['port'],
args.ssid,
args.password,
d['ip'],
args.mask if d['ip'] else None,
args.gw if d['ip'] else None,
dhcp=(args.dhcp == 1),
baud=args.cfg_baud,
)
if not ok:
print(f" WARN: config not acknowledged on {d['port']}")
if failed:
print(f"\nCompleted with flashing failures on: {failed}")
sys.exit(3)
print("\nAll done.")
print(f"[DRY RUN] Would send Wi-Fi config to {port}")
else:
send_cfg(port, args.ssid, args.password, args.dhcp, args.start_ip, args.mask, args.gw)
if __name__ == "__main__":
main()

View File

@ -1,8 +1,15 @@
idf_component_register(
SRCS
"main.c"
"iperf.c"
"wifi_cfg.c"
INCLUDE_DIRS
"."
)
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES
esp_wifi
nvs_flash
esp_netif
console
lwip
driver
esp_driver_uart
vfs
iperf
wifi_cfg
)

View File

@ -1,340 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_err.h"
#include "iperf.h"
static const char *TAG = "iperf";
typedef struct {
iperf_cfg_t cfg;
bool finish;
uint32_t total_len;
uint32_t buffer_len;
uint8_t *buffer;
uint32_t sockfd;
} iperf_ctrl_t;
static iperf_ctrl_t s_iperf_ctrl = {0};
static TaskHandle_t s_iperf_task_handle = NULL;
static void socket_send(int sockfd, const uint8_t *buffer, int len)
{
int actual_send = 0;
while (actual_send < len) {
int send_len = send(sockfd, buffer + actual_send, len - actual_send, 0);
if (send_len < 0) {
ESP_LOGE(TAG, "send failed: errno %d", errno);
break;
}
actual_send += send_len;
}
}
static int socket_recv(int sockfd, uint8_t *buffer, int len, TickType_t timeout_ticks)
{
struct timeval timeout;
timeout.tv_sec = timeout_ticks / configTICK_RATE_HZ;
timeout.tv_usec = (timeout_ticks % configTICK_RATE_HZ) * (1000000 / configTICK_RATE_HZ);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
ESP_LOGE(TAG, "setsockopt failed: errno %d", errno);
return -1;
}
return recv(sockfd, buffer, len, 0);
}
static esp_err_t iperf_start_tcp_server(iperf_ctrl_t *ctrl)
{
struct sockaddr_in addr;
int listen_sock;
int opt = 1;
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return ESP_FAIL;
}
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(ctrl->cfg.sport);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
ESP_LOGE(TAG, "Socket bind failed: errno %d", errno);
close(listen_sock);
return ESP_FAIL;
}
if (listen(listen_sock, 5) != 0) {
ESP_LOGE(TAG, "Socket listen failed: errno %d", errno);
close(listen_sock);
return ESP_FAIL;
}
ESP_LOGI(TAG, "TCP server listening on port %d", ctrl->cfg.sport);
while (!ctrl->finish) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
if (client_sock < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue;
}
ESP_LOGE(TAG, "Accept failed: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Client connected from %s", inet_ntoa(client_addr.sin_addr));
uint64_t total_len = 0;
int recv_len;
while (!ctrl->finish) {
recv_len = socket_recv(client_sock, ctrl->buffer, ctrl->buffer_len,
pdMS_TO_TICKS(IPERF_SOCKET_RX_TIMEOUT * 1000));
if (recv_len <= 0) {
break;
}
total_len += recv_len;
}
ESP_LOGI(TAG, "TCP server received: %" PRIu64 " bytes", total_len);
close(client_sock);
}
close(listen_sock);
return ESP_OK;
}
static esp_err_t iperf_start_tcp_client(iperf_ctrl_t *ctrl)
{
struct sockaddr_in addr;
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(ctrl->cfg.dport);
addr.sin_addr.s_addr = htonl(ctrl->cfg.dip);
ESP_LOGI(TAG, "Connecting to TCP server...");
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
ESP_LOGE(TAG, "Socket connect failed: errno %d", errno);
close(sockfd);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Connected to TCP server");
uint64_t total_len = 0;
uint32_t start_time = xTaskGetTickCount();
uint32_t end_time = start_time + pdMS_TO_TICKS(ctrl->cfg.time * 1000);
while (!ctrl->finish && xTaskGetTickCount() < end_time) {
socket_send(sockfd, ctrl->buffer, ctrl->buffer_len);
total_len += ctrl->buffer_len;
}
uint32_t actual_time = (xTaskGetTickCount() - start_time) / configTICK_RATE_HZ;
float bandwidth = (float)total_len * 8 / actual_time / 1000000; // Mbps
ESP_LOGI(TAG, "TCP client sent: %" PRIu64 " bytes in %" PRIu32 " seconds (%.2f Mbps)",
total_len, actual_time, bandwidth);
close(sockfd);
return ESP_OK;
}
static esp_err_t iperf_start_udp_server(iperf_ctrl_t *ctrl)
{
struct sockaddr_in addr;
int sockfd;
int opt = 1;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return ESP_FAIL;
}
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(ctrl->cfg.sport);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
ESP_LOGE(TAG, "Socket bind failed: errno %d", errno);
close(sockfd);
return ESP_FAIL;
}
ESP_LOGI(TAG, "UDP server listening on port %d", ctrl->cfg.sport);
uint64_t total_len = 0;
uint32_t packet_count = 0;
int32_t last_id = -1;
uint32_t lost_packets = 0;
while (!ctrl->finish) {
int recv_len = socket_recv(sockfd, ctrl->buffer, ctrl->buffer_len,
pdMS_TO_TICKS(IPERF_SOCKET_RX_TIMEOUT * 1000));
if (recv_len <= 0) {
continue;
}
total_len += recv_len;
packet_count++;
// Check for lost packets using UDP header
if (recv_len >= sizeof(udp_datagram)) {
udp_datagram *header = (udp_datagram *)ctrl->buffer;
int32_t current_id = ntohl(header->id);
if (last_id >= 0 && current_id > last_id + 1) {
lost_packets += (current_id - last_id - 1);
}
last_id = current_id;
}
}
float loss_rate = packet_count > 0 ? (float)lost_packets * 100 / packet_count : 0;
ESP_LOGI(TAG, "UDP server received: %" PRIu64 " bytes, %" PRIu32 " packets, %" PRIu32 " lost (%.2f%%)",
total_len, packet_count, lost_packets, loss_rate);
close(sockfd);
return ESP_OK;
}
static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl)
{
struct sockaddr_in addr;
int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(ctrl->cfg.dport);
addr.sin_addr.s_addr = htonl(ctrl->cfg.dip);
ESP_LOGI(TAG, "Starting UDP client");
uint64_t total_len = 0;
uint32_t packet_count = 0;
uint32_t start_time = xTaskGetTickCount();
uint32_t end_time = start_time + pdMS_TO_TICKS(ctrl->cfg.time * 1000);
while (!ctrl->finish && xTaskGetTickCount() < end_time) {
udp_datagram *header = (udp_datagram *)ctrl->buffer;
header->id = htonl(packet_count);
int send_len = sendto(sockfd, ctrl->buffer, ctrl->buffer_len, 0,
(struct sockaddr *)&addr, sizeof(addr));
if (send_len > 0) {
total_len += send_len;
packet_count++;
}
// Bandwidth limiting
if (ctrl->cfg.bw_lim > 0) {
vTaskDelay(1);
}
}
uint32_t actual_time = (xTaskGetTickCount() - start_time) / configTICK_RATE_HZ;
float bandwidth = actual_time > 0 ? (float)total_len * 8 / actual_time / 1000000 : 0;
ESP_LOGI(TAG, "UDP client sent: %" PRIu64 " bytes, %" PRIu32 " packets in %" PRIu32 " seconds (%.2f Mbps)",
total_len, packet_count, actual_time, bandwidth);
close(sockfd);
return ESP_OK;
}
static void iperf_task(void *arg)
{
iperf_ctrl_t *ctrl = (iperf_ctrl_t *)arg;
if (ctrl->cfg.flag & IPERF_FLAG_TCP) {
if (ctrl->cfg.flag & IPERF_FLAG_SERVER) {
iperf_start_tcp_server(ctrl);
} else {
iperf_start_tcp_client(ctrl);
}
} else {
if (ctrl->cfg.flag & IPERF_FLAG_SERVER) {
iperf_start_udp_server(ctrl);
} else {
iperf_start_udp_client(ctrl);
}
}
if (ctrl->buffer) {
free(ctrl->buffer);
ctrl->buffer = NULL;
}
ESP_LOGI(TAG, "iperf task finished");
s_iperf_task_handle = NULL;
vTaskDelete(NULL);
}
void iperf_start(iperf_cfg_t *cfg)
{
if (s_iperf_task_handle != NULL) {
ESP_LOGW(TAG, "iperf is already running");
return;
}
memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(iperf_cfg_t));
s_iperf_ctrl.finish = false;
// Allocate buffer
if (cfg->flag & IPERF_FLAG_TCP) {
s_iperf_ctrl.buffer_len = cfg->flag & IPERF_FLAG_SERVER ?
IPERF_TCP_RX_LEN : IPERF_TCP_TX_LEN;
} else {
s_iperf_ctrl.buffer_len = cfg->flag & IPERF_FLAG_SERVER ?
IPERF_UDP_RX_LEN : IPERF_UDP_TX_LEN;
}
s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
if (!s_iperf_ctrl.buffer) {
ESP_LOGE(TAG, "Failed to allocate buffer");
return;
}
memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
xTaskCreate(iperf_task, "iperf", 4096, &s_iperf_ctrl, IPERF_TRAFFIC_TASK_PRIORITY,
&s_iperf_task_handle);
}
void iperf_stop(void)
{
if (s_iperf_task_handle != NULL) {
s_iperf_ctrl.finish = true;
ESP_LOGI(TAG, "Stopping iperf...");
}
}

View File

@ -1,59 +0,0 @@
#ifndef IPERF_H
#define IPERF_H
#include <stdint.h>
#include <stdbool.h>
#define IPERF_FLAG_CLIENT (1 << 0)
#define IPERF_FLAG_SERVER (1 << 1)
#define IPERF_FLAG_TCP (1 << 2)
#define IPERF_FLAG_UDP (1 << 3)
#define IPERF_DEFAULT_PORT 5001
#define IPERF_DEFAULT_INTERVAL 3
#define IPERF_DEFAULT_TIME 30
#define IPERF_TRAFFIC_TASK_PRIORITY 4
#define IPERF_REPORT_TASK_PRIORITY 5
#define IPERF_SOCKET_RX_TIMEOUT 10
#define IPERF_SOCKET_ACCEPT_TIMEOUT 5
#define IPERF_UDP_TX_LEN (1470)
#define IPERF_UDP_RX_LEN (16 << 10)
#define IPERF_TCP_TX_LEN (16 << 10)
#define IPERF_TCP_RX_LEN (16 << 10)
typedef struct {
uint32_t flag;
uint8_t type;
uint16_t dip;
uint16_t dport;
uint16_t sport;
uint32_t interval;
uint32_t time;
uint16_t bw_lim;
uint32_t buffer_len;
} iperf_cfg_t;
typedef struct {
uint64_t total_len;
uint32_t buffer_len;
uint32_t sockfd;
uint32_t actual_len;
uint32_t packet_count;
uint8_t *buffer;
uint32_t udp_lost_counter;
uint32_t udp_packet_counter;
} iperf_traffic_t;
// UDP header for iperf
typedef struct {
int32_t id;
uint32_t tv_sec;
uint32_t tv_usec;
} udp_datagram;
void iperf_start(iperf_cfg_t *cfg);
void iperf_stop(void);
#endif // IPERF_H

View File

@ -1,4 +1,4 @@
// main.c Add support for wifi_cfg and ip address assignment
// main.c Add support for wifi_cfg and ip address assignment
#include <stdio.h>
#include <string.h>

View File

@ -1,172 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "driver/usb_serial_jtag.h"
#include "esp_vfs_dev.h"
#include "wifi_cfg.h"
static const char *TAG = "wifi_cfg";
static void trim(char *s){
int n = strlen(s);
while(n>0 && (s[n-1]=='\r' || s[n-1]=='\n' || isspace((unsigned char)s[n-1]))) s[--n]=0;
while(*s && isspace((unsigned char)*s)){
memmove(s, s+1, strlen(s));
}
}
static esp_err_t nvs_set_str2(nvs_handle_t h, const char *key, const char *val){
return val ? nvs_set_str(h, key, val) : nvs_erase_key(h, key);
}
static bool cfg_dhcp = true;
static esp_netif_t *sta_netif = NULL;
static void save_cfg(const char* ssid, const char* pass, const char* ip, const char* mask, const char* gw, bool dhcp){
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return;
if (ssid) nvs_set_str2(h, "ssid", ssid);
if (pass) nvs_set_str2(h, "pass", pass);
if (ip) nvs_set_str2(h, "ip", ip);
if (mask) nvs_set_str2(h, "mask", mask);
if (gw) nvs_set_str2(h, "gw", gw);
nvs_set_u8(h, "dhcp", dhcp ? 1 : 0);
nvs_commit(h);
nvs_close(h);
cfg_dhcp = dhcp;
}
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz,
char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz, bool* dhcp){
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false;
size_t len;
esp_err_t e;
if ((e = nvs_get_str(h, "ssid", NULL, &len)) != ESP_OK){ nvs_close(h); return false; }
if (len >= ssz){ nvs_close(h); return false; }
nvs_get_str(h, "ssid", ssid, &len);
len = psz; e = nvs_get_str(h, "pass", pass, &len); if (e!=ESP_OK) pass[0]=0;
len = isz; e = nvs_get_str(h, "ip", ip, &len); if (e!=ESP_OK) ip[0]=0;
len = msz; e = nvs_get_str(h, "mask", mask, &len); if (e!=ESP_OK) mask[0]=0;
len = gsz; e = nvs_get_str(h, "gw", gw, &len); if (e!=ESP_OK) gw[0]=0;
uint8_t d=1; nvs_get_u8(h, "dhcp", &d); *dhcp = (d!=0);
nvs_close(h);
return true;
}
void wifi_cfg_force_dhcp(bool enable){ cfg_dhcp = enable; }
static void apply_ip_static(const char* ip, const char* mask, const char* gw){
if (!sta_netif) return;
esp_netif_ip_info_t info = {0};
esp_netif_dhcpc_stop(sta_netif);
info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw);
ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) );
}
bool wifi_cfg_apply_from_nvs(void){
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
bool dhcp = true;
if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), &dhcp)){
ESP_LOGW(TAG, "No Wi-Fi config in NVS");
return false;
}
ESP_LOGI(TAG, "Applying Wi-Fi config: SSID=%s DHCP=%d IP=%s", ssid, dhcp, ip);
static bool inited = false;
if (!inited){
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
sta_netif = esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
inited = true;
}
wifi_config_t wcfg = {0};
strncpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid)-1);
strncpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password)-1);
wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wcfg) );
if (!dhcp && ip[0] && mask[0] && gw[0]){
apply_ip_static(ip, mask, gw);
}else{
if (sta_netif) esp_netif_dhcpc_start(sta_netif);
}
ESP_ERROR_CHECK( esp_wifi_start() );
ESP_ERROR_CHECK( esp_wifi_connect() );
return true;
}
static void cfg_listener_task(void *arg){
usb_serial_jtag_driver_config_t dcfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&dcfg));
// Deprecated but functional - causes only a warning
esp_vfs_usb_serial_jtag_use_driver();
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
char line[160];
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
bool dhcp = true;
bool in_cfg = false;
while (1){
if (!fgets(line, sizeof(line), stdin)){
vTaskDelay(pdMS_TO_TICKS(50));
continue;
}
trim(line);
if (!in_cfg){
if (strcmp(line, "CFG")==0){
in_cfg = true;
ssid[0]=pass[0]=ip[0]=mask[0]=gw[0]=0;
dhcp = true;
}
continue;
}
if (strcmp(line, "END")==0){
save_cfg(ssid, pass, ip, mask, gw, dhcp);
printf("OK\n");
wifi_cfg_apply_from_nvs();
in_cfg = false;
continue;
}
if (strncmp(line, "SSID:",5)==0){ strncpy(ssid, line+5, sizeof(ssid)-1); continue; }
if (strncmp(line, "PASS:",5)==0){ strncpy(pass, line+5, sizeof(pass)-1); continue; }
if (strncmp(line, "IP:",3)==0){ strncpy(ip, line+3, sizeof(ip)-1); continue; }
if (strncmp(line, "MASK:",5)==0){ strncpy(mask, line+5, sizeof(mask)-1); continue; }
if (strncmp(line, "GW:",3)==0){ strncpy(gw, line+3, sizeof(gw)-1); continue; }
if (strncmp(line, "DHCP:",5)==0){ dhcp = atoi(line+5) ? true:false; continue; }
}
}
void wifi_cfg_init(void){
nvs_flash_init();
xTaskCreatePinnedToCore(cfg_listener_task, "cfg_listener",
6144, NULL, 5, NULL, tskNO_AFFINITY);
}

View File

@ -1,15 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
void wifi_cfg_init(void); // starts serial listener task
bool wifi_cfg_apply_from_nvs(void); // reads saved config and connects WiFi
void wifi_cfg_force_dhcp(bool enable); // for testing
#ifdef __cplusplus
}
#endif