BLE pairing issue when BT_SECURITY_L4 with nRF5340

Hi,

I'm having the same problem as in:

BLE pairing issue when BT_SECURITY_L4 with nRF5340 - Nordic Q&A - Nordic DevZone - Nordic DevZone (nordicsemi.com)

I would also like to ask for a kind of general "recipe" that always works to recover if the Bluetooth stack is stuck somewhere.

The goal would be to remove all left overs from previous connections and to make it possible to start scanning again

for devices that are advertising.

Kind regards,

Arno

  • Hello,

    Is the reason for this that the connected device has bonding information about your device stored? Or vice versa? To check, if you delete bonding information on both sides, does the issue go away? If you are not sure how to delete bonding information, please let me know. And if that was in fact the case, which device had old bonding data stored, and which one did not?

    Best regards,

    Edvin

  • Hi,

    I'm basically using the central and peripheral uart example with nRF Connect SDK 2.3.0.

    I have cleared the bonding information in the following way on the peripheral uart device:


                // To notify the central to remove the pairing, pairing has to be removed
                // on both sides, this seems the only way to notify the central and to
                // remove the pairing.
                int err = bt_conn_disconnect(current_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
                // int err = bt_conn_disconnect(current_conn, BT_HCI_ERR_PAIRING_NOT_ALLOWED);
                printk("button_changed(): bt_conn_disconnect()=%d called\n", err);

                err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
                printk("button_changed(): bt_unpair=%d\n", err);

                // Stop the advertising, take this device out of the air
                err = bt_le_adv_stop();
                printk("button_changed(): bt_le_adv_stop=%d\n", err);

                // Now really stop everything, sadly this is not supported
                err = bt_disable();
                printk("button_changed(): bt_disable=%d, -134==ENOTSUP\n", err);

     I have cleared the bonding information on the central uart device as follows in the connected() routine:

        // BT_HCI_ERR_REMOTE_USER_TERM_CONN is a normal termination from the peripheral
        // BT_HCI_ERR_PAIRING_NOT_ALLOWED is not usable, does not work on peripheral side
        if (reason == BT_HCI_ERR_REMOTE_USER_TERM_CONN)
        {
            // The peripheral has disconnected, we use this to unpair
            int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
            printk("disconnected: bt_unpair=%d\n", err);
        }

        err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
        printk("disconnected: bt_scan_start=%d\n", err);
        if (err)
        {
            printk("disconnected: Scanning failed to start (err %d)\n",
                   err);
        }

    Please inform me whether this is the correct way to remove the bonding information. To my knowledge unpair()

    should be enough, or are there other ways?

    Any help greatly appreciated,

    Arno

  • Hi,

    Above problem can be reproduced with the following code. This code is more or less the central_uart example and peripheral_uart example with the extension in the central_uart example of the authorisation passkey display and confirm. This makes it possible to go to SECURITY_L4.

    The example has also been modified to make the LE connection under control of the scan_filter_match() function, so that it is possible to trace the error codes.

    Consider the following scenario:

    - Take 1 central uart and 2 peripheral uart devices. This can be done with 3 nRF5340 development kits and nRF Connect SDK 2.3.0

    - Switch on the central uart device, notice that it will start scanning

    - Switch on the first peripheral uart devices, notice that the 2 devices find each other and make a connection

    - There is no need to push the buttons to pair, but it can also be done

    - Now switch off the first peripheral device and switch on the second device.

    - Notice that the central is not able to make a connection with the second peripheral due to "a valid connection in disconnected state". Error code -12 or -22 is returned by bt_conn_le_create().

    - If we set the following variable CONFIG_BT_MAX_CONN=2, the above problem can be reproduced by 3 times switching the devices. So after the above procedure switch off the 2nd device and switch on the 1st device again. So the problem can be moved into the future a little bit. Maybe this will be our temporary work around.

    Any help very welcome, the code is attached.

    Kind regards,

    Arno

    Code on the central:

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Service Client sample (Central Uart)
     */
    
    #include <errno.h>
    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/devicetree.h>
    #include <zephyr/sys/byteorder.h>
    #include <zephyr/sys/printk.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>
    
    #include <bluetooth/services/nus.h>
    #include <bluetooth/services/nus_client.h>
    #include <bluetooth/gatt_dm.h>
    #include <bluetooth/scan.h>
    
    #include <dk_buttons_and_leds.h>
    
    #include <zephyr/settings/settings.h>
    
    #include <zephyr/drivers/uart.h>
    
    #include <zephyr/logging/log.h>
    
    #define LOG_MODULE_NAME central_uart
    LOG_MODULE_REGISTER(LOG_MODULE_NAME);
    
    /* UART payload buffer element size. */
    #define UART_BUF_SIZE 20
    
    #define KEY_PASSKEY_ACCEPT DK_BTN1_MSK
    #define KEY_PASSKEY_REJECT DK_BTN2_MSK
    
    // #define RUN_STATUS_LED DK_LED1
    #define RUN_STATUS_LED DK_LED4 // pause-resume led - green
    #define RUN_LED_BLINK_INTERVAL 1000
    
    // #define CON_STATUS_LED DK_LED2
    #define CON_STATUS_LED DK_LED3 // bleactivestatusled - blue
    
    #define NUS_WRITE_TIMEOUT K_MSEC(150)
    #define UART_WAIT_FOR_BUF_DELAY K_MSEC(50)
    #define UART_RX_TIMEOUT 50
    
    static const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
    static struct k_work_delayable uart_work;
    
    K_SEM_DEFINE(nus_write_sem, 0, 1);
    
    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 struct bt_conn *default_conn = NULL;
    static struct bt_conn *auth_conn = NULL;
    static struct bt_nus_client nus_client;
    
    int default_conn_cnt = 0;
    int default_conn_ref_cnt = 0;
    int default_conn_unref_cnt = 0;
    
    int auth_conn_cnt = 0;
    int auth_conn_ref_cnt = 0;
    int auth_conn_unref_cnt = 0;
    
    int connected_called = 0;
    int disconnected_called = 0;
    
    static void ble_data_sent(struct bt_nus_client *nus, uint8_t err,
    						  const uint8_t *const data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	struct uart_data_t *buf;
    
    	/* Retrieve buffer context. */
    	buf = CONTAINER_OF(data, struct uart_data_t, data);
    	k_free(buf);
    
    	k_sem_give(&nus_write_sem);
    
    	if (err)
    	{
    		printk("ATT error code: 0x%02X\n", err);
    	}
    }
    
    static uint8_t ble_data_received(struct bt_nus_client *nus,
    								 const uint8_t *data, uint16_t len)
    {
    	ARG_UNUSED(nus);
    
    	int err;
    
    	for (uint16_t pos = 0; pos != len;)
    	{
    		struct uart_data_t *tx = k_malloc(sizeof(*tx));
    
    		if (!tx)
    		{
    			printk("Not able to allocate UART send data buffer\n");
    			return BT_GATT_ITER_CONTINUE;
    		}
    
    		/* 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);
    		}
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    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:
    		printk("UART_TX_DONE\n");
    		if ((evt->data.tx.len == 0) ||
    			(!evt->data.tx.buf))
    		{
    			return;
    		}
    
    		if (aborted_buf)
    		{
    			buf = CONTAINER_OF(aborted_buf, struct uart_data_t,
    							   data);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		}
    		else
    		{
    			buf = CONTAINER_OF(evt->data.tx.buf,
    							   struct uart_data_t,
    							   data);
    		}
    
    		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))
    		{
    			printk("Failed to send data over UART\n");
    		}
    
    		break;
    
    	case UART_RX_RDY:
    		printk("UART_RX_RDY\n");
    		buf = CONTAINER_OF(evt->data.rx.buf, struct uart_data_t, data);
    		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:
    		printk("UART_RX_DISABLED\n");
    		disable_req = false;
    
    		buf = k_malloc(sizeof(*buf));
    		if (buf)
    		{
    			buf->len = 0;
    		}
    		else
    		{
    			printk("Not able to allocate UART receive buffer\n");
    			k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    			return;
    		}
    
    		uart_rx_enable(uart, buf->data, sizeof(buf->data),
    					   UART_RX_TIMEOUT);
    
    		break;
    
    	case UART_RX_BUF_REQUEST:
    		printk("UART_RX_BUF_REQUEST\n");
    		buf = k_malloc(sizeof(*buf));
    		if (buf)
    		{
    			buf->len = 0;
    			uart_rx_buf_rsp(uart, buf->data, sizeof(buf->data));
    		}
    		else
    		{
    			printk("Not able to allocate UART receive buffer\n");
    		}
    
    		break;
    
    	case UART_RX_BUF_RELEASED:
    		printk("UART_RX_BUF_RELEASED\n");
    		buf = CONTAINER_OF(evt->data.rx_buf.buf, struct uart_data_t,
    						   data);
    
    		if (buf->len > 0)
    		{
    			k_fifo_put(&fifo_uart_rx_data, buf);
    		}
    		else
    		{
    			k_free(buf);
    		}
    
    		break;
    
    	case UART_TX_ABORTED:
    		printk("UART_TX_ABORTED\n");
    		if (!aborted_buf)
    		{
    			aborted_buf = (uint8_t *)evt->data.tx.buf;
    		}
    
    		aborted_len += evt->data.tx.len;
    		buf = CONTAINER_OF(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
    	{
    		printk("uart_work_handler: Not able to allocate UART receive buffer\n");
    		k_work_reschedule(&uart_work, UART_WAIT_FOR_BUF_DELAY);
    		return;
    	}
    
    	uart_rx_enable(uart, buf->data, sizeof(buf->data), UART_RX_TIMEOUT);
    }
    
    static int uart_init(void)
    {
    	int err;
    	struct uart_data_t *rx;
    
    	if (!device_is_ready(uart))
    	{
    		printk("uart_init: UART device not ready\n");
    		return -ENODEV;
    	}
    
    	rx = k_malloc(sizeof(*rx));
    	if (rx)
    	{
    		rx->len = 0;
    	}
    	else
    	{
    		return -ENOMEM;
    	}
    
    	k_work_init_delayable(&uart_work, uart_work_handler);
    
    	err = uart_callback_set(uart, uart_cb, NULL);
    	if (err)
    	{
    		return err;
    	}
    
    	return uart_rx_enable(uart, rx->data, sizeof(rx->data),
    						  UART_RX_TIMEOUT);
    }
    
    void start_from_scratch()
    {
    	printk("start_from_scratch: called\n");
    
    	if (default_conn)
    	{
    		int err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    		printk("start_from_scratch: bt_conn_disconnect(default_conn)=%d called\n", err);
    	}
    	else
    	{
    		printk("start_from_scratch: bt_conn_disconnect(default_conn)=skipped\n");
    	}
    
    	if (auth_conn)
    	{
    		int err = bt_conn_disconnect(auth_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    		printk("start_from_scratch: bt_conn_disconnect(auth_conn)=%d called\n", err);
    	}
    	else
    	{
    		printk("start_from_scratch: bt_conn_disconnect(auth_conn)=skipped\n");
    	}
    
    	// Remove the pairing information on this side of the
    	// connection.
    	int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    	printk("start_from_scratch: bt_unpair=%d\n", err);
    
    	// Remove the application reference to the previous
    	// connection, we don't need it because we start from
    	// scratch.
    	if (default_conn != NULL)
    	{
    		bt_conn_unref(default_conn);
    		default_conn_unref_cnt++;
    		default_conn = NULL;
    		printk("start_from_scratch: ERROR default_conn != NULL\n");
    	}
    
    	if (auth_conn != NULL)
    	{
    		bt_conn_unref(auth_conn);
    		auth_conn_unref_cnt++;
    		auth_conn = NULL;
    		printk("start_from_scratch: ERROR: auth_conn != NULL\n");
    	}
    
    	// Restart the scanning, to look for other advertising
    	// devices.
    	err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	printk("start_from_scratch: bt_scan_start=%d\n", err);
    
    	printk("start_from_scratch: finished\n");
    }
    
    static void discovery_complete(struct bt_gatt_dm *dm,
    							   void *context)
    {
    	struct bt_nus_client *nus = context;
    	printk("discovery_complete: Service discovery completed\n");
    
    	bt_gatt_dm_data_print(dm);
    
    	bt_nus_handles_assign(dm, nus);
    	bt_nus_subscribe_receive(nus);
    
    	bt_gatt_dm_data_release(dm);
    }
    
    static void discovery_service_not_found(struct bt_conn *conn,
    										void *context)
    {
    	printk("discovery_service_not_found: Service not found\n");
    }
    
    static void discovery_error(struct bt_conn *conn,
    							int err,
    							void *context)
    {
    	printk("discovery_error: Error while discovering GATT database: (%d)\n", err);
    }
    
    struct bt_gatt_dm_cb discovery_cb = {
    	.completed = discovery_complete,
    	.service_not_found = discovery_service_not_found,
    	.error_found = discovery_error,
    };
    
    static void gatt_discover(struct bt_conn *conn)
    {
    	int err;
    
    	if (conn != default_conn)
    	{
    		return;
    	}
    
    	err = bt_gatt_dm_start(conn,
    						   BT_UUID_NUS_SERVICE,
    						   &discovery_cb,
    						   &nus_client);
    	if (err)
    	{
    		printk("gatt_discover: Could not start the discovery procedure, error "
    			   "code: %d\n",
    			   err);
    	}
    }
    
    static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
    {
    	if (!err)
    	{
    		printk("exchange_func: MTU exchange done\n");
    	}
    	else
    	{
    		printk("exchange_func: MTU exchange failed (err %" PRIu8 ")\n", err);
    	}
    }
    
    static void connected(struct bt_conn *conn, uint8_t conn_err)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    	int err;
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	connected_called++;
    
    	if (conn_err)
    	{
    		printk("connected: Failed to connect to %s (%d)\n", addr, conn_err);
    
    		if (default_conn == conn)
    		{
    			bt_conn_unref(default_conn);
    			default_conn_unref_cnt++;
    
    			default_conn = NULL;
    			printk("connected: bt_conn_unref() called\n");
    
    			err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    			if (err)
    			{
    				printk("connected: Scanning failed to start (err %d)\n",
    					   err);
    			}
    		}
    
    		return;
    	}
    
    	default_conn_cnt++;
    	default_conn = bt_conn_ref(conn);
    	default_conn_ref_cnt++;
    
    	printk("connected: Connected: %s\n", addr);
    
    	static struct bt_gatt_exchange_params exchange_params;
    
    	exchange_params.func = exchange_func;
    	err = bt_gatt_exchange_mtu(conn, &exchange_params);
    	if (err)
    	{
    		printk("connected: MTU exchange failed (err %d)\n", err);
    	}
    
    	err = bt_conn_set_security(conn, BT_SECURITY_L2);
    	if (err)
    	{
    		printk("connected: Failed to set security: %d\n", err);
    
    		gatt_discover(conn);
    	}
    
    	err = bt_scan_stop();
    	if ((!err) && (err != -EALREADY))
    	{
    		printk("connected: Stop LE scan failed (err %d)\n", err);
    	}
    
    #ifdef NEVER
    	dk_set_led_on(CON_STATUS_LED);
    #endif
    	dk_set_led_off(CON_STATUS_LED);
    }
    
    
    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    	char addr[BT_ADDR_LE_STR_LEN];
    	int err;
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	printk("disconnected: %s (reason %u)\n", addr, reason);
    	disconnected_called++;
    
    	if (default_conn != conn)
    	{
    		printk("disconnected: default_conn != conn\n");
    		return;
    	}
    
    	if (auth_conn)
    	{
    		auth_conn_cnt--;
    		bt_conn_unref(auth_conn);
    		auth_conn_unref_cnt++;
    
    		auth_conn = NULL;
    		printk("disconnected: bt_conn_unref(auth_conn)\n");
    	}
    
    	if (default_conn)
    	{
    		default_conn_cnt--;
    		bt_conn_unref(default_conn);
    		default_conn_unref_cnt++;
    
    		default_conn = NULL;
    		printk("disconnected: bt_conn_unref(default_conn)\n");
    	}
    
    	// BT_HCI_ERR_REMOTE_USER_TERM_CONN is a normal termination from the peripheral
    	// BT_HCI_ERR_PAIRING_NOT_ALLOWED is not usable, does not work on peripheral side
    	if (reason == BT_HCI_ERR_REMOTE_USER_TERM_CONN)
    	{
    		// The peripheral has disconnected, we use this to unpair
    		int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    		printk("disconnected: bt_unpair=%d\n", err);
    	}
    
    	err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    	printk("disconnected: bt_scan_start=%d\n", err);
    	if (err)
    	{
    		printk("disconnected: Scanning failed to start (err %d)\n",
    			   err);
    	}
    
    	printk("disconnected: scanning restarted\n");
    
    #ifdef NEVER
    	dk_set_led_off(CON_STATUS_LED);
    #endif
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    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: Security changed: %s level %u\n", addr, level);
    	}
    	else
    	{
    		printk("security_changed: Security failed: %s level %u err %d\n", addr,
    			   level, err);
    	}
    
    	if ((level == 1) && (err == BT_SECURITY_ERR_PIN_OR_KEY_MISSING))
    	{
    		// We failed to get to the desired security level 4,
    		// so try to start from scratch
    		int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    		printk("security_changed: bt_unpair=%d\n", err);
    
    		err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    		printk("security_changed: bt_scan_start=%d\n", err);
    	}
    
    	gatt_discover(conn);
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    	.security_changed = security_changed};
    
    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));
    
    	if (connectable == true)
    	{
    		printk("scan_filter_match: Filters matched. Address: %s connectable: yes\n", addr);
    	}
    	else
    	{
    		printk("scan_filter_match: Filters matched. Address: %s connectable: no\n", addr);
    	}
    
    	// Let's try to connect ourselves
    	bt_scan_stop();
    
    	// To check that we did not mess up the bt_conn_ref/bt_conn_uref in relation to connected/disconnected state
    	printk("scan_filter_match: default_conn_cnt=%d, default_conn_ref_cnt=%d\n", default_conn_cnt, default_conn_ref_cnt);
    	printk("scan_filter_match: auth_conn_cnt=%d, auth_conn_ref_cnt=%d\n", auth_conn_cnt, auth_conn_ref_cnt);
    	printk("scan_filter_match: connected_called=%d, disconnected_called=%d\n", connected_called, disconnected_called);
    
    	int err = bt_conn_le_create(device_info->recv_info->addr,
    								BT_CONN_LE_CREATE_CONN,
    								device_info->conn_param, &default_conn);
    
    	printk("scan_filter_match: bt_conn_le_create=%d (-22=EINVAL, -12=ENOMEM)\n", err);
    
    	if (!err)
    	{
    #ifdef NEVER
    		// Registering is only done in connected/disconnected
    		default_conn_cnt++;
    		default_conn = bt_conn_ref(default_conn);
    		default_conn_ref_cnt++;
    		// bt_conn_unref(default_conn);
    #endif
    	}
    	else
    	{
    		int err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    		printk("scan_filter_match: bt_scan_start=%d\n", err);
    	}
    	// #endif
    }
    
    static void scan_connecting_error(struct bt_scan_device_info *device_info)
    {
    	printk("scan_connecting_error: Connecting failed\n");
    	printk("scan_connecting_error: receive strength rssi %d dbM\n", device_info->recv_info->rssi);
    }
    
    static void scan_connecting(struct bt_scan_device_info *device_info,
    							struct bt_conn *conn)
    {
    	default_conn = bt_conn_ref(conn);
    	default_conn_ref_cnt++;
    	printk("scan_connecting(): called\n");
    }
    
    static int nus_client_init(void)
    {
    	int err;
    	struct bt_nus_client_init_param init = {
    		.cb = {
    			.received = ble_data_received,
    			.sent = ble_data_sent,
    		}};
    
    	err = bt_nus_client_init(&nus_client, &init);
    	if (err)
    	{
    		printk("nus_client_init: NUS Client initialization failed (err %d)\n", err);
    		return err;
    	}
    
    	printk("nus_client_init: NUS Client module initialized\n");
    	return err;
    }
    
    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL,
    				scan_connecting_error, scan_connecting);
    
    static int scan_init(void)
    {
    	int err;
    #ifdef NEVER
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 1,
    	};
    #endif
    	// We are going to try to connect ourselves
    	struct bt_scan_init_param scan_init = {
    		.connect_if_match = 0,
    	};
    
    	bt_scan_init(&scan_init);
    	bt_scan_cb_register(&scan_cb);
    
    	// Just to be sure???
    	/* Remove all scanning filters. */
    	bt_scan_filter_remove_all();
    
    	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_NUS_SERVICE);
    	if (err)
    	{
    		printk("scan_init: Scanning filters cannot be set (err %d)\n", err);
    		return err;
    	}
    
    	err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
    	if (err)
    	{
    		printk("scan_init: Filters cannot be turned on (err %d)\n", err);
    		return err;
    	}
    
    	printk("scan_init: Scan module initialized\n");
    	return err;
    }
    
    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));
    
    	printk("auth_cancel: Pairing cancelled: %s\n", 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));
    
    	printk("pairing_complete: Pairing completed: %s, bonded: %d\n", 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));
    
    	printk("pairing_failed: Pairing failed conn: %s, reason %d\n", addr, reason);
    }
    
    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));
    
    	printk("auth_passkey_display: Passkey for %s: %06u\n", 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);
    	auth_conn_ref_cnt++;
    
    	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    
    	printk("auth_passkey_confirm: Passkey for %s: %06u\n", addr, passkey);
    	printk("auth_passkey_confirm: Press Button 1 (green) to confirm, Button 2 (white) to reject\n");
    }
    
    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};
    
    // #ifdef CONFIG_BT_NUS_SECURITY_ENABLED
    static void num_comp_reply(bool accept)
    {
    	if (accept)
    	{
    		bt_conn_auth_passkey_confirm(auth_conn);
    		printk("num_comp_reply: Numeric Match, conn %p\n", (void *)auth_conn);
    	}
    	else
    	{
    		bt_conn_auth_cancel(auth_conn);
    		printk("num_comp_reply: Numeric Reject, conn %p\n", (void *)auth_conn);
    	}
    
    	bt_conn_unref(auth_conn);
    	auth_conn_unref_cnt++;
    	auth_conn = NULL;
    }
    
    void button_changed(uint32_t button_state, uint32_t has_changed)
    {
    	uint32_t buttons = button_state & has_changed;
    
    	printk("button_changed(): buttons=%d button_state=0x%x, has_changed=0x%x\n",
    		   buttons, button_state, has_changed);
    
    	if (auth_conn)
    	{
    		printk("button_changed(): authorising connection\n");
    		if (buttons & KEY_PASSKEY_ACCEPT)
    		{
    			// Pause resume -- green
    			printk("button_changed(): KEY_PASSKEY_ACCEPT\n");
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT)
    		{
    			// Mark event - white
    			printk("button_changed(): KEY_PASSKEY_REJECT\n");
    			num_comp_reply(false);
    		}
    	}
    	else
    	{
    		printk("button_changed(): doing something else (buttons)=%d \n", buttons);
    		if (buttons & KEY_PASSKEY_ACCEPT)
    		{
    			// Pause resume -- green
    			printk("button_changed(): green pressed\n");
    #ifdef NEVER
    			int err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    			printk("button_changed(): bt_conn_disconnect()=%d called\n", err);
    #endif
    			// unpair
    			// unref
    			// scan_start
    			start_from_scratch();
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT)
    		{
    			// Mark event - white
    			printk("button_changed(): white pressed\n");
    
    			int err = bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    			printk("button_changed(): bt_conn_disconnect()=%d called\n", err);
    
    			err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    			printk("button_changed(): bt_unpair=%d\n", err);
    
    			err = bt_scan_stop();
    			printk("button_changed(): bt_scan_stop=%d\n", err);
    		}
    	}
    }
    // #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)
    	{
    		printk("configure_gpio: Cannot init buttons (err: %d)\n", err);
    	}
    	// #endif /* CONFIG_BT_NUS_SECURITY_ENABLED */
    
    	err = dk_leds_init();
    	if (err)
    	{
    		printk("configure_gpio: Cannot init LEDs (err: %d)\n", err);
    	}
    
    	// Switch the leds off
    	dk_set_led_on(RUN_STATUS_LED);
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    void clear_all_bonds()
    {
    
    	/** Clear pairing information.
    	 *
    	 * @param id    Local identity (mostly just BT_ID_DEFAULT).
    	 * @param addr  Remote address, NULL or BT_ADDR_LE_ANY to clear all remote
    	 *              devices.
    	 *
    	 * @return 0 on success or negative error value on failure.
    	 */
    	printk("clear_all_bonds: clearing all bonds\n");
    	int rc = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    	printk("clear_all_bonds: done [%d]\n", rc);
    }
    
    void main(void)
    {
    	int err;
    
    	configure_gpio();
    
    	err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    	if (err)
    	{
    		printk("main: Failed to register authorization callbacks\n");
    		return;
    	}
    
    	err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    	if (err)
    	{
    		printk("main: Failed to register authorization info callbacks\n");
    		return;
    	}
    
    	err = bt_enable(NULL);
    	if (err)
    	{
    		printk("main: Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	clear_all_bonds();
    	printk("main: Bluetooth initialized\n");
    
    	if (IS_ENABLED(CONFIG_SETTINGS))
    	{
    		settings_load();
    	}
    
    	int (*module_init[])(void) = {uart_init, scan_init, nus_client_init};
    	for (size_t i = 0; i < ARRAY_SIZE(module_init); i++)
    	{
    		err = (*module_init[i])();
    		if (err)
    		{
    			return;
    		}
    	}
    
    	printk("main: Starting Bluetooth Central UART example\n");
    
    	err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    
    	if (err)
    	{
    		printk("main: Scanning failed to start (err %d)\n", err);
    		return;
    	}
    
    	printk("main: Scanning successfully started\n");
    
    	// Switch the green led on
    	dk_set_led_off(RUN_STATUS_LED);
    
    	for (;;)
    	{
    		/* Wait indefinitely for data to be sent over Bluetooth */
    		struct uart_data_t *buf = k_fifo_get(&fifo_uart_rx_data,
    											 K_FOREVER);
    
    		err = bt_nus_client_send(&nus_client, buf->data, buf->len);
    		if (err)
    		{
    			printk("main: Failed to send data over BLE connection"
    				   "(err %d)\n",
    				   err);
    		}
    
    		err = k_sem_take(&nus_write_sem, NUS_WRITE_TIMEOUT);
    		if (err)
    		{
    			printk("main: NUS send timeout\n");
    		}
    	}
    }
    

    Code on the peripheral:

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    /** @file
     *  @brief Nordic UART Bridge Service (NUS) sample (Peripheral UART)
     */
    #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/sys/byteorder.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 <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_STATUS_LED DK_LED4 // pause-resume led - green
    #define RUN_LED_BLINK_INTERVAL 1000
    
    // #define CON_STATUS_LED DK_LED2
    #define CON_STATUS_LED DK_LED3 // bleactivestatusled - blue
    
    #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 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);
    			aborted_buf = NULL;
    			aborted_len = 0;
    		}
    		else
    		{
    			buf = CONTAINER_OF(evt->data.tx.buf, struct uart_data_t,
    							   data);
    		}
    
    		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);
    		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);
    
    		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(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), 50);
    	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);
    
    #ifdef NEVER
    	dk_set_led_on(CON_STATUS_LED);
    #endif
    	dk_set_led_off(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;
    
    #ifdef NEVER
    		dk_set_led_off(CON_STATUS_LED);
    #endif
    		dk_set_led_on(CON_STATUS_LED);
    	}
    
    #ifdef NEVER
    	if (reason == BT_SECURITY_ERR_UNSPECIFIED)
    	{
    		// This happens after a timeout on the passkey confirmation, but
    		// we start advertising from scratch
    		int err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    								  ARRAY_SIZE(sd));
    		if (err)
    		{
    			printk("disconnected: advertising failed to start (err=%d)", err);
    			return;
    		}
    
    		LOG_INF("disconnected: bt_le_adv_start=%d, advertising started", err);
    	}
    #endif
    }
    
    #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);
    	}
    	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_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_INF("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_INF("Passkey for %s: %06u", addr, passkey);
    	LOG_INF("Press Button 1 (green) to confirm, Button 2 (white) 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_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: Pairing failed conn: %s, reason %d", addr, reason);
    
    	if (reason == BT_SECURITY_ERR_UNSPECIFIED)
    	{
    		// We are disconnected so we can unpair to have a clean start from
    		// scratch
    		int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    		if (err)
    		{
    			printk("pairing_failed: bt_unpair=%d", err);
    			return;
    		}
    
    		// This happens after a timeout on the passkey confirmation, but
    		// we start advertising from scratch
    		err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd,
    							  ARRAY_SIZE(sd));
    		if (err)
    		{
    			printk("pairing_failed: advertising failed to start (err=%d)", err);
    			return;
    		}
    
    		LOG_INF("pairing_failed: bt_le_adv_start=%d, advertising started", err);
    	}
    }
    
    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;
    
    	printk("button_changed(): buttons=%d button_state=0x%x, has_changed=0x%x\n",
    		   buttons, button_state, has_changed);
    
    	if (auth_conn)
    	{
    		printk("button_changed(): authorising connection\n");
    		if (buttons & KEY_PASSKEY_ACCEPT)
    		{
    			// Pause resume -- green
    			printk("button_changed(): KEY_PASSKEY_ACCEPT\n");
    			num_comp_reply(true);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT)
    		{
    			// Mark event - white
    			printk("button_changed(): KEY_PASSKEY_REJECT\n");
    			num_comp_reply(false);
    		}
    	}
    	else
    	{
    		printk("button_changed(): doing something else: \n");
    		if (buttons & KEY_PASSKEY_ACCEPT)
    		{
    			// Pause resume -- green
    			int err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    			printk("button_changed(): bt_unpair=%d\n", err);
    
    			// Does not work
    			// int err = bt_disable();
    			// printk("button_changed(): bt_disable()=%d called\n", err);
    		}
    
    		if (buttons & KEY_PASSKEY_REJECT)
    		{
    			// Mark event - white
    
    			// To notify the central to remove the pairing, pairing has to be removed
    			// on both sides, this seems the only way to notify the central and to
    			// remove the pairing.
    			int err = bt_conn_disconnect(current_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
    			// int err = bt_conn_disconnect(current_conn, BT_HCI_ERR_PAIRING_NOT_ALLOWED);
    			printk("button_changed(): bt_conn_disconnect()=%d called\n", err);
    
    			err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    			printk("button_changed(): bt_unpair=%d\n", err);
    
    			// Stop the advertising, take this device out of the air
    			err = bt_le_adv_stop();
    			printk("button_changed(): bt_le_adv_stop=%d\n", err);
    
    			// Now really stop everything, sadly this is not supported
    			err = bt_disable();
    			printk("button_changed(): bt_disable=%d, -134==ENOTSUP\n", err);
    		}
    	}
    }
    #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);
    	}
    
    	// Switch the leds off
    	dk_set_led_on(RUN_STATUS_LED);
    	dk_set_led_on(CON_STATUS_LED);
    }
    
    void clear_all_bonds()
    {
    
    	/** Clear pairing information.
    	 *
    	 * @param id    Local identity (mostly just BT_ID_DEFAULT).
    	 * @param addr  Remote address, NULL or BT_ADDR_LE_ANY to clear all remote
    	 *              devices.
    	 *
    	 * @return 0 on success or negative error value on failure.
    	 */
    	printk("clearing all bonds\n");
    	int rc = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
    	printk("done [%d]\n", rc);
    }
    
    void 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;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err)
    		{
    			printk("Failed to register authorization info callbacks.\n");
    			return;
    		}
    	}
    
    	err = bt_enable(NULL);
    	if (err)
    	{
    		error();
    	}
    
    	clear_all_bonds();
    
    	LOG_INF("Bluetooth initialized");
    
    	k_sem_give(&ble_init_ok);
    
    	if (IS_ENABLED(CONFIG_SETTINGS))
    	{
    		settings_load();
    	}
    
    	err = bt_nus_init(&nus_cb);
    	if (err)
    	{
    		LOG_ERR("Failed to initialize UART service (err: %d)", err);
    		return;
    	}
    
    	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;
    	}
    
    	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 see that you are clearing all bonds during your startup, but you don't do it at any later point, right?

    You can check out this exercise in the Developer Academy, which handles bonding, and shows how you can use bt_unpair(). 

    And perhaps you can try to set CONFIG_BT_ID_UNPAIR_MATCHING_BONDS=y, which will automatically delete old bonds to the same peer, if it finds bonding information with the same device. 

    How exactly do you want your devices to work? You have two peripherals and one central. How do you want to decide that the central should switch peripheral? A button action, or should it just accept any of the two devices at any point in time? Should it accept even more? Are there any conditions where you don't want to accept a peripheral? What if I walk by with a peripheral having the NUS service. Do you want to connect to that one?

    Best regards,

    Edvin

Related