bt_conn_disconnect() - ASSERTION FAILED [err == 0] k_sem_take return err -11

Hi,

I just started developing a BLE Central device on a nRF52840 DK.
After successfully connect my DevKit with a phone acting as a peripheral (using nRF Connect Mobile), The DK was able to subscribe to some services I configured on the phone and receive the corresponding notifications.

Right now I'm trying to disconnect every BLE connections the DK has when a specific button is pressed.
I managed to init the button and to add a callback function on it. The function works great but when I'm trying to disconnect the peripheral, I got an error:

ASSERTION FAIL [err == 0] @ ZEPHYR_BASE/subsys/bluetooth/host/hci_core.c:306
k_sem_take failed with err -11

This is the part of my code that should disconnect every BLE connection that the central has:

static void disconnect(struct bt_conn *conn, void *data)
{
 char addr[BT_ADDR_LE_STR_LEN];
 
 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
 printk("Disconnecting %s...\n", addr);

 int err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
 if (err)
 {
   printk("Failed disconnection %s\n", addr);
 }
 else
 {
   printk("Success\n");
 }
}

void ble_disconnect_all(void)
{ 
 bt_conn_foreach(BT_CONN_TYPE_LE, disconnect, NULL);
}

The function ble_disconnect_all(void) is called when the button1 is pressed, the printk("Disconnecting %s...\n", addr) is actually printed but when the program called bt_conn_disconnect(conn, BT_HCI_REMOTE_USER_TERM_CONN), then I get the error above.

I look up to the hci_core.c at the line 306. It's in the function called bt_hci_cmd_send_sync(). In this function, k_sem_take(&sync_sem, HCI_CMD_TIMEOUT) tried to take a semaphore, failed and then the BT_ASSERT_MSG() triggered. The message appears immediately when I pressed the button, it doesn't wait the 10 seconds that is configure by default in HCI_CMD_TIMEOUT.

Since this error appears, the program seems to be frozen. Nothing happen when I manually disconnect my phone (either by disabling the bluetooth or just by pressing the Disconnect option in nRF Connect Mobile). The only way I found to unfroze the device is by turning it off/on.

My setup

  • Ubuntu 22.04 in WSL2 (Host OS is Windows 10)
  • PlatformIO CLI, version 6.1.15
  • Zephyr framework, version 2.20701.220422

Prj.conf:

CONFIG_BT=y
CONFIG_BT_DEVICE_NAME="BLE_test"
CONFIG_BT_CENTRAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_CONSOLE=y
CONFIG_GPIO=y
Parents
  • Hi Victor,

    I suspect that the button was bouncing, causing the handler to be called multiple times.

    Could you please update the logic so that the button handler simply raises a flag, and the disconnection code happens elsewhere when that flag is raised?

    Hieu

  • Hi Hieu,

    I thought about this problem when I wrote the code. I made a protection with a timer to be sure that the handler isn't called multiple times. This is my callback and init functions concerning that button:

    #define BUTTON_TIMEOUT_MS 250
    
    static uint64_t last_press_time = 0;
    
    static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
    static struct gpio_callback button_cb_data;
    
    static void button_disconnect_cb(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
    {
      uint64_t now = k_uptime_get();
      if ((now - last_press_time) > BUTTON_TIMEOUT_MS)
      {
        printk("Disconnect every devices...\n");
        ble_disconnect_all();
        last_press_time = now;
      }
      else
      {
        printk("Pls wait a bit\n");
      }
    }
    
    int button_init(void)
    {
      if (!device_is_ready(button.port))
      {
        printk("Error: button device %s not ready\n", button.port->name);
        return 1;
      }
      int ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
      if (ret)
      {
        printk("Error: %d: failed to configure %s pin %d\n", ret, button.port->name, button.pin);
        return 1;
      }
      ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
      if (ret)
      {
        printk("Error: %d: failed to configure interrupt on %s pin %d\n", ret, button.port->name, button.pin);
        return 1;
      }
    
      // Setup button callback
      gpio_init_callback(&button_cb_data, button_disconnect_cb, BIT(button.pin));
      gpio_add_callback(button.port, &button_cb_data);
      return 0;
    }

    Do you think that's a good way to resolve button bounce ? Is it enough ?

    I'm going to try with the flag logic.

    Thanks,
    Victor

  • Update:

    I just remove the while loop in my main function and use the system workqueue to call my disconnect function. It works exactly as I want and I don't have pending print anymore.

    [00:00:00.462,860] <inf> main: Device name: Zenfone 9��(�<�
    ...
    [00:00:02.539,001] <inf> main: Device name: Zenfone 9��89����

    However, I still have this artifacts. The only peripheral's device name that is print normally is a Sony WH-1000XM4. Around me, there are some Jabra headsets, a nRF52805, a WH-1000XM3 and some connected watches too that don't print well either.

  • Hi

    Hieu is out of office so I have taken over this case in his place

    Okay, it seems like the Device name fills the remaining possible bytes with gibberish for some reason here, but I'm not able to see why exactly. I'm not familiar with the log_strdup wrapper, but could you maybe use just a string instead of the wrapper, as I'm suspecting the wrapper to be the reason for this.

    Best regards,

    Simon

  • Hi Simon,

    If you mean to directly use the data->data to print with the print format %s, I've already tried but it didn't solve the output, even using printk() instead of the logging API alone or with the config CONFIG_LOG_PRINTK=y didn't change anything at all.

    Kind regards,
    Victor

  • Victor, I think the reason you have the garbage after the device name is because log_strdup does not make use of the struct bt_data->datalen.

    Easiest would be to append a '\0' to data->data[bt_data->data_len] and then log_strdup might handle this better.

    I am also unsure why bt_conn_foreach does not work for disconnecting for each connection. If you are pretty sure that you have more than one device connected at this point, then bt_conn_foreach should have looped through them. I did a quick test at my end and it seems that it disconnects both devices that I was connected to.

  • Hi,

    I'm quite surprised but this '\0' was indeed missing. Thank you very much.

    Concerning bt_conn_foreach, I think it did not work because it was executed in an interrupt. Using first the flag logic suggested by Hieu solved this problem. After using a flag, I changed the logic to use the workqueue and now it works fine.

    Again, thanks

    Victor

Reply Children
No Data
Related