SOLVED: enabling/disable nimble ble server with no crash

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

SOLVED: enabling/disable nimble ble server with no crash

Postby felixcollins » Mon Apr 03, 2023 9:37 pm

I am trying to get system working based on the spp server example. I need to be able to shut off the BTLE and then re-enable it without restarting the application. This seems like a common enough thing to want to do but there are no examples or documentation on how to do it with nimble that I can find. I've tried shutting down the server completely and restarting it. This is ugly as the task is destroyed and all memory freed and then reallocated when I re-enable BTLE. It seems it does not work either as I get a crash when I try to re-enable the BTLE with the nimble_port_freertos_init() call. See stacktrace below. The code to start/stop BTLE is below that.

Have I missed something in the de-init? Am I just running out of RAM? Is there a way to "pause" the btle stack - Stopping the radio and the tasks and but not deallocating or losing all the configuration?

Thanks in advance for your help.
Felix

Code: Select all

assert failed: block_locate_free tlsf.c:572 (block_size(block) >= size)

<snipped backtrace for brevity>

0x400821ca: panic_abort at C:/Users/felix/esp5/esp-idf/components/esp_system/panic.c:412
0x40093789: esp_system_abort at C:/Users/felix/esp5/esp-idf/components/esp_system/esp_system.c:135
0x40099eea: __assert_func at C:/Users/felix/esp5/esp-idf/components/newlib/assert.c:78
0x400990e0: block_locate_free at C:/Users/felix/esp5/esp-idf/components/heap/tlsf/tlsf.c:572
 (inlined by) tlsf_malloc at C:/Users/felix/esp5/esp-idf/components/heap/tlsf/tlsf.c:934
0x40098c33: multi_heap_malloc_impl at C:/Users/felix/esp5/esp-idf/components/heap/multi_heap.c:217
0x400823cd: heap_caps_malloc_base at C:/Users/felix/esp5/esp-idf/components/heap/heap_caps.c:146
0x4008242b: heap_caps_malloc at C:/Users/felix/esp5/esp-idf/components/heap/heap_caps.c:166
0x4009528e: xTaskCreatePinnedToCore at C:/Users/felix/esp5/esp-idf/components/freertos/FreeRTOS-Kernel/tasks.c:876
0x400ec48d: esp_nimble_enable at C:/Users/felix/esp5/esp-idf/components/bt/host/nimble/nimble/porting/npl/freertos/src/nimble_port_freertos.c:41
0x400ec4b5: nimble_port_freertos_init at C:/Users/felix/esp5/esp-idf/components/bt/host/nimble/nimble/porting/npl/freertos/src/nimble_port_freertos.c:70
<my code below here - running in task context>
My code to init/deinit the BLE

Code: Select all

void gl_ble_spp_server_init(void)
{
  nimble_port_init();

  /* Initialize the NimBLE host configuration. */
  ble_hs_cfg.reset_cb = ble_spp_server_on_reset;
  ble_hs_cfg.sync_cb = ble_spp_server_on_sync;
  ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
  ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
  ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;

  ble_hs_cfg.sm_bonding = 1;
  ble_hs_cfg.sm_mitm = 1;
  ble_hs_cfg.sm_sc = 1;

  ble_hs_cfg.sm_our_key_dist = 1;
  ble_hs_cfg.sm_their_key_dist = 1;

  /* Register custom service */
  int rc = gatt_svr_register();
  assert(rc == 0);

  /* Set the default device name. */
  char name[] = "TestBLEspp";
  rc = ble_svc_gap_device_name_set(name);
  assert(rc == 0);

  /* XXX Need to have template for store */
  ble_store_config_init();

  nimble_port_freertos_init(ble_spp_server_host_task);
}

void gl_ble_spp_server_deinit(void)
{
  esp_err_t ret = ESP_ERROR_CHECK_WITHOUT_ABORT(nimble_port_stop());
  if (ret == ESP_OK)
  {
    nimble_port_deinit();
    ESP_ERROR_CHECK_WITHOUT_ABORT(esp_nimble_hci_deinit());
  }
  enabled = false;
}

Last edited by felixcollins on Wed Apr 05, 2023 2:02 am, edited 1 time in total.

MicroController
Posts: 1705
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: crash re-enabling nimble ble server

Postby MicroController » Tue Apr 04, 2023 9:23 pm

Your code looks right. I have similar code running, like:

Code: Select all

while(1) {
  nimble_port_init();
  gatt_svr_init();
  nimble_port_freertos_init(bleprph_host_task);
  ...
  nimble_port_stop();
  nimble_port_deinit();
  ...
}
Two options for improvements are on my list though to avoid deleting and re-creating a task over and over again.
a) use xTaskCreateStatic(...) to be able to re-use the task's memory for the next iteration without re-allocation, or
b) don't let the event handler task die in the first place; modify it to enable it to pause and resume as BLE gets stopped and restarted.

