{"id":418,"date":"2015-08-10T08:21:17","date_gmt":"2015-08-10T15:21:17","guid":{"rendered":"http:\/\/visualgdb.com\/w\/?p=418"},"modified":"2015-08-10T08:21:32","modified_gmt":"2015-08-10T15:21:32","slug":"using-nrf51-device-in-a-bluetooth-le-central-role","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/nrf51\/central\/","title":{"rendered":"Using the nRF51 device in a Bluetooth LE Central role"},"content":{"rendered":"<p>This tutorial shows how to use the nRF51 devices in the Bluetooth LE Central role, communicating to the peripheral devices. We will create a simple firmware that will query the button state from the <a href=\"http:\/\/www.ti.com\/tool\/cc2650stk\">TI CC2650 SensorTag<\/a> and display it using the on-board LEDs.<\/p>\n<p>We will show how to extract the relevant data from the SensorTag attribute table and how to modify the Nordic examples to communicate with a previously unsupported device like the SensorTag. Before you begin, install VisualGDB 5.0, get a CC2650 SensorTag and follow the <a href=\"http:\/\/visualgdb.com\/tutorials\/arm\/nrf51\">basic nRF51 tutorial<\/a> to ensure you can program your board.<\/p>\n<ol>\n<li>Begin with launching the VisualGDB Embedded Project Wizard and specifying the project name and path:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wizard.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-419\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wizard.png\" alt=\"01-wizard\" width=\"800\" height=\"489\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wizard.png 800w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wizard-300x183.png 300w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><\/li>\n<li>By default, the wizard will create an embedded binary from a sample. Continue with that setting:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-binary3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-420\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-binary3.png\" alt=\"02-binary\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-binary3.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-binary3-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>On the next page select your nRF51 device from the list:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device5.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-421\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device5.png\" alt=\"03-device\" width=\"702\" height=\"709\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device5.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device5-150x150.png 150w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-device5-297x300.png 297w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>On the Sample page select the Bluetooth LE Heart Rate Service <strong>Client<\/strong> sample (don&#8217;t confuse it with the Heart Rate Service Device) and specify your board type that can be found on a sticker on the board:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-sample.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-422\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-sample.png\" alt=\"04-sample\" width=\"702\" height=\"709\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-sample.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-sample-150x150.png 150w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-sample-297x300.png 297w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Select a debugging method for your board. In this example we will use the on-board Segger J-Link with the Segger software package. Enable the &#8220;reset device after programming&#8221; checkbox, as otherwise the softdevice initialization code will be skipped resulting in strange crashes:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-423\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug3.png\" alt=\"05-debug\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug3.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/05-debug3-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Press &#8220;Finish&#8221; to create the project. As stopping the Bluetooth-enabled nRF51 firmware on a breakpoint would result in loss of packets and a reset, we will use debug logging to report various events happening in the firmware. Find the uart_init() function to see the UART parameters used by the sample. By default the baud rate is 38400 and the hardware flow control is enabled. <a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-uart.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-424\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-uart.png\" alt=\"06-uart\" width=\"695\" height=\"552\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-uart.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-uart-300x238.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>Open VisualGDB Project Properties and enable the Raw Terminal on the COM port connected to your board. In this tutorial we will use the port provided by the on-board Segger J-Link. Follow <a href=\"http:\/\/visualgdb.com\/tutorials\/arm\/nrf51\/uart\/\">this tutorial<\/a> for more details:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-terminal.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-425\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-terminal.png\" alt=\"07-terminal\" width=\"843\" height=\"744\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-terminal.png 843w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-terminal-300x265.png 300w\" sizes=\"(max-width: 843px) 100vw, 843px\" \/><\/a><\/li>\n<li>The HRS client sample registers clients for 2 Bluetooth LE services: heart rate service and battery level service. As the Nordic Bluetooth LE framework has a limit on the maximum client count, remove the calls to <strong>brs_c_init()<\/strong> and <strong>bas_c_init()<\/strong> from main() before adding a new client. You can also update the welcome message to indicate the new function of the example. Then press F5 to ensure that the firmware is successfully programmed into nRF51 and started: <a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-log.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-426\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-log.png\" alt=\"08-log\" width=\"695\" height=\"550\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-log.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-log-300x237.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>We will now modify the sample to report information about all nearby Bluetooth LE devices. First of all, change the scan_start() function to scan without white lists:\n<pre class=\"\">static void scan_start(void)\r\n{\r\n\u00a0\u00a0\u00a0 uint32_t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_code;\r\n\u00a0\u00a0\u00a0 m_scan_param.active\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = 0;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Active scanning set.\r\n\u00a0\u00a0\u00a0 m_scan_param.selective\u00a0\u00a0\u00a0 = 0;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ Selective scanning not set.\r\n\u00a0\u00a0\u00a0 m_scan_param.interval\u00a0\u00a0\u00a0\u00a0 = SCAN_INTERVAL;\/\/ Scan interval.\r\n\u00a0\u00a0\u00a0 m_scan_param.window\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = SCAN_WINDOW;\u00a0 \/\/ Scan window.\r\n\u00a0\u00a0\u00a0 m_scan_param.p_whitelist\u00a0 = NULL;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ No whitelist provided.\r\n\u00a0\u00a0\u00a0 m_scan_param.timeout\u00a0\u00a0\u00a0\u00a0\u00a0 = 0x0000;\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \/\/ No timeout.\r\n\r\n\u00a0\u00a0 \u00a0err_code = sd_ble_gap_scan_start(&amp;m_scan_param);\r\n\u00a0\u00a0\u00a0 APP_ERROR_CHECK(err_code);\r\n\r\n\u00a0\u00a0\u00a0 err_code = bsp_indication_set(BSP_INDICATE_SCANNING);\r\n\u00a0\u00a0\u00a0 APP_ERROR_CHECK(err_code);\r\n}<\/pre>\n<\/li>\n<li>Also replace the BLE_GAP_EVT_ADV_REPORT handler in <strong>on_ble_evt()<\/strong> with the code that prints the contents of the advertising packet:\n<pre class=\"\">\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"ADV report: (size=%d)\\n\", p_gap_evt-&gt;params.adv_report.dlen);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int i = 0; i &lt; p_gap_evt-&gt;params.adv_report.dlen; i++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"%02x \", p_gap_evt-&gt;params.adv_report.data[i]);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"\\n\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 for (int i = 0; i &lt; p_gap_evt-&gt;params.adv_report.dlen; i++)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"%c\u00a0 \", p_gap_evt-&gt;params.adv_report.data[i]);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"\\n\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;<\/pre>\n<\/li>\n<li>Press F5 to build the code and start debugging. Insert the battery into the CC2650 SensorTag or press the power button on it if the battery is already inserted:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-button.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-427\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-button.jpg\" alt=\"09-button\" width=\"533\" height=\"400\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-button.jpg 533w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-button-300x225.jpg 300w\" sizes=\"(max-width: 533px) 100vw, 533px\" \/><\/a><\/li>\n<li>Observe the output in the COMx window in Visual Studio. It should show the advertising packets received from the SensorTag:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-adv1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-429\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-adv1.png\" alt=\"10-adv\" width=\"695\" height=\"632\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-adv1.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-adv1-300x273.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>A typical advertising packet contains one or more fields in the following format: &lt;field length&gt; &lt;field type&gt;&lt;field data&gt;. The list of field types can be found <a href=\"https:\/\/www.bluetooth.org\/en-us\/specification\/assigned-numbers\/generic-access-profile\">here<\/a> and the field length includes the field type field. The advertising packet in the example contains 2 fields: &#8220;Flags = 0x05&#8221; and &#8220;Incomplete UUID List = 0xAA10 (accelerometer service)&#8221;. To see more details from the SensorTag, we need to actively request them by sending scan requests. This is done by setting <strong>m_scan_param.active = 1<\/strong> in <strong>scan_start().<\/strong> Update the scan_start accordingly() and start the new firmware:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-advex.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-430\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-advex.png\" alt=\"11-advex\" width=\"695\" height=\"631\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-advex.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-advex-300x272.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>The extended advertisement packet send in response to our active scan requests contains the SensorTag name in the LOCAL_NAME field (type=09). We will now change the BLE_GAP_EVT_ADV_REPORT handling code to connect to the devices that report their name as &#8220;CC2650 SensorTag&#8221;:\n<pre class=\"\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data_t adv_data;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 data_t type_data;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 adv_data.p_data = (uint8_t *)p_gap_evt-&gt;params.adv_report.data;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 adv_data.data_len = p_gap_evt-&gt;params.adv_report.dlen;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;adv_data,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;type_data);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 const char expectedName[] = \"CC2650 SensorTag\";\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (err_code != NRF_SUCCESS || \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 type_data.data_len != sizeof(expectedName) - 1 ||\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 strncmp((const char *)type_data.p_data, expectedName, sizeof(expectedName) - 1))\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"Unknown device\\n\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_code = sd_ble_gap_scan_stop();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 APP_ERROR_CHECK(err_code);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_code = bsp_indication_set(BSP_INDICATE_IDLE);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 APP_ERROR_CHECK(err_code);\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 err_code = sd_ble_gap_connect(&amp;p_gap_evt-&gt;params.adv_report.peer_addr,\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;m_scan_param,\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 &amp;m_connection_param);\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 APP_ERROR_CHECK(err_code);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 printf(\"Connected to device, status %d\\n\", (int)err_code);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 break;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }<\/pre>\n<\/li>\n<li>If you start the new firmware, you will see that it is constantly resetting itself because we have not registered any clients:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-reset.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-431\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-reset.png\" alt=\"12-reset\" width=\"695\" height=\"800\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-reset.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-reset-261x300.png 261w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>To register a client to the Simple Keys service (see the <a href=\"http:\/\/processors.wiki.ti.com\/images\/a\/a8\/BLE_SensorTag_GATT_Server.pdf\">SensorTag profile<\/a> for details) we need to call the <strong>ble_db_discovery_evt_register()<\/strong> function for the service with a UUID of 0xFFE0 (which is a shortcut for 0000<span style=\"text-decoration: underline;\"><strong>FFE0<\/strong><\/span>-0000-1000-8000 00805F9B34FB as defined by the Bluetooth LE specification):\n<pre class=\"\">static void button_service_client_init()\r\n{\r\n\u00a0\u00a0 \u00a0ble_uuid_t btn_uuid;\r\n\r\n\u00a0\u00a0 \u00a0btn_uuid.type = BLE_UUID_TYPE_BLE;\r\n\u00a0\u00a0 \u00a0btn_uuid.uuid = 0xFFE0;\r\n\r\n\u00a0\u00a0 \u00a0ble_db_discovery_evt_register(&amp;btn_uuid, btnsrv_discover_handler);\r\n}<\/pre>\n<\/li>\n<li>Create an empty btnserv_discover_handler() function and set a breakpoint inside it:\n<pre class=\"\">static void btnsrv_discover_handler(ble_db_discovery_evt_t * p_evt)\r\n{\r\n    asm(\"nop\");\r\n}<\/pre>\n<\/li>\n<li>Finally call button_service_client_init() from main() after the call todb_discovery_init() and run the new firmware. Once the breakpoint is hit, use the Watch window to explore the contents of <strong>p_evt<\/strong>:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-service.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-432\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-service.png\" alt=\"13-service\" width=\"695\" height=\"784\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-service.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-service-266x300.png 266w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>The contents of p_evt shows that the discovery logic has discovered a service with a UUID of <strong>0xffe0<\/strong> with 1 characteristic (UUID=<strong>0xffe1<\/strong>) and the characteristic configuration descriptor value is <strong>0x4a<\/strong>. This matches the information from the <a href=\"http:\/\/processors.wiki.ti.com\/images\/a\/a8\/BLE_SensorTag_GATT_Server.pdf\">SensorTag profile<\/a>:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-table.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-433\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-table.png\" alt=\"14-table\" width=\"865\" height=\"249\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-table.png 865w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/14-table-300x86.png 300w\" sizes=\"(max-width: 865px) 100vw, 865px\" \/><\/a><\/li>\n<li>To start receiving notifications when a button is pressed, we need to write the values of 01:00 to the CCCD register. This is done by modifying the btnsrv_discover_handler() as follows:\n<pre class=\"\">void btnsrv_discover_handler(ble_db_discovery_evt_t * p_evt)\r\n{\r\n\u00a0\u00a0 \u00a0static uint8_t enable_notifications[2] = { 1, 0 };\r\n\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0ble_gattc_write_params_t wparams;\r\n\u00a0\u00a0 \u00a0wparams.handle\u00a0\u00a0 = p_evt-&gt;params.discovered_db.charateristics[0].cccd_handle;\r\n\u00a0\u00a0 \u00a0wparams.len\u00a0\u00a0\u00a0\u00a0\u00a0 = sizeof(enable_notifications);\r\n\u00a0\u00a0 \u00a0wparams.p_value\u00a0 = enable_notifications;\r\n\u00a0\u00a0 \u00a0wparams.offset\u00a0\u00a0 = 0;\r\n\u00a0\u00a0 \u00a0wparams.write_op = BLE_GATT_OP_WRITE_REQ;\r\n\r\n\u00a0\u00a0 \u00a0int rc = sd_ble_gattc_write(p_evt-&gt;conn_handle, &amp;wparams);\r\n\u00a0\u00a0 \u00a0if (rc != NRF_SUCCESS)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0asm(\"bkpt 255\");\r\n\u00a0\u00a0 \u00a0}\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n}<\/pre>\n<\/li>\n<li>Finally we need to handle the HVX notifications that the CC2650 SensorTag will send. Create the following function and call it from <strong>ble_evt_dispatch()<\/strong> after all other calls:\n<pre class=\"\">static void btnsrv_on_ble_evt(ble_evt_t * p_ble_evt)\r\n{\r\n\u00a0\u00a0 \u00a0switch (p_ble_evt-&gt;header.evt_id)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0case BLE_GATTC_EVT_HVX:\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (p_ble_evt-&gt;evt.gattc_evt.params.hvx.len == 1)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0int data = p_ble_evt-&gt;evt.gattc_evt.params.hvx.data[0];\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0printf(\"Status %02x\\n\", data);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (data &amp; 1)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0LEDS_ON(1 &lt;&lt; LED_1);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0else\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0LEDS_OFF(1 &lt;&lt; LED_1);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (data &amp; 2)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0LEDS_ON(1 &lt;&lt; LED_2);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0else\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0LEDS_OFF(1 &lt;&lt; LED_2);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0break;\r\n\u00a0\u00a0 \u00a0}\r\n}<\/pre>\n<\/li>\n<li>Build and start the new firmware and see how pressing buttons on the SensorTag turns on the LEDs on nRF51:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/2boards.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-435\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/2boards.png\" alt=\"2boards\" width=\"700\" height=\"334\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/2boards.png 700w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/2boards-300x143.png 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/a><\/li>\n<li>The COMx window in Visual Studio should show the &#8220;Button HVX&#8221; messages produced by the HVX event handler:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-log.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-436\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-log.png\" alt=\"15-log\" width=\"695\" height=\"591\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-log.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/15-log-300x255.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>You can use the &#8220;Show on Code Map&#8221; and &#8220;Find All References&#8221; commands to find the code in the Nordic Bluetooth LE stack that accesses certain variables. E.g. search for references to the <strong>cccd_handle<\/strong> field:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/16-cccd.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-437\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/16-cccd.png\" alt=\"16-cccd\" width=\"695\" height=\"591\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/16-cccd.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/16-cccd-300x255.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<li>By putting a breakpoint on the line that assigns it and checking the values of the related variables you can see that the <strong>cccd_handle<\/strong> gets the handle to a descriptor with a UUID of 0x2902 that matches the Client Characteristics Configuration Descriptor mentioned in the <a href=\"http:\/\/processors.wiki.ti.com\/images\/a\/a8\/BLE_SensorTag_GATT_Server.pdf\">SensorTag Profile<\/a>:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/17-desc.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-438\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/17-desc.png\" alt=\"17-desc\" width=\"695\" height=\"688\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/17-desc.png 695w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/17-desc-300x297.png 300w\" sizes=\"(max-width: 695px) 100vw, 695px\" \/><\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use the nRF51 devices in the Bluetooth LE Central role, communicating to the peripheral devices.<\/p>\n","protected":false},"author":1,"featured_media":441,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18],"tags":[53,97,56,95],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/418"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=418"}],"version-history":[{"count":3,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/418\/revisions"}],"predecessor-version":[{"id":502,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/418\/revisions\/502"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media\/441"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=418"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=418"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=418"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}