Custom service GATTServer and GATTClient

Hello experts!

I have been trying for some time now setting up my own custom service GATTServer and GATTClient with two nrf52840 DK.

On the server-side I have modified the BLE peripheral sample project and it works as it should when I try it with the NRF Connect app.

This is the main.c for that one:

/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

#include <zephyr/settings/settings.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>

#define NOTIFY_INTERVAL         500
#define VND_MAX_LEN 20

static uint8_t rxdata_value[VND_MAX_LEN + 1] = {'R','x','D', 'a', 't', 'a'};
static uint16_t txcredit_cli_value[VND_MAX_LEN + 1] = { 'T', 'x', 'C', 'r', 'e', 'd' };
static uint8_t frog_txd_frames = 100U;
static uint16_t frog_rxc_frames = 2U;
static bool frog_txdnotify_enabled;
static bool frog_rxcnotify_enabled;


#define STACKSIZE 1024
#define PRIORITY 7

/* ---------------------- FROG SERVICE UUIDs -------------------------- */

#define BT_UUID_FROG_SERVICE_VAL \
	BT_UUID_128_ENCODE(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 0x0002a5d5c51b)

static const struct bt_uuid_128 frog_uuid = BT_UUID_INIT_128(
	BT_UUID_FROG_SERVICE_VAL);

static const struct bt_uuid_128 frog_txcredit_uuid = BT_UUID_INIT_128(
	BT_UUID_128_ENCODE(0x2c1b1820, 0xb2eb, 0x11e5, 0x8f66, 0x0002a5d5c51b));

static const struct bt_uuid_128 frog_txdata_uuid = BT_UUID_INIT_128(
	BT_UUID_128_ENCODE(0x388706a0, 0xb2eb, 0x11e5, 0xa790, 0x0002a5d5c51b));

static const struct bt_uuid_128 frog_rxdata_uuid = BT_UUID_INIT_128(
	BT_UUID_128_ENCODE(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 0x0002a5d5c51b));

static const struct bt_uuid_128 frog_rxcredit_uuid = BT_UUID_INIT_128(
	BT_UUID_128_ENCODE(0x43802b40, 0xb2eb, 0x11e5, 0xb6e5, 0x0002a5d5c51b));


// Setting up CCCD
static void frog_txdccc_cfg_changed(const struct bt_gatt_attr *attr,
				  uint16_t value)
{
	frog_txdnotify_enabled = (value == BT_GATT_CCC_NOTIFY);
}

static void frog_rxcccc_cfg_changed(const struct bt_gatt_attr *attr,
				  uint16_t value)
{
	frog_rxcnotify_enabled = (value == BT_GATT_CCC_NOTIFY);
}

/* ---------------------------- END ------------------------------------*/		



static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
				     const struct bt_gatt_attr *attr,
				     const void *buf, uint16_t len, uint16_t offset,
				     uint8_t flags)
{
	uint16_t *value = attr->user_data;

	printk("user_data: %p \n", attr->user_data);

	if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
		/* Write Request received. Reject it since this Characteristic
		 * only accepts Write Without Response.
		 */
		return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
	}

	if (offset + len > VND_MAX_LEN) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	memcpy(value + offset, buf, len);
	value[offset + len] = 0;

	return len;
}

/* ----------- FROG SERVICE DECLARATION -----------  */

BT_GATT_SERVICE_DEFINE(frog_srv,
	BT_GATT_PRIMARY_SERVICE(&frog_uuid),
	BT_GATT_CHARACTERISTIC(&frog_txdata_uuid.uuid, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, NULL, NULL, NULL),
	BT_GATT_CCC(frog_txdccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&frog_rxcredit_uuid.uuid, BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ, NULL, NULL, NULL),
	BT_GATT_CCC(frog_rxcccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),	
	BT_GATT_CHARACTERISTIC(&frog_txcredit_uuid.uuid, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_without_rsp_vnd, &txcredit_cli_value),
	BT_GATT_CHARACTERISTIC(&frog_rxdata_uuid.uuid, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_without_rsp_vnd, &rxdata_value),
);


