Dear Nordic,
using NRF54L15-DK I am advertising BLE with RPA, hence it changes mac on every reset and and at specified interval. Now when Phone connects first time, it asks passkey to enter and gets bonded/paired successfully.
Issue is when I reset Board, Its mac changes in advertisement causes phone autoconnect and asks passkey. i Need once it is bonded, then it must not ask for passkey even after Power reset or reset until bond is deleted(Right now not implemented and not required).
SDK & Toolchain version:-3.3.1
please help
Reagrds
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/sys/byteorder.h>
#include <hal/nrf_ficr.h>
#include <platform/CHIPDeviceLayer.h>
#include "lock_manager.h"
#include <zephyr/drivers/hwinfo.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/random/random.h>
#include <zephyr/settings/settings.h>
#define CUSTOM_LOCK_SVC_UUID_VAL \
BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
#define CUSTOM_LOCK_STATE_CHAR_VAL \
BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1)
static struct bt_uuid_128 lock_service_uuid = BT_UUID_INIT_128(CUSTOM_LOCK_SVC_UUID_VAL);
static struct bt_uuid_128 lock_state_uuid = BT_UUID_INIT_128(CUSTOM_LOCK_STATE_CHAR_VAL);
// Variable to track BLE command state
static uint8_t ble_lock_state = 0;
unsigned int static_passkey = 123456;
// Statically define the Custom Lock Service and its writeable characteristic
// Statically define the Custom Lock Service and its writeable characteristic
BT_GATT_SERVICE_DEFINE(custom_lock_svc,
BT_GATT_PRIMARY_SERVICE(&lock_service_uuid),
BT_GATT_CHARACTERISTIC(&lock_state_uuid.uuid,
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE_AUTHEN, // <-- Changed from BT_GATT_PERM_WRITE
NULL, write_lock_state, &ble_lock_state)
);
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
// Advertise our custom 128-bit Lock Service UUID so it's easy to find!
BT_DATA_BYTES(BT_DATA_UUID128_ALL, CUSTOM_LOCK_SVC_UUID_VAL),
};
// 1. Callback matching the exact signature required by bt_foreach_bond
static void accept_list_bond_cb(const struct bt_bond_info *info, void *user_data)
{
int err = bt_le_filter_accept_list_add(&info->addr);
if (err) {
printk("Failed to add bond to accept list (err %d)\n", err);
} else {
printk("Added bonded device from Flash to Accept List\n");
}
}
// 2. Function to load ALL stored bonds into the Filter Accept List
void populate_accept_list_from_stored_bonds(void)
{
// Clear any stale accept list entries first
bt_le_filter_accept_list_clear();
// Iterate through all saved bonds and invoke the callback
bt_foreach_bond(BT_ID_DEFAULT, accept_list_bond_cb, NULL);
}
// 3. For NEW device pairing completion during runtime
static void pairing_complete(struct bt_conn *conn, bool bonded)
{
printk("Pairing completed. Bonded: %d\n", bonded);
if (bonded) {
// Extract connection peer address and add directly
const bt_addr_le_t *peer_addr = bt_conn_get_dst(conn);
if (peer_addr) {
int err = bt_le_filter_accept_list_add(peer_addr);
if (err) {
printk("Failed to add new pair to accept list (err %d)\n", err);
} else {
printk("Added new device to Accept List:%d\n",peer_addr->a.val[0]);
}
}
}
}
static void pairing_failed(struct bt_conn *conn,enum bt_security_err reason)
{
printk("Pairing failed (%d)\n", reason);
}
static struct bt_conn_auth_info_cb auth_info_cb = {
.pairing_complete = pairing_complete,
.pairing_failed = pairing_failed,
};
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) {
printk("Security changed for %s: level %u (Encryption Successful!)\n", addr, level);
} else {
printk("Security failed for %s: level %u err %d\n", addr, level, err);
/* IF security fails on a re-connection, the bond keys on mobile or lock
are out of sync. Unbond to allow a clean repair. */
if (err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING || err == 9) {
printk("Key mismatch! Clearing stale bond...\n");
bt_unpair(BT_ID_DEFAULT, bt_conn_get_dst(conn));
}
}
}
// Struct to pass context into the bond iterator
struct bond_check_ctx {
const bt_addr_le_t *target_addr;
bool is_bonded;
};
static void check_bond_cb(const struct bt_bond_info *info, void *user_data)
{
struct bond_check_ctx *ctx = (struct bond_check_ctx *)user_data;
// Compare destination address with stored bond identities
if (bt_addr_le_cmp(&info->addr, ctx->target_addr) == 0) {
ctx->is_bonded = true;
}
}
// Check if the connecting address matches a known bond identity in RAM
static bool is_device_bonded(const bt_addr_le_t *addr)
{
struct bond_check_ctx {
const bt_addr_le_t *target;
bool found;
} ctx = { .target = addr, .found = false };
auto check_cb = [](const struct bt_bond_info *info, void *user_data) {
auto *c = (struct bond_check_ctx *)user_data;
if (bt_addr_le_cmp(&info->addr, c->target) == 0) {
c->found = true;
}
};
bt_foreach_bond(BT_ID_DEFAULT, check_cb, &ctx);
return ctx.found;
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed (err %u)\n", err);
return;
}
const bt_addr_le_t *peer_addr = bt_conn_get_dst(conn);
printk("New peer address =%d",peer_addr);
if (is_device_bonded(peer_addr))
{
printk("Central is ALREADY BONDED! Elevating security to encrypt link...");
// Elevate security so stack encrypts using existing LTK
int sec_err = bt_conn_set_security(conn, BT_SECURITY_L1);
if (sec_err) {
printk("Failed to set security level (err %d)", sec_err);
}
} else {
printk("Central is NEW / UNBONDED. Waiting for phone to initiate pairing...");
/* DO NOT call bt_conn_set_security here! Let the mobile app initiate pairing */
}
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
//.disconnected = disconnected,
.security_changed = security_changed,
};
// Helper callback to populate accept list with existing bonds
// Helper function to dynamically start the 500 ms custom advertising
static void start_custom_advertising(void)
{
// Check if any bonded device exists
bt_foreach_bond(BT_ID_DEFAULT, accept_list_bond_cb, NULL);
// 2. Configure advertising set options to filter requests
struct bt_le_adv_param adv_param = *BT_LE_ADV_PARAM(
BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_FILTER_CONN,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2,
NULL
);
// // Define advertising parameters using our custom dynamic identity
// struct bt_le_adv_param adv_param = {
// .id = BT_ID_DEFAULT, // Use identity created from factory FICR MAC
// .sid = 0,
// .options = (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_USE_IDENTITY ),
// .interval_min = 800, // 500 ms (800 * 0.625 ms)
// .interval_max = 800, // 500 ms (800 * 0.625 ms)
// .peer = NULL,
// };
// Register this in your setup code:
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_info_cb_register(&auth_info_cb);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
settings_load(); // Restores keys from flash to RAM
}
populate_accept_list_from_stored_bonds();
int err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Custom BLE advertising failed to start (err %d)\n", err);
} else {
printk("Custom BLE advertising started successfully at 500ms intervals!\n");
}
}
// Helper function to extract factory MAC and set up Custom Identity
static void init_custom_ble_advertising(void)
{
// bt_addr_le_t addr;
// uint8_t mac_addr[6];
// // Fetch the 6-byte factory MAC address
// ssize_t ret = hwinfo_get_device_id(mac_addr, sizeof(mac_addr));
// if (ret > 0)
// {
// printk("nRF54L15 BLE MAC Address: %02X:%02X:%02X:%02X:%02X:%02X\n",mac_addr[5], mac_addr[4], mac_addr[3], mac_addr[2], mac_addr[1], mac_addr[0]);
// }
// else
// {
// printk("Failed to read MAC address from FICR.\n");
// }
// // Format as a valid Static Random address
// memcpy(addr.a.val, mac_addr, 6);
// addr.a.val[0] ^= 0xFF; // Flip the first byte so it doesn't match Matter's default identity
// addr.type = BT_ADDR_LE_RANDOM;
// addr.a.val[5] |= 0xC0;
bt_addr_le_t addr;
// 1. Set address type to LE Random
addr.type = BT_ADDR_LE_RANDOM;
// 2. Fill address bytes with payload/hash
sys_rand_get(addr.a.val, sizeof(addr.a.val));
// 3. Force byte 5 bits to 0b01xxxxxx (0x40 flag)
BT_ADDR_SET_RPA(&addr.a);
// 4. Verify formatting
if (bt_addr_le_is_rpa(&addr)) {
printk("Address formatted as a valid RPA structure.\n");
}
// // Create identity in Bluetooth stack (BT stack must already be enabled)
// int my_identity = bt_id_create(&addr, NULL);
// if (my_identity < 0) {
// printk("Failed to create identity from FICR MAC (err %d). Reverting to default ID.\n", my_identity);
// my_adv_identity = BT_ADDR_LE_PUBLIC_ID;
// } else {
// printk("Custom identity %d created using Chip FICR MAC.\n", my_identity);
// my_adv_identity = (uint8_t)my_identity;
// }
bt_passkey_set(static_passkey);
printk("Static Passkey set to: %06u\n", static_passkey);
// Start advertising our custom lock service
start_custom_advertising();
}Khodidas Domadiya