Hi Nordic DevZone Community,
I am currently working on a BLE peripheral application using the Zephyr stack and have encountered an issue where my device stops advertising after approximately 30 seconds. This problem only occurs when SMP (Security Manager Protocol) is enabled in the configuration.
Setup Details
-
Hardware: nRF5340 with nRF7002 companion IC
-
SDK/Stack: Zephyr / nRF Conenct SDK v2.6.0
-
Bluetooth Role: Peripheral
-
Relevant Configuration (
prj.conf
):CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_SMP=y CONFIG_BT_SETTINGS=y CONFIG_BT_GATT_CLIENT=y CONFIG_BT_GATT_AUTO_UPDATE_MTU=y CONFIG_SETTINGS=y CONFIG_FLASH=y CONFIG_NVS=y CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 CONFIG_BT_HCI_TX_STACK_SIZE=2048 CONFIG_BT_RX_STACK_SIZE=8196 CONFIG_BT_L2CAP_TX_MTU=517 CONFIG_BT_BUF_ACL_TX_SIZE=512 CONFIG_BT_BUF_ACL_RX_SIZE=512 CONFIG_BT_DIS=y CONFIG_BT_DIS_SETTINGS=y CONFIG_BT_DIS_SERIAL_NUMBER=y CONFIG_BT_DIS_FW_REV=y CONFIG_BT_DIS_HW_REV=y CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE=y CONFIG_SETTINGS_RUNTIME=y
Code:
#include <zephyr/logging/log.h> LOG_MODULE_REGISTER(ble_control, LOG_LEVEL_DBG); #include "ble_control.h" #include "power_management.h" #include <zephyr/kernel.h> #include <zephyr/types.h> #include <stddef.h> #include <string.h> #include <stdio.h> #include <zephyr/mgmt/mcumgr/transport/smp_bt.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/uuid.h> #include <zephyr/bluetooth/gatt.h> #include <zephyr/bluetooth/conn.h> #include <zephyr/settings/settings.h> #include <zephyr/bluetooth/services/dis.h> #include <zephyr/random/random.h> #include "version.h" #include "status_control.h" #include "sensor_control.h" #include "fota_control.h" #include "led_control.h" #include "../services/data_service.h" #include "../services/start_service.h" #define BLE_AUTH_DELAY_MS 2000 // ms #define MAX_AUTH_RETRIES 3 #define RANDOM_ID_KEY "device_random" #define RANDOM_ID_KEY_ID "id" #define RANDOM_ID_PREFIX "EVR-" #define MAC_ADDR_LEN 12 // 6 bytes of MAC address as hex (2 chars each) #define SIDE_SUFFIX_LEN 2 // "-L" or "-R" (2 characters) #define NULL_TERMINATOR_LEN 1 // Null terminator #define RANDOM_ID_LEN (sizeof(RANDOM_ID_PREFIX) + MAC_ADDR_LEN + SIDE_SUFFIX_LEN + NULL_TERMINATOR_LEN - 1) static struct k_work_delayable auth_work; static struct bt_conn *pending_conn = NULL; static bool bl_central_connected = false; static char random_id[RANDOM_ID_LEN + 1]; // +1 for null terminator static struct bt_data ad[3]; // Declare dynamically sized array /* Timestamps ---------------------------------------------------------*/ static uint64_t rtime_from_app = 0; static uint32_t millis_offset = 0; static struct bt_conn_le_phy_param *phy_params = BT_CONN_LE_PHY_PARAM_1M; static int bt_ready(void); static void connected(struct bt_conn *conn, uint8_t err); static void disconnected(struct bt_conn *conn, uint8_t reason); static void settings_runtime_load(void); static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err); static void auth_work_handler(struct k_work *work); static void construct_advertisement_data(void) { ad[0] = (struct bt_data)BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL)); ad[1] = (struct bt_data)BT_DATA(BT_DATA_NAME_COMPLETE, random_id, strlen(random_id)); ad[2] = (struct bt_data)BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)); } static int random_id_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { if (settings_name_steq(name, RANDOM_ID_KEY_ID, NULL)) { if (len != RANDOM_ID_LEN) { LOG_ERR("Invalid length for random ID: expected %d, got %zu", RANDOM_ID_LEN, len); return -EINVAL; } int ret = read_cb(cb_arg, random_id, RANDOM_ID_LEN); if (ret >= 0) { random_id[RANDOM_ID_LEN] = '\0'; // Ensure null-termination LOG_INF("Random ID loaded: %s", random_id); return 0; } LOG_ERR("Failed to read random ID: %d", ret); return ret; } return -ENOENT; } static struct settings_handler random_id_conf = { .name = RANDOM_ID_KEY, .h_set = random_id_set, }; static int save_random_id(const char *id) { char full_key[64]; snprintf(full_key, sizeof(full_key), "%s/%s", RANDOM_ID_KEY, RANDOM_ID_KEY_ID); int ret = settings_save_one(full_key, id, RANDOM_ID_LEN); if (ret == 0) { LOG_INF("Random ID saved: %s", id); } else { LOG_ERR("Failed to save Random ID (err %d)", ret); } return ret; } static void generate_id_from_mac(char *id, size_t len) { bt_addr_le_t addr; size_t count = 1; // Request one address size_t id_prefix_len = strlen(RANDOM_ID_PREFIX); const char *side_suffix = get_side() == 0 ? "-L" : "-R"; // Ensure the ID buffer is large enough if (len < RANDOM_ID_LEN) { LOG_ERR("Buffer too small for ID"); return; } // Copy the prefix strncpy(id, RANDOM_ID_PREFIX, len); // Get the device's public address bt_id_get(&addr, &count); if (count == 0) { LOG_ERR("No valid Bluetooth address available. Using fallback ID."); snprintf(id + id_prefix_len, len - id_prefix_len, "NO_MAC%s", side_suffix); return; } // Append the MAC address snprintf(id + id_prefix_len, len - id_prefix_len, "%02X%02X%02X%02X%02X%02X%s", addr.a.val[5], addr.a.val[4], addr.a.val[3], addr.a.val[2], addr.a.val[1], addr.a.val[0], side_suffix); LOG_INF("Generated ID from MAC with side: %s", id); } static void init_random_id(void) { int ret = settings_load_subtree(RANDOM_ID_KEY); if (ret != 0 || strlen(random_id) == 0) { LOG_INF("No Random ID found, generating a new one based on MAC address"); generate_id_from_mac(random_id, RANDOM_ID_LEN); if (save_random_id(random_id) != 0) { LOG_ERR("Failed to save Random ID"); } } else { LOG_INF("Using existing Random ID: %s", random_id); } } static void auth_work_handler(struct k_work *work) { if (!pending_conn) { LOG_ERR("No connection available for security setup"); return; } int security_err = bt_conn_set_security(pending_conn, BT_SECURITY_L2); if (security_err) { LOG_ERR("Failed to set security (err %d)", security_err); bt_conn_disconnect(pending_conn, BT_HCI_ERR_AUTH_FAIL); } else { LOG_INF("Security was successfully set"); } // Release the reference to the connection object bt_conn_unref(pending_conn); pending_conn = NULL; } int init_ble(void) { // Initialize delayed work item for setting security k_work_init_delayable(&auth_work, auth_work_handler); // Initialize the settings subsystem int ret = settings_subsys_init(); if (ret != 0) { LOG_ERR("Settings subsystem init failed: %d", ret); return ret; } // Register the settings handler ret = settings_register(&random_id_conf); if (ret != 0) { LOG_ERR("Failed to register settings handler: %d", ret); return ret; } ret = bt_enable(NULL); if (ret != 0) { LOG_ERR("Bluetooth enable failed: %d", ret); return ret; } ret = settings_load(); if (ret != 0) { LOG_ERR("Loading settings failed: %d", ret); return ret; } // Initialize the Random ID init_random_id(); // Initialize Bluetooth and start advertising ret = bt_ready(); if (ret != 0) { LOG_ERR("Bluetooth init failed: %d", ret); return ret; } // Set characteristic callbacks set_write_start_callback(start_measurement); set_write_rtime_callback(set_real_time); set_write_calib_callback(calibrate_sensor); settings_runtime_load(); LOG_INF("Bluetooth initialized!"); return ret; } uint8_t get_side(void) { return strcmp(CONFIG_BT_DEVICE_NAME, "SENSOR-R") == 0 ? 0 : 1; } static void settings_runtime_load(void) { char hw_rev_str[6]; char fw_rev_str[16]; snprintf(hw_rev_str, sizeof(hw_rev_str), "REV %d", CONFIG_SENSOR_REV); snprintf(fw_rev_str, sizeof(fw_rev_str), "%s/%s", FW_VERSION_STR,CONFIG_FW_FLAVOR); settings_runtime_set("bt/dis/model", "EVRmeasure", sizeof("EVRmeasure")); settings_runtime_set("bt/dis/manuf", "EVERSION Technologies GmbH", sizeof("EVERSION Technologies GmbH")); settings_runtime_set("bt/dis/fw", fw_rev_str, sizeof(fw_rev_str)); settings_runtime_set("bt/dis/hw", hw_rev_str, sizeof(hw_rev_str)); settings_runtime_set("bt/dis/serial", "000000000", sizeof("000000000")); } void set_real_time(uint64_t rtime_new) { if (is_fifo_running()) return; uint32_t millis_offset_tmp = k_uptime_get_32(); uint64_t rtime_old = rtime_from_app + (millis_offset_tmp - millis_offset); LOG_INF("New Timestamp: %"PRIu64, rtime_new); LOG_INF("Old Timestamp: %"PRIu64, rtime_old); LOG_INF("Delta: %"PRIu64, rtime_new - rtime_old); if (millis_offset != 0 && (rtime_old >= rtime_new)) return; LOG_INF("Update Timestamp"); millis_offset = millis_offset_tmp; rtime_from_app = rtime_new; } uint64_t get_real_time(void) { return rtime_from_app + (k_uptime_get_32() - millis_offset); } // Define connection parameters with min interval, max interval, latency, and timeout static struct bt_le_conn_param *conn_params = BT_LE_CONN_PARAM(0x0006, 0x0006, 0, 400); static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { if (!err && level >= BT_SECURITY_L2) { LOG_INF("Security successfully set to level %u", level); } else { LOG_ERR("Failed to achieve required security level: %u", level); } } static void connected(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 (err) { LOG_ERR("Connection failed %s (err 0x%02x)", addr, err); } else { LOG_INF("Connected to %s", addr); // Request 1M PHY setting int ret = bt_conn_le_phy_update(conn, phy_params); if (ret) { LOG_ERR("Failed to update PHY (err %d)", ret); } else { LOG_INF("PHY update requested to 1M"); } // Update connection parameters upon connection bt_conn_le_param_update(conn, conn_params); // Retain a reference to the connection object for the work item if (pending_conn) { bt_conn_unref(pending_conn); } pending_conn = bt_conn_ref(conn); // Schedule the delayed work to set security k_work_schedule(&auth_work, K_MSEC(BLE_AUTH_DELAY_MS)); led_switch(LED_GREEN); bl_central_connected = true; stop_power_off_work(); } } 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 from %s (reason 0x%02x)", addr, reason); bl_central_connected = false; led_switch(LED_RED); start_power_off_work(); // Restart advertising struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .interval_min = 0x0020, // 20ms .interval_max = 0x0040, // 40ms .options = BT_LE_ADV_OPT_CONNECTABLE, }; int ret = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), NULL, 0); if (ret) { LOG_ERR("Failed to restart advertising (err %d)", ret); } else { LOG_INF("Advertising restarted"); } } BT_CONN_CB_DEFINE(conn_callbacks) = { .connected = connected, .disconnected = disconnected, .security_changed = security_changed }; static int bt_ready(void) { int ret; LOG_INF("Bluetooth initialized"); // Construct advertisement data to ensure it's up to date construct_advertisement_data(); // Advertising parameters struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .interval_min = 0x0020, // 20ms .interval_max = 0x0040, // 40ms, .options = BT_LE_ADV_OPT_CONNECTABLE, }; // Start advertising ret = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), NULL, 0); if (ret) { LOG_ERR("Advertising failed to start (err %d)", ret); return ret; } LOG_INF("Advertising successfully started"); return 0; } bool central_connected(void) { return bl_central_connected; }
Problem Description
-
Advertising starts successfully when the device initializes:
I: Advertising successfully started
-
After approximately 30 seconds, advertising stops, even though:
-
No connection or disconnection events occur.
-
The
bt_le_adv_stop()
function is never explicitly called in my code.
-
-
Logs indicate a state change from
connecting-adv
todisconnected
, but no pairing or connection events are triggered.
Logs (Relevant Excerpts)
D: Advertising successfully started
D: connecting-adv -> disconnected
D: handle 0 ref 1 -> 2
D: handle 0 ref 2 -> 1
D: handle 0 ref 1 -> 0
D: Disconnected from 00:00:00:00:00:00 (reason 0x00)
Troubleshooting Steps Taken
-
Verified
bt_le_adv_stop()
calls:-
The function is not called anywhere in my code.
-
-
Adjusted SMP settings:
-
Allowed unauthenticated pairing (
CONFIG_BT_SMP_ALLOW_UNAUTH_OVERWRITE=y
).
-
-
Restarted Advertising on Disconnection:
-
Added explicit calls to restart advertising in the
disconnected
callback.
static void disconnected(struct bt_conn *conn, uint8_t reason) { LOG_INF("Disconnected, restarting advertising..."); struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .interval_min = 0x0020, // 20ms .interval_max = 0x0040, // 40ms .options = BT_LE_ADV_OPT_CONNECTABLE, }; int ret = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), NULL, 0); if (ret) { LOG_ERR("Failed to restart advertising (err %d)", ret); } }
However, this does not resolve the issue.
-
-
Enabled SMP Debugging:
-
Added
CONFIG_BT_DEBUG_SMP=y
andCONFIG_BT_DEBUG_SECURITY=y
for detailed logs.
-
Questions
-
Is this behavior expected when SMP is enabled? If so, why might advertising stop after 30 seconds?
-
Could this issue be related to incomplete pairing or a state mismatch in the SMP process?
-
What additional configurations or callbacks should I implement to ensure advertising continues permanently?
-
Are there any known bugs or limitations in the Zephyr SMP implementation that could cause this behavior?
Request for Help
Any guidance or insights into why this issue occurs and how to resolve it would be greatly appreciated. I am happy to provide more detailed logs or configurations if needed.
Thank you in advance for your support!