This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Can't filter on custom UUID with BLE on Zephyr/NCS

Hi!

So I've got one BLE Central and one BLE Peripheral that I should communicate. Ive based the Central code on nrf Connect SDK's "peripheral_gatt_dm" sample. First I tested my new "central_gatt_dm" sample up against a "perpheral_hr" Heart Rate sample successfully. It displayed what was supposed to be displayed in terminal - list of services and characteristics etc. However, when I changed the peripheral to reveal a custom service, I could no longer perform the test. Let me first show the Central code and then the peripheral code:

(Hardware: both central and peripheral are nrf52840s)

Central:

/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>
#include <drivers/gpio.h>
#include <soc.h>
#include <settings/settings.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/scan.h>
#include <bluetooth/gatt_dm.h>

#define BT_UUID_READ_WRITE_SERVICE                                          \
  BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x10, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
                      0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA0)

static struct bt_conn *default_conn;

static void scan_filter_match(struct bt_scan_device_info *device_info,
			      struct bt_scan_filter_match *filter_match,
			      bool connectable)
{
	int err;
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(device_info->addr, addr, sizeof(addr));

	printk("Filters matched. Address: %s connectable: %s\n",
		addr, connectable ? "yes" : "no");

	err = bt_scan_stop();
	if (err) {
		printk("Stop LE scan failed (err %d)\n", err);
	}
}

static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
	printk("Connecting failed\n");
}

static void scan_connecting(struct bt_scan_device_info *device_info,
			    struct bt_conn *conn)
{
	default_conn = bt_conn_ref(conn);
}

static void scan_filter_no_match(struct bt_scan_device_info *device_info,
				 bool connectable)
{
	int err;
	struct bt_conn *conn;
	char addr[BT_ADDR_LE_STR_LEN];

	if (device_info->adv_info.adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
		bt_addr_le_to_str(device_info->addr, addr, sizeof(addr));
		printk("Direct advertising received from %s\n", addr);
		bt_scan_stop();

		err = bt_conn_le_create(device_info->addr,
					BT_CONN_LE_CREATE_CONN,
					device_info->conn_param, &conn);

		if (!err) {
			default_conn = bt_conn_ref(conn);
			bt_conn_unref(conn);
		}
	}
}

BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match,
		scan_connecting_error, scan_connecting);


static void discover_all_completed(struct bt_gatt_dm *dm, void *ctx)
{
	char uuid_str[300]; //was 37

	const struct bt_gatt_dm_attr *gatt_service_attr =
			bt_gatt_dm_service_get(dm);
	const struct bt_gatt_service_val *gatt_service =
			bt_gatt_dm_attr_service_val(gatt_service_attr);

	size_t attr_count = bt_gatt_dm_attr_cnt(dm);

	bt_uuid_to_str(gatt_service->uuid, uuid_str, sizeof(uuid_str));
	printk("Found service %s\n", uuid_str);
	printk("Attribute count: %d\n", attr_count);

	bt_gatt_dm_data_print(dm);
	bt_gatt_dm_data_release(dm);

	bt_gatt_dm_continue(dm, NULL);
}

static void discover_all_service_not_found(struct bt_conn *conn, void *ctx)
{
	printk("No more services\n");
}

static void discover_all_error_found(struct bt_conn *conn, int err, void *ctx)
{
	printk("The discovery procedure failed, err %d\n", err);
}

static struct bt_gatt_dm_cb discover_all_cb = {
	.completed = discover_all_completed,
	.service_not_found = discover_all_service_not_found,
	.error_found = discover_all_error_found,
};

static void connected(struct bt_conn *conn, u8_t err)
{
	char addr[BT_ADDR_LE_STR_LEN];

	if (err) {
		printk("Connection failed (err 0x%02x)\n", err);
		return;
	}

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	printk("Connected %s\n", addr);

	err = bt_gatt_dm_start(conn, NULL, &discover_all_cb, NULL);
	if (err) {
		printk("Failed to start discovery (err %d)\n", err);
	}
}

static void disconnected(struct bt_conn *conn, u8_t reason)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);
}

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: %s level %u\n", addr, level);
	} else {
		printk("Security failed: %s level %u err %d\n", addr, level,
			err);
	}
}

static struct bt_conn_cb conn_callbacks = {
	.connected        = connected,
	.disconnected     = disconnected,
	.security_changed = security_changed,
};

static void scan_init(void)
{
	int err;

	struct bt_scan_init_param scan_init = {
		.connect_if_match = 1,
		.scan_param = NULL,
		.conn_param = BT_LE_CONN_PARAM_DEFAULT
	};

	bt_scan_init(&scan_init);
	bt_scan_cb_register(&scan_cb);

	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_READ_WRITE_SERVICE);
	if (err) {
		printk("Scanning filters cannot be set (err %d)\n", err);

		return;
	}

	err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
	if (err) {
		printk("Filters cannot be turned on (err %d)\n", 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("Pairing cancelled: %s\n", addr);
}

static void pairing_confirm(struct bt_conn *conn)
{
	char addr[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

	bt_conn_auth_pairing_confirm(conn);

	printk("Pairing confirmed: %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 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 conn: %s, reason %d\n", addr, reason);
}

static struct bt_conn_auth_cb conn_auth_callbacks = {
	.cancel = auth_cancel,
	.pairing_confirm = pairing_confirm,
	.pairing_complete = pairing_complete,
	.pairing_failed = pairing_failed
};



void init_ble_custom_central(void)
{
	int err;

	printk("Starting GATT Discovery Manager example\n");

	err = bt_enable(NULL);
	if (err) {
		printk("BLE init failed with error code %d\n", err);
		return;
	}

	printk("Bluetooth initialized\n");

	if (IS_ENABLED(CONFIG_SETTINGS)) {
		settings_load();
	}

	scan_init();

	bt_conn_cb_register(&conn_callbacks);
	if (err) {
		printk("Failed to register connection callbacks.\n");
		return;
	}

	err = bt_conn_auth_cb_register(&conn_auth_callbacks);
	if (err) {
		printk("Failed to register authorization callbacks.\n");
		return;
	}

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

	printk("Scanning successfully started\n");
}

The only thing i've changed after the working demo with the Heart Rate service, was:

err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_HRS);

changed to:

err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, BT_UUID_READ_WRITE_SERVICE);.

