]> granicus.if.org Git - esp-idf/commitdiff
Merge branch 'feature/docs_update_sigmadelta_api' into 'master'
authorIvan Grokhotkov <ivan@espressif.com>
Tue, 26 Sep 2017 08:05:18 +0000 (16:05 +0800)
committerIvan Grokhotkov <ivan@espressif.com>
Tue, 26 Sep 2017 08:05:18 +0000 (16:05 +0800)
Updated sigma delta API documenation and example

See merge request !1319

15 files changed:
components/esp32/include/esp_wifi_types.h
components/esp32/lib
components/lwip/Kconfig
components/lwip/include/lwip/port/lwipopts.h
docs/get-started/index.rst
examples/wifi/iperf/Makefile [new file with mode: 0755]
examples/wifi/iperf/README.md [new file with mode: 0644]
examples/wifi/iperf/components/component.mk [new file with mode: 0755]
examples/wifi/iperf/components/iperf.c [new file with mode: 0644]
examples/wifi/iperf/components/iperf.h [new file with mode: 0644]
examples/wifi/iperf/main/cmd_decl.h [new file with mode: 0644]
examples/wifi/iperf/main/cmd_wifi.c [new file with mode: 0644]
examples/wifi/iperf/main/component.mk [new file with mode: 0644]
examples/wifi/iperf/main/main.c [new file with mode: 0644]
examples/wifi/iperf/sdkconfig.defaults [new file with mode: 0644]

index a37b1b707e429dde6f923d0ec8ce728af50634bc..aaa0071505f6998fb3b8c79aa25b7a56fcdbc7d4 100755 (executable)
@@ -121,6 +121,16 @@ typedef struct {
     wifi_scan_time_t scan_time;  /**< scan time per channel */
 } wifi_scan_config_t;
 
