Page 1 of 1

SOLVED: enabling/disable nimble ble server with no crash

Posted: Mon Apr 03, 2023 9:37 pm
by felixcollins
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;
}


Re: crash re-enabling nimble ble server

Posted: Tue Apr 04, 2023 9:23 pm
by MicroController
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.

Re: crash re-enabling nimble ble server

Posted: Tue Apr 04, 2023 10:39 pm
by felixcollins
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.

Re: crash re-enabling nimble ble server

Posted: Tue Apr 04, 2023 10:47 pm
by felixcollins
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?

Re: crash re-enabling nimble ble server

Posted: Wed Apr 05, 2023 1:05 am
by felixcollins
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());

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

Posted: Wed Apr 05, 2023 2:05 am
by felixcollins
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.

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

Posted: Thu Jul 27, 2023 2:51 am
by felixcollins
Found another issue in nimble port. If you call stop to soon after start you crash.... viewtopic.php?f=13&t=34854

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

Posted: Wed May 15, 2024 9:58 am
by FyFaGi
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.

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

Posted: Wed May 15, 2024 2:43 pm
by DrMickeyLauer
I hope you are raising bug tracker tickets for these issues.