espressif / esp-idf

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

RMT driver and Wi-fi driver coexistence. (IDFGH-10681) #11907

Open guirespi opened 1 year ago

guirespi commented 1 year ago

Answers checklist.

IDF version.

v4.4.2

Operating System used.

Windows

How did you build your project?

Eclipse IDE

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

None

Development Kit.

ESP32 WROVER KIT

Power Supply used.

USB

What is the expected behavior?

Wi-fi and RMT driver for WS2812 should coexist without freezing the program. The code I made starts a task that constantly creates and delete anoter task that turn the WS2812 leds.

What is the actual behavior?

When try to connect to an non existent SSID and the RMT driver is used to turn on and turn off a WS2812 led strip, the program freezes. Neither Wi-fi or led strip do something after.

Steps to reproduce.

Compile and run the following code . The base is the 'station' project in the examples folder (esp_idf>examples>wifi>getting_started). Then I add the component led_strip (esp_idf>examples>common_components) to the components folder.

This a draft code, the production code uses a different implementation of led_strip component but, I achieve to recreate the same scenario with espressif's components. In the production code, the program freezes after several hours of use and it is always at the use of the RMT driver.

/* 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"
#include "../components/led_strip/include/led_strip.h"

/* The examples use WiFi configuration that you can set via project configura
 * tion 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      "RANDOM_SSID"
#define EXAMPLE_ESP_WIFI_PASS      "password"
#define EXAMPLE_ESP_MAXIMUM_RETRY  15

#if CONFIG_ESP_WIFI_AUTH_OPEN
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN
#elif CONFIG_ESP_WIFI_AUTH_WEP
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP
#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK
#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK
#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK
#endif

/* 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) {
            vTaskDelay(pdMS_TO_TICKS(100));
            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);
    }
}

led_strip_t * led = NULL;

static void exe_api(void *pv){
    while(true){
        printf("Init turn on\n");
        for(uint8_t i = 0; i<4; i++){
            led->set_pixel(led, i, 0xff,0xff,0xff);
        }
        led->refresh(led, 250);
        printf("Finish turn on\n");
    }
}

static void raw_api(void * pv){
    TaskHandle_t handler = NULL;
    while(true){
        xTaskCreate(exe_api, "test2", 2048, NULL, 1, &handler);
        vTaskDelay(pdMS_TO_TICKS(10));
        vTaskDelete(handler);
        handler = NULL;
        led->clear(led, 250);
    }

}

void wifi_init_sta(void)
{
    led = led_strip_init(0, 5, 4);
    xTaskCreate(raw_api, "test", 4096, NULL, 5, NULL);

    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 = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_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");
    }
}

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();
}

This is the code I use in the 'station_example_main.c' file. In using the ESP-IDF v4.4.2.

Debug Logs.

ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x1e (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:6664
load:0x40078000,len:14848
load:0x40080400,len:3792
entry 0x40080694
I (27) boot: ESP-IDF v4.4.2-dirty 2nd stage bootloader
I (27) boot: compile time 11:53:57
I (27) boot: chip revision: 1
I (30) boot_comm: chip revision: 1, min. bootloader chip revision: 0
I (37) boot.esp32: SPI Speed      : 40MHz
I (42) boot.esp32: SPI Mode       : DIO
I (46) boot.esp32: SPI Flash Size : 2MB
I (51) boot: Enabling RNG early entropy source...
I (56) boot: Partition Table:
I (60) boot: ## Label            Usage          Type ST Offset   Length
I (67) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (75) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (82) boot:  2 factory          factory app      00 00 00010000 00100000
I (90) boot: End of partition table
I (94) boot_comm: chip revision: 1, min. application chip revision: 0
I (101) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=1408ch ( 82060) map
I (139) esp_image: segment 1: paddr=000240b4 vaddr=3ffb0000 size=0384ch ( 14412) load
I (145) esp_image: segment 2: paddr=00027908 vaddr=40080000 size=08710h ( 34576) load
I (160) esp_image: segment 3: paddr=00030020 vaddr=400d0020 size=71bc4h (465860) map
I (328) esp_image: segment 4: paddr=000a1bec vaddr=40088710 size=0a8e8h ( 43240) load
I (347) esp_image: segment 5: paddr=000ac4dc vaddr=50000000 size=00010h (    16) load
I (356) boot: Loaded app from partition at offset 0x10000
I (356) boot: Disabling RNG early entropy source...
I (369) cpu_start: Pro cpu up.
I (369) cpu_start: Starting app cpu, entry point is 0x40081060
I (0) cpu_start: App cpu up.
I (385) cpu_start: Pro cpu start user code
I (385) cpu_start: cpu freq: 160000000
I (385) cpu_start: Application information:
I (390) cpu_start: Project name:     wifi_station
I (395) cpu_start: App version:      v4.4.2-dirty
I (401) cpu_start: Compile time:     Jul 19 2023 11:53:27
I (407) cpu_start: ELF file SHA256:  3bc20b1ca34f9941...
I (413) cpu_start: ESP-IDF:          v4.4.2-dirty
I (418) heap_init: Initializing. RAM available for dynamic allocation:
I (425) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (431) heap_init: At 3FFB7570 len 00028A90 (162 KiB): DRAM
I (438) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (444) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (451) heap_init: At 40092FF8 len 0000D008 (52 KiB): IRAM
I (458) spi_flash: detected chip: generic
I (461) spi_flash: flash io: dio
W (465) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (480) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (550) wifi station: ESP_WIFI_MODE_STA
Init turn on
Finish turn on
Init turn on

More Information.

I don't know if this is related to #7602 but, I tried the same code with an S3 and stills freezes or crash.

Xartrick commented 1 year ago

RMT and Wi-Fi have issue working together, see led_strip - RMT-based driver for WS2812B/SK6812/APA106 LED strips — esp-idf-lib 1.0 documentation.

If you try to use this driver simultaneously with Wi-Fi, you may encounter RMT transmission bugs. To avoid them, simply initialize device from the task bound to the second processor core.

More information about it here : LED Strip Driver.

If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible.

You can run the task interacting with your led strip on another core by using xTaskCreatePinnedToCore().

guirespi commented 1 year ago

I'll try it in my example and will comment the results.

Sherry616 commented 8 months ago

Hi @guirespi, could you please share the latest status of this issue? Thanks.

guirespi commented 8 months ago

Hi @Sherry616, I've already separated the WIFI and RMT tasks into different cores. The bug is less frequent but still happen. It's been a couple months since I worked on the project that uses the RMT driver and WIFI, but, I remember I use Nimble BLE too. So, do I need to keep the Nimble task in another core too?

suda-morris commented 8 months ago

There're some limitations you may want to know when using the RMT driver. https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html#faq Although this documentation is written for the new RMT driver which only appears in the esp-idf v5.x

For the legacy driver you're using the esp-idf v4.x, I would provide the following suggestions:

BTW, can you use a logic analyzer to capture the signal on the RMT GPIO when it fails to blink the LED? Can you also provide your crash log?

guirespi commented 8 months ago

Hi,

For now it's quite difficult to use the logic analyzer on the project. The logs were provided in this issue, the program just stops waiting for an interruption.

Regards.