espressif / esp-idf

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

Cannot use WiFi provisioning when using another nimble example. (IDFGH-6882) #8502

Closed ghost closed 11 months ago

ghost commented 2 years ago

Environment

Problem Description

Cannot provision wifi when another NimBLE application is running. I've the NimBLE SPP example running, if I get an input (say 'prov'), I'm de-initializing the nimble hci and controller host with the API esp_nimble_hci_and_controller_deinit() and then calling the wifi provisioning function. But once the provisioning completes successfully, my device reboots with guru meditation error. I'm guessing the it's something to do with incorrect initializing or de-initializing the NimBLE, since provisioning too uses NimBLE. It would be great if someone could help.

Expected Behavior

Run as usual without reboot

Actual Behavior

Device reboots

Steps to reproduce

  1. Run the NimBLE example
  2. call the wifi provisioning example from within SPP example, (after de-initializing).

Code to reproduce this issue

SPP Example:

if (strcmp((const char *)msg, "prov") == 0) {
    esp_nimble_hci_and_controller_deinit();

    // calling this function is causing reboot.
    if (init_provision() == ESP_OK)
        ESP_LOGI("main", "Provisioning successful");

    nimble_port_freertos_deinit();
}

Provisioning Example: init_provision()

esp_err_t init_provision(void)
    /* Initialize NVS partition */
    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());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_event_handler_register(
        WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    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_netif_create_default_wifi_sta();

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

    wifi_prov_mgr_config_t config = {
        .scheme = wifi_prov_scheme_ble,
        .scheme_event_handler = WIFI_PROV_EVENT_HANDLER_NONE
    };

    ESP_ERROR_CHECK(wifi_prov_mgr_init(config));

    bool provisioned = false;
    wifi_prov_mgr_reset_provisioning();

    ESP_ERROR_CHECK(wifi_prov_mgr_is_provisioned(&provisioned));

    if (!provisioned) {
        ESP_LOGI(TAG, "Starting provisioning");

        char service_name[12];
        get_device_service_name(service_name, sizeof(service_name));
        wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
        const char *pop = "abcd1234";
        const char *service_key = NULL;
        ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(
            security, pop, service_name, service_key));
        wifi_prov_mgr_wait();
        wifi_prov_mgr_deinit();
    } else {
        ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA");
    }

    /*
     *[> Wait for Wi-Fi connection <]
     *xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, false,
     *true, portMAX_DELAY);
     */
    return ESP_OK;
}

// If your code is longer than 30 lines, GIST is preferred.

Debug Logs

Guru Meditation Error: Core  0 panic'ed (LoadProhibited). Exception was unhandled.

Core  0 register dump:
PC      : 0x400945db  PS      : 0x00060233  A0      : 0x80093c19  A1      : 0x3ffd1e10
0x400945db: uxListRemove at /home/gogo/esp/esp-idf/components/freertos/FreeRTOS-Kernel/list.c:206

A2      : 0x3ffdcb88  A3      : 0x3ffc35c0  A4      : 0x3ffbf1b0  A5      : 0x00060223
A6      : 0x00000000  A7      : 0x00000001  A8      : 0x00000000  A9      : 0x3ffc3644
A10     : 0x3ffc3644  A11     : 0x00000000  A12     : 0x00000001  A13     : 0x00060223
A14     : 0x00000003  A15     : 0x00060023  SAR     : 0x00000004  EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000004  LBEG    : 0x400014fd  LEND    : 0x4000150d  LCOUNT  : 0xfffffffa

Backtrace:0x400945d8:0x3ffd1e100x40093c16:0x3ffd1e30 0x400e592a:0x3ffd1e50 0x400d6fff:0x3ffd1e70 0x400e3245:0x3ffd1ea0 0x400e3286:0x3ffd1ed0 0x400e8fd2:0x3ffd1f10 0x400e9877:0x3ffd1f40 0x400ea129:0x3ffd1f60 0x400ddefd:0x3ffd1f90 0x400dddbe:0x3ffd1fc0 0x400dd4b5:0x3ffd1ff0 0x400dd4c7:0x3ffd2010 0x400e5ef2:0x3ffd2030 0x400d6f2f:0x3ffd2050 0x40094639:0x3ffd2070
0x400945d8: uxListRemove at /home/gogo/esp/esp-idf/components/freertos/FreeRTOS-Kernel/list.c:200

