Connection using old pairing information saved

I am running an experiment to use a static keys for pairing information without breaking the existing flow of pairing/bonding.
What I am doing is I am storing the pairing info of my central device and storing it into a structure and then erasing the flash to remove those information, and after reset I am retrieving those bonding information and connecting again using the same central devices but I am getting "Peer removed bonding info" response on my phone.

void store_ltk_in_zephyr(void)
{
    int id, err;
    struct bt_keys pairing_info = {
    .addr = {
        .type = BT_ADDR_LE_PUBLIC,
        .a.val = {0x43,0x82,0x5E,0xC7,0xE8,0xF4,0xFD}
    },
    .irk = {0x8A, 0x27, 0x1E, 0xA7, 0x92, 0x2A, 0xF0, 0x15, 0x41, 0x69, 0x48, 0xDD, 0xC0, 0x7E, 0xDD, 0xF7},
    .ltk = {
        .val = {0xc0, 0xe6, 0x9a, 0x0a, 0xf7, 0x4b, 0xdc, 0xb7, 0x7d, 0x23, 0xf4, 0xb4, 0x89, 0x8d, 0x96, 0x02},
        .ediv = {0x00,0x00},
        .rand = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
      }
    };

    // Store the keys using the settings API
    err = bt_keys_store(&pairing_info);
    if (err) {
        printk("Failed to store keys (err %d)\n", err);
    } else {
        printk("Keys stored successfully\n");
    }
}


In main, I am calling like this to load the info onto the flash- 

  settings_subsys_init();
  //smp_bt_register();
	bt_conn_auth_cb_register(&auth_cb_display);
  bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
    k_sleep(K_MSEC(100));
		err = bt_enable(NULL);                                      //Trying one more time after this system reset
    if(err) NVIC_SystemReset();
	}
  store_ltk_in_zephyr();
  if (IS_ENABLED(CONFIG_SETTINGS)) {
    settings_load();
  }


And on connect I am using like this-
  bt_conn_set_security(conn, BT_SECURITY_L1|BT_SECURITY_FORCE_PAIR);


Do let me know, how can I connect with the same bonding info which I have made a copy of from the same bonding structure.

