hrs example with btuuid128

The zephyr peripheral hr example us altered create a custom GATT service with a 128 bit BT uuid instead of a 16 bit bt number. I tried the following which does not trigger the notify event:

On the peripheral role, I changed advertising data and service definitions. I won't bother you with my code, it works in nrf52 connectivity.

#define uuid_ppg_service_val BT_UUID_128_ENCODE(0x20000001, 0xA000, 0xB000, 0xC000, 0xC00000000001)
#define BT_UUID_PPGS BT_UUID_DECLARE_128(uuid_ppg_service_val)
#define BT_UUID_PPGS_MEASUREMENT BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x10000001, 0xA000, 0xB000, 0xC000, 0xC00000000002))

BT_GATT_SERVICE_DEFINE(ppg_svc,
	BT_GATT_PRIMARY_SERVICE(BT_UUID_PPGS),	
		BT_GATT_CHARACTERISTIC(BT_UUID_PPGS_MEASUREMENT,
					BT_GATT_CHRC_NOTIFY, 
					BT_GATT_PERM_NONE, NULL, NULL, NULL),
			BT_GATT_CCC(vnd_ccc_cfg_changed,
					BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),				
);

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL,
			  //BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
		      	BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
			  ),
	BT_DATA_BYTES(BT_DATA_UUID128_ALL, uuid_ppg_service_val),
};

On the central role, I made these modifications:

static void connected(struct bt_conn *conn, uint8_t conn_err)
{
    // ... 
    if (conn == default_conn) {
        memcpy(&uuid128, BT_UUID_PPGS, sizeof(uuid128));				
		discover_params.uuid = &uuid128.uuid;
	//...
}

static bool eir_found(struct bt_data *data, void *user_data)
{
	bt_addr_le_t *addr = user_data;
	int i;
	int err;

	printk("[AD]: %u data_len %u\n", data->type, data->data_len);
	struct bt_le_conn_param *param;
	param = BT_LE_CONN_PARAM_DEFAULT;

	switch (data->type) {
		case BT_DATA_UUID128_SOME:
			printk("128bit type some");
			break;
		case BT_DATA_UUID128_ALL:
			if (data->data_len % BT_UUID_SIZE_128 != 0U) {
				printk("AD malformed\n");
				return true;
			}
			for(i = 0; i < data->data_len; i+= BT_UUID_SIZE_128) {
				
				printk("128bit type all");
				struct bt_uuid *uuid128;
				uint8_t btadvData[16];
				memcpy(btadvData, data->data, 16);
				uuid128 = (struct bt_uuid *)btadvData;
				if(bt_uuid_cmp(uuid128, BT_UUID_PPGS_MEASUREMENT)) {
					printk("uuid128 == PPGID\n");				
					err = bt_le_scan_stop();
					if(err) {
						printk("Stop LE scan failed (err %d)\n", err);					
					}
					printk("make connection");
					err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
								param, &default_conn);
					if (err) {
						printk("Create conn failed (err %d)\n", err);					
					}		
					return false;			
				}
			}
			break;
		}
	return true;
}

static uint8_t discover_func(struct bt_conn *conn,
			     const struct bt_gatt_attr *attr,
			     struct bt_gatt_discover_params *params)
{
	int err;

	if (!attr) {
	 	printk("Discover complete\n");
	 	(void)memset(params, 0, sizeof(*params));
	 	return BT_GATT_ITER_STOP;
	}

	printk("[ATTRIBUTE] handle %u\n", attr->handle);

	if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_PPGS)) {
		memcpy(&uuid128, BT_UUID_PPGS_MEASUREMENT, sizeof(uuid128));
		discover_params.uuid = &uuid128.uuid;
	
		discover_params.start_handle = attr->handle + 1;
		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;

		err = bt_gatt_discover(conn, &discover_params);
		if (err) {
			printk("Discover failed (err %d)\n", err);
		}
	} else if (!bt_uuid_cmp(discover_params.uuid,
	
					BT_UUID_PPGS_MEASUREMENT)) {
		memcpy(&uuid16, BT_UUID_GATT_CCC, sizeof(uuid16));
		discover_params.uuid = &uuid16.uuid;
		discover_params.start_handle = attr->handle + 2;
		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
		subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);

		err = bt_gatt_discover(conn, &discover_params);
		if (err) {
			printk("Discover failed (err %d)\n", err);
		}
	} else {
		subscribe_params.notify = notify_func;
		subscribe_params.value = BT_GATT_CCC_NOTIFY;
		subscribe_params.ccc_handle = attr->handle;

		err = bt_gatt_subscribe(conn, &subscribe_params);
		if (err && err != -EALREADY) {
			printk("Subscribe failed (err %d)\n", err);
		} else {
			printk("[SUBSCRIBED]\n");
		}

		return BT_GATT_ITER_STOP;
	}

	return BT_GATT_ITER_STOP;
}

Central connects but notifications do not work. When I change back to BT_UUID_HRS value, notifications are received.

  • Hi Boris, 

    I would suggest to use a nRF Sniffer to track what happen over the air so we can have better understanding on what the central and peripheral is doing. 

    Have you checked if the central managed to do service discovery and the discover_func() callback is called ? And how many times it has been called ? 

    I would suggest to have a look into our central_uart (and peripheral_uart) sample. The examples show how to use a 128 bits UUID proprietary service/characteristic. 

  • I knew you would come up with nordic uart, but that example uses a lot of nordic libraries, I just want to use a custom uuid in the zephyr example. discover_func is called, once. I'll try the sniffer.

  • Hi Boris, 


    If you don't want to use Nordic library, you still can have a look at the gatt_dm.c and see how we used 

    Zephyr's bt_gatt_discover() library inside that. 
    From what I can see it's not much different from the central_hr example. What you need to check is to see when discover_func() returned if it managed to find the NUS service or not (please try to test your application using NUS UUID first). If not then you would need to check what's the difference in central_nus example that it managed to discover the service. Make sure you declare the exact same UUID on both the central and peripheral. 
  • Sorry I made an mistake, the discover_func callback needs an 16bit uuid for BT_UUID_GATT_CCC. I changed that now it works. Resides one more question:

    Why does eir_found loops through the data in the hr_central example. bt_data_parse function explanations says: "Callback function which will be called for each element that's found in the data. The callback should return true to continue parsing, or false to stop parsing.".

  • Hi Boris, 
    I'm sorry for late reply. You are right on the fact that the callback will be called for each element found in the advertising data.  The BT_DATA_UUID16_ALL and BT_DATA_UUID16_SOME are considered a element but as the name suggest (all or some), they may contain multiple UUIDs inside.

    This is why we need to go through it to find if the HRS UUID is inside. 

Related