espressif / esp-idf

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

Bluetooth Controller Bug (IDFGH-12901) #13861

Open oganigl opened 1 month ago

oganigl commented 1 month ago

Answers checklist.

IDF version.

V5.2

Espressif SoC revision.

esp32-s3

Operating System used.

Windows

How did you build your project?

VS Code IDE

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

None

Development Kit.

esp32-s3-wroom1

Power Supply used.

USB

What is the expected behavior?

Hi, I have this code and its giving me problems when I run because it does not initialize the Controller. This code is to use bluetooth as a port to read and send messages:#include

include "freertos/FreeRTOS.h"

include "freertos/task.h"

include "freertos/event_groups.h"

include "esp_event.h"

include "nvs_flash.h"

include "esp_log.h"

include "esp_bt.h"

include "esp_nimble_hci.h"

include "nimble/nimble_port.h"

include "nimble/nimble_port_freertos.h"

include "host/ble_hs.h"

include "services/gap/ble_svc_gap.h"

include "services/gatt/ble_svc_gatt.h"

include "sdkconfig.h"

char *TAG = "BLE-Server"; uint8_t ble_addr_type; void ble_app_advertise(void);

// Write data to ESP32 defined as server static int device_write(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt ctxt, void arg) { printf("Data from the client: %.*s\n", ctxt->om->om_len, ctxt->om->om_data); return 0; }

// Read data from ESP32 defined as server static int device_read(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt ctxt, void arg) { os_mbuf_append(ctxt->om, "Data from the server", strlen("Data from the server")); return 0; }

// Array of pointers to other service definitions // UUID - Universal Unique Identifier static const struct ble_gatt_svc_def gatt_svcs[] = { {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = BLE_UUID16_DECLARE(0x180), // Define UUID for device type .characteristics = (struct ble_gatt_chr_def[]){ {.uuid = BLE_UUID16_DECLARE(0xFEF4), // Define UUID for reading .flags = BLE_GATT_CHR_F_READ, .access_cb = device_read}, {.uuid = BLE_UUID16_DECLARE(0xDEAD), // Define UUID for writing .flags = BLE_GATT_CHR_F_WRITE, .access_cb = device_write}, {0}}}, {0}};

// BLE event handling static int ble_gap_event(struct ble_gap_event event, void arg) { switch (event->type) { // Advertise if connected case BLE_GAP_EVENT_CONNECT: ESP_LOGI("GAP", "BLE GAP EVENT CONNECT %s", event->connect.status == 0 ? "OK!" : "FAILED!"); if (event->connect.status != 0) { ble_app_advertise(); } break; // Advertise again after completion of the event case BLE_GAP_EVENT_ADV_COMPLETE: ESP_LOGI("GAP", "BLE GAP EVENT"); ble_app_advertise(); break; default: break; } return 0; }

// Define the BLE connection void ble_app_advertise(void) { // GAP - device name definition struct ble_hs_adv_fields fields; const char device_name; memset(&fields, 0, sizeof(fields)); device_name = ble_svc_gap_device_name(); // Read the BLE device name fields.name = (uint8_t )device_name; fields.name_len = strlen(device_name); fields.name_is_complete = 1; ble_gap_adv_set_fields(&fields);

// GAP - device connectivity definition
struct ble_gap_adv_params adv_params;
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; // connectable or non-connectable
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // discoverable or non-discoverable
ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event, NULL);

}

// The application void ble_app_on_sync(void) { ble_hs_id_infer_auto(0, &ble_addr_type); // Determines the best address type automatically ble_app_advertise(); // Define the BLE connection }

// The infinite task void host_task(void *param) { nimble_port_run(); // This function will return only when nimble_port_stop() is executed }

void app_main() { esp_err_t ret;

ret = nvs_flash_init();                          // 1 - Initialize NVS flash
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);
// Check if Bluetooth controller is already initialized
if (!esp_bt_controller_get_status()) {
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));           // Initialize the BT controller
    ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)); // Enable BLE mode for the BT controller
}