0x40093c16: vTaskDelete at /home/gogo/esp/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:1349 (discriminator 4)

0x400e592a: nimble_port_freertos_deinit at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/porting/npl/freertos/src/nimble_port_freertos.c:57

0x400d6fff: ble_svc_gatt_handler at /home/gogo/Documents/work/idf_projects/walk/main/ble_spp_server.c:299

0x400e3245: ble_gatts_val_access at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_gatts.c:395

0x400e3286: ble_gatts_chr_val_access at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_gatts.c:421

0x400e8fd2: ble_att_svr_write at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_att_svr.c:524

0x400e9877: ble_att_svr_write_handle at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_att_svr.c:555

0x400ea129: ble_att_svr_rx_write at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_att_svr.c:2001

0x400ddefd: ble_att_rx at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_att.c:503

0x400dddbe: ble_hs_hci_evt_acl_process at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_hs_hci_evt.c:927

0x400dd4b5: ble_hs_process_rx_data_queue at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:238

0x400dd4c7: ble_hs_event_rx_data at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:532

0x400e5ef2: ble_npl_event_run at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h:125
 (inlined by) nimble_port_run at /home/gogo/esp/esp-idf/components/bt/host/nimble/nimble/porting/nimble/src/nimble_port.c:78

0x400d6f2f: ble_spp_server_host_task at /home/gogo/Documents/work/idf_projects/walk/main/ble_spp_server.c:236

0x40094639: vPortTaskWrapper at /home/gogo/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:141
IshaESP commented 2 years ago

Hi @brahmajit ,

You are attempting to use the sample application code as it is, but it seems certain points are not considered while calling the relevant APIs . The crash appears to be due to half-way deinitialization of host when 2 standalone applications are combined.

The thing happening here while combining two standalone applications is:

  1. There are 2 host tasks which are getting created and started in the code you shared (One one SPP server and another of wifi_prov_mgr).
  2. 2 host tasks attempting to read the same queue may lead to unknown behavior, crash being one of it .
  3. Server is starting the SPP_server host task and after receiving input ('prov'), init_provision() is starting the provisioning host task. In this, all initialization happens again.
  4. In your code, esp_nimble_hci_and_controller_deinit() is only deinitializing controller and the transport link between host and controller.
  5. There is no handling for stack deinitialization considered. As mentioned above, calling init_provision() will also attempt to reinitialize an already initialized stack/ queue/ task etc.

The provided sample applications in IDF are reference applications showcasing scenario for standalone use cases. If your use case needs merging the two applications, then please make sure you are taking care of correctly deinitalizing the stack before you initialize again. Your application needs to ensure all stack cleanup happens correctly before an attempt of initializing it is done again. Since IDF code is open source, you can refer to code / documentation to get more information about how stack initialization happens.

Thanks, Isha

ghost commented 2 years ago

@IshaESP thank you. Unfortunately I'm new to IDF. Any resources (which part of the official doc should I check) where I could read how to deinitalize the stack.

I was looking at the nimble docs, but deinitializing nimble before begining provisioning example just stops the provisioning too.

IshaESP commented 2 years ago

Hi @brahmajit ,

ESP-IDF has nimble stack which is from upstream mynewt-nimble code - https://mynewt.apache.org/latest/network/ .The major changes done in IDF is for the porting layer ,rest of the code is picked from open source as it is.

The API documentation you can start referring to understand is - https://github.com/espressif/esp-idf/blob/master/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h#L89

KaeLL commented 2 years ago

@IshaESP 2nd link is invalid at this moment.

IshaESP commented 2 years ago

Hi @KaeLL, I have updated the link, please check it now.