/*-------------------- END ------------------------ */

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_UUID128_ALL, BT_UUID_FROG_SERVICE_VAL),
};

static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

/* Update Maximum Transfer Unit */

void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
{
	printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
}

/* Register callback functions */
static struct bt_gatt_cb gatt_callbacks = {
	.att_mtu_updated = mtu_updated
};

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		printk("Connection failed (err 0x%02x)\n", err);
	} else {
		printk("Connected\n");
	}
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	printk("Disconnected (reason 0x%02x)\n", reason);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
};

static void bt_ready(void)
{
	int err;

	printk("Bluetooth initialized\n");


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

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

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


// Notify for frog

int frog_txd_notify(uint8_t txd_val)
{
	if (!frog_txdnotify_enabled) {
		return -EACCES;
	}

	return bt_gatt_notify(NULL, &frog_srv.attrs[1], 
			      &txd_val,
			      sizeof(txd_val));
}

int frog_rxc_notify(uint8_t rxc_val)
{
	if (!frog_rxcnotify_enabled) {
		return -EACCES;
	}

	return bt_gatt_notify(NULL, &frog_srv.attrs[4], 
			      &rxc_val,
			      sizeof(rxc_val));
}

static void simulate_data(void)
{
	frog_txd_frames++;
	if (frog_txd_frames == 200) {
		frog_txd_frames = 100;
	}

	frog_rxc_frames++;
	if (frog_rxc_frames == 150)
	{
		frog_rxc_frames = 2;
	}
	

}

void send_data_thread(void)
{
	while(1){
		/* Simulate data */
		simulate_data();
		/* Send notification, the function sends notifications only if a client is subscribed */
		frog_txd_notify(frog_txd_frames);

		frog_rxc_notify(frog_rxc_frames);

		k_sleep(K_MSEC(NOTIFY_INTERVAL));
	}
		
}



int main(void)
{

	int err;

	err = bt_enable(NULL);
	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return 0;
	}

	bt_ready();

	bt_gatt_cb_register(&gatt_callbacks);

	/* Implement notification. At the moment there is no suitable way
	 * of starting delayed work so we do it here
	 */

	while (1) {
		k_sleep(K_SECONDS(1));

		/* Heartrate measurements simulation */
		//hrs_notify();

		/* Battery level simulation */
		//bas_notify();

		//printk("frog_txd_frames: %d \n", frog_txd_frames);
		//printk("txcredit_cli_value: %d \n", *txcredit_cli_value);
		//printk("rxdata_value: %d \n\n\n", *rxdata_value);
		//printk("frog_rxc_frames: %d \n\n\n", frog_rxc_frames);
		/* Vendor indication simulation */
	}
	return 0;
}
K_THREAD_DEFINE(send_data_thread_id, STACKSIZE, send_data_thread, NULL, NULL,
		NULL, PRIORITY, 0, 0);

My first question is:

1 a) I want to remove all other services, and I have been able to remove the hr-service and battery-service, however I can not remove the cts-service because then the project won't build properly. Meaning I can't remove the cts.c and cts.h files without something going wrong, can this be fixed somehow?


Now to the GATTClient, I have used the central_hr sample to try and set up my central/client device. This is what that code looks like:

/* main.c - Application main entry point */

/*
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <errno.h>
#include <zephyr/kernel.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 <zephyr/sys/byteorder.h>

/* --------------------- START OF FROG UUID:s ---------------- */

#define FROG_SERVICE BT_UUID_128_ENCODE(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 0x0002a5d5c51b)
// Notify characteristics
#define FROG_TXDATA_UUID BT_UUID_128_ENCODE(0x388706a0, 0xb2eb, 0x11e5, 0xa790, 0x0002a5d5c51b)

// Write without response characteristics
#define FROG_RXCREDIT BT_UUID_128_ENCODE(0x43802b40, 0xb2eb, 0x11e5, 0xb6e5, 0x0002a5d5c51b)