That said, there is of course the possibility that you may have a memory leak (or worse, fragmentation) somewhere else which eats your RAM and prevents the BLE task from being re-created once it was deleted.

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: crash re-enabling nimble ble server

Postby felixcollins » Tue Apr 04, 2023 10:39 pm

Thanks for replying MicroController. I'm not having much luck in the forums lately!

Looking at your code: I assume that you loop in task function passed to nimble_port_freertos_init(). It seems as though you do not kill the task with xTaskDelete or call nimble_port_freertos_deinit() (which just deletes the task).

My btle task code now looks like this:

Code: Select all

void ble_spp_server_host_task(void *param)
{
  //This function will return only when nimble_port_stop() is executed
  nimble_port_run();

  nimble_port_deinit();
  ESP_ERROR_CHECK_WITHOUT_ABORT(esp_nimble_hci_deinit());

  //Delete this task
  nimble_port_freertos_deinit();
}
I call nimble_port_stop from elsewhere in the system.

This seems to work except for the fact that it corrupts the heap. When I try to bring up the btle again (including the task with nimble_port_freertos_init()) I get a crash dump indicating heap corruption (I have heap checking turned on).

I'll try your strategy of leaving the task running. It can pause waiting for a notification.
Last edited by felixcollins on Wed Apr 05, 2023 2:06 am, edited 1 time in total.

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: crash re-enabling nimble ble server

Postby felixcollins » Tue Apr 04, 2023 10:47 pm

Damn. I see that nimble_port_freertos.c keeps the task handle private so there is no way for me to notify it.

Code: Select all

static TaskHandle_t host_task_h = NULL;
Luckily all it does is create the task so I'll just do that myself instead.

@MicroController How were you signalling to your task loop to begin another loop?

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: crash re-enabling nimble ble server

Postby felixcollins » Wed Apr 05, 2023 1:05 am

Okay... after burrowing my way through way too much of the nimble and nimble port code. I've come up with this scheme.

The BTLE task is created by me so I do not call nimble_port_freertos_init(), which only creates the task anyway.

My task code:

Code: Select all

  ESP_LOGI(tag, "BLE Host Task Started");
  uint32_t notification;
  for (;;)
  {

    xTaskNotifyWait(ULONG_MAX, ULONG_MAX, &notification, portMAX_DELAY);

    nimble_port_init();

    /* Register custom service */
    int rc = gatt_svr_register();
    assert(rc == 0);

    /* Set the default device name. */
    char name[25] = "MYBTLE";
    rc = ble_svc_gap_device_name_set(name);
    assert(rc == 0);

    /* XXX Need to have template for store */
    ble_store_config_init();

    /* This function will return only when nimble_port_stop() is executed */
    nimble_port_run();

    // Wait here until the event calling nimble_port_stop has finished.
    // We should not need this but there is a race condition in the nimble port! Bad! 
    vTaskDelay(pdMS_TO_TICKS(100));

    nimble_port_deinit();

    // Do we need this? The matching init function does not seem to be called
    // ESP_ERROR_CHECK_WITHOUT_ABORT(esp_nimble_hci_deinit());
  }
 }
To start BTLE just call xTaskNotify(host_task_h, 1, eSetBits);
To stop BTLE simply ESP_ERROR_CHECK_WITHOUT_ABORT(nimble_port_stop());

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: SOLVED: enabling/disable nimble ble server with no crash

Postby felixcollins » Wed Apr 05, 2023 2:05 am

One last comment on this. If you use the ESP NIMBLE PORT stack beware : - there are inherent race conditions, there is unprotected global data and some of the functions use a freertos mutex under the hood. If you call them from the wrong task you will crash. The whole thing is very fragile.

felixcollins
Posts: 125
Joined: Fri May 24, 2019 2:02 am

Re: SOLVED: enabling/disable nimble ble server with no crash

Postby felixcollins » Thu Jul 27, 2023 2:51 am

Found another issue in nimble port. If you call stop to soon after start you crash.... viewtopic.php?f=13&t=34854

FyFaGi
Posts: 1
Joined: Wed May 15, 2024 9:54 am

Re: SOLVED: enabling/disable nimble ble server with no crash

Postby FyFaGi » Wed May 15, 2024 9:58 am

Something I found usefull is the init-deinit loop functionality in the blecent example:
https://github.com/espressif/esp-idf/bl ... ain.c#L866
Maybe this can help somebody else.

DrMickeyLauer
Posts: 168
Joined: Sun May 22, 2022 2:42 pm

Re: SOLVED: enabling/disable nimble ble server with no crash

Postby DrMickeyLauer » Wed May 15, 2024 2:43 pm

I hope you are raising bug tracker tickets for these issues.

Who is online

Users browsing this forum: No registered users and 101 guests