Hello there,
I'm designing a set of devices composed of a remote and 2 peripheral. The remote's job is to be connected to the 2 peripherals as a central and occasionnaly connect to a smartphone as a peripheral.
All of the devices are custom board, we already figured out the antenna matching which help to reduce bug. My development environment is :
- NCS & toolchain 3.0.1
- nRF52833 for all 3 devices
What is working :
- The advertising and the scanning
- The pairing process
- The discovering procedure
- Read, write and subscribe to gatt atribute
What is not working :
- Mainly the reconnect procedure
I succeed to implement accept list for peripheral and to open it when i need to thank to the lesson from Academy, but i struggle to make the reconnect works. It is crucial because all 3 devices are battery powered and i need to save energy.
In my understanding, there is 2 steps :
When no bond are available on the remote, i have to go though a scan with specific filtering and then connect / pair
################ # Board config # ################ CONFIG_EVENTS=y CONFIG_SMF=y CONFIG_HWINFO=y # Enable PWM led CONFIG_PWM=y CONFIG_NRFX_PWM0=y CONFIG_LED=y # Enable button CONFIG_INPUT=y CONFIG_INPUT_GPIO_KEYS=y # Enable ADC for battery monitoring CONFIG_ADC=y # Enable sensor (temperature & IMU) CONFIG_SENSOR=y CONFIG_FPU=y CONFIG_I2C=y # CONFIG_LSM6DSL_TRIGGER_GLOBAL_THREAD=y # External memory CONFIG_SPI_NOR=y CONFIG_SPI_NOR_SFDP_DEVICETREE=y CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 ################ # Flash config # ################ CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_NVS=y CONFIG_SETTINGS=y CONFIG_HEAP_MEM_POOL_SIZE=1024 ############## # BLE config # ############## CONFIG_BT=y CONFIG_BT_CENTRAL=y CONFIG_BT_COMPANY_ID=0x0ef2 CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="Remote" CONFIG_BT_SMP=y CONFIG_BT_GATT_CLIENT=y CONFIG_BT_GATT_DM=y CONFIG_BT_GATT_DM_DATA_PRINT=y CONFIG_BT_MAX_CONN=3 CONFIG_BT_MAX_PAIRED=10 CONFIG_BT_CONN_CTX=y CONFIG_BT_SCAN=y CONFIG_BT_SCAN_FILTER_ENABLE=y CONFIG_BT_SCAN_MANUFACTURER_DATA_CNT=1 CONFIG_BT_SCAN_CONN_ATTEMPTS_FILTER=y CONFIG_BT_SCAN_CONN_ATTEMPTS_FILTER_LEN=10 CONFIG_BT_FILTER_ACCEPT_LIST=y CONFIG_BT_PRIVACY=y CONFIG_BT_SETTINGS=y # BLE services CONFIG_BT_BAS=y CONFIG_BT_ZEPHYR_NUS=y CONFIG_BT_DIS=y CONFIG_BT_DIS_MODEL_NUMBER=n CONFIG_BT_DIS_MANUF_NAME=n CONFIG_BT_DIS_PNP=n CONFIG_BT_DIS_FW_REV=y # OTA DFU with mcumgr CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS=y CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS=y ######################## # No memory optimizing # ######################## # Enable log module though RTT CONFIG_LOG=y CONFIG_USE_SEGGER_RTT=y CONFIG_LOG_BACKEND_RTT=y CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096
static void accept_list_add_cb(const struct bt_bond_info *info, void *user_data)
{
int *bond_count = (void *)user_data;
int err = 0;
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(&info->addr, addr, sizeof(addr));
if ((*bond_count) < 0) {
return;
}
/* Check if the bond is with a blinker */
for (uint8_t i = 0; i < MAX_BLINKER; i++) {
if (bt_addr_le_cmp(storage_get_blinker_addr(i), &info->addr) == 0) {
/* It is, so add it to the filter accept list */
err = bt_le_filter_accept_list_add(&info->addr);
if (err) {
LOG_ERR("%s cannot add to filter accept list (err %d)", addr, err);
(*bond_count) = -EIO;
} else {
// LOG_DBG("%s added to the filter accept list", addr);
(*bond_count)++;
}
}
}
}
static void accept_list_remove_cb(struct bt_conn *conn, void *data)
{
uint8_t *conn_count = (void *)data;
struct bt_conn_info info;
int err = 0;
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if ((*conn_count) < 0) {
return;
}
/* Check if the connection is with a blinker */
for (uint8_t i = 0; i < MAX_BLINKER; i++) {
if (bt_addr_le_cmp(bt_conn_get_dst(conn), storage_get_blinker_addr(i)) == 0) {
/* It is, is it connected tho ? */
err = bt_conn_get_info(conn, &info);
if (err) {
LOG_ERR("%s failed to get conn info (err %d)", addr, err);
return;
}
if (info.state == BT_CONN_STATE_CONNECTED) {
/* Blinker is connected, we don't need to reconnect to it */
err = bt_le_filter_accept_list_remove(bt_conn_get_dst(conn));
if (err) {
LOG_ERR("%s cannot remove to filter accept list (err %d)", addr, err);
(*conn_count) = -EIO;
} else {
// LOG_DBG("%s removed to the filter accept list", addr);
(*conn_count)--;
}
}
}
}
}
static int auto_conn_procedure(void)
{
int err = 0;
uint8_t count = 0;
/* Check firstly if we have 2 bonded blinker in memory */
for (uint8_t i = 0; i < MAX_BLINKER; i++) {
if (bt_addr_le_cmp(storage_get_blinker_addr(i), BT_ADDR_LE_NONE) == 0) {
LOG_DBG("Auto connection stopped (no address in memory)");
return -1;
}
}
err = bt_le_filter_accept_list_clear();
if (err) {
LOG_ERR("Failed to clear accept list (err %d)", err);
return err;
}
/* Increase count by the number of matching bound */
bt_foreach_bond(BT_ID_DEFAULT, accept_list_add_cb, &count);
if (count < MAX_BLINKER) {
LOG_DBG("Auto connection stopped (not enought bound count : %d)", count);
return -1;
}
/* Decrease count by the number of connected connection */
bt_conn_foreach(BT_CONN_TYPE_LE, accept_list_remove_cb, &count);
if (count <= 0) {
LOG_DBG("Auto connection stopped (enought connection count)");
return -1;
}
err = bt_conn_le_create_auto(BT_CONN_LE_CREATE_CONN_AUTO, BT_LE_CONN_PARAM_DEFAULT);
if (err) {
LOG_ERR("Auto connection start failed (err %d)", err);
return err;
} else {
LOG_DBG("Auto connection started");
}
return err;
}
static void conn_count_cb(struct bt_conn *conn, void *data)
{
uint8_t *actual_conn_count = (void *)data;
struct bt_conn_info info;
int err = 0;
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
/* Get conn info */
err = bt_conn_get_info(conn, &info);
if (err) {
LOG_ERR("%s failed to get conn info (err %d)", addr, err);
return;
}
/* Verify it is active */
if ((info.state == BT_CONN_STATE_CONNECTED) && (info.role == BT_CONN_ROLE_CENTRAL)) {
(*actual_conn_count)++;
}
}
static int scan_procedure(void)
{
int err = 0;
uint8_t conn_count = 0;
/* Make sure we're not scanning */
err = bt_scan_stop();
if ((err != 0) && (err != -EALREADY)) {
LOG_ERR("Scan stop failed (err %d)", err);
return err;
}
bt_conn_foreach(BT_CONN_TYPE_LE, conn_count_cb, &conn_count);
if (conn_count >= MAX_BLINKER) {
LOG_DBG("Scan stopped (connection count : %d)", conn_count);
return -1;
}
err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
if (err) {
LOG_ERR("Scan start failed (err %d)", err);
return err;
} else {
LOG_DBG("Scan started");
}
return err;
}
static void conn_procedure_work_handler(struct k_work *work)
{
/* If a discovering is happening, report the work for later */
if (k_event_test(&event, EVENT_BLE_DISCOVERING)) {
k_work_schedule(&conn_procedure_work, K_MSEC(CONN_PROCEDURE_RESCHEDULE_MS));
return;
}
if (k_event_test(&event, EVENT_FSM_PAIR)) {
scan_procedure();
} else {
auto_conn_procedure();
}
}
static void scan_filter_match(struct bt_scan_device_info *device_info, struct bt_scan_filter_match *filter_match,
bool connectable)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
LOG_INF("%s filters matched", addr);
}
static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
LOG_ERR("%s connecting error", addr);
k_work_schedule(&conn_procedure_work, K_NO_WAIT);
}
static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
LOG_INF("%s connecting", addr);
bt_conn_ref(conn);
}
BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, scan_connecting_error, scan_connecting);
static int scan_init(void)
{
int err = 0;
struct bt_scan_init_param scan_init = {
.connect_if_match = true,
.scan_param = BT_LE_SCAN_ACTIVE,
.conn_param = BT_LE_CONN_PARAM_DEFAULT,
};
bt_scan_init(&scan_init);
bt_scan_cb_register(&scan_cb);
/* Filtering */
adv_mfg_data_s adv_mfg_data = {CONFIG_BT_COMPANY_ID};
struct bt_scan_manufacturer_data scan_manufacturer_data = {
.data = (uint8_t *)&adv_mfg_data,
.data_len = sizeof(adv_mfg_data_s),
};
err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_MANUFACTURER_DATA, &scan_manufacturer_data);
if (err) {
LOG_ERR("Add scan filters failed (err %d)", err);
return err;
}
err = bt_scan_filter_enable(BT_SCAN_MANUFACTURER_DATA_FILTER, false);
if (err) {
LOG_ERR("Enable scan filters failed (err %d)", err);
return err;
}
return err;
}