espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.9k stars 7.33k forks source link

Using AP once slows all further STA connections (IDFGH-8448) #9911

Closed NoNullptr closed 7 months ago

NoNullptr commented 2 years ago

Answers checklist.

IDF version.

v4.4.2

Operating System used.

macOS

How did you build your project?

Command line with idf.py

If you are using Windows, please specify command line type.

No response

Development Kit.

ESP32-S3-MINI-1

Power Supply used.

USB

What is the expected behavior?

After using SoftAP for provisioning or other purposes (WIFI_MODE_AP or WIFI_MODE_APSTA), and then switching to WIFI_MODE_STA does not leave remnant configuration of the SoftAP mode, in particular, the STA initialization/connection performance is not impacted by having used AP-mode in the past.

What is the actual behavior?

Using the SoftAP-mode just once stores configuration on NVS, which are left behind when mode is switched to STA-only. Performance measurement indicates that these NVS-keys take up to 20ms when initialising and connecting wifi, even when SoftAP-mode is never used again.

Steps to reproduce.

  1. Step: set mode to WIFI_MODE_STA and set STA configuration
  2. Step: set mode to WIFI_MODE_APSTA and set AP configuration
  3. Step: set mode to WIFI_MODE_STA

Compare startup time between 1. and 3. step.

Debug Logs.

No response

More Information.

Performance can be recovered by deleting all AP-related keys from nvs.net80211

mhdong commented 2 years ago

Hi @NoNullptr Could you provide a little demo to reproduce this issue, then i can debug this issue locally.

NoNullptr commented 2 years ago

I have made a little demo to illustrate the behavior. The demo deep-sleeps between connections and does the following:

  1. remove config from NVS
  2. start M=2 times in mode STA
  3. start N=10 times in mode STA and record the time until IP_EVENT_STA_GOT_IP is received
  4. start once in mode APSTA
  5. start M=2 times in mode STA
  6. start N=10 times in mode STA and record the time until IP_EVENT_STA_GOT_IP is received
  7. print both averages

I am getting about 117ms before using APSTA once and about 135ms after using APSTA once.

#include <esp_log.h>
#include <esp_netif.h>
#include <esp_sleep.h>
#include <esp_wifi.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <nvs_flash.h>

/* number of connections to ignore */
#define M 2

/* number of connections to time */
#define N 10

static int RTC_FAST_ATTR boot_num = 0;
static float RTC_FAST_ATTR timing_before = 0;
static float RTC_FAST_ATTR timing_after = 0;

static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    ESP_ERROR_CHECK(esp_wifi_connect());
}

static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
    TickType_t t = xTaskGetTickCount() * (1000 / configTICK_RATE_HZ);

    if (M <= boot_num && boot_num <= M + N - 1) {
        timing_before += t;
        printf("boot %d (before): %d\n", boot_num, t);
    } else if (2 * M + N + 1 <= boot_num && boot_num <= 2 * M + 2 * N) {
        timing_after += t;
        printf("boot %d (after): %d\n", boot_num, t);
    } else {
        printf("boot %d (ignored): %d\n", boot_num, t);
    }
}

static wifi_config_t wifi_config_ap = {
    .ap = {
        .authmode = WIFI_AUTH_WPA2_PSK,
        .pairwise_cipher = WIFI_CIPHER_TYPE_CCMP,
        .ssid = "esp32ap",
        .ssid_hidden = 0,
        .ssid_len = 0,
        .password = "esp32pwd",
    },
};

static wifi_config_t wifi_config_sta = {
    .sta = {
        .ssid = "",
        .password = "",
    }
};

static void erase_nvs_net80211()
{
    nvs_handle_t handle;
    nvs_entry_info_t info;
    nvs_iterator_t it;
    esp_err_t rc;

    ESP_ERROR_CHECK(nvs_open("nvs.net80211", NVS_READWRITE, &handle));
    it = nvs_entry_find("nvs", "nvs.net80211", NVS_TYPE_ANY);
    while (it != NULL) {
        nvs_entry_info(it, &info);
        rc = nvs_erase_key(handle, info.key);
        if (rc != ESP_OK && rc != ESP_ERR_NVS_NOT_FOUND) {
            ESP_ERROR_CHECK(rc);
        }
        it = nvs_entry_next(it);
    }
    ESP_ERROR_CHECK(nvs_commit(handle));
    nvs_close(handle);
}

