Reconnect failed : bt_conn_le_create return error -22

Hi,

I'm working on a project with a nRF21540DK. I'm working with SDKs and toolchains V2.5.3.

I develop a BLE central following the sdf-nrf/samples/bluetooth/central_uart sample. I don't use filter and auto-connect. It can succesfully connect to a device the first time. After a disconnection the central cannot connect to a device anymore. I receive a error -22 from the bt_conn_le_create method. 

It appears that the peripheral disconnected appropriately based on the peripheral log that follows.

[01:23:45.583,404] <inf> ble_handler: BLE Connected E6:68:0D:63:46:32 (random)
[01:23:45.583,435] <inf> ble_handler: Called bt_conn_le_param_update(...) with min_interval: 618, max_interval: 937, latency: 0, timeout: 400
[01:23:47.634,765] <dbg> bt_snes: snes_ccc_status_changed: Notification for `Status` has been turned ON
[01:23:47.734,710] <dbg> bt_snes: snes_ccc_dor_changed: Notification for `Days of Records` has been turned ON
[01:23:47.834,716] <dbg> bt_snes: snes_ccc_mig_changed: Notification for `Mic INput Gain` has been turned ON
[01:23:50.934,539] <inf> ble_handler: Param updated -> interval: 937, latency: 0, timeout: 400
[01:23:50.934,570] <dbg> ble_handler: ble_param_updated: conn_param: min_interval: 618, max_interval: 937, latency: 0, timeout: 400
[01:23:59.183,441] <dbg> bt_snes: snes_ccc_status_changed: Notification for `Status` has been turned OFF
[01:23:59.183,502] <dbg> bt_snes: snes_ccc_dor_changed: Notification for `Days of Records` has been turned OFF
[01:23:59.183,563] <dbg> bt_snes: snes_ccc_mig_changed: Notification for `Mic INput Gain` has been turned OFF
[01:23:59.183,715] <inf> ble_handler: BLE Disconnected: E6:68:0D:63:46:32 (random) (reason 19)

No messages are printed in the log when a new connection is attempted after that.

Do you know what might be causing the issue?

The prj.conf is :

CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_NUS_CLIENT=y
CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=n
CONFIG_BT_GATT_DM=y
CONFIG_HEAP_MEM_POOL_SIZE=2048

CONFIG_DEBUG=y

A part of the ble.c file : 

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <bluetooth/gatt_dm.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <stdlib.h>

#include "ble.h"
#include "../define.h"
#include "connection.h"

// Define callbacks for the BLE connection
BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = ble_connected_cb,
	.disconnected = ble_disconnected_cb,
    .le_param_updated = ble_param_updated_cb,
    .le_param_req = ble_param_request_cb
};

K_THREAD_STACK_DEFINE(BLE_STACK, BLE_STACK_SIZE);
static struct k_thread bleThread;

LOG_MODULE_REGISTER(ble, LOG_LEVEL_DBG);

struct k_work work;

struct snes_client snes;
const struct snes_client_cb snes_callbacks = {
    .cmd_sent = ble_data_written_cb,
    .status_received = ble_status_received_cb,
    .dor_received = ble_dor_received_cb,
    .device_id_received = ble_device_id_received_cb,
    .mic_gain_received = ble_mic_gain_received_cb,
    .status_unsubscribed = ble_status_unsubscribed_cb,
    .dor_unsubscribed = ble_dor_unsubscribed_cb,
    .device_id_unsubscribed = ble_device_id_unsubscribed_cb,
    .mic_gain_unsubscribed = ble_mic_gain_unsubscribed_cb
};

struct snes_client_init_param snes_init_params = {
    .cb = snes_callbacks
};

//struct bt_gatt_dm snes_dm;

static struct bt_gatt_dm_cb dm_callbacks = {
    .completed = ble_discovery_complete_cb,
    .service_not_found = ble_discovery_service_not_found_cb,
};

struct Monkey connectedMonkey;
uint16_t monkey_handle;

struct bt_uuid_128 monkey_src_UUID = BT_UUID_INIT_128(BT_UUID_SNES_VAL);

//Function to initialize the ble thread
void ble_thread_init(){
    k_thread_create	(&bleThread,
                BLE_STACK,										        
                BLE_STACK_SIZE,
                (k_thread_entry_t)ble_controller,
                NULL,
                NULL,
                NULL,
                BLE_PRIORITY,
                0,
                K_NO_WAIT);	

    k_thread_name_set(&bleThread, "bleThread");
    k_thread_start(&bleThread);

    #ifdef DEBUG_MODE
        printk("ble_thread_init\n");
    #endif

    k_work_init(&work, ble_controller);
}

