example: add iperf example

Support iperf
This commit is contained in:
Liu Zhi Fu 2017-03-30 23:12:25 +08:00
parent 845c3fba35
commit 05b0d567e5
12 changed files with 1188 additions and 7 deletions

View file

@ -68,6 +68,36 @@ config LWIP_IP_REASSEMBLY
help
Enabling this option allows reassemblying incoming fragmented IP packets.
config LWIP_STATS
bool "Enable LWIP statistics"
default n
help
Enabling this option allows LWIP statistics
config LWIP_ETHARP_TRUST_IP_MAC
bool "Enable LWIP ARP trust"
default y
help
Enabling this option allows ARP table to be updated.
If this option is enabled, the incoming IP packets cause the ARP table to be
updated with the source MAC and IP addresses supplied in the packet.
You may want to disable this if you do not trust LAN peers to have the
correct addresses, or as a limited approach to attempt to handle
spoofing. If disabled, lwIP will need to make a new ARP request if
the peer is not already in the ARP table, adding a little latency.
The peer *is* in the ARP table if it requested our address before.
Also notice that this slows down input processing of every IP packet!
config TCPIP_RECVMBOX_SIZE
int "TCPIP receive mail box size"
default 32
range 6 64
help
Set TCPIP receive mail box size. Generally bigger value means higher throughput
but more memory. The value should be bigger than UDP/TCP mail box size.
menu "TCP"
config TCP_MAXRTX
@ -131,7 +161,7 @@ config TCP_WND_DEFAULT
config TCP_RECVMBOX_SIZE
int "Default TCP receive mail box size"
default 6
range 6 32
range 6 64
help
Set TCP receive mail box size. Generally bigger value means higher throughput
but more memory. The recommended value is: TCP_WND_DEFAULT/TCP_MSS + 2, e.g. if
@ -188,7 +218,7 @@ menu "UDP"
config UDP_RECVMBOX_SIZE
int "Default UDP receive mail box size"
default 6
range 6 32
range 6 64
help
Set UDP receive mail box size. The recommended value is 6.

View file

@ -416,7 +416,7 @@
* The queue size value itself is platform-dependent, but is passed to
* sys_mbox_new() when tcpip_init is called.
*/
#define TCPIP_MBOX_SIZE 32
#define TCPIP_MBOX_SIZE CONFIG_TCPIP_RECVMBOX_SIZE
/**
* DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
@ -520,10 +520,20 @@
---------- Statistics options ----------
----------------------------------------
*/
/**
* LWIP_STATS==1: Enable statistics collection in lwip_stats.
*/
#define LWIP_STATS 0
#define LWIP_STATS CONFIG_LWIP_STATS
#if LWIP_STATS
/**
* LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
*/
#define LWIP_STATS_DISPLAY CONFIG_LWIP_STATS
#endif
/*
---------------------------------
@ -674,7 +684,7 @@
* The peer *is* in the ARP table if it requested our address before.
* Also notice that this slows down input processing of every IP packet!
*/
#define ETHARP_TRUST_IP_MAC 1
#define ETHARP_TRUST_IP_MAC CONFIG_LWIP_ETHARP_TRUST_IP_MAC
/* Enable all Espressif-only options */
@ -692,8 +702,8 @@
#define ESP_IP4_ATON 1
#define ESP_LIGHT_SLEEP 1
#define ESP_L2_TO_L3_COPY CONFIG_L2_TO_L3_COPY
#define ESP_STATS_MEM 0
#define ESP_STATS_DROP 0
#define ESP_STATS_MEM CONFIG_LWIP_STATS
#define ESP_STATS_DROP CONFIG_LWIP_STATS
#define ESP_STATS_TCP 0
#define ESP_DHCP_TIMER 1
#define ESP_LWIP_LOGI(...) ESP_LOGI("lwip", __VA_ARGS__)

