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); }