ESP_ERROR_CHECK(esp_nimble_hci_init());           // 2 - Initialize ESP HCI
nimble_port_init();                               // 3 - Initialize the host stack
ble_svc_gap_device_name_set("BLE-Server");        // 4 - Initialize NimBLE configuration - server name
ble_svc_gap_init();                               // 4 - Initialize NimBLE configuration - gap service
ble_svc_gatt_init();                              // 4 - Initialize NimBLE configuration - gatt service
ble_gatts_count_cfg(gatt_svcs);                   // 4 - Initialize NimBLE configuration - config gatt services
ble_gatts_add_svcs(gatt_svcs);                    // 4 - Initialize NimBLE configuration - queues gatt services.
ble_hs_cfg.sync_cb = ble_app_on_sync;             // 5 - Initialize application
nimble_port_freertos_init(host_task);             // 6 - Run the thread

}

What is the actual behavior?

It doesn't initialize the Bluetooth controller so is bootloading and failing all the time

Steps to reproduce.

  1. Step
  2. Step
  3. Step ...

Debug Logs.

.

More Information.

.

oganigl commented 1 month ago

Hi, please could someone solve my doubt, because I have a project for tomorrow and I think that is an espressif problem

rahult-github commented 1 month ago

Hi @oganigl ,

The sequence in your app_main to start controller first and then invoke esp_nimble_hci_init followed by nimble_port_init is incorrect. You can refer to existing examples to check the correct sequence.

Calling nimble_port_init should suffice.

eriksl commented 1 month ago

Can I add here that the documentation of Nimble in ESP-IDF is close to non-existent? And that referring to an example (that doesn't even compile without errors) is not the solution? Myself, I got it working, in the end, but with very much trial and error. This really needs improvement!

rahult-github commented 1 month ago

Hi @eriksl ,

Sorry about this, but

Can I add here that the documentation of Nimble in ESP-IDF is close to non-existent?

The documentation page here does point to official NimBLE documentation . As you are aware that esp-idf nimble is fork of upstream mynewt-nimble code, so the same documentation works.

And that referring to an example (that doesn't even compile without errors) is not the solution

May i please know which example is not compiling ? Our releases undergo QA cycles to ensure we don't have these issues. But in case you are observing this , please do let us know, Will work on fixing them on priority.

Myself, I got it working, in the end, but with very much trial and error. This really needs improvement!

IDF examples are coupled with README and tutorials with walkthrough. Do let us know what more would be good to add, and we would gladly work to make it more self explanatory.

For e.g. the issue mentioned in this example ( regarding stack initialisation process ) has information here .

eriksl commented 1 month ago

Hi Rahult,

Thanks for your quick response. The problems arose when I was making something like this: a BT service that would be able to send and receive somewhat larger chunks of data (for OTA). Only after some seriously obfuscated assertion aborted I found out that the ESP32-S3 does not support BT classic, which, apparently, is required for BT SPP (serial emulation profile). It might be more user friendly to have the IDF code check for this at a suitable point and then deliver a suitable message to the developer.

So I went searching for a SPP "emulation" example, and found it here: examples/bluetooth/nimble/ble_spp. This example code cannot be built successfully. Besides that, it really isn't a good starting point for exchanging large chunks of data.

After A LOT of trial and error I got it all working. You are referring to the Nimble documentation. Problem is that this documentation is really not up to par. It's just the function declarations and a few comments from the source files rearranged into something pretty looking. It does not say in any way how to do (even simple) stuff.

A nice example here is that, apparently, there is no way to queue indications. You can send one indication and every following indications returns some unclear error, until the indication has been handled completely and then, suddenly, it's working again. It would be quite obvious to use the "notification/indication sent" event handler for this. But as it appears, this callback is executed from the Nimble API entry point itself, so it says nothing about the indication actually being sent or handled. It's even worse when you turn on Nimble assertions. Then it will abort on every indications that could not be queued.

Also the documentation (...) isn't really helpful on what values to select for the memory assignment in menuconfig and about MTU sizes. I had to learn the hard way that a maximum MTU of ~576 bytes is supported (from default ~16), but only if you set it on both sides. There is no negotiation whatsoever, even though that is implied by the event callback.

The examples are nice for experimentation projects, but for real stuff they're really too simple.

Also I'd really like to see a terse how-to doc (one page) of what API functions you need to call (depending on what you're trying to achieve) and what they're doing. Not some example that may not cover my needs at all. E.g. it appears some of the API functions from the examples are actually not really needed.

Thank you.

rahult-github commented 1 month ago

Hi @eriksl,

Ok. I suggest that you open a separate issue if need be where in this can be tracked and discussed more.

Hi @oganigl ,

Does the comment here help solve your issue ?