+typedef enum {
+    WIFI_CIPHER_TYPE_NONE = 0,   /**< the cipher type is none */
+    WIFI_CIPHER_TYPE_WEP40,      /**< the cipher type is WEP40 */
+    WIFI_CIPHER_TYPE_WEP104,     /**< the cipher type is WEP104 */
+    WIFI_CIPHER_TYPE_TKIP,       /**< the cipher type is TKIP */
+    WIFI_CIPHER_TYPE_CCMP,       /**< the cipher type is CCMP */
+    WIFI_CIPHER_TYPE_TKIP_CCMP,  /**< the cipher type is TKIP and CCMP */
+    WIFI_CIPHER_TYPE_UNKNOWN,    /**< the cipher type is unknown */
+} wifi_cipher_type_t;
+
 typedef struct {
     uint8_t bssid[6];                     /**< MAC address of AP */
     uint8_t ssid[33];                     /**< SSID of AP */
@@ -128,8 +138,14 @@ typedef struct {
     wifi_second_chan_t second;            /**< second channel of AP */
     int8_t  rssi;                         /**< signal strength of AP */
     wifi_auth_mode_t authmode;            /**< authmode of AP */
-    uint32_t low_rate_enable:1;           /**< bit: 0 flag to identify if low rate is enabled or not */
-    uint32_t reserved:31;                 /**< bit: 1..31 reserved */
+    wifi_cipher_type_t pairwise_cipher;   /**< pairwise cipher of AP */
+    wifi_cipher_type_t group_cipher;      /**< group cipher of AP */
+    uint32_t phy_11b:1;                   /**< bit: 0 flag to identify if 11b mode is enabled or not */
+    uint32_t phy_11g:1;                   /**< bit: 1 flag to identify if 11g mode is enabled or not */
+    uint32_t phy_11n:1;                   /**< bit: 2 flag to identify if 11n mode is enabled or not */
+    uint32_t phy_lr:1;                    /**< bit: 3 flag to identify if low rate is enabled or not */
+    uint32_t wps:1;                       /**< bit: 4 flag to identify if WPS is supported or not */
+    uint32_t reserved:27;                 /**< bit: 5..31 reserved */
 } wifi_ap_record_t;
 
 typedef enum {
index 1a06dd1671e386d75f98844c454c8a86e5b71c1b..c71367fb572b7710ce7950999ec5e67af57a105c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 1a06dd1671e386d75f98844c454c8a86e5b71c1b
+Subproject commit c71367fb572b7710ce7950999ec5e67af57a105c
index 65c76017c4ed9787693b03cb621b6ea0548222ba..c9c796f87f3fc0ab385182927ba9f8f860c9b4d0 100644 (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.
 
index 7bc4021e5a9f1b52001791ab02740a27381c9fdd..89d09d2d71f8c6eecdcc3eeb70a90a7b212d6990 100644 (file)
  * 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
    ---------- 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
+
 
 /*
    ---------------------------------
  * 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 */
 #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__)
index 31cda9cf3df9010f9d7252f50eb39cc247ae32bf..a19b805e3d3c2aa5a52cfb5b95af9f3e84f8cb19 100644 (file)
@@ -284,6 +284,34 @@ Several lines below, after start up and diagnostic log, you should see "Hello wo
 \r
 To exit monitor use shortcut ``Ctrl+]``. To execute ``make flash`` and ``make monitor`` in one shoot type ``make flash monitor``. Check section :doc:`IDF Monitor <idf-monitor>` for handy shortcuts and more details on using this application.\r
 \r
+That's all what you need to get started with ESP32! \r
+\r
+Now you are ready to try some other :idf:`examples`, or go right to developing your own applications.\r
+\r
+\r
+Updating ESP-IDF\r
+================\r
+\r
+After some time of using ESP-IDF, you may want to update it to take advantage of new features or bug fixes. The simplest way to do so is by deleting existing ``esp-idf`` folder and cloning it again, exactly as when doing initial installation described in sections :ref:`get-started-get-esp-idf`.\r
+\r
+Another solution is to update only what has changed. This method is useful if you have slow connection to the GiHub. To do the update run the following commands::\r
+\r
+    cd ~/esp/esp-idf\r
+    git pull\r
+    git submodule update --init --recursive\r
+\r
+The ``git pull`` command is fetching and merging changes from ESP-IDF repository on GitHub. Then ``git submodule update --init --recursive`` is updating existing submodules or getting a fresh copy of new ones. On GitHub the submodules are represented as links to other repositories and require this additional command to get them onto your PC.\r
+\r
+If you would like to use specific release of ESP-IDF, e.g. `v2.1`, run::\r
+\r
+    cd ~/esp\r
+    git clone https://github.com/espressif/esp-idf.git esp-idf-v2.1\r
+    cd esp-idf-v2.1/\r
+    git checkout v2.1\r
+    git submodule update --init --recursive\r
+\r
+After that remember to :doc:`add-idf_path-to-profile`, so the toolchain scripts know where to find the ESP-IDF in it's release specific location.\r
+\r
 \r
 Related Documents\r
 =================\r
diff --git a/examples/wifi/iperf/Makefile b/examples/wifi/iperf/Makefile
new file mode 100755 (executable)
index 0000000..46d6578
--- /dev/null
@@ -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
diff --git a/examples/wifi/iperf/README.md b/examples/wifi/iperf/README.md
new file mode 100644 (file)
index 0000000..c5fd968
--- /dev/null
@@ -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.
diff --git a/examples/wifi/iperf/components/component.mk b/examples/wifi/iperf/components/component.mk
new file mode 100755 (executable)
index 0000000..da115f7
--- /dev/null
@@ -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 := .
diff --git a/examples/wifi/iperf/components/iperf.c b/examples/wifi/iperf/components/iperf.c
new file mode 100644 (file)
index 0000000..03927d6
--- /dev/null
@@ -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;
+}
+
diff --git a/examples/wifi/iperf/components/iperf.h b/examples/wifi/iperf/components/iperf.h
new file mode 100644 (file)
index 0000000..1f5a4ac
--- /dev/null
@@ -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
diff --git a/examples/wifi/iperf/main/cmd_decl.h b/examples/wifi/iperf/main/cmd_decl.h
new file mode 100644 (file)
index 0000000..78707ea
--- /dev/null
@@ -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);
+
diff --git a/examples/wifi/iperf/main/cmd_wifi.c b/examples/wifi/iperf/main/cmd_wifi.c
new file mode 100644 (file)
index 0000000..e26900d
--- /dev/null
@@ -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) );
+}
diff --git a/examples/wifi/iperf/main/component.mk b/examples/wifi/iperf/main/component.mk
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/examples/wifi/iperf/main/main.c b/examples/wifi/iperf/main/main.c
new file mode 100644 (file)
index 0000000..66af234
--- /dev/null
@@ -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);
+    }
+}
+
diff --git a/examples/wifi/iperf/sdkconfig.defaults b/examples/wifi/iperf/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..f62698f
--- /dev/null
@@ -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=
+