Setting MAC address fails with multirole BLE (Central+Peripheral)

I'm trying to use the nRF52840 with the nRF Connect SDK 2.2.0 to create a BLE device that supports both Central and Peripheral. This basically works, but there is a problem when trying to set a public MAC address. The function `bt_ctlr_set_public_addr()` works when I'm using either CONFIG_BT_CENTRAL or CONFIG_BT_PERIPHERAL, but seems to do nothing when using both CONFIG_BT_CENTRAL and CONFIG_BT_PERIPHERAL. In the latter case there are no error messages but when calling `bt_enable()` a static random address is used instead of the public address manually set. I can reproduce the error with following code:

# prj.conf
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_PERIPHERAL=y
# Some other stuff to enable Logging via RTT and Debugging

# main.c
void main(void) {
   static const uint8_t _dbg_mac[6] = {0x01, 0x00, 0x00, 0x25, 0x80, 0x00};
   bt_ctlr_set_public_addr(_dbg_mac);

   int err = bt_enable(NULL);
   if (err) {
      LOG_ERR("Bluetooth init failed (err %d)", err);
   }

   while (1) {
      k_sleep(K_MSEC(500));
   }
}
Some more information:
* `bt_ctlr_set_public_addr()` calls the correct function in `nrf/subsys/bluetooth/controller/hci_driver.c` and seems to call into the SoftDevice
* `bt_id_init()` in `zephyr/subsys/bluetooth/host/id.c` calls `bt_id_read_public_addr()` (via `bt_setup_public_id_addr()`) which then reads the address via HCI. The HCI command does not report an error and returns an all zero address. This sets `bt_dev.id_count` to zero.
* `bt_id_init()` then calls `bt_setup_random_id_addr()` because no public address was set. This sets the static random address.

The error does not occur if I only enable CONFIG_BT_CENTRAL or CONFIG_BT_PERIPHERAL. The error also does not occur in version 2.0.2 or 2.1.3 of the nRF Connect SDK, but does occur in 2.2.0 and 2.2.99-dev3.
  • I did some more testing and it seems that the error is triggered when using CONFIG_BT_PERIPHERAL with the multirole SoftDevice library. To test this I edited `nrfxlib/softdevice_controller/CMakeLists.txt` to manually select the SoftDevice library (changed `zephyr_link_libraries(...)` at the end). When using CONFIG_BT_PERIPHERAL and the peripheral library (`nrfxlib\softdevice_controller\lib\cortex-m4\soft-float\libsoftdevice_controller_peripheral.a`) the address gets set as expected. If I force the use of the multirole library instead (`nrfxlib\softdevice_controller\lib\cortex-m4\soft-float\libsoftdevice_controller_multirole.a`) the error occurs and the random address is used instead. I could not reproduce the error in other constellations (e.g. CONFIG_BT_CENTRAL and multirole lib). I also tried to both use CONFIG_BT_CENTRAL and CONFIG_BT_PERIPHERAL, while using both central and peripheral libraries at the same time, instead of the multirole lib. This seems to work as well (although I did not test full communication).

    To sum up my tests:
    * CONFIG_BT_CENTRAL + libsoftdevice_controller_central ==> No error
    * CONFIG_BT_PERIPHERAL + libsoftdevice_controller_peripheral ==> No error
    * CONFIG_BT_CENTRAL + libsoftdevice_controller_multirole ==> No error
    * CONFIG_BT_PERIPHERAL + libsoftdevice_controller_multirole ==> Error!
    * CONFIG_BT_CENTRAL + CONFIG_BT_PERIPHERAL + libsoftdevice_controller_multirole ==> Error!
    * CONFIG_BT_CENTRAL + CONFIG_BT_PERIPHERAL + libsoftdevice_controller_central + libsoftdevice_controller_peripheral ==> No error
  • Hi, 

    Are you able to set the public address as expected for those get "No error" in your tests?

    Regards,
    Amanda H.

  • Exactly. "No error" means the public address is set correctly and "error" means the static random address is used instead. I didn't try a complete communication with all these cases, only checked the output of `bt_enable()`.

  • Hi, 

    The team found that there is a bug in the stack if bt_ctlr_set_public_addr() is called before bt_enable() and will have a bug fixed later.  The workaround for this right now is setting the address after the bt_enable(), but before settigs_load (if you have it). Please let me know whether it can work or not. Thanks. 

    -Amanda H.  

  • The workaround does not seem to work. I use the following code to test this (CONFIG_BT_CENTRAL and CONFIG_BT_PERIPHERAL enabled; I'm currently not using `settings_load`):

    void main(void) {
       int err;
    
       err = bt_enable(NULL);
       if (err) {
          LOG_ERR("Bluetooth init failed (err %d)", err);
       }
    
       k_sleep(K_MSEC(3000));     // ensure that bt_enable is done; alternatively via bt_ready_cb
    
       static const uint8_t _dbg_mac[6] = {0x01, 0x00, 0x00, 0x25, 0x80, 0x00};
       bt_ctlr_set_public_addr(_dbg_mac);
    
       bt_addr_le_t addr[CONFIG_BT_ID_MAX];
       size_t count = CONFIG_BT_ID_MAX;
       char str_buffer[30];
       bt_id_get(addr, &count);
       for (size_t i = 0; i < count; i++) {
          bt_addr_le_to_str(&addr[i], str_buffer, sizeof(str_buffer));
          LOG_INF("ID %d: %s", (int) i, str_buffer);
       }
    
       static const struct bt_data ble_ad_default[] = {};
       static const struct bt_data ble_sd_default[] = {};
       err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ble_ad_default, ARRAY_SIZE(ble_ad_default), ble_sd_default, ARRAY_SIZE(ble_sd_default));
       if (err) {
          LOG_ERR("Advertising failed to start (err %d)", err);
       }
    }

    The id returned from `bt_id_get()` as well as the MAC used in advertising are still the static random address.
Related