ghost commented 2 years ago

Thank you @IshaESP

On Fri, Mar 11, 2022 at 5:53 PM IshaESP @.***> wrote:

Hi @brahmajit https://github.com/brahmajit ,

ESP-IDF has nimble stack which is from upstream mynewt-nimble code - https://mynewt.apache.org/latest/network/ http://url .The major changes done in IDF is for the porting layer ,rest of the code is picked from open source as it is.

The API documentation you can start referring to understand is - https://github.com/espressif/esp-idf/blob/a8ba5a0264d50f9aadd79838e2672a2828704770/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h#L117 http://url

— Reply to this email directly, view it on GitHub https://github.com/espressif/esp-idf/issues/8502#issuecomment-1065065826, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVQVTSN2M6XP2LKUYA7A54DU7M3LTANCNFSM5P7HD5DQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

-- Regards Brahmajit

listout commented 2 years ago

Hi @IshaESP,

I too am facing a similar kind of issue. I took a look at the links you posted and using sample code from there as well as the code I found on the espressif doc. Basically I'm first deinitializing the NimBLE stack using the following code snippet and I've also tried nimble_port_freertos_deinit(); and then I'm calling the provisioning function.

int ret = nimble_port_stop();
if (ret == 0) {
     nimble_port_deinit();

     ret = esp_nimble_hci_and_controller_deinit();
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
     }
}

I also took a look at the esp-nimble-cpp repo and saw that they too are deinitializing the nimBLE stack in a similar way. So what am I missing?

And then starting the provisioning function and it results with the following error code

I (8159) wifi_init: rx ba win: 6
I (8159) wifi_init: tcpip mbox: 32
I (8159) wifi_init: udp mbox: 6
I (8169) wifi_init: tcp mbox: 6
I (8169) wifi_init: tcp tx win: 5744
I (8179) wifi_init: tcp rx win: 5744
I (8179) wifi_init: tcp mss: 1440
I (8179) wifi_init: WiFi IRAM OP enabled
I (8189) wifi_init: WiFi RX IRAM OP enabled
I (8229) ble prov: Starting provisioning
I (8239) wifi:mode : sta (24:62:ab:c3:ad:d0)
I (8239) wifi:enable tsf
ESP_ERROR_CHECK failed: esp_err_t 0x103 (ESP_ERR_INVALID_STATE) at 0x40090774
0x40090774: _esp_error_check_failed at /home/listout/esp/esp-idf/components/esp_system/esp_err.c:42

file: "/IDF/components/protocomm/src/transports/protocomm_nimble.c" line 482
func: simple_ble_start
expression: esp_nimble_hci_and_controller_init()

abort() was called at PC 0x40090777 on core 1
0x40090777: _esp_error_check_failed at /home/listout/esp/esp-idf/components/esp_system/esp_err.c:43
listout commented 2 years ago

I was able get the code to not panic reboot. Before starting the provisioning process I called these functions

nimble_port_deinit();
esp_nimble_hci_and_controller_deinit();
nimble_port_freertos_deinit();

But now the device cannot be connected from the provisioning app and disappears after first scan.

suryasid09 commented 12 months ago

Is there an update on proper deinitialization of the nimble stack. Can someone provide an example for this? I have been struggling with this quite a while now.

listout commented 12 months ago

I'm in the master branch, but this work for now:

    nimble_port_stop();
    nimble_port_deinit();

my provision function looks like

void
provision_start()
{
    nimble_port_stop();
    nimble_port_deinit();
    /* Initialize TCP/IP */
    ESP_ERROR_CHECK(esp_netif_init());

    /* Initialize the event loop */
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    wifi_event_group = xEventGroupCreate();

...
}

EDIT

Full code is at https://github.com/listout/provisoning_nimble.

It's a merge of nimble spp and provision example from master. Connect it to your spp client (mobile or something), send prov, provisioning should start.

Output log: https://gist.github.com/listout/910786ad26de5e73ceb9a727c3ca0857