mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 09:25:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# Wi-Fi Examples
|
||||
|
||||
Including some examples about wifi.
|
||||
|
||||
## wpa2_enterprise
|
||||
|
||||
Show how ESP32 connects to AP with wpa2 enterprise encryption.
|
||||
|
||||
See the [README.md](./wpa2_enterprise/README.md) file in the project [wpa2_enterprise](./wpa2_enterprise/).
|
||||
|
||||
## power_save
|
||||
|
||||
Show how to use power save mode of wifi.
|
||||
|
||||
See the [README.md](./power_save/README.md) file in the project [power_save](./power_save/).
|
||||
|
||||
## wps(Wifi Protected Setup)
|
||||
|
||||
shows how to use wps(Wifi Protected Setup).
|
||||
|
||||
See the [README.md](./wps/README.md) file in the project [wps](./wps/).
|
||||
|
||||
## espnow
|
||||
|
||||
shows how to use espnow.
|
||||
|
||||
See the [README.md](./espnow/README.md) file in the project [espnow](./espnow/).
|
||||
|
||||
## fast_scan
|
||||
|
||||
Show how to use fast scan while connecting to an AP.
|
||||
|
||||
See the [README.md](./fast_scan/README.md) file in the project [fast_scan](./fast_scan/).
|
||||
|
||||
## scan
|
||||
|
||||
Show how to scan for all the available APs.
|
||||
|
||||
See the [README.md](./scan/README.md) file in the project [scan](./scan/).
|
||||
|
||||
# More
|
||||
|
||||
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(espnow_example)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := espnow_example
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
# ESPNOW Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to use ESPNOW of wifi. Example does the following steps:
|
||||
|
||||
* Start WiFi.
|
||||
* Initialize ESPNOW.
|
||||
* Register ESPNOW sending or receiving callback function.
|
||||
* Add ESPNOW peer information.
|
||||
* Send and receive ESPNOW data.
|
||||
|
||||
This example need at least two ESP devices:
|
||||
|
||||
* In order to get the MAC address of the other device, Device1 firstly send broadcast ESPNOW data with 'state' set as 0.
|
||||
* When Device2 receiving broadcast ESPNOW data from Device1 with 'state' as 0, adds Device1 into the peer list.
|
||||
Then start sending broadcast ESPNOW data with 'state' set as 1.
|
||||
* When Device1 receiving broadcast ESPNOW data with 'state' as 1, compares the local magic number with that in the data.
|
||||
If the local one is bigger than that one, stop sending broadcast ESPNOW data and starts sending unicast ESPNOW data to Device2.
|
||||
* If Device2 receives unicast ESPNOW data, also stop sending broadcast ESPNOW data.
|
||||
|
||||
In practice, if the MAC address of the other device is known, it's not required to send/receive broadcast ESPNOW data first,
|
||||
just add the device into the peer list and send/receive unicast ESPNOW data.
|
||||
|
||||
There are a lot of "extras" on top of ESPNOW data, such as type, state, sequence number, CRC and magic in this example. These "extras" are
|
||||
not required to use ESPNOW. They are only used to make this example to run correctly. However, it is recommended that users add some "extras"
|
||||
to make ESPNOW data more safe and more reliable.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set WiFi mode (station or SoftAP) under Example Configuration Options.
|
||||
* Set ESPNOW primary master key under Example Configuration Options.
|
||||
This parameter must be set to the same value for sending and recving devices.
|
||||
* Set ESPNOW local master key under Example Configuration Options.
|
||||
This parameter must be set to the same value for sending and recving devices.
|
||||
* Set Channel under Example Configuration Options.
|
||||
The sending device and the recving device must be on the same channel.
|
||||
* Set Send count and Send delay under Example Configuration Options.
|
||||
* Set Send len under Example Configuration Options.
|
||||
* Set Enable Long Range Options.
|
||||
When this parameter is enabled, the ESP32 device will send data at the PHY rate of 512Kbps or 256Kbps
|
||||
then the data can be transmitted over long range between two ESP32 devices.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Here is the example of ESPNOW receiving device console output.
|
||||
|
||||
```
|
||||
I (898) phy: phy_version: 3960, 5211945, Jul 18 2018, 10:40:07, 0, 0
|
||||
I (898) wifi: mode : sta (30:ae:a4:80:45:68)
|
||||
I (898) espnow_example: WiFi started
|
||||
I (898) ESPNOW: espnow [version: 1.0] init
|
||||
I (5908) espnow_example: Start sending broadcast data
|
||||
I (6908) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (7908) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (52138) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (52138) espnow_example: Receive 0th broadcast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
I (53158) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (53158) espnow_example: Receive 1th broadcast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
I (54168) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (54168) espnow_example: Receive 2th broadcast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
I (54168) espnow_example: Receive 0th unicast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
I (54678) espnow_example: Receive 1th unicast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
I (55668) espnow_example: Receive 2th unicast data from: 30:ae:a4:0c:34:ec, len: 200
|
||||
```
|
||||
|
||||
Here is the example of ESPNOW sending device console output.
|
||||
|
||||
```
|
||||
I (915) phy: phy_version: 3960, 5211945, Jul 18 2018, 10:40:07, 0, 0
|
||||
I (915) wifi: mode : sta (30:ae:a4:0c:34:ec)
|
||||
I (915) espnow_example: WiFi started
|
||||
I (915) ESPNOW: espnow [version: 1.0] init
|
||||
I (5915) espnow_example: Start sending broadcast data
|
||||
I (5915) espnow_example: Receive 41th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (5915) espnow_example: Receive 42th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (5925) espnow_example: Receive 44th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (5935) espnow_example: Receive 45th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (6965) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (6965) espnow_example: Receive 46th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (7975) espnow_example: send data to ff:ff:ff:ff:ff:ff
|
||||
I (7975) espnow_example: Receive 47th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (7975) espnow_example: Start sending unicast data
|
||||
I (7975) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
I (9015) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
I (9015) espnow_example: Receive 48th broadcast data from: 30:ae:a4:80:45:68, len: 200
|
||||
I (10015) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
I (16075) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
I (17075) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
I (24125) espnow_example: send data to 30:ae:a4:80:45:68
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If ESPNOW data can not be received from another device, maybe the two devices are not
|
||||
on the same channel or the primary key and local key are different.
|
||||
|
||||
In real application, if the receiving device is in station mode only and it connects to an AP,
|
||||
modem sleep should be disabled. Otherwise, it may fail to revceive ESPNOW data from other devices.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "espnow_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,61 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
choice ESPNOW_WIFI_MODE
|
||||
prompt "WiFi mode"
|
||||
default ESPNOW_WIFI_MODE_STATION
|
||||
help
|
||||
WiFi mode(station or softap).
|
||||
|
||||
config ESPNOW_WIFI_MODE_STATION
|
||||
bool "Station"
|
||||
config ESPNOW_WIFI_MODE_STATION_SOFTAP
|
||||
bool "Softap"
|
||||
endchoice
|
||||
|
||||
config ESPNOW_PMK
|
||||
string "ESPNOW primary master key"
|
||||
default "pmk1234567890123"
|
||||
help
|
||||
ESPNOW primary master for the example to use. The length of ESPNOW primary master must be 16 bytes.
|
||||
|
||||
config ESPNOW_LMK
|
||||
string "ESPNOW local master key"
|
||||
default "lmk1234567890123"
|
||||
help
|
||||
ESPNOW local master for the example to use. The length of ESPNOW local master must be 16 bytes.
|
||||
|
||||
config ESPNOW_CHANNEL
|
||||
int "Channel"
|
||||
default 1
|
||||
range 1 13
|
||||
help
|
||||
The channel on which sending and receiving ESPNOW data.
|
||||
|
||||
config ESPNOW_SEND_COUNT
|
||||
int "Send count"
|
||||
default 100
|
||||
range 1 65535
|
||||
help
|
||||
Total count of unicast ESPNOW data to be sent.
|
||||
|
||||
config ESPNOW_SEND_DELAY
|
||||
int "Send delay"
|
||||
default 1000
|
||||
range 0 65535
|
||||
help
|
||||
Delay between sending two ESPNOW data, unit: ms.
|
||||
|
||||
config ESPNOW_SEND_LEN
|
||||
int "Send len"
|
||||
range 10 250
|
||||
default 10
|
||||
help
|
||||
Length of ESPNOW data to be sent, unit: byte.
|
||||
|
||||
config ESPNOW_ENABLE_LONG_RANGE
|
||||
bool "Enable Long Range"
|
||||
default "n"
|
||||
help
|
||||
When enable long range, the PHY rate of ESP32 will be 512Kbps or 256Kbps
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/* ESPNOW 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.
|
||||
*/
|
||||
|
||||
#ifndef ESPNOW_EXAMPLE_H
|
||||
#define ESPNOW_EXAMPLE_H
|
||||
|
||||
/* ESPNOW can work in both station and softap mode. It is configured in menuconfig. */
|
||||
#if CONFIG_ESPNOW_WIFI_MODE_STATION
|
||||
#define ESPNOW_WIFI_MODE WIFI_MODE_STA
|
||||
#define ESPNOW_WIFI_IF ESP_IF_WIFI_STA
|
||||
#else
|
||||
#define ESPNOW_WIFI_MODE WIFI_MODE_AP
|
||||
#define ESPNOW_WIFI_IF ESP_IF_WIFI_AP
|
||||
#endif
|
||||
|
||||
#define ESPNOW_QUEUE_SIZE 6
|
||||
|
||||
#define IS_BROADCAST_ADDR(addr) (memcmp(addr, s_example_broadcast_mac, ESP_NOW_ETH_ALEN) == 0)
|
||||
|
||||
typedef enum {
|
||||
EXAMPLE_ESPNOW_SEND_CB,
|
||||
EXAMPLE_ESPNOW_RECV_CB,
|
||||
} example_espnow_event_id_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t mac_addr[ESP_NOW_ETH_ALEN];
|
||||
esp_now_send_status_t status;
|
||||
} example_espnow_event_send_cb_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t mac_addr[ESP_NOW_ETH_ALEN];
|
||||
uint8_t *data;
|
||||
int data_len;
|
||||
} example_espnow_event_recv_cb_t;
|
||||
|
||||
typedef union {
|
||||
example_espnow_event_send_cb_t send_cb;
|
||||
example_espnow_event_recv_cb_t recv_cb;
|
||||
} example_espnow_event_info_t;
|
||||
|
||||
/* When ESPNOW sending or receiving callback function is called, post event to ESPNOW task. */
|
||||
typedef struct {
|
||||
example_espnow_event_id_t id;
|
||||
example_espnow_event_info_t info;
|
||||
} example_espnow_event_t;
|
||||
|
||||
enum {
|
||||
EXAMPLE_ESPNOW_DATA_BROADCAST,
|
||||
EXAMPLE_ESPNOW_DATA_UNICAST,
|
||||
EXAMPLE_ESPNOW_DATA_MAX,
|
||||
};
|
||||
|
||||
/* User defined field of ESPNOW data in this example. */
|
||||
typedef struct {
|
||||
uint8_t type; //Broadcast or unicast ESPNOW data.
|
||||
uint8_t state; //Indicate that if has received broadcast ESPNOW data or not.
|
||||
uint16_t seq_num; //Sequence number of ESPNOW data.
|
||||
uint16_t crc; //CRC16 value of ESPNOW data.
|
||||
uint32_t magic; //Magic number which is used to determine which device to send unicast ESPNOW data.
|
||||
uint8_t payload[0]; //Real payload of ESPNOW data.
|
||||
} __attribute__((packed)) example_espnow_data_t;
|
||||
|
||||
/* Parameters of sending ESPNOW data. */
|
||||
typedef struct {
|
||||
bool unicast; //Send unicast ESPNOW data.
|
||||
bool broadcast; //Send broadcast ESPNOW data.
|
||||
uint8_t state; //Indicate that if has received broadcast ESPNOW data or not.
|
||||
uint32_t magic; //Magic number which is used to determine which device to send unicast ESPNOW data.
|
||||
uint16_t count; //Total count of unicast ESPNOW data to be sent.
|
||||
uint16_t delay; //Delay between sending two ESPNOW data, unit: ms.
|
||||
int len; //Length of ESPNOW data to be sent, unit: byte.
|
||||
uint8_t *buffer; //Buffer pointing to ESPNOW data.
|
||||
uint8_t dest_mac[ESP_NOW_ETH_ALEN]; //MAC address of destination device.
|
||||
} example_espnow_send_param_t;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,369 @@
|
||||
/* ESPNOW 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
This example shows how to use ESPNOW.
|
||||
Prepare two device, one for sending ESPNOW data and another for receiving
|
||||
ESPNOW data.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_now.h"
|
||||
#include "esp_crc.h"
|
||||
#include "espnow_example.h"
|
||||
|
||||
static const char *TAG = "espnow_example";
|
||||
|
||||
static xQueueHandle s_example_espnow_queue;
|
||||
|
||||
static uint8_t s_example_broadcast_mac[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
static uint16_t s_example_espnow_seq[EXAMPLE_ESPNOW_DATA_MAX] = { 0, 0 };
|
||||
|
||||
static void example_espnow_deinit(example_espnow_send_param_t *send_param);
|
||||
|
||||
/* WiFi should start before using ESPNOW */
|
||||
static void example_wifi_init(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
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(ESPNOW_WIFI_MODE) );
|
||||
ESP_ERROR_CHECK( esp_wifi_start());
|
||||
|
||||
#if CONFIG_ESPNOW_ENABLE_LONG_RANGE
|
||||
ESP_ERROR_CHECK( esp_wifi_set_protocol(ESPNOW_WIFI_IF, WIFI_PROTOCOL_11B|WIFI_PROTOCOL_11G|WIFI_PROTOCOL_11N|WIFI_PROTOCOL_LR) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ESPNOW sending or receiving callback function is called in WiFi task.
|
||||
* Users should not do lengthy operations from this task. Instead, post
|
||||
* necessary data to a queue and handle it from a lower priority task. */
|
||||
static void example_espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status)
|
||||
{
|
||||
example_espnow_event_t evt;
|
||||
example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
|
||||
|
||||
if (mac_addr == NULL) {
|
||||
ESP_LOGE(TAG, "Send cb arg error");
|
||||
return;
|
||||
}
|
||||
|
||||
evt.id = EXAMPLE_ESPNOW_SEND_CB;
|
||||
memcpy(send_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
|
||||
send_cb->status = status;
|
||||
if (xQueueSend(s_example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGW(TAG, "Send send queue fail");
|
||||
}
|
||||
}
|
||||
|
||||
static void example_espnow_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len)
|
||||
{
|
||||
example_espnow_event_t evt;
|
||||
example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
|
||||
|
||||
if (mac_addr == NULL || data == NULL || len <= 0) {
|
||||
ESP_LOGE(TAG, "Receive cb arg error");
|
||||
return;
|
||||
}
|
||||
|
||||
evt.id = EXAMPLE_ESPNOW_RECV_CB;
|
||||
memcpy(recv_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
|
||||
recv_cb->data = malloc(len);
|
||||
if (recv_cb->data == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc receive data fail");
|
||||
return;
|
||||
}
|
||||
memcpy(recv_cb->data, data, len);
|
||||
recv_cb->data_len = len;
|
||||
if (xQueueSend(s_example_espnow_queue, &evt, portMAX_DELAY) != pdTRUE) {
|
||||
ESP_LOGW(TAG, "Send receive queue fail");
|
||||
free(recv_cb->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse received ESPNOW data. */
|
||||
int example_espnow_data_parse(uint8_t *data, uint16_t data_len, uint8_t *state, uint16_t *seq, int *magic)
|
||||
{
|
||||
example_espnow_data_t *buf = (example_espnow_data_t *)data;
|
||||
uint16_t crc, crc_cal = 0;
|
||||
|
||||
if (data_len < sizeof(example_espnow_data_t)) {
|
||||
ESP_LOGE(TAG, "Receive ESPNOW data too short, len:%d", data_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*state = buf->state;
|
||||
*seq = buf->seq_num;
|
||||
*magic = buf->magic;
|
||||
crc = buf->crc;
|
||||
buf->crc = 0;
|
||||
crc_cal = esp_crc16_le(UINT16_MAX, (uint8_t const *)buf, data_len);
|
||||
|
||||
if (crc_cal == crc) {
|
||||
return buf->type;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prepare ESPNOW data to be sent. */
|
||||
void example_espnow_data_prepare(example_espnow_send_param_t *send_param)
|
||||
{
|
||||
example_espnow_data_t *buf = (example_espnow_data_t *)send_param->buffer;
|
||||
|
||||
assert(send_param->len >= sizeof(example_espnow_data_t));
|
||||
|
||||
buf->type = IS_BROADCAST_ADDR(send_param->dest_mac) ? EXAMPLE_ESPNOW_DATA_BROADCAST : EXAMPLE_ESPNOW_DATA_UNICAST;
|
||||
buf->state = send_param->state;
|
||||
buf->seq_num = s_example_espnow_seq[buf->type]++;
|
||||
buf->crc = 0;
|
||||
buf->magic = send_param->magic;
|
||||
/* Fill all remaining bytes after the data with random values */
|
||||
esp_fill_random(buf->payload, send_param->len - sizeof(example_espnow_data_t));
|
||||
buf->crc = esp_crc16_le(UINT16_MAX, (uint8_t const *)buf, send_param->len);
|
||||
}
|
||||
|
||||
static void example_espnow_task(void *pvParameter)
|
||||
{
|
||||
example_espnow_event_t evt;
|
||||
uint8_t recv_state = 0;
|
||||
uint16_t recv_seq = 0;
|
||||
int recv_magic = 0;
|
||||
bool is_broadcast = false;
|
||||
int ret;
|
||||
|
||||
vTaskDelay(5000 / portTICK_RATE_MS);
|
||||
ESP_LOGI(TAG, "Start sending broadcast data");
|
||||
|
||||
/* Start sending broadcast ESPNOW data. */
|
||||
example_espnow_send_param_t *send_param = (example_espnow_send_param_t *)pvParameter;
|
||||
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Send error");
|
||||
example_espnow_deinit(send_param);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
while (xQueueReceive(s_example_espnow_queue, &evt, portMAX_DELAY) == pdTRUE) {
|
||||
switch (evt.id) {
|
||||
case EXAMPLE_ESPNOW_SEND_CB:
|
||||
{
|
||||
example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb;
|
||||
is_broadcast = IS_BROADCAST_ADDR(send_cb->mac_addr);
|
||||
|
||||
ESP_LOGD(TAG, "Send data to "MACSTR", status1: %d", MAC2STR(send_cb->mac_addr), send_cb->status);
|
||||
|
||||
if (is_broadcast && (send_param->broadcast == false)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!is_broadcast) {
|
||||
send_param->count--;
|
||||
if (send_param->count == 0) {
|
||||
ESP_LOGI(TAG, "Send done");
|
||||
example_espnow_deinit(send_param);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Delay a while before sending the next data. */
|
||||
if (send_param->delay > 0) {
|
||||
vTaskDelay(send_param->delay/portTICK_RATE_MS);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(send_cb->mac_addr));
|
||||
|
||||
memcpy(send_param->dest_mac, send_cb->mac_addr, ESP_NOW_ETH_ALEN);
|
||||
example_espnow_data_prepare(send_param);
|
||||
|
||||
/* Send the next data after the previous data is sent. */
|
||||
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Send error");
|
||||
example_espnow_deinit(send_param);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EXAMPLE_ESPNOW_RECV_CB:
|
||||
{
|
||||
example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb;
|
||||
|
||||
ret = example_espnow_data_parse(recv_cb->data, recv_cb->data_len, &recv_state, &recv_seq, &recv_magic);
|
||||
free(recv_cb->data);
|
||||
if (ret == EXAMPLE_ESPNOW_DATA_BROADCAST) {
|
||||
ESP_LOGI(TAG, "Receive %dth broadcast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
|
||||
|
||||
/* If MAC address does not exist in peer list, add it to peer list. */
|
||||
if (esp_now_is_peer_exist(recv_cb->mac_addr) == false) {
|
||||
esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
|
||||
if (peer == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc peer information fail");
|
||||
example_espnow_deinit(send_param);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
memset(peer, 0, sizeof(esp_now_peer_info_t));
|
||||
peer->channel = CONFIG_ESPNOW_CHANNEL;
|
||||
peer->ifidx = ESPNOW_WIFI_IF;
|
||||
peer->encrypt = true;
|
||||
memcpy(peer->lmk, CONFIG_ESPNOW_LMK, ESP_NOW_KEY_LEN);
|
||||
memcpy(peer->peer_addr, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
|
||||
ESP_ERROR_CHECK( esp_now_add_peer(peer) );
|
||||
free(peer);
|
||||
}
|
||||
|
||||
/* Indicates that the device has received broadcast ESPNOW data. */
|
||||
if (send_param->state == 0) {
|
||||
send_param->state = 1;
|
||||
}
|
||||
|
||||
/* If receive broadcast ESPNOW data which indicates that the other device has received
|
||||
* broadcast ESPNOW data and the local magic number is bigger than that in the received
|
||||
* broadcast ESPNOW data, stop sending broadcast ESPNOW data and start sending unicast
|
||||
* ESPNOW data.
|
||||
*/
|
||||
if (recv_state == 1) {
|
||||
/* The device which has the bigger magic number sends ESPNOW data, the other one
|
||||
* receives ESPNOW data.
|
||||
*/
|
||||
if (send_param->unicast == false && send_param->magic >= recv_magic) {
|
||||
ESP_LOGI(TAG, "Start sending unicast data");
|
||||
ESP_LOGI(TAG, "send data to "MACSTR"", MAC2STR(recv_cb->mac_addr));
|
||||
|
||||
/* Start sending unicast ESPNOW data. */
|
||||
memcpy(send_param->dest_mac, recv_cb->mac_addr, ESP_NOW_ETH_ALEN);
|
||||
example_espnow_data_prepare(send_param);
|
||||
if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Send error");
|
||||
example_espnow_deinit(send_param);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
else {
|
||||
send_param->broadcast = false;
|
||||
send_param->unicast = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ret == EXAMPLE_ESPNOW_DATA_UNICAST) {
|
||||
ESP_LOGI(TAG, "Receive %dth unicast data from: "MACSTR", len: %d", recv_seq, MAC2STR(recv_cb->mac_addr), recv_cb->data_len);
|
||||
|
||||
/* If receive unicast ESPNOW data, also stop sending broadcast ESPNOW data. */
|
||||
send_param->broadcast = false;
|
||||
}
|
||||
else {
|
||||
ESP_LOGI(TAG, "Receive error data from: "MACSTR"", MAC2STR(recv_cb->mac_addr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(TAG, "Callback type error: %d", evt.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t example_espnow_init(void)
|
||||
{
|
||||
example_espnow_send_param_t *send_param;
|
||||
|
||||
s_example_espnow_queue = xQueueCreate(ESPNOW_QUEUE_SIZE, sizeof(example_espnow_event_t));
|
||||
if (s_example_espnow_queue == NULL) {
|
||||
ESP_LOGE(TAG, "Create mutex fail");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Initialize ESPNOW and register sending and receiving callback function. */
|
||||
ESP_ERROR_CHECK( esp_now_init() );
|
||||
ESP_ERROR_CHECK( esp_now_register_send_cb(example_espnow_send_cb) );
|
||||
ESP_ERROR_CHECK( esp_now_register_recv_cb(example_espnow_recv_cb) );
|
||||
|
||||
/* Set primary master key. */
|
||||
ESP_ERROR_CHECK( esp_now_set_pmk((uint8_t *)CONFIG_ESPNOW_PMK) );
|
||||
|
||||
/* Add broadcast peer information to peer list. */
|
||||
esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t));
|
||||
if (peer == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc peer information fail");
|
||||
vSemaphoreDelete(s_example_espnow_queue);
|
||||
esp_now_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memset(peer, 0, sizeof(esp_now_peer_info_t));
|
||||
peer->channel = CONFIG_ESPNOW_CHANNEL;
|
||||
peer->ifidx = ESPNOW_WIFI_IF;
|
||||
peer->encrypt = false;
|
||||
memcpy(peer->peer_addr, s_example_broadcast_mac, ESP_NOW_ETH_ALEN);
|
||||
ESP_ERROR_CHECK( esp_now_add_peer(peer) );
|
||||
free(peer);
|
||||
|
||||
/* Initialize sending parameters. */
|
||||
send_param = malloc(sizeof(example_espnow_send_param_t));
|
||||
memset(send_param, 0, sizeof(example_espnow_send_param_t));
|
||||
if (send_param == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc send parameter fail");
|
||||
vSemaphoreDelete(s_example_espnow_queue);
|
||||
esp_now_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
send_param->unicast = false;
|
||||
send_param->broadcast = true;
|
||||
send_param->state = 0;
|
||||
send_param->magic = esp_random();
|
||||
send_param->count = CONFIG_ESPNOW_SEND_COUNT;
|
||||
send_param->delay = CONFIG_ESPNOW_SEND_DELAY;
|
||||
send_param->len = CONFIG_ESPNOW_SEND_LEN;
|
||||
send_param->buffer = malloc(CONFIG_ESPNOW_SEND_LEN);
|
||||
if (send_param->buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc send buffer fail");
|
||||
free(send_param);
|
||||
vSemaphoreDelete(s_example_espnow_queue);
|
||||
esp_now_deinit();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memcpy(send_param->dest_mac, s_example_broadcast_mac, ESP_NOW_ETH_ALEN);
|
||||
example_espnow_data_prepare(send_param);
|
||||
|
||||
xTaskCreate(example_espnow_task, "example_espnow_task", 2048, send_param, 4, NULL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void example_espnow_deinit(example_espnow_send_param_t *send_param)
|
||||
{
|
||||
free(send_param->buffer);
|
||||
free(send_param);
|
||||
vSemaphoreDelete(s_example_espnow_queue);
|
||||
esp_now_deinit();
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK( nvs_flash_erase() );
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
example_wifi_init();
|
||||
example_espnow_init();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(fast_scan)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := fast_scan
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# Wifi Fast Scan Example
|
||||
|
||||
This example shows how to use the scan functionality of the Wi-Fi driver of ESP32 for connecting to an AP.
|
||||
|
||||
Two scan methods are supported: fast scan and all channel scan.
|
||||
|
||||
* fast scan: in this mode, scan finishes right after a matching AP is detected, even if channels are not completely scanned. You can set thresholds for signal strength, as well as select desired authentication modes provided by the AP's. The Wi-Fi driver will ignore AP's that fail to meet mentioned criteria.
|
||||
|
||||
* all channel scan: scan will end only after all channels are scanned; the Wi-Fi driver will store 4 of the fully matching AP's. Sort methods for AP's include rssi and authmode. After the scan, the Wi-Fi driver selects the AP that fits best based on the sort.
|
||||
|
||||
After the scan, the Wi-Fi driver will try to connect. Because it needs to to allocate precious dynamic memory to store matching AP's, and, most of the cases, connect to the AP with the strongest reception, it does not need to record all the AP's matched. The number of matches stored is limited to 4 in order to limit dynamic memory usage. Among the 4 matches, AP's are allowed to carry the same SSID name and all possible authentication modes - Open, WEP, WPA and WPA2.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "fast_scan.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,68 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config EXAMPLE_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
choice EXAMPLE_SCAN_METHOD
|
||||
prompt "scan method"
|
||||
default EXAMPLE_WIFI_FAST_SCAN
|
||||
help
|
||||
scan method for the esp32 to use
|
||||
|
||||
config EXAMPLE_WIFI_FAST_SCAN
|
||||
bool "fast"
|
||||
config EXAMPLE_WIFI_ALL_CHANNEL_SCAN
|
||||
bool "all"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_SORT_METHOD
|
||||
prompt "sort method"
|
||||
default EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
help
|
||||
sort method for the esp32 to use
|
||||
|
||||
config EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
bool "rssi"
|
||||
config EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
|
||||
bool "authmode"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_FAST_SCAN_THRESHOLD
|
||||
bool "fast scan threshold"
|
||||
default y
|
||||
help
|
||||
wifi fast scan threshold
|
||||
|
||||
config EXAMPLE_FAST_SCAN_MINIMUM_SIGNAL
|
||||
int "fast scan minimum rssi"
|
||||
depends on EXAMPLE_FAST_SCAN_THRESHOLD
|
||||
range -127 0
|
||||
default -127
|
||||
help
|
||||
rssi is use to measure the signal
|
||||
|
||||
choice EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE
|
||||
prompt "fast scan weakest authmode"
|
||||
depends on EXAMPLE_FAST_SCAN_THRESHOLD
|
||||
default EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_OPEN
|
||||
|
||||
config EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_OPEN
|
||||
bool "open"
|
||||
config EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WEP
|
||||
bool "wep"
|
||||
config EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WPA
|
||||
bool "wpa"
|
||||
config EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WPA2
|
||||
bool "wpa2"
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/* Scan 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
This example shows how to use the All Channel Scan or Fast Scan to connect
|
||||
to a Wi-Fi network.
|
||||
|
||||
In the Fast Scan mode, the scan will stop as soon as the first network matching
|
||||
the SSID is found. In this mode, an application can set threshold for the
|
||||
authentication mode and the Signal strength. Networks that do not meet the
|
||||
threshold requirements will be ignored.
|
||||
|
||||
In the All Channel Scan mode, the scan will end only after all the channels
|
||||
are scanned, and connection will start with the best network. The networks
|
||||
can be sorted based on Authentication Mode or Signal Strength. The priority
|
||||
for the Authentication mode is: WPA2 > WPA > WEP > Open
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
/* Set the SSID and Password via project configuration, or can set directly here */
|
||||
#define DEFAULT_SSID CONFIG_EXAMPLE_WIFI_SSID
|
||||
#define DEFAULT_PWD CONFIG_EXAMPLE_WIFI_PASSWORD
|
||||
|
||||
#if CONFIG_EXAMPLE_WIFI_ALL_CHANNEL_SCAN
|
||||
#define DEFAULT_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
|
||||
#elif CONFIG_EXAMPLE_WIFI_FAST_SCAN
|
||||
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
|
||||
#else
|
||||
#define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN
|
||||
#endif /*CONFIG_EXAMPLE_SCAN_METHOD*/
|
||||
|
||||
#if CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SIGNAL
|
||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
|
||||
#elif CONFIG_EXAMPLE_WIFI_CONNECT_AP_BY_SECURITY
|
||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY
|
||||
#else
|
||||
#define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
|
||||
#endif /*CONFIG_EXAMPLE_SORT_METHOD*/
|
||||
|
||||
#if CONFIG_EXAMPLE_FAST_SCAN_THRESHOLD
|
||||
#define DEFAULT_RSSI CONFIG_EXAMPLE_FAST_SCAN_MINIMUM_SIGNAL
|
||||
#if CONFIG_EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_OPEN
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
||||
#elif CONFIG_EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WEP
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WEP
|
||||
#elif CONFIG_EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WPA
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA_PSK
|
||||
#elif CONFIG_EXAMPLE_FAST_SCAN_WEAKEST_AUTHMODE_WPA2
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_WPA2_PSK
|
||||
#else
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
||||
#endif
|
||||
#else
|
||||
#define DEFAULT_RSSI -127
|
||||
#define DEFAULT_AUTHMODE WIFI_AUTH_OPEN
|
||||
#endif /*CONFIG_EXAMPLE_FAST_SCAN_THRESHOLD*/
|
||||
|
||||
static const char *TAG = "scan";
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Initialize Wi-Fi as sta and set scan method */
|
||||
static void fast_scan(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));
|
||||
|
||||
// Initialize default station as network interface instance (esp-netif)
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
assert(sta_netif);
|
||||
|
||||
// Initialize and start WiFi
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = DEFAULT_SSID,
|
||||
.password = DEFAULT_PWD,
|
||||
.scan_method = DEFAULT_SCAN_METHOD,
|
||||
.sort_method = DEFAULT_SORT_METHOD,
|
||||
.threshold.rssi = DEFAULT_RSSI,
|
||||
.threshold.authmode = DEFAULT_AUTHMODE,
|
||||
},
|
||||
};
|
||||
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_start());
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
fast_scan();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following five 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wifi_softAP)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := wifi_softAP
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# WiFi softAP example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
|
||||
## How to use example
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set WiFi SSID and WiFi Password and Maximal STA connections under Example Configuration Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
There is the console output for this example:
|
||||
|
||||
```
|
||||
I (917) phy: phy_version: 3960, 5211945, Jul 18 2018, 10:40:07, 0, 0
|
||||
I (917) wifi: mode : softAP (30:ae:a4:80:45:69)
|
||||
I (917) wifi softAP: wifi_init_softap finished.SSID:myssid password:mypassword
|
||||
I (26457) wifi: n:1 0, o:1 0, ap:1 1, sta:255 255, prof:1
|
||||
I (26457) wifi: station: 70:ef:00:43:96:67 join, AID=1, bg, 20
|
||||
I (26467) wifi softAP: station:70:ef:00:43:96:67 join, AID=1
|
||||
I (27657) tcpip_adapter: softAP assign IP to station,IP is: 192.168.4.2
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "softap_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,26 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config ESP_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config ESP_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
config ESP_WIFI_CHANNEL
|
||||
int "WiFi Channel"
|
||||
range 1 13
|
||||
default 1
|
||||
help
|
||||
WiFi channel (network channel) for the example to use.
|
||||
|
||||
config ESP_MAX_STA_CONN
|
||||
int "Maximal STA connections"
|
||||
default 4
|
||||
help
|
||||
Max number of the STA connects to AP.
|
||||
endmenu
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it 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 ESP-IDF documents if you need to do this.
|
||||
#
|
||||
@@ -0,0 +1,96 @@
|
||||
/* WiFi softAP 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 <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
/* The examples use WiFi configuration that you can set via project configuration menu.
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
|
||||
*/
|
||||
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
|
||||
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
|
||||
#define EXAMPLE_ESP_WIFI_CHANNEL CONFIG_ESP_WIFI_CHANNEL
|
||||
#define EXAMPLE_MAX_STA_CONN CONFIG_ESP_MAX_STA_CONN
|
||||
|
||||
static const char *TAG = "wifi softAP";
|
||||
|
||||
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
|
||||
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
|
||||
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
|
||||
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
|
||||
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
|
||||
MAC2STR(event->mac), event->aid);
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_init_softap(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_ap();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&wifi_event_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
.ssid = EXAMPLE_ESP_WIFI_SSID,
|
||||
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
|
||||
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
|
||||
.password = EXAMPLE_ESP_WIFI_PASS,
|
||||
.max_connection = EXAMPLE_MAX_STA_CONN,
|
||||
.authmode = WIFI_AUTH_WPA_WPA2_PSK
|
||||
},
|
||||
};
|
||||
if (strlen(EXAMPLE_ESP_WIFI_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));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
|
||||
wifi_init_softap();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following five 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wifi_station)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := wifi_station
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# WiFi station example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
|
||||
## How to use example
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set WiFi SSID and WiFi Password and Maximum retry under Example Configuration Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
Note that the output, in particular the order of the output, may vary depending on the environment.
|
||||
|
||||
Console output if station connects to AP successfully:
|
||||
```
|
||||
I (589) wifi station: ESP_WIFI_MODE_STA
|
||||
I (599) wifi: wifi driver task: 3ffc08b4, prio:23, stack:3584, core=0
|
||||
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (629) wifi: wifi firmware version: 2d94f02
|
||||
I (629) wifi: config NVS flash: enabled
|
||||
I (629) wifi: config nano formating: disabled
|
||||
I (629) wifi: Init dynamic tx buffer num: 32
|
||||
I (629) wifi: Init data frame dynamic rx buffer num: 32
|
||||
I (639) wifi: Init management frame dynamic rx buffer num: 32
|
||||
I (639) wifi: Init management short buffer num: 32
|
||||
I (649) wifi: Init static rx buffer size: 1600
|
||||
I (649) wifi: Init static rx buffer num: 10
|
||||
I (659) wifi: Init dynamic rx buffer num: 32
|
||||
I (759) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
|
||||
I (769) wifi: mode : sta (30:ae:a4:d9:bc:c4)
|
||||
I (769) wifi station: wifi_init_sta finished.
|
||||
I (889) wifi: new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (889) wifi: state: init -> auth (b0)
|
||||
I (899) wifi: state: auth -> assoc (0)
|
||||
I (909) wifi: state: assoc -> run (10)
|
||||
I (939) wifi: connected with #!/bin/test, aid = 1, channel 6, BW20, bssid = ac:9e:17:7e:31:40
|
||||
I (939) wifi: security type: 3, phy: bgn, rssi: -68
|
||||
I (949) wifi: pm start, type: 1
|
||||
|
||||
I (1029) wifi: AP's beacon interval = 102400 us, DTIM period = 3
|
||||
I (2089) esp_netif_handlers: sta ip: 192.168.77.89, mask: 255.255.255.0, gw: 192.168.77.1
|
||||
I (2089) wifi station: got ip:192.168.77.89
|
||||
I (2089) wifi station: connected to ap SSID:myssid password:mypassword
|
||||
```
|
||||
|
||||
Console output if the station failed to connect to AP:
|
||||
```
|
||||
I (589) wifi station: ESP_WIFI_MODE_STA
|
||||
I (599) wifi: wifi driver task: 3ffc08b4, prio:23, stack:3584, core=0
|
||||
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (599) system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE
|
||||
I (629) wifi: wifi firmware version: 2d94f02
|
||||
I (629) wifi: config NVS flash: enabled
|
||||
I (629) wifi: config nano formating: disabled
|
||||
I (629) wifi: Init dynamic tx buffer num: 32
|
||||
I (629) wifi: Init data frame dynamic rx buffer num: 32
|
||||
I (639) wifi: Init management frame dynamic rx buffer num: 32
|
||||
I (639) wifi: Init management short buffer num: 32
|
||||
I (649) wifi: Init static rx buffer size: 1600
|
||||
I (649) wifi: Init static rx buffer num: 10
|
||||
I (659) wifi: Init dynamic rx buffer num: 32
|
||||
I (759) phy: phy_version: 4180, cb3948e, Sep 12 2019, 16:39:13, 0, 0
|
||||
I (759) wifi: mode : sta (30:ae:a4:d9:bc:c4)
|
||||
I (769) wifi station: wifi_init_sta finished.
|
||||
I (889) wifi: new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (889) wifi: state: init -> auth (b0)
|
||||
I (1889) wifi: state: auth -> init (200)
|
||||
I (1889) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (1889) wifi station: retry to connect to the AP
|
||||
I (1899) wifi station: connect to the AP fail
|
||||
I (3949) wifi station: retry to connect to the AP
|
||||
I (3949) wifi station: connect to the AP fail
|
||||
I (4069) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (4069) wifi: state: init -> auth (b0)
|
||||
I (5069) wifi: state: auth -> init (200)
|
||||
I (5069) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (5069) wifi station: retry to connect to the AP
|
||||
I (5069) wifi station: connect to the AP fail
|
||||
I (7129) wifi station: retry to connect to the AP
|
||||
I (7129) wifi station: connect to the AP fail
|
||||
I (7249) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (7249) wifi: state: init -> auth (b0)
|
||||
I (8249) wifi: state: auth -> init (200)
|
||||
I (8249) wifi: new:<6,0>, old:<6,0>, ap:<255,255>, sta:<6,0>, prof:1
|
||||
I (8249) wifi station: retry to connect to the AP
|
||||
I (8249) wifi station: connect to the AP fail
|
||||
I (10299) wifi station: connect to the AP fail
|
||||
I (10299) wifi station: Failed to connect to SSID:myssid, password:mypassword
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "station_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,20 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config ESP_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config ESP_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
config ESP_MAXIMUM_RETRY
|
||||
int "Maximum retry"
|
||||
default 5
|
||||
help
|
||||
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
|
||||
endmenu
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it 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 ESP-IDF documents if you need to do this.
|
||||
#
|
||||
@@ -0,0 +1,150 @@
|
||||
/* WiFi station 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 <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
/* The examples use WiFi configuration that you can set via project configuration menu
|
||||
|
||||
If you'd rather not, just change the below entries to strings with
|
||||
the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
|
||||
*/
|
||||
#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID
|
||||
#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD
|
||||
#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected*/
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
/* The event group allows multiple bits for each event, but we only care about two events:
|
||||
* - we are connected to the AP with an IP
|
||||
* - we failed to connect after the maximum amount of retries */
|
||||
#define WIFI_CONNECTED_BIT BIT0
|
||||
#define WIFI_FAIL_BIT BIT1
|
||||
|
||||
static const char *TAG = "wifi station";
|
||||
|
||||
static int s_retry_num = 0;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
|
||||
esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGI(TAG, "retry to connect to the AP");
|
||||
} else {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
ESP_LOGI(TAG,"connect to the AP fail");
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||
s_retry_num = 0;
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_init_sta(void)
|
||||
{
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
esp_event_handler_instance_t instance_any_id;
|
||||
esp_event_handler_instance_t instance_got_ip;
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_any_id));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&event_handler,
|
||||
NULL,
|
||||
&instance_got_ip));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = EXAMPLE_ESP_WIFI_SSID,
|
||||
.password = EXAMPLE_ESP_WIFI_PASS,
|
||||
/* Setting a password implies station will connect to all security modes including WEP/WPA.
|
||||
* However these modes are deprecated and not advisable to be used. Incase your Access point
|
||||
* doesn't support WPA2, these mode can be enabled by commenting below line */
|
||||
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
|
||||
|
||||
.pmf_cfg = {
|
||||
.capable = true,
|
||||
.required = false
|
||||
},
|
||||
},
|
||||
};
|
||||
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_start() );
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished.");
|
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
|
||||
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
portMAX_DELAY);
|
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
|
||||
* happened. */
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
|
||||
} else if (bits & WIFI_FAIL_BIT) {
|
||||
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
|
||||
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UNEXPECTED EVENT");
|
||||
}
|
||||
|
||||
/* The event will not be processed after unregister */
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
|
||||
vEventGroupDelete(s_wifi_event_group);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
//Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
|
||||
wifi_init_sta();
|
||||
}
|
||||
@@ -0,0 +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.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(iperf)
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := iperf
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/system/console/components
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
@@ -0,0 +1,61 @@
|
||||
# Iperf Example
|
||||
|
||||
## Note about iperf version
|
||||
The iperf example doesn't support all features in standard iperf. It's compitable with iperf version 2.x.
|
||||
|
||||
## Note about 80MHz flash frequency
|
||||
The iperf can get better throughput if the SPI flash frequency is set to 80MHz, but the system may crash in 80MHz mode for ESP-WROVER-KIT.
|
||||
Removing R140~R145 from the board can fix this issue. Currently the default SPI frequency is set to 40MHz, if you want to change the SPI flash
|
||||
frequency to 80MHz, please make sure R140~R145 are removed from ESP-WROVER-KIT or use ESP32 DevKitC.
|
||||
|
||||
## Introduction
|
||||
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.
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "iperf.c"
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES lwip)
|
||||
@@ -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 := .
|
||||
@@ -0,0 +1,435 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.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 sockfd;
|
||||
} 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));
|
||||
}
|
||||
|
||||
static int iperf_get_socket_error_code(int sockfd)
|
||||
{
|
||||
|
||||
return errno;
|
||||
}
|
||||
|
||||
static int iperf_show_socket_error_reason(const char *str, int sockfd)
|
||||
{
|
||||
int err = errno;
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void iperf_report_task(void *arg)
|
||||
{
|
||||
uint32_t interval = s_iperf_ctrl.cfg.interval;
|
||||
uint32_t time = s_iperf_ctrl.cfg.time;
|
||||
TickType_t delay_interval = (interval * 1000) / portTICK_PERIOD_MS;
|
||||
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);
|
||||
}
|
||||
|
||||
static esp_err_t iperf_start_report(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
|
||||
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t IRAM_ATTR 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 sockfd;
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
sockfd = accept(listen_socket, (struct sockaddr *)&remote_addr, &addr_len);
|
||||
if (sockfd < 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));
|
||||
iperf_start_report();
|
||||
|
||||
t.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
|
||||
}
|
||||
|
||||
while (!s_iperf_ctrl.finish) {
|
||||
actual_recv = recv(sockfd, buffer, want_recv, 0);
|
||||
if (actual_recv < 0) {
|
||||
iperf_show_socket_error_reason("tcp server recv", listen_socket);
|
||||
s_iperf_ctrl.finish = true;
|
||||
break;
|
||||
} else {
|
||||
s_iperf_ctrl.total_len += actual_recv;
|
||||
}
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
s_iperf_ctrl.finish = true;
|
||||
close(listen_socket);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t IRAM_ATTR 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 sockfd;
|
||||
int opt;
|
||||
bool udp_recv_start = true ;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sockfd < 0) {
|
||||
iperf_show_socket_error_reason("udp server create", sockfd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
setsockopt(sockfd, 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(sockfd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
|
||||
iperf_show_socket_error_reason("udp server bind", sockfd);
|
||||
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;
|
||||
|
||||
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(sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t));
|
||||
|
||||
while (!s_iperf_ctrl.finish) {
|
||||
actual_recv = recvfrom(sockfd, buffer, want_recv, 0, (struct sockaddr *)&addr, &addr_len);
|
||||
if (actual_recv < 0) {
|
||||
iperf_show_socket_error_reason("udp server recv", sockfd);
|
||||
} else {
|
||||
if(udp_recv_start){
|
||||
iperf_start_report();
|
||||
udp_recv_start = false;
|
||||
}
|
||||
s_iperf_ctrl.total_len += actual_recv;
|
||||
}
|
||||
}
|
||||
|
||||
s_iperf_ctrl.finish = true;
|
||||
close(sockfd);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static 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 sockfd;
|
||||
int opt;
|
||||
int err;
|
||||
int id;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sockfd < 0) {
|
||||
iperf_show_socket_error_reason("udp client create", sockfd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
|
||||
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(sockfd, buffer, want_send, 0, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
if (actual_send != want_send) {
|
||||
err = iperf_get_socket_error_code(sockfd);
|
||||
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(sockfd);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t iperf_run_tcp_client(void)
|
||||
{
|
||||
struct sockaddr_in remote_addr;
|
||||
int actual_send = 0;
|
||||
int want_send = 0;
|
||||
uint8_t *buffer;
|
||||
int sockfd;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sockfd < 0) {
|
||||
iperf_show_socket_error_reason("tcp client create", sockfd);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
memset(&remote_addr, 0, sizeof(remote_addr));
|
||||
remote_addr.sin_family = AF_INET;
|
||||
remote_addr.sin_port = htons(s_iperf_ctrl.cfg.dport);
|
||||
remote_addr.sin_addr.s_addr = s_iperf_ctrl.cfg.dip;
|
||||
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) < 0) {
|
||||
iperf_show_socket_error_reason("tcp client connect", sockfd);
|
||||
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(sockfd, buffer, want_send, 0);
|
||||
if (actual_send <= 0) {
|
||||
iperf_show_socket_error_reason("tcp client send", sockfd);
|
||||
break;
|
||||
} else {
|
||||
s_iperf_ctrl.total_len += actual_send;
|
||||
}
|
||||
}
|
||||
|
||||
s_iperf_ctrl.finish = true;
|
||||
close(sockfd);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static 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 = NULL;
|
||||
}
|
||||
ESP_LOGI(TAG, "iperf exit");
|
||||
s_iperf_is_running = false;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
BaseType_t 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: not enough memory");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
|
||||
|
||||
ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, portNUM_PROCESSORS - 1);
|
||||
if (ret != pdPASS) {
|
||||
ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
|
||||
free(s_iperf_ctrl.buffer);
|
||||
s_iperf_ctrl.buffer = NULL;
|
||||
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_PERIOD_MS);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* 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
|
||||
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#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 10
|
||||
#define IPERF_TRAFFIC_TASK_STACK 4096
|
||||
#define IPERF_REPORT_TASK_NAME "iperf_report"
|
||||
#define IPERF_REPORT_TASK_PRIORITY 20
|
||||
#define IPERF_REPORT_TASK_STACK 4096
|
||||
#define IPERF_REPORT_TASK_NAME "iperf_report"
|
||||
|
||||
#define IPERF_UDP_TX_LEN (1472)
|
||||
#define IPERF_UDP_RX_LEN (16 << 10)
|
||||
#define IPERF_TCP_TX_LEN (16 << 10)
|
||||
#define IPERF_TCP_RX_LEN (16 << 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
|
||||
@@ -0,0 +1,621 @@
|
||||
"""
|
||||
Test case for iperf example.
|
||||
|
||||
This test case might have problem running on windows:
|
||||
|
||||
1. direct use of `make`
|
||||
2. use `sudo killall iperf` to force kill iperf, didn't implement windows version
|
||||
|
||||
The test env Example_ShieldBox do need the following config::
|
||||
|
||||
Example_ShieldBox:
|
||||
ap_list:
|
||||
- ssid: "ssid"
|
||||
password: "password"
|
||||
outlet: 1
|
||||
apc_ip: "192.168.1.88"
|
||||
attenuator_port: "/dev/ttyUSB0"
|
||||
iperf: "/dev/ttyUSB1"
|
||||
apc_ip: "192.168.1.88"
|
||||
pc_nic: "eth0"
|
||||
"""
|
||||
from __future__ import division
|
||||
from __future__ import unicode_literals
|
||||
from builtins import str
|
||||
from builtins import range
|
||||
from builtins import object
|
||||
import re
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from tiny_test_fw import TinyFW, DUT, Utility
|
||||
import ttfw_idf
|
||||
from idf_iperf_test_util import (Attenuator, PowerControl, LineChart, TestReport)
|
||||
|
||||
|
||||
# configurations
|
||||
TEST_TIME = TEST_TIMEOUT = 60
|
||||
WAIT_AP_POWER_ON_TIMEOUT = 90
|
||||
SCAN_TIMEOUT = 3
|
||||
SCAN_RETRY_COUNT = 3
|
||||
RETRY_COUNT_FOR_BEST_PERFORMANCE = 2
|
||||
ATTEN_VALUE_LIST = range(0, 60, 2)
|
||||
|
||||
# constants
|
||||
FAILED_TO_SCAN_RSSI = -97
|
||||
INVALID_HEAP_SIZE = 0xFFFFFFFF
|
||||
|
||||
PC_IPERF_TEMP_LOG_FILE = ".tmp_iperf.log"
|
||||
CONFIG_NAME_PATTERN = re.compile(r"sdkconfig\.ci\.(.+)")
|
||||
|
||||
# We need to auto compare the difference between adjacent configs (01 -> 00, 02 -> 01, ...) and put them to reports.
|
||||
# Using numbers for config will make this easy.
|
||||
# Use default value `99` for config with best performance.
|
||||
BEST_PERFORMANCE_CONFIG = "99"
|
||||
|
||||
|
||||
class TestResult(object):
|
||||
""" record, analysis test result and convert data to output format """
|
||||
|
||||
PC_BANDWIDTH_LOG_PATTERN = re.compile(r"(\d+).0\s*-\s*(\d+).0\s+sec\s+[\d.]+\s+MBytes\s+([\d.]+)\s+Mbits/sec")
|
||||
DUT_BANDWIDTH_LOG_PATTERN = re.compile(r"(\d+)-\s+(\d+)\s+sec\s+([\d.]+)\s+Mbits/sec")
|
||||
|
||||
ZERO_POINT_THRESHOLD = -88 # RSSI, dbm
|
||||
ZERO_THROUGHPUT_THRESHOLD = -92 # RSSI, dbm
|
||||
BAD_POINT_RSSI_THRESHOLD = -85 # RSSI, dbm
|
||||
BAD_POINT_MIN_THRESHOLD = 3 # Mbps
|
||||
BAD_POINT_PERCENTAGE_THRESHOLD = 0.3
|
||||
|
||||
# we need at least 1/2 valid points to qualify the test result
|
||||
THROUGHPUT_QUALIFY_COUNT = TEST_TIME // 2
|
||||
|
||||
def __init__(self, proto, direction, config_name):
|
||||
self.proto = proto
|
||||
self.direction = direction
|
||||
self.config_name = config_name
|
||||
self.throughput_by_rssi = dict()
|
||||
self.throughput_by_att = dict()
|
||||
self.att_rssi_map = dict()
|
||||
self.heap_size = INVALID_HEAP_SIZE
|
||||
self.error_list = []
|
||||
|
||||
def _save_result(self, throughput, ap_ssid, att, rssi, heap_size):
|
||||
"""
|
||||
save the test results:
|
||||
|
||||
* record the better throughput if att/rssi is the same.
|
||||
* record the min heap size.
|
||||
"""
|
||||
if ap_ssid not in self.att_rssi_map:
|
||||
# for new ap, create empty dict()
|
||||
self.throughput_by_att[ap_ssid] = dict()
|
||||
self.throughput_by_rssi[ap_ssid] = dict()
|
||||
self.att_rssi_map[ap_ssid] = dict()
|
||||
|
||||
self.att_rssi_map[ap_ssid][att] = rssi
|
||||
|
||||
def record_throughput(database, key_value):
|
||||
try:
|
||||
# we save the larger value for same att
|
||||
if throughput > database[ap_ssid][key_value]:
|
||||
database[ap_ssid][key_value] = throughput
|
||||
except KeyError:
|
||||
database[ap_ssid][key_value] = throughput
|
||||
|
||||
record_throughput(self.throughput_by_att, att)
|
||||
record_throughput(self.throughput_by_rssi, rssi)
|
||||
|
||||
if int(heap_size) < self.heap_size:
|
||||
self.heap_size = int(heap_size)
|
||||
|
||||
def add_result(self, raw_data, ap_ssid, att, rssi, heap_size):
|
||||
"""
|
||||
add result for one test
|
||||
|
||||
:param raw_data: iperf raw data
|
||||
:param ap_ssid: ap ssid that tested
|
||||
:param att: attenuate value
|
||||
:param rssi: AP RSSI
|
||||
:param heap_size: min heap size during test
|
||||
:return: throughput
|
||||
"""
|
||||
fall_to_0_recorded = 0
|
||||
throughput_list = []
|
||||
result_list = self.PC_BANDWIDTH_LOG_PATTERN.findall(raw_data)
|
||||
if not result_list:
|
||||
# failed to find raw data by PC pattern, it might be DUT pattern
|
||||
result_list = self.DUT_BANDWIDTH_LOG_PATTERN.findall(raw_data)
|
||||
|
||||
for result in result_list:
|
||||
if int(result[1]) - int(result[0]) != 1:
|
||||
# this could be summary, ignore this
|
||||
continue
|
||||
throughput_list.append(float(result[2]))
|
||||
if float(result[2]) == 0 and rssi > self.ZERO_POINT_THRESHOLD \
|
||||
and fall_to_0_recorded < 1:
|
||||
# throughput fall to 0 error. we only record 1 records for one test
|
||||
self.error_list.append("[Error][fall to 0][{}][att: {}][rssi: {}]: 0 throughput interval: {}-{}"
|
||||
.format(ap_ssid, att, rssi, result[0], result[1]))
|
||||
fall_to_0_recorded += 1
|
||||
|
||||
if len(throughput_list) > self.THROUGHPUT_QUALIFY_COUNT:
|
||||
throughput = sum(throughput_list) / len(throughput_list)
|
||||
else:
|
||||
throughput = 0.0
|
||||
|
||||
if throughput == 0 and rssi > self.ZERO_THROUGHPUT_THRESHOLD:
|
||||
self.error_list.append("[Error][Fatal][{}][att: {}][rssi: {}]: No throughput data found"
|
||||
.format(ap_ssid, att, rssi))
|
||||
|
||||
self._save_result(throughput, ap_ssid, att, rssi, heap_size)
|
||||
|
||||
return throughput
|
||||
|
||||
def post_analysis(self):
|
||||
"""
|
||||
some rules need to be checked after we collected all test raw data:
|
||||
|
||||
1. throughput value 30% worse than the next point with lower RSSI
|
||||
2. throughput value 30% worse than the next point with larger attenuate
|
||||
"""
|
||||
def analysis_bad_point(data, index_type):
|
||||
for ap_ssid in data:
|
||||
result_dict = data[ap_ssid]
|
||||
index_list = list(result_dict.keys())
|
||||
index_list.sort()
|
||||
if index_type == "att":
|
||||
index_list.reverse()
|
||||
|
||||
for i, index_value in enumerate(index_list[1:]):
|
||||
if index_value < self.BAD_POINT_RSSI_THRESHOLD or \
|
||||
result_dict[index_list[i]] < self.BAD_POINT_MIN_THRESHOLD:
|
||||
continue
|
||||
_percentage = result_dict[index_value] / result_dict[index_list[i]]
|
||||
if _percentage < 1 - self.BAD_POINT_PERCENTAGE_THRESHOLD:
|
||||
self.error_list.append("[Error][Bad point][{}][{}: {}]: drop {:.02f}%"
|
||||
.format(ap_ssid, index_type, index_value,
|
||||
(1 - _percentage) * 100))
|
||||
|
||||
analysis_bad_point(self.throughput_by_rssi, "rssi")
|
||||
analysis_bad_point(self.throughput_by_att, "att")
|
||||
|
||||
@staticmethod
|
||||
def _convert_to_draw_format(data, label):
|
||||
keys = data.keys()
|
||||
keys.sort()
|
||||
return {
|
||||
"x-axis": keys,
|
||||
"y-axis": [data[x] for x in keys],
|
||||
"label": label,
|
||||
}
|
||||
|
||||
def draw_throughput_figure(self, path, ap_ssid, draw_type):
|
||||
"""
|
||||
:param path: folder to save figure. make sure the folder is already created.
|
||||
:param ap_ssid: ap ssid string or a list of ap ssid string
|
||||
:param draw_type: "att" or "rssi"
|
||||
:return: file_name
|
||||
"""
|
||||
if draw_type == "rssi":
|
||||
type_name = "RSSI"
|
||||
data = self.throughput_by_rssi
|
||||
elif draw_type == "att":
|
||||
type_name = "Att"
|
||||
data = self.throughput_by_att
|
||||
else:
|
||||
raise AssertionError("draw type not supported")
|
||||
if isinstance(ap_ssid, list):
|
||||
file_name = "ThroughputVs{}_{}_{}_{}.png".format(type_name, self.proto, self.direction,
|
||||
hash(ap_ssid)[:6])
|
||||
data_list = [self._convert_to_draw_format(data[_ap_ssid], _ap_ssid)
|
||||
for _ap_ssid in ap_ssid]
|
||||
else:
|
||||
file_name = "ThroughputVs{}_{}_{}_{}.png".format(type_name, self.proto, self.direction, ap_ssid)
|
||||
data_list = [self._convert_to_draw_format(data[ap_ssid], ap_ssid)]
|
||||
|
||||
LineChart.draw_line_chart(os.path.join(path, file_name),
|
||||
"Throughput Vs {} ({} {})".format(type_name, self.proto, self.direction),
|
||||
"Throughput (Mbps)",
|
||||
"{} (dbm)".format(type_name),
|
||||
data_list)
|
||||
return file_name
|
||||
|
||||
def draw_rssi_vs_att_figure(self, path, ap_ssid):
|
||||
"""
|
||||
:param path: folder to save figure. make sure the folder is already created.
|
||||
:param ap_ssid: ap to use
|
||||
:return: file_name
|
||||
"""
|
||||
if isinstance(ap_ssid, list):
|
||||
file_name = "AttVsRSSI_{}.png".format(hash(ap_ssid)[:6])
|
||||
data_list = [self._convert_to_draw_format(self.att_rssi_map[_ap_ssid], _ap_ssid)
|
||||
for _ap_ssid in ap_ssid]
|
||||
else:
|
||||
file_name = "AttVsRSSI_{}.png".format(ap_ssid)
|
||||
data_list = [self._convert_to_draw_format(self.att_rssi_map[ap_ssid], ap_ssid)]
|
||||
LineChart.draw_line_chart(os.path.join(path, file_name),
|
||||
"Att Vs RSSI",
|
||||
"Att (dbm)",
|
||||
"RSSI (dbm)",
|
||||
data_list)
|
||||
return file_name
|
||||
|
||||
def get_best_throughput(self):
|
||||
""" get the best throughput during test """
|
||||
best_for_aps = [max(self.throughput_by_att[ap_ssid].values())
|
||||
for ap_ssid in self.throughput_by_att]
|
||||
return max(best_for_aps)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
returns summary for this test:
|
||||
|
||||
1. test result (success or fail)
|
||||
2. best performance for each AP
|
||||
3. min free heap size during test
|
||||
"""
|
||||
if self.throughput_by_att:
|
||||
ret = "[{}_{}][{}]: {}\r\n\r\n".format(self.proto, self.direction, self.config_name,
|
||||
"Fail" if self.error_list else "Success")
|
||||
ret += "Performance for each AP:\r\n"
|
||||
for ap_ssid in self.throughput_by_att:
|
||||
ret += "[{}]: {:.02f} Mbps\r\n".format(ap_ssid, max(self.throughput_by_att[ap_ssid].values()))
|
||||
if self.heap_size != INVALID_HEAP_SIZE:
|
||||
ret += "Minimum heap size: {}".format(self.heap_size)
|
||||
else:
|
||||
ret = ""
|
||||
return ret
|
||||
|
||||
|
||||
class IperfTestUtility(object):
|
||||
""" iperf test implementation """
|
||||
|
||||
def __init__(self, dut, config_name, ap_ssid, ap_password,
|
||||
pc_nic_ip, pc_iperf_log_file, test_result=None):
|
||||
self.config_name = config_name
|
||||
self.dut = dut
|
||||
|
||||
self.pc_iperf_log_file = pc_iperf_log_file
|
||||
self.ap_ssid = ap_ssid
|
||||
self.ap_password = ap_password
|
||||
self.pc_nic_ip = pc_nic_ip
|
||||
|
||||
if test_result:
|
||||
self.test_result = test_result
|
||||
else:
|
||||
self.test_result = {
|
||||
"tcp_tx": TestResult("tcp", "tx", config_name),
|
||||
"tcp_rx": TestResult("tcp", "rx", config_name),
|
||||
"udp_tx": TestResult("udp", "tx", config_name),
|
||||
"udp_rx": TestResult("udp", "rx", config_name),
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
setup iperf test:
|
||||
|
||||
1. kill current iperf process
|
||||
2. reboot DUT (currently iperf is not very robust, need to reboot DUT)
|
||||
3. scan to get AP RSSI
|
||||
4. connect to AP
|
||||
"""
|
||||
try:
|
||||
subprocess.check_output("sudo killall iperf 2>&1 > /dev/null", shell=True)
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
self.dut.write("restart")
|
||||
self.dut.expect("iperf>")
|
||||
self.dut.write("scan {}".format(self.ap_ssid))
|
||||
for _ in range(SCAN_RETRY_COUNT):
|
||||
try:
|
||||
rssi = int(self.dut.expect(re.compile(r"\[{}]\[rssi=(-\d+)]".format(self.ap_ssid)),
|
||||
timeout=SCAN_TIMEOUT)[0])
|
||||
break
|
||||
except DUT.ExpectTimeout:
|
||||
continue
|
||||
else:
|
||||
raise AssertionError("Failed to scan AP")
|
||||
self.dut.write("sta {} {}".format(self.ap_ssid, self.ap_password))
|
||||
dut_ip = self.dut.expect(re.compile(r"sta ip: ([\d.]+), mask: ([\d.]+), gw: ([\d.]+)"))[0]
|
||||
return dut_ip, rssi
|
||||
|
||||
def _save_test_result(self, test_case, raw_data, att, rssi, heap_size):
|
||||
return self.test_result[test_case].add_result(raw_data, self.ap_ssid, att, rssi, heap_size)
|
||||
|
||||
def _test_once(self, proto, direction):
|
||||
""" do measure once for one type """
|
||||
# connect and scan to get RSSI
|
||||
dut_ip, rssi = self.setup()
|
||||
|
||||
assert direction in ["rx", "tx"]
|
||||
assert proto in ["tcp", "udp"]
|
||||
|
||||
# run iperf test
|
||||
if direction == "tx":
|
||||
with open(PC_IPERF_TEMP_LOG_FILE, "w") as f:
|
||||
if proto == "tcp":
|
||||
process = subprocess.Popen(["iperf", "-s", "-B", self.pc_nic_ip,
|
||||
"-t", str(TEST_TIME), "-i", "1", "-f", "m"],
|
||||
stdout=f, stderr=f)
|
||||
self.dut.write("iperf -c {} -i 1 -t {}".format(self.pc_nic_ip, TEST_TIME))
|
||||
else:
|
||||
process = subprocess.Popen(["iperf", "-s", "-u", "-B", self.pc_nic_ip,
|
||||
"-t", str(TEST_TIME), "-i", "1", "-f", "m"],
|
||||
stdout=f, stderr=f)
|
||||
self.dut.write("iperf -c {} -u -i 1 -t {}".format(self.pc_nic_ip, TEST_TIME))
|
||||
|
||||
for _ in range(TEST_TIMEOUT):
|
||||
if process.poll() is not None:
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
process.terminate()
|
||||
|
||||
with open(PC_IPERF_TEMP_LOG_FILE, "r") as f:
|
||||
pc_raw_data = server_raw_data = f.read()
|
||||
else:
|
||||
with open(PC_IPERF_TEMP_LOG_FILE, "w") as f:
|
||||
if proto == "tcp":
|
||||
self.dut.write("iperf -s -i 1 -t {}".format(TEST_TIME))
|
||||
process = subprocess.Popen(["iperf", "-c", dut_ip,
|
||||
"-t", str(TEST_TIME), "-f", "m"],
|
||||
stdout=f, stderr=f)
|
||||
else:
|
||||
self.dut.write("iperf -s -u -i 1 -t {}".format(TEST_TIME))
|
||||
process = subprocess.Popen(["iperf", "-c", dut_ip, "-u", "-b", "100M",
|
||||
"-t", str(TEST_TIME), "-f", "m"],
|
||||
stdout=f, stderr=f)
|
||||
|
||||
for _ in range(TEST_TIMEOUT):
|
||||
if process.poll() is not None:
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
process.terminate()
|
||||
|
||||
server_raw_data = self.dut.read()
|
||||
with open(PC_IPERF_TEMP_LOG_FILE, "r") as f:
|
||||
pc_raw_data = f.read()
|
||||
|
||||
# save PC iperf logs to console
|
||||
with open(self.pc_iperf_log_file, "a+") as f:
|
||||
f.write("## [{}] `{}`\r\n##### {}"
|
||||
.format(self.config_name,
|
||||
"{}_{}".format(proto, direction),
|
||||
time.strftime("%m-%d %H:%M:%S", time.localtime(time.time()))))
|
||||
f.write('\r\n```\r\n\r\n' + pc_raw_data + '\r\n```\r\n')
|
||||
self.dut.write("heap")
|
||||
heap_size = self.dut.expect(re.compile(r"min heap size: (\d+)\D"))[0]
|
||||
|
||||
# return server raw data (for parsing test results) and RSSI
|
||||
return server_raw_data, rssi, heap_size
|
||||
|
||||
def run_test(self, proto, direction, atten_val):
|
||||
"""
|
||||
run test for one type, with specified atten_value and save the test result
|
||||
|
||||
:param proto: tcp or udp
|
||||
:param direction: tx or rx
|
||||
:param atten_val: attenuate value
|
||||
"""
|
||||
rssi = FAILED_TO_SCAN_RSSI
|
||||
heap_size = INVALID_HEAP_SIZE
|
||||
try:
|
||||
server_raw_data, rssi, heap_size = self._test_once(proto, direction)
|
||||
throughput = self._save_test_result("{}_{}".format(proto, direction),
|
||||
server_raw_data, atten_val,
|
||||
rssi, heap_size)
|
||||
Utility.console_log("[{}][{}_{}][{}][{}]: {:.02f}"
|
||||
.format(self.config_name, proto, direction, rssi, self.ap_ssid, throughput))
|
||||
except Exception as e:
|
||||
self._save_test_result("{}_{}".format(proto, direction), "", atten_val, rssi, heap_size)
|
||||
Utility.console_log("Failed during test: {}".format(e))
|
||||
|
||||
def run_all_cases(self, atten_val):
|
||||
"""
|
||||
run test for all types (udp_tx, udp_rx, tcp_tx, tcp_rx).
|
||||
|
||||
:param atten_val: attenuate value
|
||||
"""
|
||||
self.run_test("tcp", "tx", atten_val)
|
||||
self.run_test("tcp", "rx", atten_val)
|
||||
self.run_test("udp", "tx", atten_val)
|
||||
self.run_test("udp", "rx", atten_val)
|
||||
|
||||
def wait_ap_power_on(self):
|
||||
"""
|
||||
AP need to take sometime to power on. It changes for different APs.
|
||||
This method will scan to check if the AP powers on.
|
||||
|
||||
:return: True or False
|
||||
"""
|
||||
self.dut.write("restart")
|
||||
self.dut.expect("iperf>")
|
||||
for _ in range(WAIT_AP_POWER_ON_TIMEOUT // SCAN_TIMEOUT):
|
||||
try:
|
||||
self.dut.write("scan {}".format(self.ap_ssid))
|
||||
self.dut.expect(re.compile(r"\[{}]\[rssi=(-\d+)]".format(self.ap_ssid)),
|
||||
timeout=SCAN_TIMEOUT)
|
||||
ret = True
|
||||
break
|
||||
except DUT.ExpectTimeout:
|
||||
pass
|
||||
else:
|
||||
ret = False
|
||||
return ret
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox_Basic", category="stress")
|
||||
def test_wifi_throughput_with_different_configs(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. build iperf with specified configs
|
||||
2. test throughput for all routers
|
||||
"""
|
||||
pc_nic_ip = env.get_pc_nic_info("pc_nic", "ipv4")["addr"]
|
||||
pc_iperf_log_file = os.path.join(env.log_path, "pc_iperf_log.md")
|
||||
ap_info = {
|
||||
"ssid": env.get_variable("ap_ssid"),
|
||||
"password": env.get_variable("ap_password"),
|
||||
}
|
||||
|
||||
config_names_raw = subprocess.check_output(["ls", os.path.dirname(os.path.abspath(__file__))])
|
||||
config_names = CONFIG_NAME_PATTERN.findall(config_names_raw)
|
||||
if not config_names:
|
||||
raise ValueError("no configs found in {}".format(os.path.dirname(__file__)))
|
||||
|
||||
test_result = dict()
|
||||
sdkconfig_files = dict()
|
||||
|
||||
for config_name in config_names:
|
||||
# 1. get the config
|
||||
sdkconfig_files[config_name] = os.path.join(os.path.dirname(__file__),
|
||||
"sdkconfig.ci.{}".format(config_name))
|
||||
|
||||
# 2. get DUT and download
|
||||
dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
|
||||
app_config_name=config_name)
|
||||
dut.start_app()
|
||||
dut.expect("iperf>")
|
||||
|
||||
# 3. run test for each required att value
|
||||
test_result[config_name] = {
|
||||
"tcp_tx": TestResult("tcp", "tx", config_name),
|
||||
"tcp_rx": TestResult("tcp", "rx", config_name),
|
||||
"udp_tx": TestResult("udp", "tx", config_name),
|
||||
"udp_rx": TestResult("udp", "rx", config_name),
|
||||
}
|
||||
|
||||
test_utility = IperfTestUtility(dut, config_name, ap_info["ssid"],
|
||||
ap_info["password"], pc_nic_ip, pc_iperf_log_file, test_result[config_name])
|
||||
|
||||
for _ in range(RETRY_COUNT_FOR_BEST_PERFORMANCE):
|
||||
test_utility.run_all_cases(0)
|
||||
|
||||
for result_type in test_result[config_name]:
|
||||
summary = str(test_result[config_name][result_type])
|
||||
if summary:
|
||||
Utility.console_log(summary, color="orange")
|
||||
|
||||
# 4. check test results
|
||||
env.close_dut("iperf")
|
||||
|
||||
# 5. generate report
|
||||
report = TestReport.ThroughputForConfigsReport(os.path.join(env.log_path, "ThroughputForConfigsReport"),
|
||||
ap_info["ssid"], test_result, sdkconfig_files)
|
||||
report.generate_report()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox", category="stress")
|
||||
def test_wifi_throughput_vs_rssi(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. build with best performance config
|
||||
2. switch on one router
|
||||
3. set attenuator value from 0-60 for each router
|
||||
4. test TCP tx rx and UDP tx rx throughput
|
||||
"""
|
||||
att_port = env.get_variable("attenuator_port")
|
||||
ap_list = env.get_variable("ap_list")
|
||||
pc_nic_ip = env.get_pc_nic_info("pc_nic", "ipv4")["addr"]
|
||||
apc_ip = env.get_variable("apc_ip")
|
||||
pc_iperf_log_file = os.path.join(env.log_path, "pc_iperf_log.md")
|
||||
|
||||
test_result = {
|
||||
"tcp_tx": TestResult("tcp", "tx", BEST_PERFORMANCE_CONFIG),
|
||||
"tcp_rx": TestResult("tcp", "rx", BEST_PERFORMANCE_CONFIG),
|
||||
"udp_tx": TestResult("udp", "tx", BEST_PERFORMANCE_CONFIG),
|
||||
"udp_rx": TestResult("udp", "rx", BEST_PERFORMANCE_CONFIG),
|
||||
}
|
||||
|
||||
# 1. get DUT and download
|
||||
dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
|
||||
app_config_name=BEST_PERFORMANCE_CONFIG)
|
||||
dut.start_app()
|
||||
dut.expect("iperf>")
|
||||
|
||||
# 2. run test for each required att value
|
||||
for ap_info in ap_list:
|
||||
test_utility = IperfTestUtility(dut, BEST_PERFORMANCE_CONFIG, ap_info["ssid"], ap_info["password"],
|
||||
pc_nic_ip, pc_iperf_log_file, test_result)
|
||||
|
||||
PowerControl.Control.control_rest(apc_ip, ap_info["outlet"], "OFF")
|
||||
PowerControl.Control.control(apc_ip, {ap_info["outlet"]: "ON"})
|
||||
Attenuator.set_att(att_port, 0)
|
||||
|
||||
if not test_utility.wait_ap_power_on():
|
||||
Utility.console_log("[{}] failed to power on, skip testing this AP"
|
||||
.format(ap_info["ssid"]), color="red")
|
||||
continue
|
||||
|
||||
for atten_val in ATTEN_VALUE_LIST:
|
||||
assert Attenuator.set_att(att_port, atten_val) is True
|
||||
test_utility.run_all_cases(atten_val)
|
||||
|
||||
# 3. check test results
|
||||
env.close_dut("iperf")
|
||||
|
||||
# 4. generate report
|
||||
report = TestReport.ThroughputVsRssiReport(os.path.join(env.log_path, "ThroughputVsRssiReport"),
|
||||
test_result)
|
||||
report.generate_report()
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="Example_ShieldBox_Basic")
|
||||
def test_wifi_throughput_basic(env, extra_data):
|
||||
"""
|
||||
steps: |
|
||||
1. test TCP tx rx and UDP tx rx throughput
|
||||
2. compare with the pre-defined pass standard
|
||||
"""
|
||||
pc_nic_ip = env.get_pc_nic_info("pc_nic", "ipv4")["addr"]
|
||||
pc_iperf_log_file = os.path.join(env.log_path, "pc_iperf_log.md")
|
||||
ap_info = {
|
||||
"ssid": env.get_variable("ap_ssid"),
|
||||
"password": env.get_variable("ap_password"),
|
||||
}
|
||||
|
||||
# 1. get DUT
|
||||
dut = env.get_dut("iperf", "examples/wifi/iperf", dut_class=ttfw_idf.ESP32DUT,
|
||||
app_config_name=BEST_PERFORMANCE_CONFIG)
|
||||
dut.start_app()
|
||||
dut.expect("iperf>")
|
||||
|
||||
# 2. preparing
|
||||
test_result = {
|
||||
"tcp_tx": TestResult("tcp", "tx", BEST_PERFORMANCE_CONFIG),
|
||||
"tcp_rx": TestResult("tcp", "rx", BEST_PERFORMANCE_CONFIG),
|
||||
"udp_tx": TestResult("udp", "tx", BEST_PERFORMANCE_CONFIG),
|
||||
"udp_rx": TestResult("udp", "rx", BEST_PERFORMANCE_CONFIG),
|
||||
}
|
||||
|
||||
test_utility = IperfTestUtility(dut, BEST_PERFORMANCE_CONFIG, ap_info["ssid"],
|
||||
ap_info["password"], pc_nic_ip, pc_iperf_log_file, test_result)
|
||||
|
||||
# 3. run test for TCP Tx, Rx and UDP Tx, Rx
|
||||
for _ in range(RETRY_COUNT_FOR_BEST_PERFORMANCE):
|
||||
test_utility.run_all_cases(0)
|
||||
|
||||
# 4. log performance and compare with pass standard
|
||||
performance_items = []
|
||||
for throughput_type in test_result:
|
||||
ttfw_idf.log_performance("{}_throughput".format(throughput_type),
|
||||
"{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput()))
|
||||
performance_items.append(["{}_throughput".format(throughput_type),
|
||||
"{:.02f} Mbps".format(test_result[throughput_type].get_best_throughput())])
|
||||
|
||||
# 5. save to report
|
||||
TinyFW.JunitReport.update_performance(performance_items)
|
||||
# do check after logging, otherwise test will exit immediately if check fail, some performance can't be logged.
|
||||
for throughput_type in test_result:
|
||||
ttfw_idf.check_performance("{}_throughput".format(throughput_type),
|
||||
test_result[throughput_type].get_best_throughput(), dut.TARGET)
|
||||
|
||||
env.close_dut("iperf")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_wifi_throughput_basic(env_config_file="EnvConfig.yml")
|
||||
test_wifi_throughput_with_different_configs(env_config_file="EnvConfig.yml")
|
||||
test_wifi_throughput_vs_rssi(env_config_file="EnvConfig.yml")
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "cmd_wifi.c"
|
||||
"iperf_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,20 @@
|
||||
/* 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cmd_system.h"
|
||||
#include "cmd_wifi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,459 @@
|
||||
/* 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 "esp_netif.h"
|
||||
#include "esp_event.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;
|
||||
|
||||
typedef struct {
|
||||
struct arg_str *ssid;
|
||||
struct arg_end *end;
|
||||
} wifi_scan_arg_t;
|
||||
|
||||
static wifi_args_t sta_args;
|
||||
static wifi_scan_arg_t scan_args;
|
||||
static wifi_args_t ap_args;
|
||||
static bool reconnect = true;
|
||||
static const char *TAG="cmd_wifi";
|
||||
static esp_netif_t *netif_ap = NULL;
|
||||
static esp_netif_t *netif_sta = NULL;
|
||||
|
||||
static EventGroupHandle_t wifi_event_group;
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
const int DISCONNECTED_BIT = BIT1;
|
||||
|
||||
static void scan_done_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
uint16_t sta_number = 0;
|
||||
uint8_t i;
|
||||
wifi_ap_record_t *ap_list_buffer;
|
||||
|
||||
esp_wifi_scan_get_ap_num(&sta_number);
|
||||
ap_list_buffer = malloc(sta_number * sizeof(wifi_ap_record_t));
|
||||
if (ap_list_buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to malloc buffer to print scan results");
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_wifi_scan_get_ap_records(&sta_number,(wifi_ap_record_t *)ap_list_buffer) == ESP_OK) {
|
||||
for(i=0; i<sta_number; i++) {
|
||||
ESP_LOGI(TAG, "[%s][rssi=%d]", ap_list_buffer[i].ssid, ap_list_buffer[i].rssi);
|
||||
}
|
||||
}
|
||||
free(ap_list_buffer);
|
||||
ESP_LOGI(TAG, "sta scan done");
|
||||
}
|
||||
|
||||
static void got_ip_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
}
|
||||
|
||||
static void disconnect_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void initialise_wifi(void)
|
||||
{
|
||||
esp_log_level_set("wifi", ESP_LOG_WARN);
|
||||
static bool initialized = false;
|
||||
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK( esp_event_loop_create_default() );
|
||||
netif_ap = esp_netif_create_default_wifi_ap();
|
||||
assert(netif_ap);
|
||||
netif_sta = esp_netif_create_default_wifi_sta();
|
||||
assert(netif_sta);
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_SCAN_DONE,
|
||||
&scan_done_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_STA_DISCONNECTED,
|
||||
&disconnect_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&got_ip_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
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) {
|
||||
strlcpy((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_sta_scan(const char* ssid)
|
||||
{
|
||||
wifi_scan_config_t scan_config = { 0 };
|
||||
scan_config.ssid = (uint8_t *) ssid;
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_scan_start(&scan_config, false) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int wifi_cmd_scan(int argc, char** argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void**) &scan_args);
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, scan_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "sta start to scan");
|
||||
if ( scan_args.ssid->count == 1 ) {
|
||||
wifi_cmd_sta_scan(scan_args.ssid->sval[0]);
|
||||
} else {
|
||||
wifi_cmd_sta_scan(NULL);
|
||||
}
|
||||
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;
|
||||
strlcpy((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;
|
||||
}
|
||||
strlcpy((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);
|
||||
esp_netif_t * netif = netif_ap;
|
||||
esp_netif_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) {
|
||||
netif = netif_sta;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "sta has no IP");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
esp_netif_get_ip_info(netif, &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 = esp_ip4addr_aton(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;
|
||||
}
|
||||
|
||||
void register_wifi(void)
|
||||
{
|
||||
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) );
|
||||
|
||||
scan_args.ssid = arg_str0(NULL, NULL, "<ssid>", "SSID of AP want to be scanned");
|
||||
scan_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t scan_cmd = {
|
||||
.command = "scan",
|
||||
.help = "WiFi is station mode, start scan ap",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_scan,
|
||||
.argtable = &scan_args
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&scan_cmd) );
|
||||
|
||||
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) );
|
||||
|
||||
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) );
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Register WiFi functions
|
||||
void register_wifi(void);
|
||||
void initialise_wifi(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/* Wi-Fi 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 "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_console.h"
|
||||
#include "cmd_decl.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
initialise_wifi();
|
||||
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
repl_config.prompt = "iperf>";
|
||||
// init console REPL environment
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
|
||||
/* Register commands */
|
||||
register_system();
|
||||
register_wifi();
|
||||
|
||||
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");
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
@@ -0,0 +1,25 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=12
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=48
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=48
|
||||
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=12
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=12
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=11488
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=11488
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=12
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=12
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=48
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=11488
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=11488
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=12
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=12
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=48
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=32768
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=32768
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65535
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65535
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=y
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=y
|
||||
@@ -0,0 +1,30 @@
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=y
|
||||
@@ -0,0 +1,30 @@
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=y
|
||||
@@ -0,0 +1,14 @@
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_MEMMAP_SMP=y
|
||||
|
||||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
CONFIG_FREERTOS_UNICORE=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=n
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=y
|
||||
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# ESP32-specific
|
||||
#
|
||||
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_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=32
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=65534
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=64
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# ESP32S2-specific
|
||||
#
|
||||
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=8
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=24
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=24
|
||||
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=16
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=16
|
||||
|
||||
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=28000
|
||||
CONFIG_LWIP_TCP_WND_DEFAULT=28000
|
||||
CONFIG_LWIP_TCP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_UDP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32
|
||||
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
|
||||
|
||||
CONFIG_ESP32S2_INSTRUCTION_CACHE_16KB=y
|
||||
CONFIG_ESP32S2_INSTRUCTION_CACHE_LINE_16B=y
|
||||
CONFIG_ESP32S2_INSTRUCTION_CACHE_WRAP=y
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(power_save)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := power_save
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Wifi Power Save Example
|
||||
|
||||
This example shows how to use power save mode of wifi.
|
||||
|
||||
Power save mode only works in station mode. If the modem sleep mode is enabled, station will switch between active and sleep state periodically after connecting to AP successfully. In sleep state, RF, PHY and BB are turned off in order to reduce power consumption. Station can keep connection with AP in modem sleep mode.
|
||||
|
||||
* No power save: This is default mode. And the esp32 will work with full power.
|
||||
|
||||
* Minimum modem sleep: In minimum modem sleep mode, station wakes up every DTIM to receive beacon. Broadcast data will not be lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short for DTIM is determined by AP.
|
||||
|
||||
* Maximum modem sleep: In maximum modem sleep mode, station wakes up every listen interval to receive beacon. Broadcast data may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power is saved but broadcast data is more easy to lose.
|
||||
|
||||
* others: not supported yet.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "power_save.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,96 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "myssid"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config EXAMPLE_WIFI_PASSWORD
|
||||
string "WiFi Password"
|
||||
default "mypassword"
|
||||
help
|
||||
WiFi password (WPA or WPA2) for the example to use.
|
||||
|
||||
config EXAMPLE_WIFI_LISTEN_INTERVAL
|
||||
int "WiFi listen interval"
|
||||
default 3
|
||||
help
|
||||
Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval.
|
||||
For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen
|
||||
to beacon is 300 ms.
|
||||
|
||||
choice EXAMPLE_POWER_SAVE_MODE
|
||||
prompt "power save mode"
|
||||
default EXAMPLE_POWER_SAVE_MIN_MODEM
|
||||
help
|
||||
Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes.
|
||||
In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be
|
||||
lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short
|
||||
for DTIM is determined by AP.
|
||||
In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data
|
||||
may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power
|
||||
is saved but broadcast data is more easy to lose.
|
||||
|
||||
config EXAMPLE_POWER_SAVE_NONE
|
||||
bool "none"
|
||||
config EXAMPLE_POWER_SAVE_MIN_MODEM
|
||||
bool "minimum modem"
|
||||
config EXAMPLE_POWER_SAVE_MAX_MODEM
|
||||
bool "maximum modem"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MAX_CPU_FREQ
|
||||
prompt "Maximum CPU frequency"
|
||||
default EXAMPLE_MAX_CPU_FREQ_80
|
||||
help
|
||||
Maximum CPU frequency to use for dynamic frequency scaling.
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_80
|
||||
bool "80 MHz"
|
||||
config EXAMPLE_MAX_CPU_FREQ_160
|
||||
bool "160 MHz"
|
||||
config EXAMPLE_MAX_CPU_FREQ_240
|
||||
bool "240 MHz"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_MHZ
|
||||
int
|
||||
default 80 if EXAMPLE_MAX_CPU_FREQ_80
|
||||
default 160 if EXAMPLE_MAX_CPU_FREQ_160
|
||||
default 240 if EXAMPLE_MAX_CPU_FREQ_240
|
||||
|
||||
|
||||
choice EXAMPLE_MIN_CPU_FREQ
|
||||
prompt "Minimum CPU frequency"
|
||||
default EXAMPLE_MIN_CPU_FREQ_10M
|
||||
help
|
||||
Minimum CPU frequency to use for dynamic frequency scaling.
|
||||
Should be set to XTAL frequency or XTAL frequency divided by integer.
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_40M
|
||||
bool "40 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_20M
|
||||
bool "20 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_10M
|
||||
bool "10 MHz (use with 40MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_26M
|
||||
bool "26 MHz (use with 26MHz XTAL)"
|
||||
depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_13M
|
||||
bool "13 MHz (use with 26MHz XTAL)"
|
||||
depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_MHZ
|
||||
int
|
||||
default 40 if EXAMPLE_MIN_CPU_FREQ_40M
|
||||
default 20 if EXAMPLE_MIN_CPU_FREQ_20M
|
||||
default 10 if EXAMPLE_MIN_CPU_FREQ_10M
|
||||
default 26 if EXAMPLE_MIN_CPU_FREQ_26M
|
||||
default 13 if EXAMPLE_MIN_CPU_FREQ_13M
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/* Power save 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
this example shows how to use power save mode
|
||||
set a router or a AP using the same SSID&PASSWORD as configuration of this example.
|
||||
start esp32 and when it connected to AP it will enter power save mode
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_pm.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
/*set the ssid and password via "idf.py menuconfig"*/
|
||||
#define DEFAULT_SSID CONFIG_EXAMPLE_WIFI_SSID
|
||||
#define DEFAULT_PWD CONFIG_EXAMPLE_WIFI_PASSWORD
|
||||
|
||||
#define DEFAULT_LISTEN_INTERVAL CONFIG_EXAMPLE_WIFI_LISTEN_INTERVAL
|
||||
|
||||
#if CONFIG_EXAMPLE_POWER_SAVE_MIN_MODEM
|
||||
#define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM
|
||||
#elif CONFIG_EXAMPLE_POWER_SAVE_MAX_MODEM
|
||||
#define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM
|
||||
#elif CONFIG_EXAMPLE_POWER_SAVE_NONE
|
||||
#define DEFAULT_PS_MODE WIFI_PS_NONE
|
||||
#else
|
||||
#define DEFAULT_PS_MODE WIFI_PS_NONE
|
||||
#endif /*CONFIG_POWER_SAVE_MODEM*/
|
||||
|
||||
|
||||
static const char *TAG = "power_save";
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
esp_wifi_connect();
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
|
||||
ESP_LOGI(TAG, "got ip: " IPSTR, IP2STR(&event->ip_info.ip));
|
||||
}
|
||||
}
|
||||
|
||||
/*init wifi as sta and set power save mode*/
|
||||
static void wifi_power_save(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
assert(sta_netif);
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = DEFAULT_SSID,
|
||||
.password = DEFAULT_PWD,
|
||||
.listen_interval = DEFAULT_LISTEN_INTERVAL,
|
||||
},
|
||||
};
|
||||
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_start());
|
||||
|
||||
ESP_LOGI(TAG, "esp_wifi_set_ps().");
|
||||
esp_wifi_set_ps(DEFAULT_PS_MODE);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
// Configure dynamic frequency scaling:
|
||||
// maximum and minimum frequencies are set in sdkconfig,
|
||||
// automatic light sleep is enabled if tickless idle support is enabled.
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_pm_config_esp32s2_t pm_config = {
|
||||
#endif
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
.light_sleep_enable = true
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
|
||||
#endif // CONFIG_PM_ENABLE
|
||||
|
||||
wifi_power_save();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Use lower CPU frequency
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y
|
||||
# Enable support for power management
|
||||
CONFIG_PM_ENABLE=y
|
||||
# Enable tickless idle mode
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
# Use RTC timer as reference
|
||||
CONFIG_PM_USE_RTC_TIMER_REF=y
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(scan)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := scan
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Wifi SCAN Example
|
||||
|
||||
This example shows how to scan for available set of APs.
|
||||
|
||||
* The SCAN_LIST_SIZE parameter needs to be set from the example configuration menu. It represents maximum number of scan list entries that driver can populate.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "scan.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,11 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_SCAN_LIST_SIZE
|
||||
int "Max size of scan list"
|
||||
range 0 20
|
||||
default 10
|
||||
help
|
||||
The size of array that will be used to retrieve the list of access points.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/* Scan 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
This example shows how to scan for available set of APs.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#define DEFAULT_SCAN_LIST_SIZE CONFIG_EXAMPLE_SCAN_LIST_SIZE
|
||||
|
||||
static const char *TAG = "scan";
|
||||
|
||||
static void print_auth_mode(int authmode)
|
||||
{
|
||||
switch (authmode) {
|
||||
case WIFI_AUTH_OPEN:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_OPEN");
|
||||
break;
|
||||
case WIFI_AUTH_WEP:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WEP");
|
||||
break;
|
||||
case WIFI_AUTH_WPA_PSK:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_PSK");
|
||||
break;
|
||||
case WIFI_AUTH_WPA2_PSK:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_PSK");
|
||||
break;
|
||||
case WIFI_AUTH_WPA_WPA2_PSK:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA_WPA2_PSK");
|
||||
break;
|
||||
case WIFI_AUTH_WPA2_ENTERPRISE:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_ENTERPRISE");
|
||||
break;
|
||||
case WIFI_AUTH_WPA3_PSK:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA3_PSK");
|
||||
break;
|
||||
case WIFI_AUTH_WPA2_WPA3_PSK:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_WPA2_WPA3_PSK");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Authmode \tWIFI_AUTH_UNKNOWN");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_cipher_type(int pairwise_cipher, int group_cipher)
|
||||
{
|
||||
switch (pairwise_cipher) {
|
||||
case WIFI_CIPHER_TYPE_NONE:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_NONE");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_WEP40:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP40");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_WEP104:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_WEP104");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_TKIP:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_CCMP:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_CCMP");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_TKIP_CCMP:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Pairwise Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (group_cipher) {
|
||||
case WIFI_CIPHER_TYPE_NONE:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_NONE");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_WEP40:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP40");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_WEP104:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_WEP104");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_TKIP:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_CCMP:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_CCMP");
|
||||
break;
|
||||
case WIFI_CIPHER_TYPE_TKIP_CCMP:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_TKIP_CCMP");
|
||||
break;
|
||||
default:
|
||||
ESP_LOGI(TAG, "Group Cipher \tWIFI_CIPHER_TYPE_UNKNOWN");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize Wi-Fi as sta and set scan method */
|
||||
static void wifi_scan(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
assert(sta_netif);
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
uint16_t number = DEFAULT_SCAN_LIST_SIZE;
|
||||
wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
|
||||
uint16_t ap_count = 0;
|
||||
memset(ap_info, 0, sizeof(ap_info));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
|
||||
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
|
||||
ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
|
||||
for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
|
||||
ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
|
||||
ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
|
||||
print_auth_mode(ap_info[i].authmode);
|
||||
if (ap_info[i].authmode != WIFI_AUTH_WEP) {
|
||||
print_cipher_type(ap_info[i].pairwise_cipher, ap_info[i].group_cipher);
|
||||
}
|
||||
ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Initialize NVS
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
wifi_scan();
|
||||
}
|
||||
@@ -0,0 +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.5)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/system/console/components)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(simple_sniffer)
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := simple_sniffer
|
||||
|
||||
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/system/console/components
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
# Simple Sniffer Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates basic usage of WiFi sniffer mode by saving packets into SD card with pcap format. We can send pcap file to host via JTAG interface as well.
|
||||
|
||||
For more information about pcap, please go to [wikipedia](https://en.wikipedia.org/wiki/Pcap).
|
||||
|
||||
This example is based on console component. For more information about console, please refer to [console guide](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/console.html).
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you should have one ESP32 dev board integrated with a SD card slot (e.g [ESP-WROVER-KIT](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp-wrover-kit-v4-1)) or just connect [ESP32-DevKitC](https://docs.espressif.com/projects/esp-idf/en/latest/hw-reference/modules-and-boards.html#esp32-devkitc-v4) to a SD card breakout board.
|
||||
If you want to send packets to host, make sure to connect ESP32 to some kind of [JTAG adapter](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#jtag-debugging-selecting-jtag-adapter).
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu (`idf.py menuconfig`). Then go into `Example Configuration` menu.
|
||||
|
||||
- Check `Store command history in flash` if you want to save command history into flash (recommend).
|
||||
- Select where to save the pcap file in `Select destination to store pcap file` menu item.
|
||||
- `SD Card` means saving packets (pcap format) into the SD card you plug in.
|
||||
- `JTAG (App Trace)` means sending packets (pcap format) to host via JTAG interface. This feature depends on [app trace component](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html).
|
||||
- Set the mount point in your filesystem in `SD card mount point in the filesystem` menu item. This configuration only takes effect when you choose to save packets into SD card.
|
||||
- Set max name length of pcap file in `Max name length of pcap file` menu item.
|
||||
- Set the length of sniffer work queue in `Length of sniffer work queue` menu item.
|
||||
- Set the stack size of the sniffer task in `Stack size of sniffer task` menu item.
|
||||
- Set the priority of the sniffer task `Length of sniffer work queue` menu item.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with name of the serial port.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
### `sniffer` Command Usage
|
||||
|
||||
> sniffer [-f <file>][-i ] [-F <mgmt|data|ctrl|misc|mpdu|ampdu>]... [-c <channel>][--stop]
|
||||
> Capture specific packet and store in pcap format
|
||||
> -f, --file=<file> name of the file storing the packets in pcap format
|
||||
> -i, --interface=<wlan> which interface to capture packet
|
||||
> -F, --filter=<mgmt|data|ctrl|misc|mpdu|ampdu> filter parameters
|
||||
> -c, --channel=<channel> communication channel to use
|
||||
> --stop stop running sniffer
|
||||
|
||||
The `sniffer` command support some important options as follow:
|
||||
|
||||
* `-f`: Specify the name of file who will store the packets, default value is `sniffer`, and the resulting file name will be like “snifferX.pcap”, here ‘X’ shows the file’s order.
|
||||
* `-i`: Specify the interface to sniffer packets, currently only support `wlan`
|
||||
* `-c` :Specify the channel to sniffer packet
|
||||
* `-F`: Specify the filter condition, currently only support following filter conditions, you can select any number of them
|
||||
* mgmt: Management packets
|
||||
* data: Data packets
|
||||
* ctrl: Control packets
|
||||
* misc: Other packets
|
||||
* mpdu: MPDU packets
|
||||
* ampdu: AMPDU packets
|
||||
* `--stop`: Stop sniffer job
|
||||
|
||||
### Mount SD Card
|
||||
|
||||
```bash
|
||||
=======================================================
|
||||
| Steps to sniffer WiFi packets |
|
||||
| |
|
||||
| 1. Enter 'help' to check all commands' usage |
|
||||
| 2. Enter 'mount <device>' to mount filesystem |
|
||||
| 3. Enter 'sniffer' to start capture packets |
|
||||
| 4. Enter 'unmount <device>' to unmount filesystem |
|
||||
| |
|
||||
=======================================================
|
||||
|
||||
esp32> mount sd
|
||||
I (158912) example: Initializing SD card
|
||||
I (158912) example: Using SDMMC peripheral
|
||||
I (158912) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
Name: SA16G
|
||||
Type: SDHC/SDXC
|
||||
Speed: 20 MHz
|
||||
Size: 14832MB
|
||||
```
|
||||
|
||||
### Start Sniffer
|
||||
|
||||
```bash
|
||||
esp32> sniffer -f sniffer-example -i wlan -c 2
|
||||
I (8946) cmd_sniffer: open file successfully
|
||||
W (8966) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
|
||||
I (9176) phy: phy_version: 4100, 6fa5e27, Jan 25 2019, 17:02:06, 0, 2
|
||||
I (9186) wifi: ic_enable_sniffer
|
||||
I (9196) cmd_sniffer: start WiFi promiscuous ok
|
||||
esp32> sniffer --stop
|
||||
I (31456) wifi: ic_disable_sniffer
|
||||
I (31456) wifi: flush txq
|
||||
I (31456) wifi: stop sw txq
|
||||
I (31456) wifi: lmac stop hw txq
|
||||
I (31456) cmd_sniffer: stop WiFi promiscuous ok
|
||||
```
|
||||
|
||||
### Unmount SD Card
|
||||
|
||||
```bash
|
||||
esp32> unmount sd
|
||||
I (248800) example: Card unmounted
|
||||
```
|
||||
|
||||
### Steps for sending packets to host via JTAG interface
|
||||
1. Select `JTAG (App Trace)` as the destination of pcap files.
|
||||
2. Build & Flash with `idf.py -p PORT flash`
|
||||
3. Connect JTAG, run OpenOCD (for more information about how-to please refer to [JTAG Debugging](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html)).
|
||||
4. Telnet to localhost with 4444 port: `telnet localhost 4444`.
|
||||
5. In the telnet session, run command like `esp32 apptrace start file://sniffer-esp32.pcap 1 -1 20` (more information about this command, please refer to [apptrace command](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/app_trace.html#openocd-application-level-tracing-commands)).
|
||||
6. Run the example, start sniffer with command `sniffer` (you don't need to specify the filename, because it has been set in step5).
|
||||
7. Stop sniffer by entering command `sniffer --stop` in the example console.
|
||||
8. Stop tracing by entering command `esp32 apptrace stop` in the telnet session.
|
||||
|
||||
|
||||
### Open PCap File in Wireshark
|
||||
|
||||

|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Make sure you have pluged in your SD card and mount it into filesystem before doing sniffer work or you will get error message like “Create file /sdcard/sniffer0.pcap failed”.
|
||||
- To protect the SD card, we recommand you to execute command `unmount sd` before you plug out your SD card.
|
||||
- Make sure to run `esp32 apptrace` command before or immediately after a new sniffer task started when you try this example with JTAG. Otherwise the console will issue warning message `waiting for apptrace established` every 1 second. If the apptrace communication doesn't be established within 10 seconds (can be altered by macro `SNIFFER_APPTRACE_RETRY`), this sniffer command will failed with an error message `waiting for apptrace established timeout`.
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "pcap.c"
|
||||
INCLUDE_DIRS .)
|
||||
@@ -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 := .
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include "esp_types.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "pcap.h"
|
||||
|
||||
static const char *PCAP_TAG = "pcap";
|
||||
#define PCAP_CHECK(a, str, goto_tag, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(PCAP_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* @brief Pcap File Header
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t magic; /*!< Magic Number */
|
||||
uint16_t major; /*!< Major Version */
|
||||
uint16_t minor; /*!< Minor Version */
|
||||
uint32_t zone; /*!< Time Zone Offset */
|
||||
uint32_t sigfigs; /*!< Timestamp Accuracy */
|
||||
uint32_t snaplen; /*!< Max Length to Capture */
|
||||
uint32_t link_type; /*!< Link Layer Type */
|
||||
} pcap_file_header_t;
|
||||
|
||||
/**
|
||||
* @brief Pcap Packet Header
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t seconds; /*!< Number of seconds since January 1st, 1970, 00:00:00 GMT */
|
||||
uint32_t microseconds; /*!< Number of microseconds when the packet was captured (offset from seconds) */
|
||||
uint32_t capture_length; /*!< Number of bytes of captured data, no longer than packet_length */
|
||||
uint32_t packet_length; /*!< Actual length of current packet */
|
||||
} pcap_packet_header_t;
|
||||
|
||||
/**
|
||||
* @brief Pcap Runtime Handle
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
FILE *file; /*!< File handle */
|
||||
} pcap_runtime_t;
|
||||
|
||||
esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds)
|
||||
{
|
||||
PCAP_CHECK(handle, "pcap handle is NULL", err);
|
||||
pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle;
|
||||
PCAP_CHECK(pcap_rt->file, "pcap file is NULL", err);
|
||||
size_t real_write = 0;
|
||||
pcap_packet_header_t header = {
|
||||
.seconds = seconds,
|
||||
.microseconds = microseconds,
|
||||
.capture_length = length,
|
||||
.packet_length = length
|
||||
};
|
||||
real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file);
|
||||
PCAP_CHECK(real_write == 1, "write packet header error", err);
|
||||
real_write = fwrite(payload, sizeof(uint8_t), length, pcap_rt->file);
|
||||
PCAP_CHECK(real_write == length, "write packet payload error", err);
|
||||
/* Flush content in the buffer into device */
|
||||
fflush(pcap_rt->file);
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t pcap_deinit(pcap_handle_t handle)
|
||||
{
|
||||
PCAP_CHECK(handle, "pcap handle is NULL", err);
|
||||
pcap_runtime_t *pcap_rt = (pcap_runtime_t *)handle;
|
||||
PCAP_CHECK(pcap_rt->file, "pcap file is NULL", err);
|
||||
PCAP_CHECK(fclose(pcap_rt->file) == 0, "close pcap file failed", err);
|
||||
pcap_rt->file = NULL;
|
||||
free(pcap_rt);
|
||||
ESP_LOGD(PCAP_TAG, "pcap deinit OK");
|
||||
return ESP_OK;
|
||||
err:
|
||||
ESP_LOGW(PCAP_TAG, "pcap deinit failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle)
|
||||
{
|
||||
PCAP_CHECK(config, "config is NULL", err);
|
||||
PCAP_CHECK(handle, "pcap handle is NULL", err);
|
||||
pcap_runtime_t *pcap_rt = calloc(sizeof(pcap_runtime_t), 1);
|
||||
PCAP_CHECK(pcap_rt, "calloc pcap runtime failed", err);
|
||||
pcap_rt->file = config->fp;
|
||||
/* Write Pcap File header */
|
||||
pcap_file_header_t header = {
|
||||
.magic = PCAP_MAGIC_BIG_ENDIAN,
|
||||
.major = PCAP_VERSION_MAJOR,
|
||||
.minor = PCAP_VERSION_MINOR,
|
||||
.zone = PCAP_TIME_ZONE_GMT,
|
||||
.sigfigs = 0,
|
||||
.snaplen = 0x40000,
|
||||
.link_type = config->link_type
|
||||
};
|
||||
size_t real_write = fwrite(&header, sizeof(header), 1, pcap_rt->file);
|
||||
PCAP_CHECK(real_write == 1, "write pcap file header failed", err_write);
|
||||
/* Flush content in the buffer into device */
|
||||
fflush(pcap_rt->file);
|
||||
*handle = (pcap_handle_t)pcap_rt;
|
||||
ESP_LOGD(PCAP_TAG, "pcap init OK");
|
||||
return ESP_OK;
|
||||
/* Error Handling */
|
||||
err_write:
|
||||
fclose(pcap_rt->file);
|
||||
pcap_rt->file = NULL;
|
||||
free(pcap_rt);
|
||||
err:
|
||||
ESP_LOGW(PCAP_TAG, "pcap init failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#define PCAP_MAGIC_BIG_ENDIAN 0xA1B2C3D4 /*!< Big-Endian */
|
||||
#define PCAP_MAGIC_LITTLE_ENDIAN 0xD4C3B2A1 /*!< Little-Endian */
|
||||
|
||||
#define PCAP_VERSION_MAJOR 0x02 /*!< Major Version */
|
||||
#define PCAP_VERSION_MINOR 0x04 /*!< Minor Version */
|
||||
|
||||
#define PCAP_TIME_ZONE_GMT 0x00 /*!< Time Zone */
|
||||
|
||||
/**
|
||||
* @brief Link layer Type Definition, used for Pcap reader to decode payload
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PCAP_LINK_TYPE_LOOPBACK = 0, /*!< Loopback devices, except for later OpenBSD */
|
||||
PCAP_LINK_TYPE_ETHERNET = 1, /*!< Ethernet, and Linux loopback devices */
|
||||
PCAP_LINK_TYPE_TOKEN_RING = 6, /*!< 802.5 Token Ring */
|
||||
PCAP_LINK_TYPE_ARCNET = 7, /*!< ARCnet */
|
||||
PCAP_LINK_TYPE_SLIP = 8, /*!< SLIP */
|
||||
PCAP_LINK_TYPE_PPP = 9, /*!< PPP */
|
||||
PCAP_LINK_TYPE_FDDI = 10, /*!< FDDI */
|
||||
PCAP_LINK_TYPE_ATM = 100, /*!< LLC/SNAP encapsulated ATM */
|
||||
PCAP_LINK_TYPE_RAW_IP = 101, /*!< Raw IP, without link */
|
||||
PCAP_LINK_TYPE_BSD_SLIP = 102, /*!< BSD/OS SLIP */
|
||||
PCAP_LINK_TYPE_BSD_PPP = 103, /*!< BSD/OS PPP */
|
||||
PCAP_LINK_TYPE_CISCO_HDLC = 104, /*!< Cisco HDLC */
|
||||
PCAP_LINK_TYPE_802_11 = 105, /*!< 802.11 */
|
||||
PCAP_LINK_TYPE_BSD_LOOPBACK = 108, /*!< OpenBSD loopback devices(with AF_value in network byte order) */
|
||||
PCAP_LINK_TYPE_LOCAL_TALK = 114 /*!< LocalTalk */
|
||||
} pcap_link_type_t;
|
||||
|
||||
/**
|
||||
* @brief Pcap configuration Type Definition
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
FILE *fp; /* Pointer to a standard file handle */
|
||||
pcap_link_type_t link_type; /* Pcap Link Type */
|
||||
} pcap_config_t;
|
||||
|
||||
/**
|
||||
* @brief Pcap Handle Type Definition
|
||||
*
|
||||
*/
|
||||
typedef void *pcap_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a pcap session
|
||||
*
|
||||
* @param config configuration of creating pcap object
|
||||
* @param handle pcap handle
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t pcap_init(pcap_config_t *config, pcap_handle_t *handle);
|
||||
|
||||
/**
|
||||
* @brief De-initialize a pcap session
|
||||
*
|
||||
* @param handle pcap handle
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t pcap_deinit(pcap_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Capture one packet into pcap file
|
||||
*
|
||||
* @param handle pcap handle
|
||||
* @param payload pointer of the captured data
|
||||
* @param length length of captured data
|
||||
* @param seconds second of capture time
|
||||
* @param microseconds microsecond of capture time
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t pcap_capture_packet(pcap_handle_t handle, void *payload, uint32_t length, uint32_t seconds, uint32_t microseconds);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "simple_sniffer_example_main.c"
|
||||
"cmd_sniffer.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,61 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config SNIFFER_STORE_HISTORY
|
||||
bool "Store command history into flash"
|
||||
default y
|
||||
help
|
||||
Linenoise line editing library provides functions to save and load command history.
|
||||
If this option is enabled, initalizes a FAT filesystem and uses it to store command history.
|
||||
|
||||
choice SNIFFER_PCAP_DESTINATION
|
||||
prompt "Select destination to store pcap file"
|
||||
default SNIFFER_PCAP_DESTINATION_SD if IDF_TARGET_ESP32
|
||||
default SNIFFER_PCAP_DESTINATION_JTAG if IDF_TARGET_ESP32S2
|
||||
help
|
||||
Select where to store the pcap file.
|
||||
Currently support storing files to SD card or to host via JTAG interface.
|
||||
config SNIFFER_PCAP_DESTINATION_SD
|
||||
bool "SD Card"
|
||||
help
|
||||
Store pcap file to SD card.
|
||||
config SNIFFER_PCAP_DESTINATION_JTAG
|
||||
bool "JTAG (App Trace)"
|
||||
help
|
||||
Store pcap file to host via JTAG interface.
|
||||
endchoice
|
||||
|
||||
if SNIFFER_PCAP_DESTINATION_SD
|
||||
config SNIFFER_MOUNT_POINT
|
||||
string "SD card mount point in the filesystem"
|
||||
default "/sdcard"
|
||||
help
|
||||
Specify the mount point in the VFS (Virtual File System) for SD card.
|
||||
|
||||
config SNIFFER_PCAP_FILE_NAME_MAX_LEN
|
||||
int "Max name length of pcap file"
|
||||
default 32
|
||||
help
|
||||
Specify maximum name length of pcap file.
|
||||
endif
|
||||
|
||||
config SNIFFER_WORK_QUEUE_LEN
|
||||
int "Length of sniffer work queue"
|
||||
default 128
|
||||
help
|
||||
The sniffer callback function should not do heavy work, so we put all heavy IO operation to another task.
|
||||
The task gets some basic info of sniffer packet via queue.
|
||||
Here you should specify the length of queue.
|
||||
|
||||
config SNIFFER_TASK_STACK_SIZE
|
||||
int "Stack size of sniffer task"
|
||||
default 4096
|
||||
help
|
||||
Stack size of sniffer task.
|
||||
|
||||
config SNIFFER_TASK_PRIORITY
|
||||
int "Priority of sniffer task"
|
||||
default 2
|
||||
help
|
||||
Priority of sniffer task.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,395 @@
|
||||
/* cmd_sniffer 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 <string.h>
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_app_trace.h"
|
||||
#include "cmd_sniffer.h"
|
||||
#include "pcap.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define SNIFFER_DEFAULT_FILE_NAME "esp-sniffer"
|
||||
#define SNIFFER_FILE_NAME_MAX_LEN CONFIG_SNIFFER_PCAP_FILE_NAME_MAX_LEN
|
||||
#define SNIFFER_DEFAULT_CHANNEL (1)
|
||||
#define SNIFFER_PAYLOAD_FCS_LEN (4)
|
||||
#define SNIFFER_PROCESS_PACKET_TIMEOUT_MS (100)
|
||||
#define SNIFFER_PROCESS_APPTRACE_TIMEOUT_US (100)
|
||||
#define SNIFFER_APPTRACE_RETRY (10)
|
||||
|
||||
static const char *SNIFFER_TAG = "cmd_sniffer";
|
||||
#define SNIFFER_CHECK(a, str, goto_tag, ...) \
|
||||
do \
|
||||
{ \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(SNIFFER_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
goto goto_tag; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct {
|
||||
char *filter_name;
|
||||
uint32_t filter_val;
|
||||
} wlan_filter_table_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_running;
|
||||
sniffer_intf_t interf;
|
||||
uint32_t channel;
|
||||
uint32_t filter;
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
char filename[SNIFFER_FILE_NAME_MAX_LEN];
|
||||
#endif
|
||||
pcap_handle_t pcap;
|
||||
TaskHandle_t task;
|
||||
QueueHandle_t work_queue;
|
||||
SemaphoreHandle_t sem_task_over;
|
||||
} sniffer_runtime_t;
|
||||
|
||||
typedef struct {
|
||||
void *payload;
|
||||
uint32_t length;
|
||||
uint32_t seconds;
|
||||
uint32_t microseconds;
|
||||
} sniffer_packet_info_t;
|
||||
|
||||
static sniffer_runtime_t snf_rt = {0};
|
||||
static wlan_filter_table_t wifi_filter_hash_table[SNIFFER_WLAN_FILTER_MAX] = {0};
|
||||
|
||||
static uint32_t hash_func(const char *str, uint32_t max_num)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
char *p = (char *)str;
|
||||
while (*p) {
|
||||
ret += *p;
|
||||
p++;
|
||||
}
|
||||
return ret % max_num;
|
||||
}
|
||||
|
||||
static void create_wifi_filter_hashtable(void)
|
||||
{
|
||||
char *wifi_filter_keys[SNIFFER_WLAN_FILTER_MAX] = {"mgmt", "data", "ctrl", "misc", "mpdu", "ampdu"};
|
||||
uint32_t wifi_filter_values[SNIFFER_WLAN_FILTER_MAX] = {WIFI_PROMIS_FILTER_MASK_MGMT, WIFI_PROMIS_FILTER_MASK_DATA,
|
||||
WIFI_PROMIS_FILTER_MASK_CTRL, WIFI_PROMIS_FILTER_MASK_MISC,
|
||||
WIFI_PROMIS_FILTER_MASK_DATA_MPDU, WIFI_PROMIS_FILTER_MASK_DATA_AMPDU
|
||||
};
|
||||
for (int i = 0; i < SNIFFER_WLAN_FILTER_MAX; i++) {
|
||||
uint32_t idx = hash_func(wifi_filter_keys[i], SNIFFER_WLAN_FILTER_MAX);
|
||||
while (wifi_filter_hash_table[idx].filter_name) {
|
||||
idx++;
|
||||
if (idx >= SNIFFER_WLAN_FILTER_MAX) {
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
wifi_filter_hash_table[idx].filter_name = wifi_filter_keys[i];
|
||||
wifi_filter_hash_table[idx].filter_val = wifi_filter_values[i];
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t search_wifi_filter_hashtable(const char *key)
|
||||
{
|
||||
uint32_t len = strlen(key);
|
||||
uint32_t start_idx = hash_func(key, SNIFFER_WLAN_FILTER_MAX);
|
||||
uint32_t idx = start_idx;
|
||||
while (strncmp(wifi_filter_hash_table[idx].filter_name, key, len)) {
|
||||
idx++;
|
||||
if (idx >= SNIFFER_WLAN_FILTER_MAX) {
|
||||
idx = 0;
|
||||
}
|
||||
/* wrong key */
|
||||
if (idx == start_idx) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return wifi_filter_hash_table[idx].filter_val;
|
||||
}
|
||||
|
||||
static void wifi_sniffer_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type)
|
||||
{
|
||||
sniffer_packet_info_t packet_info;
|
||||
wifi_promiscuous_pkt_t *sniffer = (wifi_promiscuous_pkt_t *)recv_buf;
|
||||
/* prepare packet_info */
|
||||
packet_info.seconds = sniffer->rx_ctrl.timestamp / 1000000U;
|
||||
packet_info.microseconds = sniffer->rx_ctrl.timestamp % 1000000U;
|
||||
packet_info.length = sniffer->rx_ctrl.sig_len;
|
||||
/* For now, the sniffer only dumps the length of the MISC type frame */
|
||||
if (type != WIFI_PKT_MISC && !sniffer->rx_ctrl.rx_state) {
|
||||
packet_info.length -= SNIFFER_PAYLOAD_FCS_LEN;
|
||||
void *backup = malloc(packet_info.length);
|
||||
if (backup) {
|
||||
memcpy(backup, sniffer->payload, packet_info.length);
|
||||
packet_info.payload = backup;
|
||||
if (snf_rt.work_queue) {
|
||||
/* send packet_info */
|
||||
if (xQueueSend(snf_rt.work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) != pdTRUE) {
|
||||
ESP_LOGE(SNIFFER_TAG, "sniffer work queue full");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(SNIFFER_TAG, "No enough memory for promiscuous packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sniffer_task(void *parameters)
|
||||
{
|
||||
sniffer_packet_info_t packet_info;
|
||||
sniffer_runtime_t *sniffer = (sniffer_runtime_t *)parameters;
|
||||
|
||||
while (sniffer->is_running) {
|
||||
/* receive paclet info from queue */
|
||||
if (xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS)) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
if (pcap_capture_packet(sniffer->pcap, packet_info.payload, packet_info.length,
|
||||
packet_info.seconds, packet_info.microseconds) != ESP_OK) {
|
||||
ESP_LOGW(SNIFFER_TAG, "save captured packet failed");
|
||||
}
|
||||
free(packet_info.payload);
|
||||
}
|
||||
/* notify that sniffer task is over */
|
||||
xSemaphoreGive(sniffer->sem_task_over);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static esp_err_t sniffer_stop(sniffer_runtime_t *sniffer)
|
||||
{
|
||||
SNIFFER_CHECK(sniffer->is_running, "sniffer is already stopped", err);
|
||||
|
||||
switch (sniffer->interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
/* Disable wifi promiscuous mode */
|
||||
SNIFFER_CHECK(esp_wifi_set_promiscuous(false) == ESP_OK, "stop wifi promiscuous failed", err);
|
||||
break;
|
||||
default:
|
||||
SNIFFER_CHECK(false, "unsupported interface", err);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(SNIFFER_TAG, "stop WiFi promiscuous ok");
|
||||
|
||||
/* stop sniffer local task */
|
||||
sniffer->is_running = false;
|
||||
/* wait for task over */
|
||||
xSemaphoreTake(sniffer->sem_task_over, portMAX_DELAY);
|
||||
vSemaphoreDelete(sniffer->sem_task_over);
|
||||
sniffer->sem_task_over = NULL;
|
||||
/* make sure to free all resources in the left items */
|
||||
UBaseType_t left_items = uxQueueMessagesWaiting(sniffer->work_queue);
|
||||
sniffer_packet_info_t packet_info;
|
||||
while (left_items--) {
|
||||
xQueueReceive(sniffer->work_queue, &packet_info, pdMS_TO_TICKS(SNIFFER_PROCESS_PACKET_TIMEOUT_MS));
|
||||
free(packet_info.payload);
|
||||
}
|
||||
vQueueDelete(sniffer->work_queue);
|
||||
sniffer->work_queue = NULL;
|
||||
/* stop pcap session */
|
||||
SNIFFER_CHECK(pcap_deinit(sniffer->pcap) == ESP_OK, "stop pcap session failed", err);
|
||||
return ESP_OK;
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
static int trace_writefun(void *cookie, const char *buf, int len)
|
||||
{
|
||||
return esp_apptrace_write(ESP_APPTRACE_DEST_TRAX, buf, len, SNIFFER_PROCESS_APPTRACE_TIMEOUT_US) == ESP_OK ? len : -1;
|
||||
}
|
||||
|
||||
static int trace_closefun(void *cookie)
|
||||
{
|
||||
return esp_apptrace_flush(ESP_APPTRACE_DEST_TRAX, ESP_APPTRACE_TMO_INFINITE) == ESP_OK ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t sniffer_start(sniffer_runtime_t *sniffer)
|
||||
{
|
||||
pcap_config_t pcap_config;
|
||||
wifi_promiscuous_filter_t wifi_filter;
|
||||
|
||||
switch (sniffer->interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
pcap_config.link_type = PCAP_LINK_TYPE_802_11;
|
||||
break;
|
||||
default:
|
||||
SNIFFER_CHECK(false, "unsupported interface", err);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create file to write, binary format */
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
pcap_config.fp = funopen("trace", NULL, trace_writefun, NULL, trace_closefun);
|
||||
#elif CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
pcap_config.fp = fopen(sniffer->filename, "wb");
|
||||
#else
|
||||
#error "pcap file destination hasn't specified"
|
||||
#endif
|
||||
SNIFFER_CHECK(pcap_config.fp, "open file failed", err);
|
||||
ESP_LOGI(SNIFFER_TAG, "open file successfully");
|
||||
|
||||
/* init a pcap session */
|
||||
SNIFFER_CHECK(pcap_init(&pcap_config, &sniffer->pcap) == ESP_OK, "init pcap session failed", err);
|
||||
|
||||
sniffer->is_running = true;
|
||||
sniffer->work_queue = xQueueCreate(CONFIG_SNIFFER_WORK_QUEUE_LEN, sizeof(sniffer_packet_info_t));
|
||||
SNIFFER_CHECK(sniffer->work_queue, "create work queue failed", err_queue);
|
||||
sniffer->sem_task_over = xSemaphoreCreateBinary();
|
||||
SNIFFER_CHECK(sniffer->sem_task_over, "create sem failed", err_sem);
|
||||
SNIFFER_CHECK(xTaskCreate(sniffer_task, "snifferT", CONFIG_SNIFFER_TASK_STACK_SIZE,
|
||||
sniffer, CONFIG_SNIFFER_TASK_PRIORITY, &sniffer->task) == pdTRUE,
|
||||
"create task failed", err_task);
|
||||
|
||||
switch (sniffer->interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
/* Start WiFi Promicuous Mode */
|
||||
wifi_filter.filter_mask = sniffer->filter;
|
||||
esp_wifi_set_promiscuous_filter(&wifi_filter);
|
||||
esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_cb);
|
||||
SNIFFER_CHECK(esp_wifi_set_promiscuous(true) == ESP_OK, "start wifi promiscuous failed", err_start);
|
||||
esp_wifi_set_channel(sniffer->channel, WIFI_SECOND_CHAN_NONE);
|
||||
ESP_LOGI(SNIFFER_TAG, "start WiFi promiscuous ok");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
err_start:
|
||||
vTaskDelete(sniffer->task);
|
||||
sniffer->task = NULL;
|
||||
err_task:
|
||||
vSemaphoreDelete(sniffer->sem_task_over);
|
||||
sniffer->sem_task_over = NULL;
|
||||
err_sem:
|
||||
vQueueDelete(sniffer->work_queue);
|
||||
sniffer->work_queue = NULL;
|
||||
err_queue:
|
||||
sniffer->is_running = false;
|
||||
pcap_deinit(sniffer->pcap);
|
||||
err:
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct arg_str *file;
|
||||
struct arg_str *interface;
|
||||
struct arg_str *filter;
|
||||
struct arg_int *channel;
|
||||
struct arg_lit *stop;
|
||||
struct arg_end *end;
|
||||
} sniffer_args;
|
||||
|
||||
static int do_sniffer_cmd(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&sniffer_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, sniffer_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether or not to stop sniffer: "--stop" option */
|
||||
if (sniffer_args.stop->count) {
|
||||
/* stop sniffer */
|
||||
sniffer_stop(&snf_rt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check interface: "-i" option */
|
||||
snf_rt.interf = SNIFFER_INTF_WLAN;
|
||||
if (sniffer_args.interface->count) {
|
||||
if (!strncmp(sniffer_args.interface->sval[0], "wlan", 4)) {
|
||||
snf_rt.interf = SNIFFER_INTF_WLAN;
|
||||
} else {
|
||||
ESP_LOGE(SNIFFER_TAG, "unsupported interface %s", sniffer_args.interface->sval[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check channel: "-c" option */
|
||||
snf_rt.channel = SNIFFER_DEFAULT_CHANNEL;
|
||||
if (sniffer_args.channel->count) {
|
||||
snf_rt.channel = sniffer_args.channel->ival[0];
|
||||
}
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
/* set pcap file name: "-f" option */
|
||||
int len = snprintf(snf_rt.filename, sizeof(snf_rt.filename), "%s/%s.pcap", CONFIG_SNIFFER_MOUNT_POINT, SNIFFER_DEFAULT_FILE_NAME);
|
||||
if (sniffer_args.file->count) {
|
||||
len = snprintf(snf_rt.filename, sizeof(snf_rt.filename), "%s/%s.pcap", CONFIG_SNIFFER_MOUNT_POINT, sniffer_args.file->sval[0]);
|
||||
}
|
||||
if (len >= sizeof(snf_rt.filename)) {
|
||||
ESP_LOGW(SNIFFER_TAG, "pcap file name too long, try to enlarge memory in menuconfig");
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_JTAG
|
||||
uint32_t retry = 0;
|
||||
/* wait until apptrace communication established or timeout */
|
||||
while (!esp_apptrace_host_is_connected(ESP_APPTRACE_DEST_TRAX) && (retry < SNIFFER_APPTRACE_RETRY)) {
|
||||
retry++;
|
||||
ESP_LOGW(SNIFFER_TAG, "waiting for apptrace established");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
if (retry >= SNIFFER_APPTRACE_RETRY) {
|
||||
ESP_LOGE(SNIFFER_TAG, "waiting for apptrace established timeout");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check filter setting: "-F" option */
|
||||
switch (snf_rt.interf) {
|
||||
case SNIFFER_INTF_WLAN:
|
||||
if (sniffer_args.filter->count) {
|
||||
for (int i = 0; i < sniffer_args.filter->count; i++) {
|
||||
snf_rt.filter += search_wifi_filter_hashtable(sniffer_args.filter->sval[i]);
|
||||
}
|
||||
/* When filter conditions are all wrong */
|
||||
if (snf_rt.filter == 0) {
|
||||
snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL;
|
||||
}
|
||||
} else {
|
||||
snf_rt.filter = WIFI_PROMIS_FILTER_MASK_ALL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* start sniffer */
|
||||
sniffer_start(&snf_rt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_sniffer(void)
|
||||
{
|
||||
sniffer_args.file = arg_str0("f", "file", "<file>",
|
||||
"name of the file storing the packets in pcap format");
|
||||
sniffer_args.interface = arg_str0("i", "interface", "<wlan>",
|
||||
"which interface to capture packet");
|
||||
sniffer_args.filter = arg_strn("F", "filter", "<mgmt|data|ctrl|misc|mpdu|ampdu>", 0, 6, "filter parameters");
|
||||
sniffer_args.channel = arg_int0("c", "channel", "<channel>", "communication channel to use");
|
||||
sniffer_args.stop = arg_lit0(NULL, "stop", "stop running sniffer");
|
||||
sniffer_args.end = arg_end(1);
|
||||
const esp_console_cmd_t sniffer_cmd = {
|
||||
.command = "sniffer",
|
||||
.help = "Capture specific packet and store in pcap format",
|
||||
.hint = NULL,
|
||||
.func = &do_sniffer_cmd,
|
||||
.argtable = &sniffer_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&sniffer_cmd));
|
||||
|
||||
create_wifi_filter_hashtable();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* cmd_sniffer 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
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Supported Sniffer Interface
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SNIFFER_INTF_WLAN = 0, /*!< WLAN interface */
|
||||
} sniffer_intf_t;
|
||||
|
||||
/**
|
||||
* @brief WLAN Sniffer Filter
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SNIFFER_WLAN_FILTER_MGMT = 0, /*!< MGMT */
|
||||
SNIFFER_WLAN_FILTER_CTRL, /*!< CTRL */
|
||||
SNIFFER_WLAN_FILTER_DATA, /*!< DATA */
|
||||
SNIFFER_WLAN_FILTER_MISC, /*!< MISC */
|
||||
SNIFFER_WLAN_FILTER_MPDU, /*!< MPDU */
|
||||
SNIFFER_WLAN_FILTER_AMPDU, /*!< AMPDU */
|
||||
SNIFFER_WLAN_FILTER_MAX
|
||||
} sniffer_wlan_filter_t;
|
||||
|
||||
void register_sniffer(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,213 @@
|
||||
/* Sniffer 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "linenoise/linenoise.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#endif
|
||||
#include "nvs_flash.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "cmd_system.h"
|
||||
#include "cmd_sniffer.h"
|
||||
|
||||
#if CONFIG_SNIFFER_STORE_HISTORY
|
||||
#define HISTORY_MOUNT_POINT "/data"
|
||||
#define HISTORY_FILE_PATH HISTORY_MOUNT_POINT "/history.txt"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
#if CONFIG_SNIFFER_STORE_HISTORY
|
||||
/* Initialize filesystem for command history store */
|
||||
static void initialize_filesystem(void)
|
||||
{
|
||||
static wl_handle_t wl_handle;
|
||||
const esp_vfs_fat_mount_config_t mount_config = {
|
||||
.max_files = 4,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount(HISTORY_MOUNT_POINT, "storage", &mount_config, &wl_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void initialize_nvs(void)
|
||||
{
|
||||
esp_err_t err = nvs_flash_init();
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
err = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
/* Initialize wifi with tcp/ip adapter */
|
||||
static void initialize_wifi(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
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));
|
||||
}
|
||||
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
static struct {
|
||||
struct arg_str *device;
|
||||
struct arg_end *end;
|
||||
} mount_args;
|
||||
|
||||
/** 'mount' command */
|
||||
static int mount(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&mount_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, mount_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
/* mount sd card */
|
||||
if (!strncmp(mount_args.device->sval[0], "sd", 2)) {
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
ESP_LOGI(TAG, "Using SDMMC peripheral");
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
|
||||
gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1-line modes
|
||||
gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
|
||||
gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
|
||||
gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
|
||||
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 4,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
|
||||
// initialize SD card and mount FAT filesystem.
|
||||
sdmmc_card_t *card;
|
||||
esp_err_t ret = esp_vfs_fat_sdmmc_mount(CONFIG_SNIFFER_MOUNT_POINT, &host, &slot_config, &mount_config, &card);
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
||||
"If you want the card to be formatted, set format_if_mount_failed = true.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
||||
"Make sure SD card lines have pull-up resistors in place.",
|
||||
esp_err_to_name(ret));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* print card info if mount successfully */
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_mount(void)
|
||||
{
|
||||
mount_args.device = arg_str1(NULL, NULL, "<sd>", "choose a proper device to mount/unmount");
|
||||
mount_args.end = arg_end(1);
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "mount",
|
||||
.help = "mount the filesystem",
|
||||
.hint = NULL,
|
||||
.func = &mount,
|
||||
.argtable = &mount_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
|
||||
static int unmount(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **)&mount_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, mount_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
/* unmount sd card */
|
||||
if (!strncmp(mount_args.device->sval[0], "sd", 2)) {
|
||||
if (esp_vfs_fat_sdmmc_unmount() != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Card unmount failed");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Card unmounted");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void register_unmount(void)
|
||||
{
|
||||
mount_args.device = arg_str1(NULL, NULL, "<sd>", "choose a proper device to mount/unmount");
|
||||
mount_args.end = arg_end(1);
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "unmount",
|
||||
.help = "unmount the filesystem",
|
||||
.hint = NULL,
|
||||
.func = &unmount,
|
||||
.argtable = &mount_args
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
#endif // CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
initialize_nvs();
|
||||
|
||||
/* Initialize WiFi */
|
||||
initialize_wifi();
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
#if CONFIG_SNIFFER_STORE_HISTORY
|
||||
initialize_filesystem();
|
||||
repl_config.history_save_path = HISTORY_FILE_PATH;
|
||||
#endif
|
||||
repl_config.prompt = "sniffer>";
|
||||
// init console REPL environment
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl));
|
||||
|
||||
/* Register commands */
|
||||
#if CONFIG_SNIFFER_PCAP_DESTINATION_SD
|
||||
register_mount();
|
||||
register_unmount();
|
||||
#endif
|
||||
register_sniffer();
|
||||
register_system();
|
||||
|
||||
printf("\n =======================================================\n");
|
||||
printf(" | Steps to sniffer WiFi packets |\n");
|
||||
printf(" | |\n");
|
||||
printf(" | 1. Enter 'help' to check all commands' usage |\n");
|
||||
printf(" | 2. Enter 'mount <device>' to mount filesystem |\n");
|
||||
printf(" | 3. Enter 'sniffer' to start capture packets |\n");
|
||||
printf(" | 4. Enter 'unmount <device>' to unmount filesystem |\n");
|
||||
printf(" | |\n");
|
||||
printf(" =======================================================\n\n");
|
||||
|
||||
// start console REPL
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, fat, , 1M,
|
||||
|
@@ -0,0 +1,26 @@
|
||||
# Reduce bootloader log verbosity
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||
CONFIG_BOOTLOADER_LOG_LEVEL=2
|
||||
|
||||
# Increase main task stack size
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7168
|
||||
|
||||
# Enable filesystem
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
|
||||
# Enable FreeRTOS stats formatting functions, needed for 'tasks' command
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
|
||||
# FatFS
|
||||
CONFIG_FATFS_LFN_HEAP=y
|
||||
CONFIG_FATFS_MAX_LFN=31
|
||||
|
||||
# App trace
|
||||
CONFIG_APPTRACE_DEST_TRAX=y
|
||||
CONFIG_APPTRACE_ENABLE=y
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 293 KiB |
@@ -0,0 +1,6 @@
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(smart_config)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := smart_config
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# smartconfig Example
|
||||
|
||||
This example shows how ESP32 connects to a target AP with ESPTOUCH.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Download ESPTOUCH APP from app store:
|
||||
[Android source code](https://github.com/EspressifApp/EsptouchForAndroid)
|
||||
[iOS source code](https://github.com/EspressifApp/EsptouchForIOS) is available.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example output
|
||||
|
||||
* Make sure your phone connect to the target AP (2.4GHz).
|
||||
* Open ESPTOUCH app and input password. There will be success message after few sec.
|
||||
|
||||
Here is an example of smartconfig console output.
|
||||
```
|
||||
I (372) wifi: mode : sta (24:0a:c4:00:44:86)
|
||||
I (422) smartconfig: SC version: V2.6.6
|
||||
I (3802) wifi: ic_enable_sniffer
|
||||
I (3802) sc: SC_STATUS_FIND_CHANNEL
|
||||
I (234592) smartconfig: TYPE: ESPTOUCH
|
||||
I (234592) smartconfig: T|PHONE MAC:68:3e:34:88:59:bf
|
||||
I (234592) smartconfig: T|AP MAC:a4:56:02:47:30:07
|
||||
I (234592) sc: SC_STATUS_GETTING_SSID_PSWD
|
||||
I (239922) smartconfig: T|pswd: 123456789
|
||||
I (239922) smartconfig: T|ssid: IOT_DEMO_TEST
|
||||
I (239922) smartconfig: T|bssid: a4:56:02:47:30:07
|
||||
I (239922) wifi: ic_disable_sniffer
|
||||
I (239922) sc: SC_STATUS_LINK
|
||||
I (239932) sc: SSID:IOT_DEMO_TEST
|
||||
I (239932) sc: PASSWORD:123456789
|
||||
I (240062) wifi: n:1 0, o:1 0, ap:255 255, sta:1 0, prof:1
|
||||
I (241042) wifi: state: init -> auth (b0)
|
||||
I (241042) wifi: state: auth -> assoc (0)
|
||||
I (241052) wifi: state: assoc -> run (10)
|
||||
I (241102) wifi: connected with IOT_DEMO_TEST, channel 1
|
||||
I (244892) event: ip: 192.168.0.152, mask: 255.255.255.0, gw: 192.168.0.1
|
||||
I (244892) sc: WiFi Connected to ap
|
||||
I (247952) sc: SC_STATUS_LINK_OVER
|
||||
I (247952) sc: Phone ip: 192.168.0.31
|
||||
I (247952) sc: smartconfig over
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "smartconfig_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* Esptouch 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wpa2.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_smartconfig.h"
|
||||
|
||||
/* FreeRTOS event group to signal when we are connected & ready to make a request */
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
/* The event group allows multiple bits for each event,
|
||||
but we only care about one event - are we connected
|
||||
to the AP with an IP? */
|
||||
static const int CONNECTED_BIT = BIT0;
|
||||
static const int ESPTOUCH_DONE_BIT = BIT1;
|
||||
static const char *TAG = "smartconfig_example";
|
||||
|
||||
static void smartconfig_example_task(void * parm);
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
{
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
|
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
esp_wifi_connect();
|
||||
xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
|
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||
xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
|
||||
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
|
||||
ESP_LOGI(TAG, "Scan done");
|
||||
} else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
|
||||
ESP_LOGI(TAG, "Found channel");
|
||||
} else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
|
||||
ESP_LOGI(TAG, "Got SSID and password");
|
||||
|
||||
smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
|
||||
wifi_config_t wifi_config;
|
||||
uint8_t ssid[33] = { 0 };
|
||||
uint8_t password[65] = { 0 };
|
||||
|
||||
bzero(&wifi_config, sizeof(wifi_config_t));
|
||||
memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
|
||||
memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
|
||||
wifi_config.sta.bssid_set = evt->bssid_set;
|
||||
if (wifi_config.sta.bssid_set == true) {
|
||||
memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
|
||||
}
|
||||
|
||||
memcpy(ssid, evt->ssid, sizeof(evt->ssid));
|
||||
memcpy(password, evt->password, sizeof(evt->password));
|
||||
ESP_LOGI(TAG, "SSID:%s", ssid);
|
||||
ESP_LOGI(TAG, "PASSWORD:%s", password);
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
|
||||
ESP_ERROR_CHECK( esp_wifi_connect() );
|
||||
} else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
|
||||
xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void initialise_wifi(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||
assert(sta_netif);
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
|
||||
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
|
||||
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_start() );
|
||||
}
|
||||
|
||||
static void smartconfig_example_task(void * parm)
|
||||
{
|
||||
EventBits_t uxBits;
|
||||
ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
|
||||
smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
|
||||
while (1) {
|
||||
uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
|
||||
if(uxBits & CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "WiFi Connected to ap");
|
||||
}
|
||||
if(uxBits & ESPTOUCH_DONE_BIT) {
|
||||
ESP_LOGI(TAG, "smartconfig over");
|
||||
esp_smartconfig_stop();
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_ERROR_CHECK( nvs_flash_init() );
|
||||
initialise_wifi();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
# (Automatically converted from project Makefile by convert_to_cmake.py.)
|
||||
|
||||
# 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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(wpa2-enterprise)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := wpa2-enterprise
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# WPA2 Enterprise Example
|
||||
|
||||
This example shows how ESP32 connects to AP with wpa2 enterprise encryption. Example does the following steps:
|
||||
|
||||
1. Install CA certificate which is optional.
|
||||
2. Install client certificate and client key which is required in TLS method and optional in PEAP and TTLS methods.
|
||||
3. Set identity of phase 1 which is optional.
|
||||
4. Set user name and password of phase 2 which is required in PEAP and TTLS methods.
|
||||
5. Enable wpa2 enterprise.
|
||||
6. Connect to AP.
|
||||
|
||||
*Note:* 1. certificate currently is generated when compiling the example and then stored in flash.
|
||||
2. The expiration date of the certificates is 2027/06/05.
|
||||
|
||||
## The file wpa2_ca.pem, wpa2_ca.key, wpa2_server.pem, wpa2_server.crt and wpa2_server.key can be used to configure AP with
|
||||
wpa2 enterprise encryption. The steps how to generate new certificates and keys using openssl is as follows:
|
||||
|
||||
1. wpa2_ca.pem wpa2_ca.key:
|
||||
openssl req -new -x509 -keyout wpa2_ca.key -out wpa2_ca.pem
|
||||
2. wpa2_server.key:
|
||||
openssl req -new -key wpa2_server.key -out wpa2_server.csr
|
||||
3. wpa2_csr:
|
||||
openssl req -new -key server.key -out server.csr
|
||||
4. wpa2_server.crt:
|
||||
openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_server.csr -key ca1234 -out wpa2_server.crt -extensions xpserver_ext -extfile xpextensions
|
||||
5. wpa2_server.p12:
|
||||
openssl pkcs12 -export -in wpa2_server.crt -inkey wpa2_server.key -out wpa2_server.p12 -passin pass:sv1234 -passout pass:sv1234
|
||||
6. wpa2_server.pem:
|
||||
openssl pkcs12 -in wpa2_server.p12 -out wpa2_server.pem -passin pass:sv1234 -passout pass:sv1234
|
||||
7. wpa2_client.key:
|
||||
openssl genrsa -out wpa2_client.key 1024
|
||||
8. wpa2_client.csr:
|
||||
openssl req -new -key wpa2_client.key -out wpa2_client.csr
|
||||
9. wpa2_client.crt:
|
||||
openssl ca -batch -keyfile wpa2_ca.key -cert wpa2_ca.pem -in wpa2_client.csr -key ca1234 -out wpa2_client.crt -extensions xpclient_ext -extfile xpextensions
|
||||
10. wpa2_client.p12:
|
||||
openssl pkcs12 -export -in wpa2_client.crt -inkey wpa2_client.key -out wpa2_client.p12
|
||||
11. wpa2_client.pem:
|
||||
openssl pkcs12 -in wpa2_client.p12 -out wpa2_client.pem
|
||||
|
||||
### Example output
|
||||
|
||||
Here is an example of wpa2 enterprise(PEAP method) console output.
|
||||
|
||||
I (1352) example: Setting WiFi configuration SSID wpa2_test...
|
||||
I (1362) wpa: WPA2 ENTERPRISE VERSION: [v2.0] enable
|
||||
|
||||
I (1362) wifi: rx_ba=1 tx_ba=1
|
||||
|
||||
I (1372) wifi: mode : sta (24:0a:c4:03:b8:dc)
|
||||
I (3002) wifi: n:11 0, o:1 0, ap:255 255, sta:11 0, prof:11
|
||||
I (3642) wifi: state: init -> auth (b0)
|
||||
I (3642) wifi: state: auth -> assoc (0)
|
||||
I (3652) wifi: state: assoc -> run (10)
|
||||
I (3652) wpa: wpa2_task prio:24, stack:6144
|
||||
|
||||
I (3972) wpa: >>>>>wpa2 FINISH
|
||||
|
||||
I (3982) wpa: wpa2 task delete
|
||||
|
||||
I (3992) wifi: connected with wpa2_test, channel 11
|
||||
I (5372) example: ~~~~~~~~~~~
|
||||
I (5372) example: IP:0.0.0.0
|
||||
I (5372) example: MASK:0.0.0.0
|
||||
I (5372) example: GW:0.0.0.0
|
||||
I (5372) example: ~~~~~~~~~~~
|
||||
I (6832) event: ip: 192.168.1.112, mask: 255.255.255.0, gw: 192.168.1.1
|
||||
I (7372) example: ~~~~~~~~~~~
|
||||
I (7372) example: IP:192.168.1.112
|
||||
I (7372) example: MASK:255.255.255.0
|
||||
I (7372) example: GW:192.168.1.1
|
||||
I (7372) example: ~~~~~~~~~~~
|
||||
I (9372) example: ~~~~~~~~~~~
|
||||
I (9372) example: IP:192.168.1.112
|
||||
I (9372) example: MASK:255.255.255.0
|
||||
I (9372) example: GW:192.168.1.1
|
||||
I (9372) example: ~~~~~~~~~~~
|
||||
@@ -0,0 +1,4 @@
|
||||
# Embed CA, certificate & key directly into binary
|
||||
idf_component_register(SRCS "wpa2_enterprise_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES wpa2_ca.pem wpa2_client.crt wpa2_client.key)
|
||||
@@ -0,0 +1,52 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_WIFI_SSID
|
||||
string "WiFi SSID"
|
||||
default "wpa2_test"
|
||||
help
|
||||
SSID (network name) for the example to connect to.
|
||||
|
||||
config EXAMPLE_VALIDATE_SERVER_CERT
|
||||
bool "Validate server"
|
||||
default y
|
||||
help
|
||||
Validate the servers' certificate using CA cert.
|
||||
|
||||
choice
|
||||
prompt "EAP method for the example to use"
|
||||
default EXAMPLE_EAP_METHOD_PEAP
|
||||
config EXAMPLE_EAP_METHOD_TLS
|
||||
bool "TLS"
|
||||
config EXAMPLE_EAP_METHOD_PEAP
|
||||
bool "PEAP"
|
||||
config EXAMPLE_EAP_METHOD_TTLS
|
||||
bool "TTLS"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_EAP_METHOD
|
||||
int
|
||||
default 0 if EXAMPLE_EAP_METHOD_TLS
|
||||
default 1 if EXAMPLE_EAP_METHOD_PEAP
|
||||
default 2 if EXAMPLE_EAP_METHOD_TTLS
|
||||
|
||||
config EXAMPLE_EAP_ID
|
||||
string "EAP ID"
|
||||
default "example@espressif.com"
|
||||
help
|
||||
Identity in phase 1 of EAP procedure.
|
||||
|
||||
config EXAMPLE_EAP_USERNAME
|
||||
string "EAP USERNAME"
|
||||
depends on EXAMPLE_EAP_METHOD_PEAP || EXAMPLE_EAP_METHOD_TTLS
|
||||
default "espressif"
|
||||
help
|
||||
Username for EAP method (PEAP and TTLS).
|
||||
|
||||
config EXAMPLE_EAP_PASSWORD
|
||||
string "EAP PASSWORD"
|
||||
depends on EXAMPLE_EAP_METHOD_PEAP || EXAMPLE_EAP_METHOD_TTLS
|
||||
default "test11"
|
||||
help
|
||||
Password for EAP method (PEAP and TTLS).
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,12 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
# embed files from the "certs" directory as binary data symbols
|
||||
# in the app
|
||||
COMPONENT_EMBED_TXTFILES := wpa2_ca.pem
|
||||
COMPONENT_EMBED_TXTFILES += wpa2_client.crt
|
||||
COMPONENT_EMBED_TXTFILES += wpa2_client.key
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3DCCA0WgAwIBAgIJAMnlgL1czsmjMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD
|
||||
VQQGEwJGUjEPMA0GA1UECAwGUmFkaXVzMRIwEAYDVQQHDAlTb21ld2hlcmUxFTAT
|
||||
BgNVBAoMDEV4YW1wbGUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBs
|
||||
ZS5jb20xJjAkBgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4X
|
||||
DTE3MDYwNzA4MDY0OVoXDTI3MDYwNTA4MDY0OVowgZMxCzAJBgNVBAYTAkZSMQ8w
|
||||
DQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhh
|
||||
bXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTEmMCQG
|
||||
A1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwgZ8wDQYJKoZIhvcN
|
||||
AQEBBQADgY0AMIGJAoGBALpWR23fn/TmHxsXsHdrydzPSd17fZkc71WsaicgQR66
|
||||
1tIVYb22UWGfj9KPM8THMsV74ew4ZkaQ39qvU0iuQIRrKARFHFok+vbaecgWMeWe
|
||||
vGIqdnmyB9gJYaFOKgtSkfXsu2ddsqdvLYwcDbczrq8X9yEXpN6mnxXeCcPG4F0p
|
||||
AgMBAAGjggE0MIIBMDAdBgNVHQ4EFgQUgigpdAUpONoDq0pQ3yfxrslCSpcwgcgG
|
||||
A1UdIwSBwDCBvYAUgigpdAUpONoDq0pQ3yfxrslCSpehgZmkgZYwgZMxCzAJBgNV
|
||||
BAYTAkZSMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNvbWV3aGVyZTEVMBMG
|
||||
A1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxl
|
||||
LmNvbTEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCCQDJ
|
||||
5YC9XM7JozAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly93
|
||||
d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZIhvcNAQELBQADgYEA
|
||||
euxOBPInSJRKAIseMxPmAabtAqKNslZSmpG4He3lkKt+HM3jfznUt3psmD7j1hFW
|
||||
S4l7KXzzajvaGYybDq5N9MqrDjhGn3VXZqOLMUNDL7OQq96TzgqsTBT1dmVSbNlt
|
||||
PQgiAeKAk3tmH4lRRi9MTBSyJ6I92JYcS5H6Bs4ZwCc=
|
||||
-----END CERTIFICATE-----
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user