Now let me present the Peripheral code in case that is needed:

Peripheral main.c:

#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/hci.h>
#include <bluetooth/uuid.h>
#include <device.h>
#include <errno.h>
#include <services/read_write_service.h>
#include <settings/settings.h>
#include <stddef.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <zephyr.h>
#include <zephyr/types.h>

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

struct bt_conn *default_conn;

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 void connected(struct bt_conn *conn, u8_t err) {
  if (err) {
    printk("Connection failed (err %u)\n", err);
  } else {
    printk("Connected\n");
    default_conn = bt_conn_ref(conn);
  }
}

static void disconnected(struct bt_conn *conn, u8_t reason) {
  printk("Disconnected (reason %u)\n", reason);

  if (default_conn) {
    bt_conn_unref(default_conn);
    default_conn = NULL;
  }
}

static struct bt_conn_cb conn_callbacks = {
    .connected = connected,
    .disconnected = disconnected,
};

static void bt_ready(int err) {
  if (err) {
    printk("Bluetooth init failed (err %d)\n", err);
    return;
  }

  read_write_service_init();

  /*if (IS_ENABLED(CONFIG_SETTINGS)) {
    settings_load();
  }*/

  err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
  if (err) {
    printk("Advertising failed to start (err %d)\n", err);
    return;
  }

  printk("Advertising successfully started\n");
}

void main(void) {
  printk("starting in main \n\n");

  int err;

  err = bt_enable(bt_ready);
  if (err) {
    printk("Bluetooth initialization failed.");
    return;
  }

  bt_conn_cb_register(&conn_callbacks);
}

With the accompanying read_write_service.c:

#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/gatt.h>
#include <bluetooth/hci.h>
#include <bluetooth/uuid.h>
#include <errno.h>
#include <services/read_write_service.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/printk.h>
#include <zephyr.h>
#include <zephyr/types.h>

#define BT_UUID_READ_WRITE_SERVICE                                          \
  BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x10, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
                      0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA0)
// input to the peripheral device
#define BT_UUID_INPUT                                                       \
  BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
                      0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA1)
// output from the peripheral device
#define BT_UUID_OUTPUT                                                      \
  BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
                      0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA2)

#define BT_UUID_NAMING_CHARACTERISTIC                                       \
  BT_UUID_DECLARE_128(0x3E, 0x09, 0x99, 0x11, 0x29, 0x3F, 0x11, 0xE4, 0x93, \
                      0xBD, 0xAF, 0xD0, 0xFE, 0x6D, 0x1D, 0xA3)

// BT_GATT_CHARACTERISTIC(_uuid, _props, _perm, _read, _write, _value)

static struct bt_gatt_attr attrs[] = {
    BT_GATT_PRIMARY_SERVICE(BT_UUID_READ_WRITE_SERVICE),
    BT_GATT_CHARACTERISTIC(BT_UUID_INPUT, BT_GATT_CHRC_WRITE,
                           BT_GATT_PERM_WRITE, NULL, write_input, NULL),
    BT_GATT_CHARACTERISTIC(BT_UUID_OUTPUT, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
                           read_output, NULL, NULL),
    BT_GATT_CHARACTERISTIC(BT_UUID_NAMING_CHARACTERISTIC, BT_GATT_CHRC_READ,
                           BT_GATT_PERM_READ, naming_output, NULL, NULL),
};

static struct bt_gatt_service read_write_service = BT_GATT_SERVICE(attrs);

u8_t a_number = 0;
const char characteristic_name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

void read_write_service_init() {
  bt_gatt_service_register(&read_write_service);
}

ssize_t write_input(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                    const void *buf, u16_t len, u16_t offset, u8_t flags) {
  const u8_t *new_number = buf;
  if (!len) {
    return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
  }

  if (*new_number >= 0 && *new_number <= 10) {
    a_number = *new_number;
  } else {
    return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
  }

  printk("write_input [%d]\n", a_number);
  return len;
}

ssize_t read_output(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                    void *buf, u16_t len, u16_t offset) {
  printk("read_output\n");
  return bt_gatt_attr_read(conn, attr, buf, len, offset, &a_number,
                           sizeof(a_number));
}

ssize_t naming_output(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                      void *buf, u16_t len, u16_t offset) {
  printk("read_output\n");
  return bt_gatt_attr_read(conn, attr, buf, len, offset, characteristic_name,
                           sizeof(characteristic_name));
}

Now, I can connect to the read/write custom service on nrf connect mobile and access the characteristics such that I know that it works. Why can I not find the peripheral when I am filtering on my UUID?

Looking forward to your response!

Parents Reply Children
  • Jonas, 

    The uart example (central_uart and peripheral_uart) uses a custom UUID for NUS_SERVICE and they have worked well on my desk. I have editied the NUS service UUID to something random and it still works with the scan filter.

    I think you are missing to initialize the rest of the scanning module members as it is done in the uart example, 

    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL,
    scan_connecting_error, scan_connecting);

    Please take UART NUS as template and do your tests, because it works just find here on my desk. Tested on NCSv1.4.99 on nRF52840 DK on both ends

Related