Parents
  • Hello,

    To troubleshoot this, I recommend you start by enabling CONFIG_BT_LOG_SNIFFER_INFO to have the keys printed on boot. This would help confirm us confirm if they keys were successfully stored or not.

    Best regards,

    Vidar

  • I am reading the pairing info using nrfjprog --memrd 0xfe000 -n 1024, as I know its taking the info from the settings page and the address of the page is this -

    Not getting any logs related to bonding on boot using CONFIG_BT_LOG_SNIFFER_INFO.

  •  I have made it work now, whole copy LTK,EDIV and RAND endianess was the issue.

    But after this device got conencted but there is one more major issue, services and characteristics are still showing up like nRFSDK one, as in the order of services and characteristics are different on nRFSDK version and the one on Zephyr.
    But the UUIDs are same, with this issue if I am trying to read the relevant values then I am seeing garbage values.

    Two of the services which were meant for only nRF5SDK versions i.e., Buttonloess DFU are still visible on the Zephyr version, I think the services are not getting refreshed.

  • This is what is known as attribute caching. Basically the attribute table from the previous application has been cached by the your GATT Client. About attribute caching from the Core spec:

    Did you existing nRF5 SDK based application include the Service Changed characteristic? In that case, you should be able to send a service changed indication to trigger the client to perform a new service discovery. 

  • Did you existing nRF5 SDK based application include the Service Changed characteristic? In that case, you should be able to send a service changed indication to trigger the client to perform a new service discovery. 

    Yes we have one.
    What is the API for indicating the service change, I didn`t find any online and have enabled the CONFIG_BT_GATT_SERVICE_CHANGED=y also.

  •  I have implemented this part too but nRF Connect application on iOS is not changing the discovery

    static void sc_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
    {
        bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
        printk("Service Changed notifications %s\n", notif_enabled ? "enabled" : "disabled");
    }
    
    BT_GATT_SERVICE_DEFINE(sc_svc,
        BT_GATT_PRIMARY_SERVICE(BT_UUID_GATT),
        BT_GATT_CHARACTERISTIC(BT_UUID_GATT_SC, BT_GATT_CHRC_INDICATE, BT_GATT_PERM_NONE,
                               NULL, NULL, sc_value),
        BT_GATT_CCC(sc_ccc_cfg_changed,BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
    );
    
    void notify_service_changed(struct bt_conn *conn)
    {
        //struct bt_conn *conn = NULL;  // Update this to point to the correct connection if needed
        uint16_t start_handle = 0x0001; // Start handle of the affected range
        uint16_t end_handle = 0xFFFF;   // End handle of the affected range
    
        sc_value[0] = start_handle & 0xFF;
        sc_value[1] = (start_handle >> 8) & 0xFF;
        sc_value[2] = end_handle & 0xFF;
        sc_value[3] = (end_handle >> 8) & 0xFF;
    
        int err = bt_gatt_notify(conn, &sc_svc.attrs[1], sc_value, sizeof(sc_value));
        if (err) {
            printk("Service Changed notification failed (err %d)\n", err);
        } else {
            printk("Service Changed notification sent\n");
        }
    }

  •  I have tried to expose the sc_indicate(0x00010xffff); and use it on the connected callback so that the services get refreshed but dont see anything changing on nRF Connect application on iOS with these changes.

    static void connected(struct bt_conn *conn, uint8_t err) {
      char addr[BT_ADDR_LE_STR_LEN];
      const bt_addr_le_t *addr_info = bt_conn_get_dst(conn);
      sc_indicate(0x0001, 0xffff);
      start_notif_timer();                                        //Starting timer to notfy for battery data after 5 seconds
      atomic_set(&is_advertising,0);
      atomic_set(&is_connected,1);
      wdt_wdtFeed();
      k_timer_stop(&fast_adv_timer);
      k_timer_start(&wdttimer_feed, K_SECONDS(AVDERTISING_UPDATE_SEC), K_SECONDS(AVDERTISING_UPDATE_SEC));
      k_timer_stop(&advertising_timer);
      //Checking connection count and referencing local variable around that
      if(!local_connection){
        local_connection = bt_conn_ref(conn);
      }
      if (is_first_conn)    hrm_sensor_indication_off();        //Coming out of first time connection indication
      static struct bt_gatt_exchange_params exchange_params;
    	exchange_params.func = exchange_func;
    	int res = bt_gatt_exchange_mtu(conn, &exchange_params);
    	if (err) {
    		printk("Connection failed (err 0x%02x)\n", err);
    	} else {
    		printk("Connected\n");
    	}
      // if (bt_conn_set_security(conn, BT_SECURITY_L1)) {
    	// 	printk("Failed to set security : %d\n", bonded_device_count); 
    	// }
      // bt_set_bondable(true);
    }

Reply
  •  I have tried to expose the sc_indicate(0x00010xffff); and use it on the connected callback so that the services get refreshed but dont see anything changing on nRF Connect application on iOS with these changes.

    static void connected(struct bt_conn *conn, uint8_t err) {
      char addr[BT_ADDR_LE_STR_LEN];
      const bt_addr_le_t *addr_info = bt_conn_get_dst(conn);
      sc_indicate(0x0001, 0xffff);
      start_notif_timer();                                        //Starting timer to notfy for battery data after 5 seconds
      atomic_set(&is_advertising,0);
      atomic_set(&is_connected,1);
      wdt_wdtFeed();
      k_timer_stop(&fast_adv_timer);
      k_timer_start(&wdttimer_feed, K_SECONDS(AVDERTISING_UPDATE_SEC), K_SECONDS(AVDERTISING_UPDATE_SEC));
      k_timer_stop(&advertising_timer);
      //Checking connection count and referencing local variable around that
      if(!local_connection){
        local_connection = bt_conn_ref(conn);
      }
      if (is_first_conn)    hrm_sensor_indication_off();        //Coming out of first time connection indication
      static struct bt_gatt_exchange_params exchange_params;
    	exchange_params.func = exchange_func;
    	int res = bt_gatt_exchange_mtu(conn, &exchange_params);
    	if (err) {
    		printk("Connection failed (err 0x%02x)\n", err);
    	} else {
    		printk("Connected\n");
    	}
      // if (bt_conn_set_security(conn, BT_SECURITY_L1)) {
    	// 	printk("Failed to set security : %d\n", bonded_device_count); 
    	// }
      // bt_set_bondable(true);
    }

Children
  •  
    I just tried the above flow with an android phone, and the flow works on it. After manually calling "Refresh Services" got the correct order of the services and characteristics.
    In this one I saw GATT ATTRIBUTE (0x1801) which contains the services changed characteristics to be present twice. 

    And on iOS I am still stuck on the same page where the services are still cached and seeing on nRF Connect application, my custom application is stuck in connection as its trying to read some info from the fixed UUIDs and it reads those values to be garbage.

  • iOS does not expose any APIs to the app to force service discovery like Android. Therefore, the only way to manually clear the attribute cache is to toggle Bluetooth enable from settings, or reboot the phone.

    Here is an updated version of my code which attempts to send the SC indication on the first connection. I verified that it worked with my iPhone.

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample
     */
    #include "uart_async_adapter.h"
    
    #include <zephyr/types.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/uart.h>
    #include <zephyr/usb/usb_device.h>
    
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <soc.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/addr.h>
    #include <zephyr/sys/byteorder.h>
    
    /* BLE internal headers, use with caution. */
    #include <host/keys.h>
    #include <host/smp.h>
    
    #include <bluetooth/services/nus.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/settings/settings.h>
    
    #include <stdio.h>
    
    #include <zephyr/logging/log.h>
    
    #define LOG_MODULE_NAME peripheral_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    #define STACKSIZE CONFIG_BT_NUS_THREAD_STACK_SIZE
    #define PRIORITY 7
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN	(sizeof(DEVICE_NAME) - 1)
    
    #define RUN_STATUS_LED DK_LED1
    #define RUN_LED_BLINK_INTERVAL 1000
    
    #define CON_STATUS_LED DK_LED2
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    #define UART_BUF_SIZE CONFIG_BT_NUS_UART_BUFFER_SIZE
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_WAIT_FOR_RX CONFIG_BT_NUS_UART_RX_WAIT_TIME
    
    static K_SEM_DEFINE(ble_init_ok, 0, 1);
    static bool pre_shared_bond_applied;
    static void service_changed_work_handle(struct k_work *item);
    static K_WORK_DEFINE(sc_work, service_changed_work_handle);
    
    static struct bt_conn *current_conn;
    static struct bt_conn *auth_conn;
    
    static const struct device *uart = DEVICE_DT_GET(DT_CHOSEN(nordic_nus_uart));
    static struct k_work_delayable uart_work;
    
    struct uart_data_t {
    	void *fifo_reserved;
    	uint8_t data[UART_BUF_SIZE];
    	uint16_t len;
    };
    
    static K_FIFO_DEFINE(fifo_uart_tx_data);
    static K_FIFO_DEFINE(fifo_uart_rx_data);
    
    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, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static const struct bt_data sd[] = {
    	BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
    };
    
    #if CONFIG_BT_NUS_UART_ASYNC_ADAPTER
    UART_ASYNC_ADAPTER_INST_DEFINE(async_adapter);
    #else
    static const struct device *const async_adapter;
    #endif
    
    static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
    {
    	ARG_UNUSED(dev);
    
    	static size_t aborted_len;
    	struct uart_data_t *buf;
    	static uint8_t *aborted_buf;
    	static bool disable_req;
    
    	switch (evt->type) {
    	case UART_TX_DONE:
    		LOG_DBG("UART_TX_DONE");
    		if ((evt->data.tx.len == 0) ||
    		    (!evt->data.tx.buf)) {
    			return;
    		}
    
    		if (aborted_buf) {
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    					   data[0]);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		} else {
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    					   data[0]);
    		}
    
    		k_free(buf);
    
    		buf = k_fifo_get(&fifo_uart_tx_data, K_NO_WAIT);
    		if (!buf) {
    			return;
    		}
    
    		if (uart_tx(uart, buf->data, buf->len, SYS_FOREVER_MS)) {
    			LOG_WRN("Failed to send data over UART");
    		}
    
    		break;
    
    	case UART_RX_RDY:
    		LOG_DBG("UART_RX_RDY");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data[0]);
    		buf->len += evt->data.rx.len;
    
    		if (disable_req) {
    			return;
    		}
    
    		if ((evt->data.rx.buf[buf->len - 1] == '\n') ||
    		    (evt->data.rx.buf[buf->len - 1] == '\r')) {
    			disable_req = true;
    			uart_rx_disable(uart);
    		}
    
    		break;
    
    	case UART_RX_DISABLED:
    		LOG_DBG("UART_RX_DISABLED");
    		disable_req = false;
    
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    			       UART_WAIT_FOR_RX);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		LOG_DBG("UART_RX_BUF_REQUEST");
    		buf = k_malloc(sizeof(*buf));
    		if (buf) {
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		} else {
    			LOG_WRN("Not able to allocate UART receive buffer");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		LOG_DBG("UART_RX_BUF_RELEASED");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    				   data[0]);
    
    		if (buf->len > 0) {
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		} else {
    			k_free(buf);
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    		LOG_DBG("UART_TX_ABORTED");
    		if (!aborted_buf) {
    			aborted_buf = (uint8_t *)evt->data.tx.buf;
    		}
    
    		aborted_len += evt->data.tx.len;
    		buf = CONTAINER_OF((void *)aborted_buf, struct uart_data_t,
    				   data);
    
    		uart_tx(uart, &buf->data[aborted_len],
    			buf->len - aborted_len, SYS_FOREVER_MS);
    
    		break;
    
    	default:
    		break;
    	}
    }
    
    static void uart_work_handler(struct k_work *item)
    {
    	struct uart_data_t *buf;
    
    	buf = k_malloc(sizeof(*buf));
    	if (buf) {
    		buf->len = 0;
    	} else {
    		LOG_WRN("Not able to allocate UART receive buffer");
    		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    		return;
    	}
    
    	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_WAIT_FOR_RX);
    }
    
    static bool uart_test_async_api(const struct device *dev)
    {
    	const struct uart_driver_api *api =
    			(const struct uart_driver_api *)dev->api;
    
    	return (api->callback_set != NULL);
    }
    
    static int uart_init(void)
    {
    	int err;
    	int pos;
    	struct uart_data_t *rx;
    	struct uart_data_t *tx;
    
    	if (!device_is_ready(uart)) {
    		return -ENODEV;
    	}
    
    	if (IS_ENABLED(CONFIG_USB_DEVICE_STACK)) {
    		err = usb_enable(NULL);
    		if (err && (err != -EALREADY)) {
    			LOG_ERR("Failed to enable USB");
    			return err;
    		}
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx) {
    		rx->len = 0;
    	} else {
    		return -ENOMEM;
    	}
    
    	k_work_init_delayable(&uart_work, uart_work_handler);
    
    
    	if (IS_ENABLED(CONFIG_BT_NUS_UART_ASYNC_ADAPTER) && !uart_test_async_api(uart)) {
    		/* Implement API adapter */
    		uart_async_adapter_init(async_adapter, uart);
    		uart = async_adapter;
    	}
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err) {
    		k_free(rx);
    		LOG_ERR("Cannot initialize UART callback");
    		return err;
    	}
    
    	if (IS_ENABLED(CONFIG_UART_LINE_CTRL)) {
    		LOG_INF("Wait for DTR");
    		while (true) {
    			uint32_t dtr = 0;
    
    			uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr);
    			if (dtr) {
    				break;
    			}
    			/* Give CPU resources to low priority threads. */
    			k_sleep(K_MSEC(100));
    		}
    		LOG_INF("DTR set");
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DCD, 1);
    		if (err) {
    			LOG_WRN("Failed to set DCD, ret code %d", err);
    		}
    		err = uart_line_ctrl_set(uart, UART_LINE_CTRL_DSR, 1);
    		if (err) {
    			LOG_WRN("Failed to set DSR, ret code %d", err);
    		}
    	}
    
    	tx = k_malloc(sizeof(*tx));
    
    	if (tx) {
    		pos = snprintf(tx->data, sizeof(tx->data),
    			       "Starting Nordic UART service example\r\n");
    
    		if ((pos < 0) || (pos >= sizeof(tx->data))) {
    			k_free(rx);
    			k_free(tx);
    			LOG_ERR("snprintf returned %d", pos);
    			return -ENOMEM;
    		}
    
    		tx->len = pos;
    	} else {
    		k_free(rx);
    		return -ENOMEM;
    	}
    
    	err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    	if (err) {
    		k_free(rx);
    		k_free(tx);
    		LOG_ERR("Cannot display welcome message (err: %d)", err);
    		return err;
    	}
    
    	err = uart_rx_enable(uart, rx->data, sizeof(rx->data), UART_WAIT_FOR_RX);
    	if (err) {
    		LOG_ERR("Cannot enable uart reception (err: %d)", err);
    		/* Free the rx buffer only because the tx buffer will be handled in the callback */
    		k_free(rx);
    	}
    
    	return err;
    }
    
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    	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);
    
    	current_conn = bt_conn_ref(conn);
    
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    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);
    
    	if (auth_conn) {
    		bt_conn_unref(auth_conn);
    		auth_conn = NULL;
    	}
    
    	if (current_conn) {
    		bt_conn_unref(current_conn);
    		current_conn = NULL;
    		dk_set_led_off(CON_STATUS_LED);
    	}
    
    	/* Clear flag for subsequent connections */
    	pre_shared_bond_applied = false;
    }
    static void sc_indicate_rsp(struct bt_conn *conn,
                        struct bt_gatt_indicate_params *params,
                        uint8_t err)
    {
        LOG_INF("sc_indicate_rsp received. (err %d)", err);
    }
    
    void service_changed_work_handle(struct k_work *item)
    {
    	int err;
    	static struct bt_gatt_indicate_params indicate_params;
    	/* Harcode the attribute handle for SC characteristic from nRF5 SDK application (12 in this case).
    	 * CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION must be disabled in project configuration
    	 * to allow the indication to be sent, as the Zephyr host has know way
    	 * of knowing if the client was subscribed to this characteristic. 
    	 * 
    	 * IMPORTANT: consider if disabling CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION
    	 * has security implications for your application.
    	 */
    	static struct bt_gatt_attr attr = {.handle = 12};
    	static uint16_t sc_range[2];
    
    	sc_range[0] = sys_cpu_to_le16(0x0001);
    	sc_range[1] = 0xffff;
    
    	indicate_params.attr = &attr;
    	indicate_params.func = sc_indicate_rsp;
    	indicate_params.data = &sc_range[0];
    	indicate_params.len = sizeof(sc_range);
    
    	err = bt_gatt_indicate(current_conn, &indicate_params);
    	if (err) {
    		LOG_ERR("Failed to send SC indication. (err %d)", err);
    	}
    }
    
    #ifdef CONFIG_BT_NUS_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_INF("Security changed: %s level %u", addr, level);
    		if (pre_shared_bond_applied) {
    			/* Inform GATT client that the attribute table has changed by sending a
    			 * SC changed indication.
    			 */
    			k_work_submit(&sc_work);
    		}
    	} else {
    		LOG_WRN("Security failed: %s level %u err %d", addr,
    			level, err);
    	}
    }
    #endif
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected    = connected,
    	.disconnected = disconnected,
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	.security_changed = security_changed,
    #endif
    };
    
    #if defined(CONFIG_BT_NUS_SECURITY_ENABLED)
    
    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_INF("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_INF("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_INF("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
    
    static void bt_receive_cb(struct bt_conn *conn, const uint8_t *const data,
    			  uint16_t len)
    {
    	int err;
    	char addr[BT_ADDR_LE_STR_LEN] = {0};
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, ARRAY_SIZE(addr));
    
    	LOG_INF("Received data from: %s", addr);
    
    	for (uint16_t pos = 0; pos != len;) {
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx) {
    			LOG_WRN("Not able to allocate UART send data buffer");
    			return;
    		}
    
    		/* Keep the last byte of TX buffer for potential LF char. */
    		size_t tx_data_size = sizeof(tx->data) - 1;
    
    		if ((len - pos) > tx_data_size) {
    			tx->len = tx_data_size;
    		} else {
    			tx->len = (len - pos);
    		}
    
    		memcpy(tx->data, &data[pos], tx->len);
    
    		pos += tx->len;
    
    		/* Append the LF character when the CR character triggered
    		 * transmission from the peer.
    		 */
    		if ((pos == len) && (data[len - 1] == '\r')) {
    			tx->data[tx->len] = '\n';
    			tx->len++;
    		}
    
    		err = uart_tx(uart, tx->data, tx->len, SYS_FOREVER_MS);
    		if (err) {
    			k_fifo_put(&fifo_uart_tx_data, tx);
    		}
    	}
    }
    
    static struct bt_nus_cb nus_cb = {
    	.received = bt_receive_cb,
    };
    
    void error(void)
    {
    	dk_set_leds_state(DK_ALL_LEDS_MSK, DK_NO_LEDS_MSK);
    
    	while (true) {
    		/* Spin for ever */
    		k_sleep(K_MSEC(1000));
    	}
    }
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept) {
    		bt_conn_auth_passkey_confirm(auth_conn);
    		LOG_INF("Numeric Match, conn %p", (void *)auth_conn);
    	} else {
    		bt_conn_auth_cancel(auth_conn);
    		LOG_INF("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_BT_NUS_SECURITY_ENABLED */
    
    static void configure_gpio(void)
    {
    	int err;
    
    #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    	err = dk_buttons_init(button_changed);
    	if (err) {
    		LOG_ERR("Cannot init buttons (err: %d)", err);
    	}
    #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err) {
    		LOG_ERR("Cannot init LEDs (err: %d)", err);
    	}
    }
    
    static void bond_cb(const struct bt_bond_info *info, void *user_data)
    {
    	ARG_UNUSED(info);
    
    	*(int *) user_data += 1;
    	LOG_INF("Existing bond found");
    
    	return;
    }
    
    static void pre_shared_bond_set(void)
    {
    	int err;
    	int bond_num = 0;
    	struct bt_keys *pairing_info;
    
    	// true if pm_peer_data_bonding.own_ltk.enc_info.ltk.lesc == 0
    	bool legacy_pairing = true;
    
    	bt_foreach_bond(BT_ID_DEFAULT, bond_cb, &bond_num);
    
    	if (bond_num) {
    		return;
    	}
    
    	//pm_peer_data_bonding.peer_ble_id.id_addr_info
    	bt_addr_le_t addr = {
    	    .type = BT_ADDR_LE_PUBLIC,
    	    .a.val = {0x23,...}
    	};
    
    	//pm_peer_data_bonding.own_ltk.enc_info.ltk
    	struct bt_ltk ltk = {
    	    .val = {0x91, 0x1a, 0x96, 0x6d, 0xe8, 0x69, 0x0e, 0x78, 0x56, 0x93, 0x86,
    	            0x0c, 0x9b, 0x43, 0x49, 0xee},
    	    .ediv = {0xb8,0x78},
    	    .rand = {0x36, 0x7b, 0xad, 0x8e, 0x31, 0xb4, 0x2c, 0xab}		
    	};
    	
    	//pm_peer_data_bonding.peer_ble_id.id_addr_info
    	struct bt_irk irk = {
    		.val = {0xd6,...},
    		.rpa.val = {0}		
    	}; 
    
    	pairing_info = bt_keys_get_addr(0, &addr);
    	if (pairing_info == NULL) {
    		LOG_ERR("Failed to get keyslot");
    		return;
    	}
    
    	if (legacy_pairing) {
    		memcpy(&pairing_info->periph_ltk, &ltk, sizeof(pairing_info->periph_ltk));
    	} 
    	else {
    		memcpy(&pairing_info->ltk, &ltk, sizeof(pairing_info->ltk));
    	}
    	
    	memcpy(&pairing_info->irk, &irk, sizeof(pairing_info->irk));
    
    	if (legacy_pairing) {
    		pairing_info->flags = 0;
    		pairing_info->enc_size = BT_SMP_MAX_ENC_KEY_SIZE;
    		pairing_info->keys = BT_KEYS_IRK | BT_KEYS_PERIPH_LTK;
    	}
    	else {
    		pairing_info->flags = BT_KEYS_SC;
    		pairing_info->enc_size = BT_SMP_MAX_ENC_KEY_SIZE;
    		pairing_info->keys = BT_KEYS_IRK | BT_KEYS_LTK_P256;
    	}
    
    	err = bt_keys_store(pairing_info);
        if (err) {
            LOG_ERR("Failed to store keys (err %d)\n", err);
        } else {
            LOG_INF("Keys stored successfully\n");
    		pre_shared_bond_applied = true;
        }
    }
    
    int main(void)
    {
    	int blink_status = 0;
    	int err = 0;
    
    	configure_gpio();
    
    	err = uart_init();
    	if (err) {
    		error();
    	}
    
    	if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) {
    		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    		if (err) {
    			printk("Failed to register authorization callbacks.\n");
    			return 0;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err) {
    			printk("Failed to register authorization info callbacks.\n");
    			return 0;
    		}
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		error();
    	}
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    	
    	pre_shared_bond_set();
    
    	err = bt_nus_init(&nus_cb);
    	if (err) {
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return 0;
    	}
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    			      ARRAY_SIZE(sd));
    	if (err) {
    		LOG_ERR("Advertising failed to start (err %d)", err);
    		return 0;
    	}
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }
    
    void ble_write_thread(void)
    {
    	/* Don't go any further until BLE is initialized */
    	k_sem_take(&ble_init_ok, K_FOREVER);
    
    	for (;;) {
    		/* Wait indefinitely for data to be sent over bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    						     K_FOREVER);
    
    		if (bt_nus_send(NULL, buf->data, buf->len)) {
    			LOG_WRN("Failed to send data over BLE connection");
    		}
    
    		k_free(buf);
    	}
    }
    
    K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL,
    		NULL, PRIORITY, 0, 0);
    

  •  I am getting error -22 from bt_gatt_indicate ,Invalid argument. On Android phones, discover services is doing the trick but on iOS phone I have to do a phone restart to get the GATT cache cleared.

    If toggling bluetooth button is also a possibility then that will be a better solution for this issue. Please do suggest on this.

    BTW I have also enabled CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION as suggested.

    00> [00:00:12.531,250] <wrn> bt_gatt: Device is not subscribed to characteristic
    00> Failed to send SC indication. (err -22)

  • Please verify that CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION got selected by checking the generated config in build/zephyr/.config. The error suggests that it is not.

    If toggling bluetooth button is also a possibility then that will be a better solution for this issue. Please do suggest on this.

    This requires the user to go to settings->bluetooth and turn bluetooth off and on.

  • Please verify that CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION got selected by checking the generated config in build/zephyr/.config. The error suggests that it is not.

    Yes  its selected.

    This requires the user to go to settings->bluetooth and turn bluetooth off and on.

    Tried this but didn`t worked

Related