#define BT_UUID_FROG BT_UUID_DECLARE_128(FROG_SERVICE)
#define BT_UUID_TXDATA BT_UUID_DECLARE_128(FROG_TXDATA_UUID)
#define BT_UUID_RXCREDIT BT_UUID_DECLARE_128(FROG_RXCREDIT)

/* ----------------------------- END ------------------------- */


static void start_scan(void);

static struct bt_conn *default_conn;


static struct bt_uuid_128 discover_frog_uuid = BT_UUID_INIT_128(0);
static const struct bt_uuid *ccc_uuid = BT_UUID_GATT_CCC;
static struct bt_gatt_discover_params discover_params;
static struct bt_gatt_subscribe_params subscribe_params;


 
uint64_t total_rx_count; /* This value is exposed to test code */

static uint8_t notify_func(struct bt_conn *conn,
			   struct bt_gatt_subscribe_params *params,
			   const void *data, uint16_t length)
{
	if (!data) {
		printk("[UNSUBSCRIBED]\n");
		params->value_handle = 0U;
		return BT_GATT_ITER_STOP;
	}

	printk("[NOTIFICATION] data %p length %u\n", data, length);

	total_rx_count++;

	return BT_GATT_ITER_CONTINUE;
}

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_FROG)) {
		printk("Discovering TxDATA\n");
		memcpy(&discover_frog_uuid, BT_UUID_TXDATA, sizeof(discover_frog_uuid));
		discover_params.uuid = &discover_frog_uuid.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_FROG)) {
		printk("Discovering CCC\n");
		//memcpy(&discover_frog_uuid, ccc_uuid, sizeof(*ccc_uuid));
		discover_params.uuid = ccc_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;
}

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

	printk("[AD]: %u data_len %u\n", data->type, data->data_len);

	switch (data->type) {
	case BT_DATA_UUID128_SOME:
	case BT_DATA_UUID128_ALL:
		if (data->data_len % sizeof(uint16_t) != 0U) {
			printk("AD malformed\n");
			return true;
		}

		for (i = 0; i < data->data_len; i += sizeof(uint16_t)) {
			struct bt_conn_le_create_param *create_param;
			struct bt_le_conn_param *param;
			const struct bt_uuid *uuid;
			uint16_t u16;
			int err;

			memcpy(&u16, &data->data[i], sizeof(u16));
			uuid = BT_UUID_DECLARE_128(sys_le16_to_cpu(u16));
			if (bt_uuid_cmp(uuid, BT_UUID_FROG)) {
				continue;
			}

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

			printk("Creating connection with Coded PHY support\n");
			param = BT_LE_CONN_PARAM_DEFAULT;
			create_param = BT_CONN_LE_CREATE_CONN;
			create_param->options |= BT_CONN_LE_OPT_CODED;
			err = bt_conn_le_create(addr, create_param, param,
						&default_conn);
			if (err) {
				printk("Create connection with Coded PHY support failed (err %d)\n",
				       err);

				printk("Creating non-Coded PHY connection\n");
				create_param->options &= ~BT_CONN_LE_OPT_CODED;
				err = bt_conn_le_create(addr, create_param,
							param, &default_conn);
				if (err) {
					printk("Create connection failed (err %d)\n", err);
					start_scan();
				}
			}

			return false;
		}
	}

	return true;
}

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char dev[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(addr, dev, sizeof(dev));
	printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
	       dev, type, ad->len, rssi);

	/* We're only interested in legacy connectable events or
	 * possible extended advertising that are connectable.
	 */
	if (type == BT_GAP_ADV_TYPE_ADV_IND ||
	    type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND ||
	    type == BT_GAP_ADV_TYPE_EXT_ADV) {
		bt_data_parse(ad, eir_found, (void *)addr);
	}
}