int ble_init(void){
    setConnectCallback(ble_connect);
    setDisconnectCallback(ble_disconnect);
    setRecordingToggleCallback(ble_toggle_recording);
    setResetCollarCallback(ble_reset_collar);
    setOpenCollarCallback(ble_open_collar);
    
    memset(&snes, 0, sizeof(snes));
    memset(&connectedMonkey, 0, sizeof(connectedMonkey));

    int err = snes_client_init(&snes, &snes_init_params);
    if (err) {
        #ifdef DEBUG_MODE
            printk("SNES client init failed (err %d)\n", err);
        #endif
        return -1;
    }

    err = bt_enable(NULL);

    if (err) {
        #ifdef DEBUG_MODE
            printk("Bluetooth init failed (err %d)\n", err);
        #endif
        return -1;
    }

    #ifdef DEBUG_MODE
        printk("Bluetooth initialized\n");
    #endif
    return 0;
}

// Funtion to start the ble controller
void ble_controller(struct k_work *work){
    ble_init(); 
    ble_start_scan();

    while (true)
    {   
        
    }
}

// Function to start the ble scan
int ble_start_scan(){
    int err;

	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, ble_device_found_cb);
	if (err) {
        #ifdef DEBUG_MODE
            printk("ble_start_scan failed (err %d)\n", err);
        #endif
		return err;
	}
    #ifdef DEBUG_MODE
        printk("ble_start_scan\n");
    #endif
    return 0;
}

// Function to stop the ble scan
int ble_stop_scan(){
    int err;

    err = bt_le_scan_stop();
    if (err) {
        #ifdef DEBUG_MODE
            printk("ble_stop_scan failed (err %d)\n", err);
        #endif
        return err;
    }
    #ifdef DEBUG_MODE
        //printk("ble_stop_scan\n");
    #endif

    return 0;
}

// Function called when a device is found. It will parse the advertising data to find the device name and the manufacturer data
void ble_device_found_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad){
    char addr_str[BT_ADDR_LE_STR_LEN];
    char name[NAME_LEN];
    uint8_t manufacturerData[MANUFACTURER_DATA_LEN];

    memset(name, 0, NAME_LEN);
    memset(manufacturerData, 0, MANUFACTURER_DATA_LEN);

    struct net_buf_simple *data1 = malloc(sizeof(struct net_buf_simple));
    struct net_buf_simple *data2 = malloc(sizeof(struct net_buf_simple));

    *data1 = *ad;
    *data2 = *ad;

    // Process the received data to extract the useful information
    bt_data_parse(data1, ble_manufacturer_data_cb, manufacturerData);
    bt_data_parse(data2, ble_data_cb, name);
    bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
    
    // If the device is a device of interest (manufacturer = 0x5A02 = University of Applied Sciences Valais / 
    // Haute Ecole Valaisanne), add it to the list or udpate if already exist
    if(manufacturerData[0] == 0x5A && manufacturerData[1] == 0x02){
        uint32_t currentTime = k_uptime_get_32();
        appendOrModifyMonkey(ble_parse_device_name(name), rssi, manufacturerData[2], manufacturerData[3], *addr, currentTime);
        #ifdef DEBUG_MODE
            //printMonkeys();
        #endif 
        ble_remove_device();
    }  

    free(data1);
    free(data2);
}

// Function to connect to a specific device
void ble_connect(struct Monkey monkey){
    struct bt_conn* conn;
    int err = ble_stop_scan();

	if (err) {
        #ifdef DEBUG_MODE
            printk("%s: Stop LE scan failed (err %d)\n", __func__, err);
        #endif  
        connectionFailed();
		return;
	}
    struct bt_conn_le_create_param create_param;

    create_param.options = BT_CONN_LE_OPT_NONE;
	create_param.interval = BT_GAP_SCAN_FAST_INTERVAL;
	create_param.window = BT_GAP_SCAN_FAST_WINDOW;
	create_param.interval_coded = 0;
	create_param.window_coded = 0;
	create_param.timeout = 0;

    struct bt_le_conn_param conn_param;
    conn_param.interval_min = BT_GAP_INIT_CONN_INT_MIN;
	conn_param.interval_max = BT_GAP_INIT_CONN_INT_MAX;
	conn_param.latency = 0;
	conn_param.timeout = 400;
    
    err = bt_conn_le_create((const bt_addr_le_t*) &monkey.btAddress, &create_param,
				        &conn_param, &conn);

	if (err) {
        #ifdef DEBUG_MODE
            printk("%s: Create conn failed (err %d)\n", __func__, err);
        #endif  
        connectionFailed();
		ble_start_scan();
        return;
	}
        
    snes.conn = bt_conn_ref(conn);
    connectedMonkey = monkey;

    #ifdef DEBUG_MODE
        printk("Connecting to Monkey %d\n", connectedMonkey.num);
    #endif
}