8
examples/wifi/iperf/Makefile Executable file
View file

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := iperf
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,52 @@
# Iperf Example
This example implements the protocol used by the common performance measurement tool [iPerf](https://iperf.fr/).
Performance can be measured between two ESP32s running this example, or between a single ESP32 and a computer running the iPerf tool
Demo steps to test station TCP Tx performance:
1. Build the iperf example with sdkconfig.defaults, which contains performance test specific configurations
2. Run the demo as station mode and join the target AP
sta ssid password
3. Run iperf as server on AP side
iperf -s -i 3
4. Run iperf as client on ESP32 side
iperf -c 192.168.10.42 -i 3 -t 60
The console output, which is printed by station TCP RX throughput test, looks like:
>esp32> sta aptest
>
>I (5325) iperf: sta connecting to 'aptest'
>
>esp32> I (6017) event: ip: 192.168.10.248, mask: 255.255.255.0, gw: 192.168.10.1
>
>esp32> iperf -s -i 3 -t 1000
>
>I (14958) iperf: mode=tcp-server sip=192.168.10.248:5001, dip=0.0.0.0:5001, interval=3, time=1000
>
>Interval Bandwidth
>
>esp32> accept: 192.168.10.42,62958
>
>0- 3 sec 8.43 Mbits/sec
>
>3- 6 sec 36.16 Mbits/sec
>
>6- 9 sec 36.22 Mbits/sec
>
>9- 12 sec 36.44 Mbits/sec
>
>12- 15 sec 36.25 Mbits/sec
>
>15- 18 sec 24.36 Mbits/sec
>
>18- 21 sec 27.79 Mbits/sec
Steps to test station/soft-AP TCP/UDP RX/TX throughput are similar as test steps in station TCP TX.
See the README.md file in the upper level 'examples' directory for more information about examples.

View file

@ -0,0 +1,11 @@
#
# Component Makefile
#
# This Makefile should, at the very least, just include $(SDK_PATH)/Makefile. By default,
# this will take the sources in the src/ directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
#include $(IDF_PATH)/make/component_common.mk
COMPONENT_ADD_INCLUDEDIRS := .

View file

@ -0,0 +1,455 @@
/* Iperf Example - iperf implementation
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "iperf.h"
typedef struct {
iperf_cfg_t cfg;
bool finish;
uint32_t total_len;
uint32_t buffer_len;
uint8_t *buffer;
uint32_t socket;
} iperf_ctrl_t;
typedef struct {
int32_t id;
uint32_t sec;
uint32_t usec;
} iperf_udp_pkt_t;
static bool s_iperf_is_running = false;
static iperf_ctrl_t s_iperf_ctrl;
static const char *TAG = "iperf";
inline static bool iperf_is_udp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_udp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_tcp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
inline static bool iperf_is_tcp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
int iperf_get_socket_error_code(int socket)
{
uint32_t optlen = sizeof(int);
int result;
int err;
err = getsockopt(socket, SOL_SOCKET, SO_ERROR, &result, &optlen);
if (err == -1) {
ESP_LOGE(TAG, "getsockopt failed: ret=%d", err);
return -1;
}
return result;
}
int iperf_show_socket_error_reason(const char *str, int socket)
{
int err = iperf_get_socket_error_code(socket);
if (err != 0) {
ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
}
return err;
}
void iperf_report_task(void* arg)
{
TickType_t delay_interval = (s_iperf_ctrl.cfg.interval * 1000)/portTICK_RATE_MS;
uint32_t interval = s_iperf_ctrl.cfg.interval;
uint32_t time = s_iperf_ctrl.cfg.time;
uint32_t last_len = 0;
uint32_t cur = 0;
printf("\n%16s %s\n", "Interval", "Bandwidth");
while (!s_iperf_ctrl.finish) {
vTaskDelay(delay_interval);
printf("%4d-%4d sec %.2f Mbits/sec\n", cur, cur+interval, (double)((s_iperf_ctrl.total_len - last_len)*8)/interval/1e6);
cur += interval;
last_len = s_iperf_ctrl.total_len;
if (cur == time) {
break;
}
}
if (cur != 0) {
printf("%4d-%4d sec %.2f Mbits/sec\n", 0, time, (double)(s_iperf_ctrl.total_len*8)/cur/1e6);
}
s_iperf_ctrl.finish = true;
vTaskDelete(NULL);
}
esp_err_t iperf_start_report(void)
{
int ret;
ret = xTaskCreate(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t iperf_run_tcp_server(void)
{
socklen_t addr_len = sizeof(struct sockaddr);
struct sockaddr_in remote_addr;
struct sockaddr_in addr;
int actual_recv = 0;
int want_recv = 0;
uint8_t *buffer;
int listen_socket;
struct timeval t;
int socket;
int opt;
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_socket < 0) {
iperf_show_socket_error_reason("tcp server create", listen_socket);
return ESP_FAIL;
}
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(listen_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("tcp server bind", listen_socket);
close(listen_socket);
return ESP_FAIL;
}
if (listen(listen_socket, 5) < 0) {
iperf_show_socket_error_reason("tcp server listen", listen_socket);
close(listen_socket);
return ESP_FAIL;
}
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_recv = s_iperf_ctrl.buffer_len;
while (!s_iperf_ctrl.finish) {
/*TODO need to change to non-block mode */
socket = accept(listen_socket, (struct sockaddr*)&remote_addr, &addr_len);
if (socket < 0) {
iperf_show_socket_error_reason("tcp server listen", listen_socket);
close(listen_socket);
return ESP_FAIL;
} else {
printf("accept: %s,%d\n", inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
}
while (!s_iperf_ctrl.finish) {
actual_recv = recv(socket, buffer, want_recv, 0);
if (actual_recv < 0) {
iperf_show_socket_error_reason("tcp server recv", listen_socket);
break;
} else {
s_iperf_ctrl.total_len += actual_recv;
}
}
close(socket);
}
s_iperf_ctrl.finish = true;
close(listen_socket);
return ESP_OK;
}
esp_err_t iperf_run_udp_server(void)
{
socklen_t addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in addr;
int actual_recv = 0;
struct timeval t;
int want_recv = 0;
uint8_t *buffer;
int socket;
int opt;
socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (socket < 0) {
iperf_show_socket_error_reason("udp server create", socket);
return ESP_FAIL;
}
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("udp server bind", socket);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.sport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_recv = s_iperf_ctrl.buffer_len;
ESP_LOGI(TAG, "want recv=%d", want_recv);
t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
while (!s_iperf_ctrl.finish) {
actual_recv = recvfrom(socket, buffer, want_recv, 0, (struct sockaddr *)&addr, &addr_len);
if (actual_recv < 0) {
iperf_show_socket_error_reason("udp server recv", socket);
} else {
s_iperf_ctrl.total_len += actual_recv;
}
}
s_iperf_ctrl.finish = true;
close(socket);
return ESP_OK;
}
esp_err_t iperf_run_udp_client(void)
{
struct sockaddr_in addr;
iperf_udp_pkt_t *udp;
int actual_send = 0;
bool retry = false;
uint32_t delay = 1;
int want_send = 0;
uint8_t *buffer;
int socket;
int opt;
int err;
int id;
socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (socket < 0) {
iperf_show_socket_error_reason("udp server create", socket);
return ESP_FAIL;
}
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("udp server bind", socket);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
udp = (iperf_udp_pkt_t *)buffer;
want_send = s_iperf_ctrl.buffer_len;
id = 0;
while (!s_iperf_ctrl.finish) {
if (false == retry) {
id ++;
udp->id = htonl(id);
delay = 1;
}
retry = false;
actual_send = sendto(socket, buffer, want_send, 0, (struct sockaddr *)&addr, sizeof(addr));
if (actual_send != want_send) {
err = iperf_get_socket_error_code(socket);
if (err == ENOMEM) {
vTaskDelay(delay);
if (delay < IPERF_MAX_DELAY) {
delay <<= 1;
}
retry = true;
continue;
} else {
ESP_LOGE(TAG, "udp client send abort: err=%d", err);
break;
}
} else {
s_iperf_ctrl.total_len += actual_send;
}
}
s_iperf_ctrl.finish = true;
close(socket);
return ESP_OK;
}
esp_err_t iperf_run_tcp_client(void)
{
struct sockaddr_in addr;
int actual_send = 0;
int want_send = 0;
uint8_t *buffer;
int socket;
int opt;
socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket < 0) {
iperf_show_socket_error_reason("tcp client create", socket);
return ESP_FAIL;
}
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.sip;
if (bind(socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
iperf_show_socket_error_reason("tcp client bind", socket);
return ESP_FAIL;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
if (connect(socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
iperf_show_socket_error_reason("tcp client connect", socket);
return ESP_FAIL;
}
iperf_start_report();
buffer = s_iperf_ctrl.buffer;
want_send = s_iperf_ctrl.buffer_len;
while (!s_iperf_ctrl.finish) {
actual_send = send(socket, buffer, want_send, 0);
if (actual_send <= 0) {
vTaskDelay(1);
} else {
s_iperf_ctrl.total_len += actual_send;
}
}
close(socket);
return ESP_OK;
}
void iperf_task_traffic(void *arg)
{
if (iperf_is_udp_client()) {
iperf_run_udp_client();
} else if (iperf_is_udp_server()) {
iperf_run_udp_server();
} else if (iperf_is_tcp_client()) {
iperf_run_tcp_client();
} else {
iperf_run_tcp_server();
}
if (s_iperf_ctrl.buffer) {
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = 0;
}
ESP_LOGI(TAG, "iperf exit");
s_iperf_is_running = false;
vTaskDelete(NULL);
}
uint32_t iperf_get_buffer_len(void)
{
if (iperf_is_udp_client()) {
return IPERF_UDP_TX_LEN;
} else if (iperf_is_udp_server()) {
return IPERF_UDP_RX_LEN;
} else if (iperf_is_tcp_client()) {
return IPERF_TCP_TX_LEN;
} else {
return IPERF_TCP_RX_LEN;
}
return 0;
}
esp_err_t iperf_start(iperf_cfg_t *cfg)
{
int ret;
if (!cfg) {
return ESP_FAIL;
}
if (s_iperf_is_running) {
ESP_LOGW(TAG, "iperf is running");
return ESP_FAIL;
}
memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
s_iperf_is_running = true;
s_iperf_ctrl.finish = false;
s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
if (!s_iperf_ctrl.buffer) {
ESP_LOGE(TAG, "create buffer: out of memory");
return ESP_FAIL;
}
ret = xTaskCreate(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = 0;
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t iperf_stop(void)
{
if (s_iperf_is_running) {
s_iperf_ctrl.finish = true;
}
while (s_iperf_is_running) {
ESP_LOGI(TAG, "wait current iperf to stop ...");
vTaskDelay(300/portTICK_RATE_MS);
}
return ESP_OK;
}

View file

@ -0,0 +1,61 @@
/* Iperf Example - iperf declaration
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef __IPERF_H_
#define __IPERF_H_
#ifdef __cplusplus
extern "C" {
#endif
#define IPERF_FLAG_CLIENT (1)
#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_NAME "iperf_traffic"
#define IPERF_TRAFFIC_TASK_PRIORITY 19
#define IPERF_TRAFFIC_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_REPORT_TASK_PRIORITY 10
#define IPERF_REPORT_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_UDP_TX_LEN (1472)
#define IPERF_UDP_RX_LEN (32<<10)
#define IPERF_TCP_TX_LEN (32<<10)
#define IPERF_TCP_RX_LEN (64<<10)
#define IPERF_MAX_DELAY 64
#define IPERF_SOCKET_RX_TIMEOUT 10
#define IPERF_SOCKET_ACCEPT_TIMEOUT 5
typedef struct {
uint32_t flag;
uint32_t dip;
uint32_t sip;
uint16_t dport;
uint16_t sport;
uint32_t interval;
uint32_t time;
} iperf_cfg_t;
esp_err_t iperf_start(iperf_cfg_t *cfg);
esp_err_t iperf_stop(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,14 @@
/* Iperf example — declarations of command registration functions.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
// Register WiFi functions
void register_wifi(void);
void initialise_wifi(void);

View file

@ -0,0 +1,381 @@
/* Iperf Example - wifi commands
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "tcpip_adapter.h"
#include "esp_event_loop.h"
#include "iperf.h"
typedef struct {
struct arg_str *ip;
struct arg_lit *server;
struct arg_lit *udp;
struct arg_int *port;
struct arg_int *interval;
struct arg_int *time;
struct arg_lit *abort;
struct arg_end *end;
} wifi_iperf_t;
static wifi_iperf_t iperf_args;
typedef struct {
struct arg_str *ssid;
struct arg_str *password;
struct arg_end *end;
} wifi_args_t;
static wifi_args_t sta_args;
static wifi_args_t ap_args;
static bool reconnect = true;
static const char *TAG="iperf";
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
const int DISCONNECTED_BIT = BIT1;
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
if (reconnect) {
ESP_LOGI(TAG, "sta disconnect, reconnect...");
esp_wifi_connect();
} else {
ESP_LOGI(TAG, "sta disconnect");
}
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
void initialise_wifi(void)
{
esp_log_level_set("wifi", ESP_LOG_WARN);
static bool initialized = false;
if (initialized) {
return;
}
tcpip_adapter_init();
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_NULL) );
ESP_ERROR_CHECK( esp_wifi_start() );
initialized = true;
}
static bool wifi_cmd_sta_join(const char* ssid, const char* pass)
{
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
wifi_config_t wifi_config = { 0 };
strlcpy((char*) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (pass) {
strncpy((char*) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
}
if (bits & CONNECTED_BIT) {
reconnect = false;
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
ESP_ERROR_CHECK( esp_wifi_disconnect() );
xEventGroupWaitBits(wifi_event_group, DISCONNECTED_BIT, 0, 1, portTICK_RATE_MS);
}
reconnect = true;
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK( esp_wifi_connect() );
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000/portTICK_RATE_MS);
return true;
}
static int wifi_cmd_sta(int argc, char** argv)
{
int nerrors = arg_parse(argc, argv, (void**) &sta_args);
if (nerrors != 0) {
arg_print_errors(stderr, sta_args.end, argv[0]);
return 1;
}
ESP_LOGI(TAG, "sta connecting to '%s'", sta_args.ssid->sval[0]);
wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0]);
return 0;
}
static bool wifi_cmd_ap_set(const char* ssid, const char* pass)
{
wifi_config_t wifi_config = {
.ap = {
.ssid = "",
.ssid_len = 0,
.max_connection = 4,
.password = "",
.authmode = WIFI_AUTH_WPA_WPA2_PSK
},
};
reconnect = false;
strncpy((char*) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
if (pass) {
if (strlen(pass) != 0 && strlen(pass) < 8) {
reconnect = true;
ESP_LOGE(TAG, "password less than 8");
return false;
}
strncpy((char*) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
}
if (strlen(pass) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
return true;
}
static int wifi_cmd_ap(int argc, char** argv)
{
int nerrors = arg_parse(argc, argv, (void**) &ap_args);
if (nerrors != 0) {
arg_print_errors(stderr, ap_args.end, argv[0]);
return 1;
}
wifi_cmd_ap_set(ap_args.ssid->sval[0], ap_args.password->sval[0]);
ESP_LOGI(TAG, "AP mode, %s %s", ap_args.ssid->sval[0], ap_args.password->sval[0]);
return 0;
}
static int wifi_cmd_query(int argc, char** argv)
{
wifi_config_t cfg;
wifi_mode_t mode;
esp_wifi_get_mode(&mode);
if (WIFI_MODE_AP == mode) {
esp_wifi_get_config(WIFI_IF_AP, &cfg);
ESP_LOGI(TAG, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
} else if (WIFI_MODE_STA == mode) {
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
if (bits & CONNECTED_BIT) {
esp_wifi_get_config(WIFI_IF_STA, &cfg);
ESP_LOGI(TAG, "sta mode, connected %s", cfg.ap.ssid);
} else {
ESP_LOGI(TAG, "sta mode, disconnected");
}
} else {
ESP_LOGI(TAG, "NULL mode");
return 0;
}
return 0;
}
static uint32_t wifi_get_local_ip(void)
{
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
tcpip_adapter_if_t ifx = TCPIP_ADAPTER_IF_AP;
tcpip_adapter_ip_info_t ip_info;
wifi_mode_t mode;
esp_wifi_get_mode(&mode);
if (WIFI_MODE_STA == mode) {
bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
if (bits & CONNECTED_BIT) {
ifx = TCPIP_ADAPTER_IF_STA;
} else {
ESP_LOGE(TAG, "sta has no IP");
return 0;
}
}
tcpip_adapter_get_ip_info(ifx, &ip_info);
return ip_info.ip.addr;
}
static int wifi_cmd_iperf(int argc, char** argv)
{
int nerrors = arg_parse(argc, argv, (void**) &iperf_args);
iperf_cfg_t cfg;
if (nerrors != 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 0;
}
memset(&cfg, 0, sizeof(cfg));
if ( iperf_args.abort->count != 0) {
iperf_stop();
return 0;
}
if ( ((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0)) ) {
ESP_LOGE(TAG, "should specific client/server mode");
return 0;
}
if (iperf_args.ip->count == 0) {
cfg.flag |= IPERF_FLAG_SERVER;
} else {
cfg.dip = ipaddr_addr(iperf_args.ip->sval[0]);
cfg.flag |= IPERF_FLAG_CLIENT;
}
cfg.sip = wifi_get_local_ip();
if (cfg.sip == 0) {
return 0;
}
if (iperf_args.udp->count == 0) {
cfg.flag |= IPERF_FLAG_TCP;
} else {
cfg.flag |= IPERF_FLAG_UDP;
}
if (iperf_args.port->count == 0) {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = IPERF_DEFAULT_PORT;
} else {
if (cfg.flag & IPERF_FLAG_SERVER) {
cfg.sport = iperf_args.port->ival[0];
cfg.dport = IPERF_DEFAULT_PORT;
} else {
cfg.sport = IPERF_DEFAULT_PORT;
cfg.dport = iperf_args.port->ival[0];
}
}
if (iperf_args.interval->count == 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
} else {
cfg.interval = iperf_args.interval->ival[0];
if (cfg.interval <= 0) {
cfg.interval = IPERF_DEFAULT_INTERVAL;
}
}
if (iperf_args.time->count == 0) {
cfg.time = IPERF_DEFAULT_TIME;
} else {
cfg.time = iperf_args.time->ival[0];
if (cfg.time <= cfg.interval) {
cfg.time = cfg.interval;
}
}
ESP_LOGI(TAG, "mode=%s-%s sip=%d.%d.%d.%d:%d, dip=%d.%d.%d.%d:%d, interval=%d, time=%d",
cfg.flag&IPERF_FLAG_TCP?"tcp":"udp",
cfg.flag&IPERF_FLAG_SERVER?"server":"client",
cfg.sip&0xFF, (cfg.sip>>8)&0xFF, (cfg.sip>>16)&0xFF, (cfg.sip>>24)&0xFF, cfg.sport,
cfg.dip&0xFF, (cfg.dip>>8)&0xFF, (cfg.dip>>16)&0xFF, (cfg.dip>>24)&0xFF, cfg.dport,
cfg.interval, cfg.time);
iperf_start(&cfg);
return 0;
}
static int restart(int argc, char** argv)
{
ESP_LOGI(TAG, "Restarting");
esp_restart();
}
void register_wifi()
{
sta_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
sta_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
sta_args.end = arg_end(2);
const esp_console_cmd_t sta_cmd = {
.command = "sta",
.help = "WiFi is station mode, join specified soft-AP",
.hint = NULL,
.func = &wifi_cmd_sta,
.argtable = &sta_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&sta_cmd) );
ap_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
ap_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
ap_args.end = arg_end(2);
const esp_console_cmd_t ap_cmd = {
.command = "ap",
.help = "AP mode, configure ssid and password",
.hint = NULL,
.func = &wifi_cmd_ap,
.argtable = &ap_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&ap_cmd) );
const esp_console_cmd_t query_cmd = {
.command = "query",
.help = "query WiFi info",
.hint = NULL,
.func = &wifi_cmd_query,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&query_cmd) );
const esp_console_cmd_t restart_cmd = {
.command = "restart",
.help = "Restart the program",
.hint = NULL,
.func = &restart,
};
ESP_ERROR_CHECK( esp_console_cmd_register(&restart_cmd) );
iperf_args.ip = arg_str0("c", "client", "<ip>", "run in client mode, connecting to <host>");
iperf_args.server = arg_lit0("s", "server", "run in server mode");
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
iperf_args.port = arg_int0("p", "port", "<port>", "server port to listen on/connect to");
iperf_args.interval = arg_int0("i", "interval", "<interval>", "seconds between periodic bandwidth reports");
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
iperf_args.end = arg_end(1);
const esp_console_cmd_t iperf_cmd = {
.command = "iperf",
.help = "iperf command",
.hint = NULL,
.func = &wifi_cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&iperf_cmd) );
}

View file

View file

@ -0,0 +1,136 @@
/* Iperf Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <errno.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#define WIFI_CONNECTED_BIT BIT0
static void initialize_console()
{
/* Disable buffering on stdin and stdout */
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );
/* Tell VFS to use UART driver */
esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = 32,
.max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK( esp_console_init(&console_config) );
/* Configure linenoise line completion library */
/* Enable multiline editing. If not set, long commands will scroll within
* single line.
*/
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
}
void app_main(void)
{
initialise_wifi();
initialize_console();
/* Register commands */
esp_console_register_help_command();
register_wifi();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
const char* prompt = LOG_COLOR_I "esp32> " LOG_RESET_COLOR;
printf("\n ==================================================\n");
printf(" | Steps to test WiFi throughput |\n");
printf(" | |\n");
printf(" | 1. Print 'help' to gain overview of commands |\n");
printf(" | 2. Configure device to station or soft-AP |\n");
printf(" | 3. Setup WiFi connection |\n");
printf(" | 4. Run iperf to test UDP/TCP RX/TX throughput |\n");
printf(" | |\n");
printf(" =================================================\n\n");
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Putty instead.\n");
linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt.
*/
prompt = "esp32> ";
#endif //CONFIG_LOG_COLORS
}
/* Main loop */
while(true) {
/* Get a line using linenoise.
* The line is returned when ENTER is pressed.
*/
char* line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x\n", ret);
} else if (err != ESP_OK) {
printf("Internal error: 0x%x\n", err);
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
}

View file

@ -0,0 +1,23 @@
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_MEMMAP_SMP=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=64
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=64
CONFIG_ESP32_WIFI_TX_BA_WIN=16
CONFIG_ESP32_WIFI_RX_BA_WIN=16
CONFIG_FREERTOS_UNICORE=
CONFIG_FREERTOS_HZ=1000
CONFIG_INT_WDT=
CONFIG_TASK_WDT=
CONFIG_TCP_SND_BUF_DEFAULT=65535
CONFIG_TCP_WND_DEFAULT=65535
CONFIG_TCP_RECVMBOX_SIZE=64
CONFIG_UDP_RECVMBOX_SIZE=64
CONFIG_TCPIP_RECVMBOX_SIZE=64
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=