static void start_scan(void)
{
	int err;

	/* Use active scanning and disable duplicate filtering to handle any
	 * devices that might update their advertising data at runtime. */
	struct bt_le_scan_param scan_param = {
		.type       = BT_LE_SCAN_TYPE_ACTIVE,
		.options    = BT_LE_SCAN_OPT_CODED,
		.interval   = BT_GAP_SCAN_FAST_INTERVAL,
		.window     = BT_GAP_SCAN_FAST_WINDOW,
	};

	err = bt_le_scan_start(&scan_param, device_found);
	if (err) {
		printk("Scanning with Coded PHY support failed (err %d)\n", err);

		printk("Scanning without Coded PHY\n");
		scan_param.options &= ~BT_LE_SCAN_OPT_CODED;
		err = bt_le_scan_start(&scan_param, device_found);
		if (err) {
			printk("Scanning failed to start (err %d)\n", err);
			return;
		}
	}

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

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));

	if (conn_err) {
		printk("Failed to connect to %s (%u)\n", addr, conn_err);

		bt_conn_unref(default_conn);
		default_conn = NULL;

		start_scan();
		return;
	}

	printk("Connected: %s\n", addr);

	total_rx_count = 0U;

	if (conn == default_conn) {
		memcpy(&discover_frog_uuid, BT_UUID_FROG, sizeof(discover_frog_uuid));
		discover_params.uuid = &discover_frog_uuid.uuid;
		discover_params.func = discover_func;
		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
		discover_params.type = BT_GATT_DISCOVER_PRIMARY;

		err = bt_gatt_discover(default_conn, &discover_params);
		if (err) {
			printk("Discover failed(err %d)\n", err);
			return;
		}
	}
}

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));

	printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);

	if (default_conn != conn) {
		return;
	}

	bt_conn_unref(default_conn);
	default_conn = NULL;

	start_scan();
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
};

int main(void)
{
	int err;
	err = bt_enable(NULL);

	if (err) {
		printk("Bluetooth init failed (err %d)\n", err);
		return 0;
	}

	printk("Bluetooth initialized\n");

	start_scan();
	return 0;
}

My questions here are many because I can't get a proper handle on how this sample works:
2 a) Will this code that I have, find ALL my characteristics and subscribe to both my notification characteristics (only one is present in the above client code?


b) When I do print-outs from the client I see that it detects a lot of devices, but I don't know if it is the server I have set up, how can I find my device address easiest or is this always randomly generated from the server when it starts advertising?
Also, in the central_hr sample it says that it should automatically connect to the peripheral_hr sample but my units are not connecting automatically, have I set some security-level wrong?

