<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/70201/zephyr-central-to-peripheral-communication</link><description>Hi, 
 
 I have some issues understanding exactly how a BT central acting as a Client can get the info it wants from a BT peripheral acting as a Server. I have a custom periheral (running zephyr BLE on a nrf52840) that I want to communicate with a custom</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Fri, 10 May 2024 11:24:23 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/70201/zephyr-central-to-peripheral-communication" /><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/482774?ContentTypeID=1</link><pubDate>Fri, 10 May 2024 11:24:23 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:3ed294ed-3b42-477a-b1c9-8b728649de31</guid><dc:creator>Karthik p</dc:creator><description>&lt;p&gt;Hi guys&lt;/p&gt;
&lt;p&gt;it&amp;#39;s funny :)that on chatting you guys created nice documentation for BLE central&amp;nbsp;&lt;/p&gt;
&lt;p&gt;grate job this helps on understanding central&lt;/p&gt;
&lt;p&gt;thank you&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/288845?ContentTypeID=1</link><pubDate>Wed, 13 Jan 2021 09:02:54 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:9fdc6048-80bb-403d-8192-4b7f84ad0fee</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Please check out the implementation in the Nordic example, foiund in NCS\nrf\samples\bluetooth\central_hr_coded, and it&amp;#39;s notify_func:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;static uint8_t notify_func(struct bt_conn *conn,
		struct bt_gatt_subscribe_params *params,
		const void *data, uint16_t length)
{
	if (!data) {
		printk(&amp;quot;[UNSUBSCRIBED]\n&amp;quot;);
		params-&amp;gt;value_handle = 0U;
		return BT_GATT_ITER_STOP;
	}

	if (length == 2) {
		uint8_t hr_bpm = ((uint8_t *)data)[1];

		printk(&amp;quot;[NOTIFICATION] Heart Rate %u bpm\n&amp;quot;, hr_bpm);
	} else {
		printk(&amp;quot;[NOTIFICATION] data %p length %u\n&amp;quot;, data, length);
	}

	return BT_GATT_ITER_CONTINUE;
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;which prints:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;Filters matched. Address: ef:1d:3e:56:18:9a (random) connectable: yes
Connection pending
Connected: ef:1d:3e:56:18:9a (random), tx_phy 4, rx_phy 4
The discovery procedure succeeded
[SUBSCRIBED]
[NOTIFICATION] Heart Rate 91 bpm
[NOTIFICATION] Heart Rate 92 bpm
[NOTIFICATION] Heart Rate 93 bpm
[NOTIFICATION] Heart Rate 94 bpm
[NOTIFICATION] Heart Rate 95 bpm
[NOTIFICATION] Heart Rate 96 bpm
[NOTIFICATION] Heart Rate 97 bpm
[NOTIFICATION] Heart Rate 98 bpm
[NOTIFICATION] Heart Rate 99 bpm
[NOTIFICATION] Heart Rate 100 bpm
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;So what you are looking for is the line:&lt;/p&gt;
&lt;p&gt;uint8_t hr_bpm = ((uint8_t *)data)[1];&lt;/p&gt;
&lt;p&gt;Without having checked, I see that data[0] is 6. I believe it is following the Bluetooth specification for the Heart Rate Service. I believe it is the location of the sensor, or some similar metadata. In fact, it is. Check out the implementation of int bt_hrs_notify(uint16_t heartrate) in hrs.c, which is used by the peripheral: NCS\nrf\samples\bluetooth\peripheral_hr_coded.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;int bt_hrs_notify(uint16_t heartrate)
{
	int rc;
	static uint8_t hrm[2];

	hrm[0] = 0x06; /* uint8, sensor contact */
	hrm[1] = heartrate;

	rc = bt_gatt_notify(NULL, &amp;amp;hrs_svc.attrs[1], &amp;amp;hrm, sizeof(hrm));

	return rc == -ENOTCONN ? 0 : rc;
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Best regards,&lt;/p&gt;
&lt;p&gt;Edvin&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/288720?ContentTypeID=1</link><pubDate>Tue, 12 Jan 2021 15:54:04 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:715776e2-be19-411d-bd3b-3189424e6ea4</guid><dc:creator>Jonas Lien</dc:creator><description>&lt;p&gt;Thanks a lot for such a detailed answer, I really appreciate it and it helps clear things up. I sort of understand the callback process for service discovery now, but there&amp;#39;s still a couple of things I&amp;#39;m unsure about.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;If we continue one the central heart rate service example and look at the notify_func as you mentioned. First of all, what the peripheral_hr sends out is:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;static void hrs_notify(void)
{
	static u8_t heartrate = 90U;

	/* Heartrate measurements simulation */
	heartrate++;
	if (heartrate == 160U) {
		heartrate = 90U;
	}

	bt_gatt_hrs_notify(heartrate);
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Which is the simulated heartrate that we expect. However, what is displayed by the central_hr is&lt;br /&gt;&lt;br /&gt;&lt;pre class="ui-code" data-mode="text"&gt;static u8_t notify_func(struct bt_conn *conn,
			struct bt_gatt_subscribe_params *params,
			const void *data, u16_t length)
{
	if (!data) {
		printk(&amp;quot;[UNSUBSCRIBED]\n&amp;quot;);
		params-&amp;gt;value_handle = 0U;
		return BT_GATT_ITER_STOP;
	}

	printk(&amp;quot;[NOTIFICATION] data %p length %u,\t&amp;quot;, data, length);

	return BT_GATT_ITER_CONTINUE;
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;and corresponds to terminal output:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=" " src="https://devzone.nordicsemi.com/resized-image/__size/320x240/__key/communityserver-discussions-components-files/4/8171.img1.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Alright. So my understanding is what is printed is the address that stores the data. Trying to dereference it, however, gets me no further. The data was sent as u8_t, but dereferencing it as either u8_t or u16_t (shown below) gives me the following (shown further below):&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="text"&gt;static u8_t notify_func(struct bt_conn *conn,
			struct bt_gatt_subscribe_params *params,
			const void *data, u16_t length)
{
	if (!data) {
		printk(&amp;quot;[UNSUBSCRIBED]\n&amp;quot;);
		params-&amp;gt;value_handle = 0U;
		return BT_GATT_ITER_STOP;
	}

	printk(&amp;quot;[NOTIFICATION] data %p length %u,\t&amp;quot;, data, length);
	u8_t bleData = *(u8_t*)data;
	//memcpy(&amp;amp;bleData, data, sizeof(bleData));
	printk(&amp;quot;bleData: %u\n&amp;quot;, bleData);


	return BT_GATT_ITER_CONTINUE;
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Output for u8_t:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=" " src="https://devzone.nordicsemi.com/resized-image/__size/320x240/__key/communityserver-discussions-components-files/4/5037.img2.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Output for u16_t:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=" " src="https://devzone.nordicsemi.com/resized-image/__size/320x240/__key/communityserver-discussions-components-files/4/3884.img3.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;What am I doing wrong?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Looking forward once again to your answer.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/288194?ContentTypeID=1</link><pubDate>Fri, 08 Jan 2021 13:36:40 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:c9ba12b5-1090-4dd8-8ac1-06b65d5a032e</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Hello,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
[quote user="Jonas Lien"]Is it possible for you to draw out an example of retrieving the read value from the peripheral and store it in e.g. a struct or push it to console/printk?&amp;nbsp;[/quote]
&lt;p&gt;&amp;nbsp;That is more or less what the central_uart example does.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Look at how this example is built up:&lt;/p&gt;
&lt;p&gt;In the connected() callback function, gatt_discover() is called with a pointer to the connection, conn.&lt;/p&gt;
&lt;p&gt;Inside gatt_discover(), it calls bt_gatt_dm_start(), with the service that it is looking for as an input parameter. Exactly how you would do it if you want to list all services, I am not sure. However, a central typically (and also in your case) knows what services it is looking for. Isn&amp;#39;t that also the case in your case?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now, after calling bt_gatt_dm_start, you will hopefully get a callback to discovery_complete, or whatever you called your discovery_cb.completed function.&lt;/p&gt;
&lt;p&gt;You need to look at the definitions of&amp;nbsp;&lt;/p&gt;
&lt;p&gt;bt_nus_handles_assign()&lt;br /&gt;bt_nus_subscribe_receive()&lt;/p&gt;
&lt;p&gt;to see what they do, and implement the similar thing in your application.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Looking at the central_hrs example, which follows the implementation you have already started on, which looks to be the zephyr method (not used in NCS as far as I can see), it works in a similar fashion.&lt;/p&gt;
&lt;p&gt;From the zephyr\samples\bluetooth\central_hr example&amp;#39;s connected callback function:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;printk(&amp;quot;Connected: %s\n&amp;quot;, addr);

	if (conn == default_conn) {
		memcpy(&amp;amp;uuid, BT_UUID_HRS, sizeof(uuid));
		discover_params.uuid = &amp;amp;uuid.uuid;
		discover_params.func = discover_func;
		discover_params.start_handle = 0x0001;
		discover_params.end_handle = 0xffff;
		discover_params.type = BT_GATT_DISCOVER_PRIMARY;

		err = bt_gatt_discover(default_conn, &amp;amp;discover_params);
		if (err) {
			printk(&amp;quot;Discover failed(err %d)\n&amp;quot;, err);
			return;
		}
	}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;So it will start the discovery procedure for the Heart Rate Service UUID. If found, this will trigger the discover_func() callback:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="c_cpp"&gt;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(&amp;quot;Discover complete\n&amp;quot;);
		(void)memset(params, 0, sizeof(*params));
		return BT_GATT_ITER_STOP;
	}

	printk(&amp;quot;[ATTRIBUTE] handle %u\n&amp;quot;, attr-&amp;gt;handle);

	if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_HRS)) {
		memcpy(&amp;amp;uuid, BT_UUID_HRS_MEASUREMENT, sizeof(uuid));
		discover_params.uuid = &amp;amp;uuid.uuid;
		discover_params.start_handle = attr-&amp;gt;handle + 1;
		discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;

		err = bt_gatt_discover(conn, &amp;amp;discover_params);
		if (err) {
			printk(&amp;quot;Discover failed (err %d)\n&amp;quot;, err);
		}
	} else if (!bt_uuid_cmp(discover_params.uuid,
				BT_UUID_HRS_MEASUREMENT)) {
		memcpy(&amp;amp;uuid, BT_UUID_GATT_CCC, sizeof(uuid));
		discover_params.uuid = &amp;amp;uuid.uuid;
		discover_params.start_handle = attr-&amp;gt;handle + 2;
		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
		subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);

		err = bt_gatt_discover(conn, &amp;amp;discover_params);
		if (err) {
			printk(&amp;quot;Discover failed (err %d)\n&amp;quot;, err);
		}
	} else {
		subscribe_params.notify = notify_func;
		subscribe_params.value = BT_GATT_CCC_NOTIFY;
		subscribe_params.ccc_handle = attr-&amp;gt;handle;

		err = bt_gatt_subscribe(conn, &amp;amp;subscribe_params);
		if (err &amp;amp;&amp;amp; err != -EALREADY) {
			printk(&amp;quot;Subscribe failed (err %d)\n&amp;quot;, err);
		} else {
			printk(&amp;quot;[SUBSCRIBED]\n&amp;quot;);
		}

		return BT_GATT_ITER_STOP;
	}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;As you can see, it looks for several characteristic UUIDs. This is because it has several things that it wants to discover. First, if this is the callback for the service, it will pass the test:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&amp;nbsp;(!&lt;/span&gt;&lt;span&gt;bt_uuid_cmp&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;discover_params&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;uuid&lt;/span&gt;&lt;span&gt;,&amp;nbsp;&lt;/span&gt;&lt;span&gt;BT_UUID_HRS&lt;/span&gt;&lt;span&gt;))&amp;nbsp;{&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;meaning that the service was discovered (the first bt_gatt_discover() was called with discover_params.type = BT_GATT_DISCOVER_PRIMARY.)&lt;/div&gt;
&lt;div&gt;Next it will try to discover the hrs characteristic, and when that is discovered in the next callback, it will try to discover the descriptor (CCC), which is used to enable and disable notifications. If you prefer this method, try to replace the UUIDs with the ones that you are looking for. First for the service, then the characteristic, and finally the descriptor that you want to enable notifications on.&lt;/div&gt;
&lt;div&gt;When the notifications are enabled, look how it is linked to the notification callback, &amp;quot;notify_func&amp;quot;, which will be called whenever a notification is received. Then you can print the data that you received using printk() from the notification callback.&lt;/div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;div&gt;Best regards,&lt;/div&gt;
&lt;div&gt;Edvin&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/288127?ContentTypeID=1</link><pubDate>Fri, 08 Jan 2021 10:30:57 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:0797940e-e07e-4aa8-a2a3-3a22f2fd442f</guid><dc:creator>Jonas Lien</dc:creator><description>&lt;p&gt;Hi Edvin, and thanks for your swift reply!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Yes, I have tested with the nrf Connect Mobile successfully picks up the peripheral and can connect + read/write.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;With regards to the central_uart link, it was a bit hard to grasp. What I struggle with, is understanding how the elaborate callback scheme works. Is it possible for you to draw out an example of retrieving the read value from the peripheral and store it in e.g. a struct or push it to console/printk?&amp;nbsp;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: Zephyr Central to Peripheral communication</title><link>https://devzone.nordicsemi.com/thread/287931?ContentTypeID=1</link><pubDate>Thu, 07 Jan 2021 11:36:39 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:ab461441-580a-4a62-b766-a3493b94f06e</guid><dc:creator>Edvin</dc:creator><description>&lt;p&gt;Hello Jonas,&lt;/p&gt;
&lt;p&gt;I see you have attached some snippets from your peripheral, but before you start working on your central, are you able to write to and read from the characteristics that you want to using a phone and the nRF Connect app as a central? Alternatively on nRF Connect for Desktop using an extra DK?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I believe that the discovery callback function (in your case &amp;quot;discovered()&amp;quot; is triggered several times, one time for each service and characteristic.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Do you see any callbacks to your discover function?&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;For service discovery and how to use the characteristics from a central, I would recommend that you take a look at the sample found in:&lt;/p&gt;
&lt;p&gt;NCS\nrf\samples\bluetooth\central_uart&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Best regards,&lt;/p&gt;
&lt;p&gt;Edvin&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>