// Function called when a device is connected
void ble_connected_cb(struct bt_conn *conn, uint8_t err)
{
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    if(conn != snes.conn){
        return;
    }

    if (err != 0) {
	printk("Failed to connect to %s (%u)\n", addr, err);

	bt_conn_unref(snes.conn);
	snes = (struct snes_client){0};
        connectedMonkey = (struct Monkey){0};

        connectionFailed();
		return;
	}

    err = bt_gatt_dm_start(snes.conn, &monkey_src_UUID.uuid, &dm_callbacks, &snes);
      
    if (err != 0) {
		printk("Failed to assign service handle");

		bt_conn_unref(snes.conn);
		snes = (struct snes_client){0};
        connectedMonkey = (struct Monkey){0};

        connectionFailed();
		return;
	}

    #ifdef DEBUG_MODE
        printk("Connected to monkey %d\n", connectedMonkey.num);
    #endif
}

void ble_discovery_complete_cb(struct bt_gatt_dm *dm, void *context){
    snes.conn = context;
    #ifdef DEBUG_MODE
        printk("Discovery complete\n");
    #endif

    snes_handles_assign(dm, &snes);
    snes_status_subscribe_receive(&snes);
    snes_dor_subscribe_receive(&snes);
    snes_device_id_subscribe_receive(&snes);
    snes_mic_gain_subscribe_receive(&snes);

    bt_gatt_dm_data_release(dm);
}

void ble_discovery_service_not_found_cb(struct bt_gatt_dm *dm, void *context){
    #ifdef DEBUG_MODE
        printk("Service not found\n");
    #endif
}

void ble_exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
{
	if (!err) {
		printk("MTU exchange done\n");
	} else {
		printk("MTU exchange failed (err %" PRIu8 ")", err);
	}
}

// Function to disconnect from a specific device
void ble_disconnect(void){
    bt_conn_disconnect(snes.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
}

// Function called when a device is disconnected
void ble_disconnected_cb(struct bt_conn *conn, uint8_t reason)
{
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	if (conn != snes.conn) {
        printk("Conn different from snes.conn");
		return;
	}

    #ifdef DEBUG_MODE
        printk("Disconnected from monkey %d, reason 0x%x\n", connectedMonkey.num, reason);
    #endif
	
	bt_conn_unref(snes.conn);

    snes.conn = NULL;
    //memset(&snes, 0, sizeof(snes));
    //memset(&connectedMonkey, 0, sizeof(connectedMonkey));
    
    ble_remove_device();
    disconnected();
    ble_start_scan();
}

bool ble_param_request_cb(struct bt_conn *conn, struct bt_le_conn_param *param){
    #ifdef DEBUG_MODE
        printk("Connection parameters update request. min: %d, max: %d, latency: %d, timeout: %d\n", param->interval_min, param->interval_max, param->latency, param->timeout); 
    #endif

    int ret = bt_conn_le_param_update(conn, param);
    return (ret == 0);
}

void ble_param_updated_cb(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout){
    #ifdef DEBUG_MODE
        printk("Connection parameters updated. interval: %d, latency: %d, timeout: %d\n", interval, latency, timeout);
    #endif
    connected(connectedMonkey);
}



Parents
  • Hello,

    bt_conn_le_create() is returning -EINVAL(-22), which indicates that an invalid paramater was passed to the function. Are you able to step through the function with a debugger function to see why this error is returned?

    Best regards,

    Vidar

  • Hello,

    I've used a debugger function to step through the function.
    -EINVAL is the result of the conn_le_create_common_checks function. This value is being returned by the bt_conn_exists_le in this function. As it goes through the if statement in the bt_conn_exists_le function, it appears that a connection object already exists.

    Is it possible that the disconnect was not completed correctly?

    The disconnecting process appears to be correct from the peripheral perspective, as indicated in the original message. I have already tried to replace the disconnecting reason in the following method to see if the behavior was different.

    // Function to disconnect from a specific device
    void ble_disconnect(void){
        bt_conn_disconnect(snes.conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    }
    

    Adrien

  • Hello Adrien,

    Thanks for the update. Are you calling the connect function within the disconnected callback by any chanche? The previous connection object will not be freed at that point.

    Vidar

  • No, the connect function is not called in the disconnect callback.

    // Function called when a device is disconnected
    void ble_disconnected_cb(struct bt_conn *conn, uint8_t reason)
    {
        char addr[BT_ADDR_LE_STR_LEN];
        bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	if (conn != snes.conn) {
            printk("Conn different from snes.conn");
    		return;
    	}
    
        #ifdef DEBUG_MODE
            printk("Disconnected from monkey %d, reason 0x%x\n", connectedMonkey.num, reason);
        #endif
    	
    	bt_conn_unref(snes.conn);
    
        snes.conn = NULL;
        
        ble_remove_device();
        disconnected();
        ble_start_scan();
    }

  • Thanks for confirming. Please try to increase the max. number of BLE connections as a test. You can do this with the CONFIG_BT_MAX_CONN symbol.

Reply Children
Related