void app_main()
{
    ESP_ERROR_CHECK(nvs_flash_init());

    if (boot_num == 0) {
        erase_nvs_net80211();
    }

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    esp_netif_create_default_wifi_sta();

    if (boot_num == 1 + N) {
        esp_netif_create_default_wifi_ap();
    }

    wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_init_config));

    esp_event_handler_instance_t instance_wifi;
    esp_event_handler_instance_t instance_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_START, &wifi_event_handler, NULL, &instance_wifi));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL, &instance_ip));

    if (boot_num == M + N) {
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config_ap));
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config_sta));
    } else {
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
        ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config_sta));
    }

    ESP_ERROR_CHECK(esp_wifi_start());
    vTaskDelay(pdMS_TO_TICKS(3000));

    if (++boot_num <= 2 * M + 2 * N) {
        esp_deep_sleep(1000000);
    } else {
        printf("average connection time before using AP: %.2f\n", timing_before / N);
        printf("average connection time after using AP: %.2f\n", timing_after / N);
    }
}
mhdong commented 2 years ago

Hi @NoNullptr For the robustness of the program, erase_nvs_net80211() is not recommended to use. Using erase_nvs_net80211() may cause some nvs.net80211 keys to be lost.

NoNullptr commented 2 years ago

Hi @NoNullptr For the robustness of the program, erase_nvs_net80211() is not recommended to use. Using erase_nvs_net80211() may cause some nvs.net80211 keys to be lost.

It is a demo specifically illustrating the issue; it won't cause "some" keys to be lost, it will erase all of them. If you prefer to manually erase NVS instead of programmatically doing so, then you can do comment out the offending line and use idf.py erase-flash.

mhdong commented 2 years ago

Hi @NoNullptr It is also feasible if erase some keys does not introduce problems. You demo illustrating that erase some keys can reduce connect time.

NoNullptr commented 2 years ago

@mhdong Please go ahead and test it after erasing flash and not calling erase_nvs_net80211(). The results are the same and you should have already tested that anyways.

The issue seems to be that connecting to AP writes persistent values to NVS, which are not removed later. These values seem to slow down future connections even when one doesn't use AP. Since the code reading the NVS is not open sourced, there is no way for me to pinpoint the exact problem.

Alvin1Zhang commented 1 year ago

Thanks for reporting, feel free to reopen.

NoNullptr commented 1 year ago

In which commit is it fixed?

mhdong commented 1 year ago

I am deeply sorry for my late reply. As reply at https://github.com/espressif/esp-idf/issues/9909#issuecomment-1305321729, we will not erase all key about AP, even if the mode is set to STA. We can provide example to demonstrate how to erase these keys manually. We'd appreciate you providing relevant examples by pull request.

NoNullptr commented 1 year ago

@mhdong, the reason given in #9909 is to reboot faster, which is not even a valid response to #9909 since it adds keys that do not even exist in the documented code. With the code I already gave in this bug report, you can check that it not only slows down new connections, it also slows down booting. This makes the reasoning given in #9909 invalid.

Franck78 commented 1 year ago

Hello,

I have a bug that I think is related to this one.

When I use scanNetworks() in STA mode, ok, esp32 as wifi client is ok

When I use scanNetworks() in AP mode, esp32 as wifi server will every time screw the client connected to it.

As soon as scanNetworks() starts, the client connected to the ESP32(in AP mode) lost connectivity. Easy to watch with a continuous ping.

Why scanNetworks() in AP mode ??

Well, the only way to configure a fresh ESP32 device is to login with known credential From the configure page, I want to help customer join my gadget to his wifi network. here comes the scan networks to display SSID available to him.

After random delay, the client can (not often) recover the wifi link and receive the server response. But most of the time, no way to have reliable server/client link.

I can open a specific bug if this behavior is not a hint to this one.

hansw123 commented 9 months ago

sorry for my late reply @Franck78 do the scanNetworks() means esp_wifi_scan_start()? esp_wifi_scan_start() only work in sta or sta/softap coexistence mode or could you provide some examples about it thank for you report!

Sherry616 commented 7 months ago

Thanks for reporting, will close due to short of feedback, feel free to reopen with more updates. Thanks for using our Espressif product!