Hi Devzone,
On my nrf52840-based custom board, I am implementing the scanning of devices broadcasting the Heart Rate service while advertising a custom service and connecting to another Central device. In order to do this I based myself off of the Central and Peripheral HRS example:
developer.nordicsemi.com/.../README.html
I can advertise my custom service, connect to it, and send commands to start scanning, however, when I connect to my board (my board acting as a peripheral in that case), I get the following warning:
[00:00:16.727,966] <inf> BLE_MANAGER: Connected 44:1C:A8:E2:B6:96 (public) [00:00:16.729,675] <wrn> bt_hci_core: opcode 0x2039 status 0x0d
Even though it works as expected for now, I would like to get rid of the error message. The status states BT_HCI_ERR_INSUFFICIENT_RESOURCES, and from what I've found, the opcode 0x2039 stands for BT_HCI_OP_LE_SET_EXT_ADV_ENABLE (I do have CONFIG_BT_EXT_ADV enabled in my prj.conf). Do you have an idea where this warning originates from ?
Here is the ble_manager.c file that handles the ble connections:
#include "include/ble_manager.h" LOG_MODULE_REGISTER(BLE_MANAGER, LOG_LEVEL_DBG); static const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, (CONFIG_BT_DEVICE_APPEARANCE >> 0) & 0xff, (CONFIG_BT_DEVICE_APPEARANCE >> 8) & 0xff), BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_HRS_VAL), BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)), }; static const struct bt_data sd[] = { BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_EQS_SERVICE_VAL), }; K_MSGQ_DEFINE(hrs_queue, sizeof(struct bt_hrs_client_measurement), HRS_QUEUE_SIZE, 4); static K_SEM_DEFINE(ble_init_ok, 0, 1); static struct bt_hrs_client hrs_c; static struct bt_conn *current_conn_as_peripheral; static struct bt_conn *current_hrs_conn_as_central; static struct bt_conn *auth_conn; static ble_manager_evt_handler_t m_app_evt_handler; static bool _scan_in_progress = false; static const char * const sensor_location_str[] = { "Other", "Chest", "Wrist", "Finger", "Hand", "Ear lobe", "Foot" }; static void hrs_sensor_location_read_cb(struct bt_hrs_client *hrs_c, enum bt_hrs_client_sensor_location location, int err) { if (err) { LOG_ERR("HRS Sensor Body Location read error (err %d)", err); return; } LOG_INF("Heart Rate Sensor body location: %s", sensor_location_str[location]); } static void hrs_measurement_notify_cb(struct bt_hrs_client *hrs_c, const struct bt_hrs_client_measurement *meas, int err) { if (err) { LOG_ERR("Error during receiving Heart Rate Measurement notification, err: %d", err); return; } LOG_DBG("Heart Rate Measurement notification received:"); LOG_DBG("Heart Rate Measurement Value Format: %s", meas->flags.value_format ? "16 - bit" : "8 - bit"); LOG_DBG("Sensor Contact detected: %d", meas->flags.sensor_contact_detected); LOG_DBG("Sensor Contact supported: %d", meas->flags.sensor_contact_supported); LOG_DBG("Energy Expended present: %d", meas->flags.energy_expended_present); LOG_DBG("RR-Intervals present: %d", meas->flags.rr_intervals_present); LOG_DBG("Heart Rate Measurement Value: %d bpm", meas->hr_value); if (meas->flags.energy_expended_present) { LOG_DBG("Energy Expended: %d J", meas->energy_expended); } if (meas->flags.rr_intervals_present) { LOG_DBG("RR-intervals: "); for (size_t i = 0; i < meas->rr_intervals_count; i++) { LOG_DBG("%d ", meas->rr_intervals[i]); } } err = k_msgq_put(&hrs_queue, meas, K_NO_WAIT); if (err) { LOG_ERR("Notification queue is full. Discarding HRS notification (err %d)", err); } } static void discovery_completed_cb(struct bt_gatt_dm *dm, void *context) { int err; LOG_DBG("The discovery procedure succeeded"); bt_gatt_dm_data_print(dm); err = bt_hrs_client_handles_assign(dm, &hrs_c); if (err) { LOG_ERR("Could not init HRS client object (err %d)", err); return; } err = bt_hrs_client_sensor_location_read(&hrs_c, hrs_sensor_location_read_cb); if (err) { LOG_ERR("Could not read Heart Rate Sensor location (err %d)", err); } err = bt_hrs_client_measurement_subscribe(&hrs_c, hrs_measurement_notify_cb); if (err) { LOG_ERR("Could not subscribe to Heart Rate Measurement characteristic (err %d)", err); } err = bt_gatt_dm_data_release(dm); if (err) { LOG_ERR("Could not release the discovery data (err %d)", err); } } static void discovery_not_found_cb(struct bt_conn *conn, void *context) { LOG_WRN("Heart Rate Service could not be found during the discovery"); } static void discovery_error_found_cb(struct bt_conn *conn, int err, void *context) { LOG_ERR("The discovery procedure failed with %d", err); } static const struct bt_gatt_dm_cb discovery_cb = { .completed = discovery_completed_cb, .service_not_found = discovery_not_found_cb, .error_found = discovery_error_found_cb }; static void gatt_discover(struct bt_conn *conn) { int err; err = bt_gatt_dm_start(conn, BT_UUID_HRS, &discovery_cb, NULL); if (err) { LOG_ERR("Could not start the discovery procedure, error " "code: %d", err); } } static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params) { LOG_INF("%s: MTU exchange %s (%u)", __func__, err == 0U ? "successful" : "failed", bt_gatt_get_mtu(conn)); } static struct bt_gatt_exchange_params mtu_exchange_params = { .func = mtu_exchange_cb }; static void connected(struct bt_conn *conn, uint8_t err) { struct bt_conn_info info; char addr[BT_ADDR_LE_STR_LEN]; if (err) { LOG_ERR("Connection failed (err %u)", err); return; } bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_INF("Connected %s", addr); app_event_t event; event.type = EVENT_BLE_CONNECTION; bt_conn_get_info(conn, &info); if (info.role == BT_CONN_ROLE_PERIPHERAL) { event.data = BLE_CONNECTED; current_conn_as_peripheral = bt_conn_ref(conn); int ret = bt_gatt_exchange_mtu(conn, &mtu_exchange_params); if (ret) { LOG_ERR("%s: MTU exchange failed (err %d)", __func__, err); } } else if (info.role == BT_CONN_ROLE_CENTRAL) { event.data = BLE_HRS_CONNECTED; // Don't ref the connection here, it's already done in scan_connecting. gatt_discover(conn); _scan_in_progress = false; } app_post_event(event); } static void disconnected(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)); LOG_INF("Disconnected: %s (reason %u)", addr, reason); app_event_t event; event.type = EVENT_BLE_CONNECTION; if (conn == auth_conn) { bt_conn_unref(auth_conn); auth_conn = NULL; } if (conn == current_conn_as_peripheral) { event.data = BLE_DISCONNECTED; bt_conn_unref(conn); current_conn_as_peripheral = NULL; } if (conn == current_hrs_conn_as_central){ event.data = BLE_HRS_DISCONNECTED; bt_conn_unref(conn); current_hrs_conn_as_central = NULL; } app_post_event(event); } #ifdef CONFIG_EQS_SERVICE_SECURITY_ENABLED static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); if (!err) { LOG_DBG("Security changed: %s level %u", addr, level); } else { LOG_ERR("Security failed: %s level %u err %d", addr, level, err); } } #endif BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, #ifdef CONFIG_EQS_SERVICE_SECURITY_ENABLED .security_changed = security_changed, #endif }; #if defined(CONFIG_EQS_SERVICE_SECURITY_ENABLED) static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_DBG("Passkey for %s: %06u", addr, passkey); } static void auth_passkey_confirm(struct bt_conn *conn, unsigned int passkey) { char addr[BT_ADDR_LE_STR_LEN]; auth_conn = bt_conn_ref(conn); bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_DBG("Passkey for %s: %06u", addr, passkey); LOG_DBG("Press Button 1 to confirm, Button 2 to reject."); } static void auth_cancel(struct bt_conn *conn) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_DBG("Pairing cancelled: %s", addr); } static void pairing_complete(struct bt_conn *conn, bool bonded) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_DBG("Pairing completed: %s, bonded: %d", addr, bonded); } static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason) { char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); LOG_DBG("Pairing failed conn: %s, reason %d", addr, reason); } static struct bt_conn_auth_cb conn_auth_callbacks = { .passkey_display = auth_passkey_display, .passkey_confirm = auth_passkey_confirm, .cancel = auth_cancel, }; static struct bt_conn_auth_info_cb conn_auth_info_callbacks = { .pairing_complete = pairing_complete, .pairing_failed = pairing_failed}; #else static struct bt_conn_auth_cb conn_auth_callbacks; static struct bt_conn_auth_info_cb conn_auth_info_callbacks; #endif struct bt_le_scan_param scan_param = { .type = BT_HCI_LE_SCAN_PASSIVE, .options = BT_LE_SCAN_OPT_NONE, .interval = 0x0010, .window = 0x0010, .timeout = 1000, // N*10ms => timeout = 10s }; static int scan_start(void) { LOG_DBG("Starting scanning for HRS devices"); int err = bt_le_scan_start(&scan_param, NULL); if (err) { LOG_ERR("Scanning failed to start (err %d)", err); return err; } _scan_in_progress = true; return err; } 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("Filters matched. Address: %s connectable: %d", addr, connectable); } static void scan_connecting_error(struct bt_scan_device_info *device_info) { LOG_WRN("Connecting failed"); } static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn) { current_hrs_conn_as_central = bt_conn_ref(conn); } BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL, scan_connecting_error, scan_connecting); static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf){ if (info){ char addr[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(info->addr, addr, sizeof(addr)); LOG_INF("Received advertising data from %s", addr); } } static void scan_timeout(void){ LOG_WRN("Scan timeout"); _scan_in_progress = false; } struct bt_le_scan_cb scan_callback = { .recv = scan_recv, .timeout = scan_timeout, }; static void scan_init(void) { int err; struct bt_scan_init_param param = { .scan_param = NULL, .conn_param = BT_LE_CONN_PARAM_DEFAULT, .connect_if_match = 1 }; bt_scan_init(¶m); bt_scan_cb_register(&scan_cb); bt_le_scan_cb_register(&scan_callback); err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_HRS); if (err) { LOG_ERR("Scanning filters cannot be set (err %d)", err); } err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false); if (err) { LOG_ERR("Filters cannot be turned on (err %d)", err); } } static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data, uint16_t len) { char addr[BT_ADDR_LE_STR_LEN] = {0}; bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr)); ble_eqss_control_process(data, len); } static void bt_subscribe_cb(enum eqs_service_send_status status) { return; } static void bt_subscribe_cb_processed(enum eqs_service_send_status status) { sync_mgr_char_ind(AREA_PROCESSED, status == EQS_SERVICE_SEND_STATUS_ENABLED ? true : false); } static void bt_subscribe_cb_raw_imu(enum eqs_service_send_status status) { sync_mgr_char_ind(AREA_RAW_IMU, status == EQS_SERVICE_SEND_STATUS_ENABLED ? true : false); } static void bt_subscribe_cb_raw_ecg(enum eqs_service_send_status status) { sync_mgr_char_ind(AREA_RAW_ECG, status == EQS_SERVICE_SEND_STATUS_ENABLED ? true : false); } static void bt_subscribe_cb_storage(enum eqs_service_send_status status) { sync_mgr_char_ind(AREA_STORAGE, status == EQS_SERVICE_SEND_STATUS_ENABLED ? true : false); } static struct eqs_service_cb eqs_cb = { .control_received = bt_receive_cb, .control_send_enabled = bt_subscribe_cb, .processed_send_enabled = bt_subscribe_cb_processed, .raw_imu_send_enabled = bt_subscribe_cb_raw_imu, .raw_ecg_send_enabled = bt_subscribe_cb_raw_ecg, .storage_send_enabled = bt_subscribe_cb_storage, }; #ifdef CONFIG_EQS_SERVICE_SECURITY_ENABLED static void num_comp_reply(bool accept) { if (accept) { bt_conn_auth_passkey_confirm(auth_conn); LOG_DBG("Numeric Match, conn %p", (void *)auth_conn); } else { bt_conn_auth_cancel(auth_conn); LOG_DBG("Numeric Reject, conn %p", (void *)auth_conn); } bt_conn_unref(auth_conn); auth_conn = NULL; } void button_changed(uint32_t button_state, uint32_t has_changed) { uint32_t buttons = button_state & has_changed; if (auth_conn) { if (buttons & KEY_PASSKEY_ACCEPT) { num_comp_reply(true); } if (buttons & KEY_PASSKEY_REJECT) { num_comp_reply(false); } } } #endif /* CONFIG_EQS_SERVICE_SECURITY_ENABLED */ bool ble_manager_is_connected() { return current_conn_as_peripheral != NULL; } bool ble_manager_control_is_subscribed(){ return bt_gatt_is_subscribed(current_conn_as_peripheral, bt_gatt_find_by_uuid(NULL, 1, BT_UUID_CONTROL), BT_GATT_CCC_INDICATE); } uint32_t ble_mgr_eqss_processed_data(app_pkt_data_t* pkt, uint16_t len){ return processed_send(NULL, pkt->bytes, len); } uint32_t ble_mgr_eqss_raw_imu(app_pkt_data_t* pkt, uint16_t len){ return raw_imu_send(NULL, pkt->bytes, len); } uint32_t ble_mgr_eqss_raw_ecg(app_pkt_data_t* pkt, uint16_t len){ return raw_ecg_send(NULL, pkt->bytes, len); } uint32_t ble_mgr_eqss_storage(app_pkt_data_t* pkt, uint16_t len){ return storage_send(NULL, pkt->bytes, len); } uint32_t ble_manager_start_hrs_scan() { int err = 0; err = scan_start(); if (err) { LOG_ERR("Error launching scan"); return 0; } return 1; } uint32_t ble_manager_stop_hrs_scan() { int err = 0; if (_scan_in_progress) { LOG_DBG("Stopping scanning for HRS devices"); err = bt_le_scan_stop(); if (err) { LOG_ERR("Error stopping scan"); return 0; } } else { // No scan in progress if (current_hrs_conn_as_central){ // There is an active connection with a HR device. bt_conn_disconnect(current_hrs_conn_as_central, BT_HCI_ERR_REMOTE_USER_TERM_CONN); } } _scan_in_progress = false; return 1; } uint32_t ble_manager_start_advertising() { int err = 0; err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (err) { if (err == -EALREADY) { LOG_DBG("Advertising already started."); } else { LOG_ERR("Advertising failed to start (err %d)", err); return 0; } } return 1; } uint32_t ble_manager_stop_advertising() { int err = 0; err = bt_le_adv_stop(); if (err) { if (err == -EALREADY) { LOG_DBG("Advertising already stopped."); } else { LOG_ERR("Advertising failed to stop (err %d)", err); return 0; } } return 1; } uint32_t ble_manager_disconnect() { int err = 0; if (current_conn_as_peripheral) { err = bt_conn_disconnect(current_conn_as_peripheral, BT_HCI_ERR_REMOTE_USER_TERM_CONN); if (err) { LOG_ERR("Failed to disconnect (err %d)", err); return 0; } } return 1; } uint8_t ble_manager_init() { int err = 0; if (IS_ENABLED(CONFIG_EQS_SERVICE_SECURITY_ENABLED)) { err = bt_conn_auth_cb_register(&conn_auth_callbacks); if (err) { LOG_ERR("Failed to register authorization callbacks."); return 0; } err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); if (err) { LOG_ERR("Failed to register authorization info callbacks."); return 0; } } err = bt_enable(NULL); if (err) { LOG_ERR("Error enabling the Bluetooth stack..."); } LOG_INF("Bluetooth initialized"); k_sem_give(&ble_init_ok); if (IS_ENABLED(CONFIG_SETTINGS)) { settings_load(); } err = eqs_service_init(&eqs_cb); if (err) { LOG_ERR("Failed to initialize EQS service (err: %d)", err); return 0; } err = bt_hrs_client_init(&hrs_c); if (err) { LOG_ERR("Heart Rate Service client failed to init (err %d)", err); return 0; } scan_init(); return 1; } static void hrs_notify_thread(void) { struct bt_hrs_client_measurement meas; uint16_t heart_rate; k_sem_take(&ble_init_ok, K_FOREVER); while (1) { k_msgq_get(&hrs_queue, &meas, K_FOREVER); if (meas.flags.value_format) { heart_rate = (uint8_t)(meas.hr_value >> 0x08); } else { heart_rate = meas.hr_value; } bt_hrs_notify(heart_rate); } } void ble_mgr_start(ble_manager_evt_handler_t app_handler) { m_app_evt_handler = app_handler; } K_THREAD_DEFINE(hrs_notify_thread_id, 1024, hrs_notify_thread, NULL, NULL, NULL, 7, 0, 0);
And the proj.conf file if that's relevant:
# Copyright (c) 2021 Nordic Semiconductor ASA # SPDX-License-Identifier: Apache-2.0 # # This file contains selected Kconfig options for the application. CONFIG_SENSOR=y # Config for Immutable Secure Bootloader CONFIG_SECURE_BOOT=n CONFIG_B0_MIN_PARTITION_SIZE=n # Config for Upgradable Bootloader CONFIG_BOOTLOADER_MCUBOOT=n CONFIG_BUILD_S1_VARIANT=n # enable logging CONFIG_LOG=y CONFIG_LOG_INFO_COLOR_GREEN=y CONFIG_LOG_MODE_IMMEDIATE=y # enable Hardware info CONFIG_HWINFO=y CONFIG_HWINFO_NRF=y # enable ADC CONFIG_ADC=y CONFIG_ADC_NRFX_SAADC=y # enable SPI CONFIG_SPI=y CONFIG_SPI_NOR=y # enabling State Machine Framework and events CONFIG_SMF=y CONFIG_EVENTS=y CONFIG_SMF_ANCESTOR_SUPPORT=y # enabling Bluetooth: both central an peripheral mode. CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y CONFIG_BT_DEVICE_NAME="Zephyr" CONFIG_BT_DEVICE_APPEARANCE=5184 # 5184 means Generic Outdoor Sports Activity. Complete list of appearance values can be found on the Bluetooth website under "assigned values" : https://www.bluetooth.com/specifications/assigned-numbers/ CONFIG_BT_MAX_CONN=2 CONFIG_BT_MAX_PAIRED=2 CONFIG_BT_GATT_CLIENT=y CONFIG_BT_L2CAP_TX_MTU=247 CONFIG_BT_BUF_ACL_RX_SIZE=251 CONFIG_BT_EXT_ADV=y CONFIG_BT_SMP=y CONFIG_BT_SCAN=y CONFIG_BT_SCAN_FILTER_ENABLE=y CONFIG_BT_SCAN_UUID_CNT=1 CONFIG_BT_GATT_DM=y CONFIG_HEAP_MEM_POOL_SIZE=512 # enabling bonding CONFIG_BT_SETTINGS=y CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_NVS=y CONFIG_SETTINGS=y # enabling BAS service and HRS service CONFIG_BT_BAS=y CONFIG_BT_HRS=y CONFIG_BT_HRS_CLIENT=y # This example requires more workqueue stack CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_ASSERT=y # Enable the UART driver (for the BG77 UART?) CONFIG_UART_ASYNC_API=n CONFIG_SERIAL=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_NRFX_UARTE0=y # Enbale Ring Buffer CONFIG_RING_BUFFER=y # Config for enabling newlib C CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y # Enabling FPU CONFIG_FPU=y CONFIG_MAIN_STACK_SIZE=4096 CONFIG_BT_RX_STACK_SIZE=4096 CONFIG_BT_HCI_TX_STACK_SIZE_WITH_PROMPT=y CONFIG_BT_HCI_TX_STACK_SIZE=4096 #CONFIG_THREAD_ANALYZER=y #CONFIG_THREAD_ANALYZER_AUTO=y #CONFIG_THREAD_ANALYZER_USE_LOG=y #CONFIG_THREAD_NAME=y #Enable Power Off of the device CONFIG_POWEROFF=y CONFIG_PM_DEVICE=y # USB CONFIG_STDOUT_CONSOLE=y CONFIG_USB_DEVICE_STACK=y CONFIG_USB_DEVICE_PRODUCT="test" CONFIG_USB_DEVICE_PID=0x0001 CONFIG_USB_DRIVER_LOG_LEVEL_ERR=y CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y CONFIG_UART_LINE_CTRL=y # Enable MBED TFM PSA for hash and crypto CONFIG_MBEDTLS=y CONFIG_MBEDTLS_BUILTIN=y CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" CONFIG_MBEDTLS_ENTROPY_ENABLED=y CONFIG_MBEDTLS_PSA_CRYPTO_C=y CONFIG_MBEDTLS_ZEPHYR_ENTROPY=y