Setting BLE in nRF52
Image for post

Prerequisites

This is tutorial is not intended to be a guide for learning C language or about the Nordic SDK platform. It’s primary target is to provide developers a concise guide about integrating peripheral modules and features into active applications.

If you are a beginner, I would recommend you look into an nRF52 Project Setup guide like this one.

https://medium.com/vicara-hardware-university/nrf52-project-setup-with-segger-embedded-system-f64958052a2d

Another easy way to get started with coding, without bothering with all basic stuff like files and driver inclusion, check out this Code Generation Tool

nrf52 Code Generator: https://vicara.co/nrf52-code-generator

BLE

In nRF52 devices, the BLE data is received and parsed as an UART transceiver port. Thus, to enable BLE communication, we will need to use a NUS (Nordic UART Service) module.

Image for post

Implementing BLE NUS in nRF52832

In the following section, I will provide a guide as it has been tested in the nRF52832 Dev Kit. However, the same structure will remain common across all nRF52 devices.

Including correct Headers

Image for post

Include the SPI Driver files. This file should be titled, ble_nus.cUpdate sdk_config.h File

  • BLE_NUS_ENABLED has to be set to set to 1.

In main.c File

Header File

#include "ble_nus.h"Connection Status Check Variablebool is_ble_connected;

API Function Definitions

static uint16_t check_nus_rx_data_available(void); // -> Returns an uint16 with the number of bytes of data available in the RX bufferstatic void get_nus_data(uint8_t *buf, uint16_t len); // -> len number of received bytes copied to bufstatic void send_nus_data(uint8_t *buf, uint16_t size); // -> Sends an array of uint8/* ************* API Functions ************** */
staticuint16_tcheck_nus_rx_data_available(void)
{
return nus_buffer.head;
}

//VCE: Function to get data from NUS RX buffer

staticvoidget_nus_data(uint8_t *buf, uint16_t len)
{
if(len < NUS_BUFFER_LENGTH)
   {
       memcpy(buf, nus_buffer.buffer, len);
       memset(nus_buffer.buffer, 0, NUS_BUFFER_LENGTH);
   }
}

staticvoidsend_nus_data(uint8_t *buf, uint16_t size)
{
   ble_nus_data_send(&m_nus, buf, &size, m_conn_handle);
}

Instance Definition

#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */
BLE_NUS_DEF (m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);
staticuint16_t   m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;            /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */

#define NUS_BUFFER_LENGTH 512
BUFFER(nus, NUS_BUFFER_LENGTH);

NUS Data Handler

/**@brief Function for handling the data from the Nordic UART Service.
*
* @details This function will process the data received from the Nordic UART BLE Service and send
*          it to the UART module.
*
* @param[in] p_evt       Nordic UART Service event.
*//**@snippet [Handling the data received over BLE] */

staticvoidnus_data_handler(ble_nus_evt_t * p_evt)
{

if (p_evt->type == BLE_NUS_EVT_RX_DATA)
   {
uint32_t err_code;

       NRF_LOG_DEBUG("Received data from BLE NUS. Writing data on UART.");
       NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);

if(NUS_BUFFER_LENGTH - nus_buffer.head >=  p_evt->params.rx_data.length)
       {
           memcpy(&nus_buffer.buffer[nus_buffer.head], p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
           nus_buffer.head += p_evt->params.rx_data.length;
       }
elseif(NUS_BUFFER_LENGTH > p_evt->params.rx_data.length)
       {
           memset(nus_buffer.buffer, 0, NUS_BUFFER_LENGTH);
           memcpy(nus_buffer.buffer, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
           nus_buffer.head = p_evt->params.rx_data.length;
       }
   }

}

Init Function

/**@brief Function for initializing services that will be used by the application.
*/
staticvoidservices_init(void)
{
ret_code_t         err_code;
nrf_ble_qwr_init_t qwr_init = {0};

// Initialize Queued Write Module.
   qwr_init.error_handler = nrf_qwr_error_handler;

   err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
   APP_ERROR_CHECK(err_code);


// VCE: Initialize NUS.
ble_nus_init_t     nus_init;
   memset(&nus_init, 0,
sizeof(nus_init));

   nus_init.data_handler = nus_data_handler;

   err_code = ble_nus_init(&m_nus, &nus_init);
   APP_ERROR_CHECK(err_code);
}

Update gatt_init()

Note: The existing gatt_init function needs to be replaced with the below one. gatt_evt_handler also needs to be added

/**@brief Function for handling events from the GATT library. */
voidgatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_tconst * p_evt)
{
if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
   {
       m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
       NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
   }
   NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
                 p_gatt->att_mtu_desired_central,
                 p_gatt->att_mtu_desired_periph);
}


/**@brief Function for initializing the GATT module.
*/
staticvoidgatt_init(void)
{
ret_code_t err_code;

   err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
   APP_ERROR_CHECK(err_code);

   err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
   APP_ERROR_CHECK(err_code);
}

Call services_init() in main()

services_init();

Conclusion

With the above steps anyone can easily get started with BLE.

NOTE

There is another easier method to initialize and auto-generate code for nRF52. This tool, will handle all library additions and code generations for a variety of peripherals like SPI, I2C, UART etc.

Link: https://vicara.co/nrf52-code-generator