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