If possible, please provide answers that tell me explicitly how to fix things since I am a student and have only worked with this for a month or so so everything is very new.

  • Hi Bjorn, 

    1. Please elaborate more on the issue you are seeing when removing cts. Could you point me to where you declare it ? 
    I would suggest to take a look at the peripheral_lbs sample or peripheral_uart sample as reference. 

    Have you looked at our Academy course for BLE ? https://academy.nordicsemi.com/courses/bluetooth-low-energy-fundamentals/


    2. Usually the service discovery process only discover the service/characteristic that you look for. if you want to discover all service characteristic you will need to develop your own process. I would suggest you to go through this guide: Building a Bluetooth application on nRF Connect SDK - Contrasting to SoftDevice - Part 2 Central role 

  • Hello Hung!

    1: No that is the problem, if you see this screen shot from my nrf connect app:

    The service found is correct, but the attribute table is wrong right? I should not see the current time service here?
    I have watched these examples, is there something wrong in how I do it in my code?
    Yes I have looked at that course and this problem isn't really discussed as I understand that a service needs to be announced and the characteristics hold attributes. But since I don't use the cts at all I don't understand why it shows up in my attribute table in nrf connect?

    2: Thank you! This is the first up to date useful guide I have been given. I will check this one and see if it helps me. However I am not interested in using the NUS so are all these functions in your tutorial applicable if you don't want to use NUS?

  • Hi, 
    Please show the whole attribute table. Do you see your FROG service ? 
    If you flash a sample, for example the NUS sample, do you see the issue ? 
    I suspect that the Attribute table has been cached from the previous application you flashed on the board. You can try to turn off and on  Bluetooth on the phone, also remove bonding if any. Please try to use another phone or use NRF Connect on PC to check. 

    Our suggestion is that you go through our tutorial with the NUS sample to get familiar with the SDK before you move to your own code. 
    Converting NUS to your service should be straight forward after you managed to make the client/central firmware works. 

  • Sorry for the late answer, but yes I see my FROG service and I have tried restarting Bluetooth, took away the connection on my phone and still the attribute shown in the image above is there. 

    With this code:

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2020 SixOctets Systems
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <stddef.h>
    #include <errno.h>
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/byteorder.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>
    
    /* --------------------- START OF FROG UUID:s ---------------- */
    
    #define FROG_SERVICE BT_UUID_128_ENCODE(0x2016ec20, 0xb2eb, 0x11e5, 0xa307, 0x0002a5d5c51b)
    // Notify characteristics
    #define FROG_TXDATA_UUID BT_UUID_128_ENCODE(0x388706a0, 0xb2eb, 0x11e5, 0xa790, 0x0002a5d5c51b)
    
    // Write without response characteristics
    #define FROG_RXCREDIT BT_UUID_128_ENCODE(0x43802b40, 0xb2eb, 0x11e5, 0xb6e5, 0x0002a5d5c51b)
    
    
    #define BT_UUID_FROG BT_UUID_DECLARE_128(FROG_SERVICE)
    #define BT_UUID_TXDATA BT_UUID_DECLARE_128(FROG_TXDATA_UUID)
    #define BT_UUID_RXCREDIT BT_UUID_DECLARE_128(FROG_RXCREDIT)
    
    /* ----------------------------- END ------------------------- */
    
    
    static int scan_start(void);
    
    static struct bt_conn *default_conn;
    //static struct bt_uuid_16 frog_discover_uuid = BT_UUID_INIT_16(0);
    static struct bt_uuid_128 frog_discover_uuid = BT_UUID_INIT_128(0);
    static const struct bt_uuid *ccc_uuid = BT_UUID_GATT_CCC;
    static struct bt_gatt_discover_params discover_params;
    static struct bt_gatt_subscribe_params subscribe_params;
    
    static double pow(double x, double y)
    {
    	double result = 1;
    
    	if (y < 0) {
    		y = -y;
    		while (y--) {
    			result /= x;
    		}
    	} else {
    		while (y--) {
    			result *= x;
    		}
    	}
    
    	return result;
    }
    
    static uint8_t notify_func(struct bt_conn *conn,
    			   struct bt_gatt_subscribe_params *params,
    			   const void *data, uint16_t length)
    {
    
    	if (!data) {
    		printk("[UNSUBSCRIBED]\n");
    		params->value_handle = 0U;
    		return BT_GATT_ITER_STOP;
    	}
    
    
    	printf("[NOTIFICATION]: Data from server %p.\n", data);
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    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_FROG)) {
    		printk("Discovering TxDATA\n");
    		memcpy(&frog_discover_uuid, BT_UUID_TXDATA, sizeof(frog_discover_uuid));
    		discover_params.uuid = &frog_discover_uuid.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_FROG)) {
    		printk("Discovering CCC\n");
    		//memcpy(&frog_discover_uuid, BT_UUID_GATT_CCC, sizeof(frog_discover_uuid));
    		discover_params.uuid = ccc_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_INDICATE;
    		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;
    }
    
    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));
    
    	if (conn_err) {
    		printk("Failed to connect to %s (%u)\n", addr, conn_err);
    
    		bt_conn_unref(default_conn);
    		default_conn = NULL;
    
    		scan_start();
    		return;
    	}
    
    	printk("Connected: %s\n", addr);
    
    	if (conn == default_conn) {
    		memcpy(&frog_discover_uuid, BT_UUID_FROG, sizeof(frog_discover_uuid));
    		discover_params.uuid = &frog_discover_uuid.uuid;
    		discover_params.func = discover_func;
    		discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
    		discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
    		discover_params.type = BT_GATT_DISCOVER_PRIMARY;
    
    		err = bt_gatt_discover(default_conn, &discover_params);
    		if (err) {
    			printk("Discover failed(err %d)\n", err);
    			return;
    		}
    	}
    }
    
    static bool eir_found(struct bt_data *data, void *user_data)
    {
    	bt_addr_le_t *addr = user_data;
    	int i;
    
    	printk("[AD]: %u data_len %u\n", data->type, data->data_len);
    
    	switch (data->type) {
    	case BT_DATA_UUID128_SOME:
    	case BT_DATA_UUID128_ALL:
    		if (data->data_len % sizeof(uint16_t) != 0U) {
    			printk("AD malformed\n");
    			return true;
    		}
    
    		for (i = 0; i < data->data_len; i += sizeof(uint16_t)) {
    			const struct bt_uuid *uuid;
    			uint16_t u16;
    			int err;
    
    			memcpy(&u16, &data->data[i], sizeof(u16));
    			uuid = BT_UUID_DECLARE_128(sys_le16_to_cpu(u16));
    			if (bt_uuid_cmp(uuid, BT_UUID_FROG)) {
    				continue;
    			}
    
    			err = bt_le_scan_stop();
    			if (err) {
    				printk("Stop LE scan failed (err %d)\n", err);
    				continue;
    			}
    
    			err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
    						BT_LE_CONN_PARAM_DEFAULT,
    						&default_conn);
    			if (err) {
    				printk("Create connection failed (err %d)\n",
    				       err);
    				scan_start();
    			}
    
    			return false;
    		}
    	}
    
    	return true;
    }
    
    static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
    			 struct net_buf_simple *ad)
    {
    	char dev[BT_ADDR_LE_STR_LEN];
    
    	bt_addr_le_to_str(addr, dev, sizeof(dev));
    	printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n",
    	       dev, type, ad->len, rssi);
    
    	/* We're only interested in connectable events */
    	if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) {
    		bt_data_parse(ad, eir_found, (void *)addr);
    	}
    }
    
    static int scan_start(void)
    {
    	/* Use active scanning and disable duplicate filtering to handle any
    	 * devices that might update their advertising data at runtime.
    	 */
    	struct bt_le_scan_param scan_param = {
    		.type       = BT_LE_SCAN_TYPE_ACTIVE,
    		.options    = BT_LE_SCAN_OPT_NONE,
    		.interval   = BT_GAP_SCAN_FAST_INTERVAL,
    		.window     = BT_GAP_SCAN_FAST_WINDOW,
    	};
    
    	return bt_le_scan_start(&scan_param, device_found);
    }
    
    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 0x%02x)\n", addr, reason);
    
    	if (default_conn != conn) {
    		return;
    	}
    
    	bt_conn_unref(default_conn);
    	default_conn = NULL;
    
    	err = scan_start();
    	if (err) {
    		printk("Scanning failed to start (err %d)\n", err);
    	}
    }
    
    BT_CONN_CB_DEFINE(conn_callbacks) = {
    	.connected = connected,
    	.disconnected = disconnected,
    };
    
    int main(void)
    {
    	int err;
    
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	err = scan_start();
    
    	if (err) {
    		printk("Scanning failed to start (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Scanning successfully started\n");
    	return 0;
    }
    

    The GATTServer is found but gets disconnected directly and no service is found.
    The server code is the same as the one originally posted.

  • Hi Bjorn, 

    Please try to debug. If you see it's disconnected there must be a disconnected reason. You can use the sniffer trace to debug more. 
    Please make sure that you have gone through the NCS fundamental course and the BLE fundamental course in our Academy. 

    My suggestion is to start with the peripheral_nus and the central_nus. If you test with the unmodified samples do they work ? 
    I don't see much different between your application and the nus samples.  

Related