This web page is a Bluetooth NRF51822 example project. It is constantly updated with a new code and circuit diagrams.
Have a comment, question, or need a project to develop?
Email to: info@cnktechlabs.com.

Bluetooth Low Energy (BLE, also marketed as Bluetooth Smart) started as part of the Bluetooth 4.0 Core Specification. It's tempting to present BLE as a smaller, highly optimized version of its bigger brother, classic Bluetooth, but in reality, BLE has an entirely different lineage and design goals. The nRF51 series of System on Chip (SoC) devices embed a powerful yet low power ARM Cortex-M0 processor with our industry leading 2.4 GHz RF transceivers. BLE's over the air data rate is 1Mbit/s using GFSK modulation.
A BLE device may operate in different modes depending on required functionality:

In advertising mode, the BLE device periodically transmits advertising information and may respond with more information upon request from other devices. The scanner device, on the other hand, listens advertising information transmitted by other devices and may request additional information if active scan mode is enabled. A scanner-only device works in passive mode whereby it only listens for advertising packets. In that case, only receiver functionality is required in the RF part of the design. Similarly, an advertising-only device may just have a transmitter part of the design. It definitely enables additional use cases with cost-sensitive applications.
In order to establish a connection, one device has to be in advertising mode (periferal device) (and allow for a connection) and the other device in Initiator mode (central device).
Once a connection is established, the initiator assumes the role of Master device (central device) and the advertiser becomes a Slave device (periferal device). Slave devices may have only one connection at a time, while master devices may have multiple connections with different slave devices simultaneously.
Basic BLE over the air packets include a 1 byte preamble, 4 byte access codes correlated with the RF channel number used, a PDU that can be between 2 to 39 bytes and 3 bytes of CRC. Hence the shortest packet would have 80 bits transmitted within 80usec., and the longest packet of 376 bits will be transmitted within less than 0.3 millisecond.

In combination with the very flexible orthogonal power management system and a Programmable Peripheral Interconnect (PPI) event system, the nRF51 series enables you to make ultra-low power wireless solutions. The nRF51 series offers pin compatible device options for Bluetooth low energy, proprietary 2.4 GHz, and ANT solutions giving you the freedom to develop your wireless system using the technology that suits your application the best.


Physical Layer

The physical (PHY) layer is the part that actually contains the analog communications circuitry, capable of modulating and demodulating analog signals and transforming them into digital symbols.
The radio uses the 2.4 GHz ISM (Industrial, Scientific, and Medical) band to communicate and divides this band into 40 channels from 2.4000 GHz to 2.4835 GHz.
The standard uses a technique called frequency hopping spread spectrum, in which the radio hops between channels on each connection event using the following formula:

channel = (current channel + hop) mod 37

The value of the hop is communicated when the connection is established and is therefore different for every new established connection. This technique minimizes the effect of any radio interference potentially present in the 2.4 GHz band across any single channel, especially since WiFi and classic Bluetooth are prevalent in this band and devices might experience heavy interference near devices with a strong transmission power. The modulation chosen to encode the bitstream over the air is Gaussian Frequency Shift Keying (GFSK), the same modulation used by classic Bluetooth and several other proprietary low-power wireless protocols. The modulation rate for Bluetooth Low Energy is fixed at 1 Mbit/s, which is therefore the upper physical throughput limit for the technology


Bluetooth Device Address

The fundamental identifier of a Bluetooth device, similar to an Ethernet Media Access Control (MAC) adddress, is the Bluetooth device address. This 48-bit (6-byte) number uniquely identifies a device among peers. There are two types of device addresses, and one or both can be set on a particular device:

  • Public device address
    This is the equivalent to a fixed, BR/EDR, factory-programmed device address. It must be registered with the IEEE Registration Authority and will never change during the lifetime of the device.
  • Random device address
    This address can either be preprogrammed on the device or dynamically generated at runtime.

On NRF51822 the BLE address can be read from the NRF_FICR->DEVICEADDR[X] and is of 48 bits length (6 bytes). This registers are randomly generated during the IC production, and they are unique for each device (2^46 different combinations, due to MSbits set to '11')
You can read out the address using the API call, "sd_ble_gap_addr_get(&address);"

ble_gap_addr_t    my_addr;    // Declare my_addr where the address will be stored
err_code = sd_ble_gap_addr_get( &my_addr );    // Read the address

or reading the FICR->DEVICEADDR[x] registers.

nrf51_deprecated.h:
/* The registers FICR.DEVICEADDR0 and FICR.DEVICEADDR1 were renamed into an array. */
#define DEVICEADDR0 DEVICEADDR[0]
#define DEVICEADDR1 DEVICEADDR[1]

nrf51.h:
__I uint32_t DEVICEADDR[2]; /*!< Device address.*/

nfr51_bitfields.h:
/* Bit 0 : Device address type. */
#define FICR_DEVICEADDRTYPE_DEVICEADDRTYPE_Pos (0UL) /*!< Position of DEVICEADDRTYPE field. */
#define FICR_DEVICEADDRTYPE_DEVICEADDRTYPE_Msk (0x1UL << FICR_DEVICEADDRTYPE_DEVICEADDRTYPE_Pos) /*!< Bit mask of DEVICEADDRTYPE field. */
#define FICR_DEVICEADDRTYPE_DEVICEADDRTYPE_Public (0UL) /*!< Public address. */
#define FICR_DEVICEADDRTYPE_DEVICEADDRTYPE_Random (1UL) /*!< Random address. */


Advertising and Scanning

During Advertising BLE broadcasts data. BLE broadcasts data for device discovery and data publishing. There are 2 types of data packets that can be transmitted, Advertising packet and Scan Response packet, each can have up to 31 bytes payload. The advertiser address is included in the broadcast data in addition to the payload. Advertising packets serve two purposes:

  • To broadcast data for applications that do not need the overhead of a full connection establishment
  • To discover slaves and to connect to them

Each advertising packet can carry up to 31 bytes of advertising data payload as well the basic header information (including Bluetooth device address). These packets are simply broadcast blindly over the air by the advertiser without the previous knowledge of the presence of any scanning device. Packets are sent at a fixed rate defined by the advertising interval. The advertising interval can range from 20 ms to 10.24 s. The shorter the interval, the higher the frequency at which advertising packets are broadcast, leading to a higher probability of those packets being received by a scanner, but higher amounts of packets transmitted also translate to higher power consumption.

Because advertising uses a maximum of three frequency channels and the advertiser and the scanner are not synchronized in any way, an advertising packet will be received successfully by the scanner only when they randomly overlap



Link Layer

The Link Layer is the part that directly interfaces with the PHY, and it is usually implemented as a combination of custom hardware and software.
Part of the link layer implemented in hardware:

  • Preamble - used for internal protocol management. Advertising packets have 10101010b as the preamble.
  • Access Address - This is always 0x8E89BED6 (10001110100010011011111011010110b) for advertising packets.
  • Protocol framing
  • CRC generation and verification - 3 byte value calculated over PDU.
  • Data whitening
  • Random number generation
  • AES encryption

There are three fixed Advertisement Channels operating on frequencies 2402 MHz (channel 37), 2426 MHz (channel 38) and 2480 MHz (channel 39).




GAP (Advertising and Connections)

GAP provides a framework that any BLE implementation must follow to allow devices to discover each other, broadcast data, establish secure connections, and perform many other fundamental operations in a standard, universally understood manner. GAP controls connections and advertising in Bluetooth. GAP is what makes your device visible to the outside world, and determines how two devices can (or can't) interact with each other.

Generic Access Profile Assigned Numbers


GAP specifies four roles that a device can adopt to join a BLE network:

  • Broadcaster
    Optimized for transmit-only applications that distribute data regularly, the broadcaster role periodically sends out advertising packets with data. This role is usually assigned to a device capable of both transmitting and receiving. A public thermometer that broadcasts temperature readings to any interested devices would be a good example of a broadcaster. Broadcasters send data in advertising packets rather than connection data packets, and the data is accessible to any device that is listening. The broadcaster role uses the Link Layer advertiser role.
  • Observer
    Optimized for receive-only applications that want to collect data from broadcasting devices, the observer role listens for data embedded in advertising packets from broadcasting peers. A good example isa device with a display is a typical application of this role, such as a table computer that displays temperature data from a broadcastonly temperature sensor. The observer role uses the Link Layer scanner role.
  • Central
    The central role corresponds to the Link Layer master. A device capable of establishing multiple connections to peers, the central role is always the initiator of connections and essentially allows devices onto the network. The BLE protocol is asymmetric, which means that the computing requirements of the Link Layer master are larger than the ones of a Link Layer slave. The central role is usually played by a smartphone or tablet in the network, because it has access to powerful CPUs and memory resources. This allows it to maintain connections to multiple devices. The central starts by listening for other devices' advertising packets and then initiates a connection with a selected device. This process can be repeated to include multiple devices in a single network.
  • Peripheral
    The peripheral role corresponds to the Link Layer slave. This role uses advertising packets to allow centrals to find it and, subsequently, to establish a connection with it. The BLE protocol is optimized to require few resources for peripheral implementation, at least in terms of processing power and memory. This paves the way to a large market of inexpensive BLE peripherals.

Example - A fitness tracker is paired with a smartphone.
The fitness tracker's GAP role is peripheral, and it acts as a GATT server when the phone requests data from its sensors. It can also sometimes act as a GATT client when it requests accurate time data from the smartphone to update its internal clock for data timestamping. The GATT client/server roles depend exclusively on the direction in which the data requests and responses transactions flow, whereas GAP roles stay constant as peripheral for the fitness tracker and central for the smartphone.



GATT

The Generic Attribute Profile establishes in detail how to exchange all profile and user data over a BLE connection. In contrast with GAP, which defines the low-level interactions with devices, GATT deals only with actual data transfer procedures and formats.

Periferal device is a server

Periferal device is a client




Nordic Semiconductor

Nordic Semiconductor provides a variety of code examples ranging from a basic LED ON/OFF function to a complete Bluetooth communication system. Download and install Nordic SDK or import example code from ARM KEIL development studio Pack Installer.

The key component that is responsible for connection between a user application and NRF51822 hardware is the NRF51822 SoftDevice. The S130 SoftDevice integrates a BLE Controller and Host, and provides a full and flexible API.
The SoftDevice is essentially a black box that sits in the bottom part of flash memory and implements features such as the BLE stack and peripheral role support. The user (application) code sits at a higher address in flash memory and makes calls to this lower-level SoftDevice as appropriate.



UART over the BLE code example: Sends Text Messages over the Bluetooth link


UART On WiKi


For my project I will need to send binary data over the bluetooth link. A good code example to start with is the ble_app_uart_s130_pca10028 UART connection over the Bluetooth link example. Sending text messages and sending binary data over the bluetooth link are very similar. The example can be found in the NRF51822 Software Development Kit. Download the SDK then open nRF5_SDK_11.0.0_89a8197\examples\ble_peripheral\ble_app_uart\pca10028\s130\arm5_no_packs\ble_app_uart_s130_pca10028.uvprojx. This example code demonstrates how to send simple text messages between NRF51822 SoC and a mobile device. You might need to procure a UART-USB converter cable. NRF51822 use UART serial port for serial communication where a stsndard PC has USB ports for serial communication. To bridge between the two ports you will need to use UART-USB adapter (conveter).

FTDI UART-USB cable offers a simple way to display on a PC of the received data over the bluetooth link. FTDI offers a variety of UART-USB converters, one of them is shown below (TTL-232RG-VSW3V3-WE). If you connect "loose wires" end of the cable to your Bluetooth board as shown below and connect USB side of the cable to a PC, the data received over the BLE link can be printed to a serial terminal like Termite.

Click on the image to go to the datasheet

An example of a connection diagram between RedBearLab BLE Nano Kit - nRF51822 a non USB board and TTL232RG UART to USB converter

TTL-232RG Generic Cables Signal Descriptions

TTL-232RG-VSW3V3-WE Electrical Parameters

TTL-232RG-VREG3V3-WE Electrical Parameters

TTL-232RG-VSW3V3-WE circuit diagram

TTL-232RG-VSW3V3-WE is a generic UART to USB converter that can be used with any UART 3.3V devices.
Raytac corporation offers an inexpensive demo board - mdbt40.
This board does not have a USB interface. To connect this board to a PC you will need to use TTL-232RG-VSW3V3-WE or a similar cable.

Raytac Demo Board

Initializing BLE: Click on the text below to view a function details.

  1. Initialize the application timer module
  2. Initialize the UART module
  3. Initialize Buttons and LEDs
  4. Initialize SoftDevice
  5. Initialize GAP parameters
  6. Initialize Services
  7. Initialize Advertising


1 - Initialize the application timer module

#define APP_TIMER_INIT(PRESCALER, OP_QUEUE_SIZE, SCHEDULER_FUNC)    \
do                           \
{                           \
    static uint32_t APP_TIMER_BUF[CEIL_DIV(APP_TIMER_BUF_SIZE(OP_QUEUE_SIZE),\
        sizeof(uint32_t))];   \
    uint32_t ERR_CODE = app_timer_init((PRESCALER),     \
        (OP_QUEUE_SIZE) + 1,   \
        APP_TIMER_BUF,   \
        SCHEDULER_FUNC);   \
    APP_ERROR_CHECK(ERR_CODE);                \
} while (0)

2 - Initialize the UART module

/**@brief Function for initializing the UART module.*/
/**@snippet [UART Initialization] */
static void uart_init(void)
{
    uint32_t err_code;
    const app_uart_comm_params_t comm_params =
    {
         RX_PIN_NUMBER,
         TX_PIN_NUMBER,
         RTS_PIN_NUMBER,
         CTS_PIN_NUMBER,
         APP_UART_FLOW_CONTROL_ENABLED,
         false,
         UART_BAUDRATE_BAUDRATE_Baud115200
    };
    APP_UART_FIFO_INIT( &comm_params,
          UART_RX_BUF_SIZE,
          UART_TX_BUF_SIZE,
          uart_event_handle,
          APP_IRQ_PRIORITY_LOW,
          err_code);
    APP_ERROR_CHECK(err_code);
}

3 - Initialize Buttons and LEDs

/** *@brief Function for initializing buttons and leds.
*
* @param[out] p_erase_bonds Will be true if the clear bonding button was pressed to wake the application up.
*/
static void buttons_leds_init(bool * p_erase_bonds)
{
    bsp_event_t startup_event;

    uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS,
         APP_TIMER_TICKS(100, APP_TIMER_PRESCALER),
         bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}

4 - Initialize SoftDevice

static void ble_stack_init(void)
{
    uint32_t err_code;

    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    // Initialize SoftDevice.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
        PERIPHERAL_LINK_COUNT,
        &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    // Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);

    // Enable BLE stack.
    #if (NRF_SD_BLE_API_VERSION == 3)
    ble_enable_params.gatt_enable_params.att_mtu = NRF_BLE_MAX_MTU_SIZE;
    #endif
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);
    // Subscribe for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

5 - Initialize GAP parameters

/**@brief Function for the GAP initialization.
*
* @details This function will set up all the necessary GAP (Generic Access Profile) parameters of
* the device. It also sets the permissions and appearance.
*/ static void gap_params_init(void)
{
    uint32_t     err_code;
    ble_gap_conn_params_t gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
     (const uint8_t *) DEVICE_NAME,
     strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);
    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
    gap_conn_params.slave_latency = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}

5 - Initializing services

/**@brief Function for initializing services that will be used by the application.
*/
static void services_init(void)
{
    uint32_t     err_code;
    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);
}

6 - Initializing Advertising

/**@brief Function for initializing the Advertising functionality.
*/
static void advertising_init(void)
{
    uint32_t err_code;
    ble_advdata_t advdata;
        ble_advdata_t scanrsp;
    ble_adv_modes_config_t options;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    advdata.name_type = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = false;
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;

    memset(&scanrsp, 0, sizeof(scanrsp));
    scanrsp.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    scanrsp.uuids_complete.p_uuids = m_adv_uuids;
    memset(&options, 0, sizeof(options));
    options.ble_adv_fast_enabled = true;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, &scanrsp, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}

Handling data received over the Bluetooth connection

Lets take a look at the "nus_data_handler" function. This function is called when a character is received over the bluetooth link, BLE. When initializing the service in the services_init() function, the application passes the function nus_data_handler, which is used for handling the received data. When the Nordic UART Service indicates that data has been received over BLE from the peer, the same data is relayed to the UART.
This function processes data received from the Nordic UART BLE Service, the data over the Bluetooth link, and sends it to the UART module. It will print the received data to a serial terminal like Termite.

/**@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. "nus_data_handler" is triggered when any data is received over 
 *          the BLE channel. 
 *
 * @param[in] p_nus    Nordic UART Service structure - 
 * - This structure contains status information related to the service
 * @param[in] p_data   Data to be send to UART module.
 * @param[in] length   Length of the data.
 */
/**@snippet [Handling the data received over BLE] */

static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length)
{
    for (uint32_t i = 0; i < length; i++)
    {
        // Print the received data from BLE one character at a time to a UART terminal
        while(app_uart_put(p_data[i]) != NRF_SUCCESS); 
    }
    while(app_uart_put('\n') != NRF_SUCCESS);
}

		

The nus_data_handler has to be declared to the Nordic UART Service structure, nus_init.data_handler = nus_data_handler, as shown below.

/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    uint32_t       err_code;
    ble_nus_init_t nus_init;
    
    memset(&nus_init, 0, sizeof(nus_init));
	
    //The application passes the function nus_data_handler, which is used for handling the received data.
    nus_init.data_handler = nus_data_handler;
    
    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);
}		
		

Handling data received over the UART

When you type message into a serial terminal on a PC UART module gets activated. Data that is received from the UART undergoes certain checks before it is relayed to the BLE peer using the Nordic UART Service. The code shown below is part of the app_uart event handler, which is called every time a character is received over the UART. Received characters are buffered into a string until a new line character is received or the size of the string exceeds the limit defined by NUS_MAX_DATA_LENGTH. When one of these two conditions is met, the string is sent over BLE using the ble_nus_send_string function.

/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to 
 *          a string. The string will be be sent over BLE when the last character received was a 
 *          'new line' i.e '\n' (hex 0x0D) or if the string has reached a length of 
 *          @ref NUS_MAX_DATA_LENGTH.
 */
/**@snippet [Handling the data received over UART] */
void uart_event_handle(app_uart_evt_t * p_event)
{
    static uint8_t data_array[BLE_NUS_MAX_DATA_LEN]; // BLE_NUS_MAX_DATA_LEN = 20
    static uint8_t index = 0;
    uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            // Get string of data from UART and load it into data_array
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;

            if ((data_array[index - 1] == '\n') || (index >= (BLE_NUS_MAX_DATA_LEN)))
            {
              /*
               * Function for sending a string to the peer.
               * This function sends the input string as an RX characteristic notification to the peer.
               * uint32_t ble_nus_string_send	(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length )
               * p_nus	Pointer to the Nordic UART Service structure.
               * p_string	String to be sent.
               * length	Length of the string.
               */
							
                err_code = ble_nus_string_send(&m_nus, data_array, index);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
                
                index = 0;
            }
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}						
		


Data is sent using using ble_nus_string_send() function.

uint32_t ble_nus_string_send(ble_nus_t * p_nus, uint8_t * p_string, uint16_t length)
{
    ble_gatts_hvx_params_t hvx_params;

    VERIFY_PARAM_NOT_NULL(p_nus);

    if ((p_nus->conn_handle == BLE_CONN_HANDLE_INVALID) || (!p_nus->is_notification_enabled))
    {
        return NRF_ERROR_INVALID_STATE;
    }

    if (length > BLE_NUS_MAX_DATA_LEN)
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    memset(&hvx_params, 0, sizeof(hvx_params));

    hvx_params.handle = p_nus->rx_handles.value_handle;
    hvx_params.p_data = p_string;
    hvx_params.p_len  = &length;
    hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;

    return sd_ble_gatts_hvx(p_nus->conn_handle, &hvx_params);
}

I will be making changes to the UART over BLE project and keeping updated files on my Github. Here is the image of KEIL project page. It shows project tree with all the files that need to be included.



Adding ADC

For my project I am going to use an external analog-to-digital converter (ADC). TI's ADS1247 SPI 24 bit ADC seems to be a good choice.
The following is a preliminary schematics diagram of the ADC converter:

Calculating input noise filters:

Input stage of the ADS1247 is a differential amplifier, labeled PGA (Programmable Gain Amplifier) on the diagram below.

All instrumentation and differential amplifiers rectify small out of band signals. The disturbance may appear as a small dc voltage offset. High frequency signals can be filtered with a low pass R-C network placed at the input of the instrumentation amplifier.

CD affects the difference signal. CC affects the common-mode signal. Any mismatch in R x CC will degrade the input amplifier's CMRR. To avoid inadvertently reducing CMRR-bandwidth performance, make sure that CC is at least one magnitude smaller than CD. The effect of mismatched CCs is reduced with a larger CD:CC ratio

From the schematics above:
C1, C3, C6, C9 - CC
C2, C8 - CD
R1, R3, R6, R8 - R


For the thermocouple input:



Adding SPI

Under nRF_Drivers I have added nrf_drv_spi.c file for SPI communication I will be using later. The file is located in nRF5_SDK_11.0.0_89a8197\components\drivers_nrf\spi_master. When you add nrf_drv_spi.c file to the project you will have to add search directories to "Options for target":

If you compile the project and find erros, like as follows:

you will either need to point to the missing header file like I did in nrf_drv-spi.h file:
H:\Projects\examples\ble_peripheral\ble_app_uart\nrf_drv_config.h
or point to the library file using the "Include Paths."

To add search directories first Click on "Include Paths" as shown below.

Click on "Include Paths":

Then add search paths.

Add search paths for all the library files (.h files):

The following is the main.c function of the ble_app_uart_s130_pca10028 example. I will be making changes and post updated files to the Github.


		
/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

/** @file
 *
 * @defgroup ble_sdk_uart_over_ble_main main.c
 * @{
 * @ingroup  ble_sdk_app_nus_eval
 * @brief    UART over BLE application main file.
 *
 * This file contains the source code for a sample application that uses the Nordic UART service.
 * This application uses the @ref srvlib_conn_params module.
 */

#include <stdint.h>
#include <string.h>
#include "nordic_common.h"
#include "nrf.h"
#include "ble_hci.h"
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "ble_conn_params.h"
#include "softdevice_handler.h"
#include "app_timer.h"
#include "app_button.h"
#include "ble_nus.h"
#include "app_uart.h"
#include "app_util_platform.h"
#include "bsp.h"
#include "bsp_btn_ble.h"

#define IS_SRVC_CHANGED_CHARACT_PRESENT 0                                           /**< Include the service_changed characteristic. If not enabled, the server's database cannot be changed for the lifetime of the device. */

#define CENTRAL_LINK_COUNT              0                                           /**< Number of central links used by the application. When changing this number remember to adjust the RAM settings*/
#define PERIPHERAL_LINK_COUNT           1                                           /**< Number of peripheral links used by the application. When changing this number remember to adjust the RAM settings*/

#define DEVICE_NAME                     "Nordic_UART"                               /**< Name of device. Will be included in the advertising data. */
#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */

#define APP_ADV_INTERVAL                64                                          /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */
#define APP_ADV_TIMEOUT_IN_SECONDS      180                                         /**< The advertising timeout (in units of seconds). */

#define APP_TIMER_PRESCALER             0                                           /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_OP_QUEUE_SIZE         4                                           /**< Size of timer operation queues. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(75, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
#define SLAVE_LATENCY                   0                                           /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)             /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000, APP_TIMER_PRESCALER)  /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000, APP_TIMER_PRESCALER) /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                           /**< Number of attempts before giving up the connection parameter negotiation. */

#define DEAD_BEEF                       0xDEADBEEF                                  /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */

#define UART_TX_BUF_SIZE                256                                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE                256                                         /**< UART RX buffer size. */

static ble_nus_t                        m_nus;                                      /**< Structure to identify the Nordic UART Service. */
static uint16_t                         m_conn_handle = BLE_CONN_HANDLE_INVALID;    /**< Handle of the current connection. */

static ble_uuid_t                       m_adv_uuids[] = {{BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}};  /**< Universally unique service identifier. */


/**@brief Function for assert macro callback.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyse 
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num    Line number of the failing ASSERT call.
 * @param[in] p_file_name File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Function for the GAP initialization.
 *
 * @details This function will set up all the necessary GAP (Generic Access Profile) parameters of 
 *          the device. It also sets the permissions and appearance.
 */
static void gap_params_init(void)
{
    uint32_t                err_code;
    ble_gap_conn_params_t   gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *) DEVICE_NAME,
                                          strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);

    memset(&gap_conn_params, 0, sizeof(gap_conn_params));

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}


/**@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_nus    Nordic UART Service structure.
 * @param[in] p_data   Data to be send to UART module.
 * @param[in] length   Length of the data.
 */
/**@snippet [Handling the data received over BLE] */
static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length)
{
    for (uint32_t i = 0; i < length; i++)
    {
        while(app_uart_put(p_data[i]) != NRF_SUCCESS);
    }
    while(app_uart_put('\n') != NRF_SUCCESS);
}
/**@snippet [Handling the data received over BLE] */


/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    uint32_t       err_code;
    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);
}


/**@brief Function for handling an event from the Connection Parameters Module.
 *
 * @details This function will be called for all events in the Connection Parameters Module
 *          which are passed to the application.
 *
 * @note All this function does is to disconnect. This could have been done by simply setting
 *       the disconnect_on_fail config parameter, but instead we use the event handler
 *       mechanism to demonstrate its use.
 *
 * @param[in] p_evt  Event received from the Connection Parameters Module.
 */
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    uint32_t err_code;
    
    if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for handling errors from the Connection Parameters module.
 *
 * @param[in] nrf_error  Error code containing information about what went wrong.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for initializing the Connection Parameters module.
 */
static void conn_params_init(void)
{
    uint32_t               err_code;
    ble_conn_params_init_t cp_init;
    
    memset(&cp_init, 0, sizeof(cp_init));

    cp_init.p_conn_params                  = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.evt_handler                    = on_conn_params_evt;
    cp_init.error_handler                  = conn_params_error_handler;
    
    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for putting the chip into sleep mode.
 *
 * @note This function will not return.
 */
static void sleep_mode_enter(void)
{
    uint32_t err_code = bsp_indication_set(BSP_INDICATE_IDLE);
    APP_ERROR_CHECK(err_code);

    // Prepare wakeup buttons.
    err_code = bsp_btn_ble_sleep_mode_prepare();
    APP_ERROR_CHECK(err_code);

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    err_code = sd_power_system_off();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling advertising events.
 *
 * @details This function will be called for advertising events which are passed to the application.
 *
 * @param[in] ble_adv_evt  Advertising event.
 */
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    uint32_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;
        case BLE_ADV_EVT_IDLE:
            sleep_mode_enter();
            break;
        default:
            break;
    }
}


/**@brief Function for the application's SoftDevice event handler.
 *
 * @param[in] p_ble_evt SoftDevice event.
 */
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    uint32_t                         err_code;
    
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;
            
        case BLE_GAP_EVT_DISCONNECTED:
            err_code = bsp_indication_set(BSP_INDICATE_IDLE);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
            break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            // Pairing not supported
            err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            // No system attributes have been stored.
            err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
            APP_ERROR_CHECK(err_code);
            break;

        default:
            // No implementation needed.
            break;
    }
}


/**@brief Function for dispatching a SoftDevice event to all modules with a SoftDevice 
 *        event handler.
 *
 * @details This function is called from the SoftDevice event interrupt handler after a 
 *          SoftDevice event has been received.
 *
 * @param[in] p_ble_evt  SoftDevice event.
 */
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    ble_conn_params_on_ble_evt(p_ble_evt);
    ble_nus_on_ble_evt(&m_nus, p_ble_evt);
    on_ble_evt(p_ble_evt);
    ble_advertising_on_ble_evt(p_ble_evt);
    bsp_btn_ble_on_ble_evt(p_ble_evt);
    
}


/**@brief Function for the SoftDevice initialization.
 *
 * @details This function initializes the SoftDevice and the BLE event interrupt.
 */
static void ble_stack_init(void)
{
    uint32_t err_code;
    
    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;
    
    // Initialize SoftDevice.
    SOFTDEVICE_HANDLER_INIT(&clock_lf_cfg, NULL);
    
    ble_enable_params_t ble_enable_params;
    err_code = softdevice_enable_get_default_config(CENTRAL_LINK_COUNT,
                                                    PERIPHERAL_LINK_COUNT,
                                                    &ble_enable_params);
    APP_ERROR_CHECK(err_code);
        
    //Check the ram settings against the used number of links
    CHECK_RAM_START_ADDR(CENTRAL_LINK_COUNT,PERIPHERAL_LINK_COUNT);
    // Enable BLE stack.
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);
    
    // Subscribe for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling events from the BSP module.
 *
 * @param[in]   event   Event generated by button press.
 */
void bsp_event_handler(bsp_event_t event)
{
    uint32_t err_code;
    switch (event)
    {
        case BSP_EVENT_SLEEP:
            sleep_mode_enter();
            break;

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        case BSP_EVENT_WHITELIST_OFF:
            err_code = ble_advertising_restart_without_whitelist();
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            break;
    }
}


/**@brief   Function for handling app_uart events.
 *
 * @details This function will receive a single character from the app_uart module and append it to 
 *          a string. The string will be be sent over BLE when the last character received was a 
 *          'new line' i.e '\n' (hex 0x0D) or if the string has reached a length of 
 *          @ref NUS_MAX_DATA_LENGTH.
 */
/**@snippet [Handling the data received over UART] */
void uart_event_handle(app_uart_evt_t * p_event)
{
    static uint8_t data_array[BLE_NUS_MAX_DATA_LEN];
    static uint8_t index = 0;
    uint32_t       err_code;

    switch (p_event->evt_type)
    {
        case APP_UART_DATA_READY:
            UNUSED_VARIABLE(app_uart_get(&data_array[index]));
            index++;

            if ((data_array[index - 1] == '\n') || (index >= (BLE_NUS_MAX_DATA_LEN)))
            {
                err_code = ble_nus_string_send(&m_nus, data_array, index);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
                
                index = 0;
            }
            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_communication);
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER(p_event->data.error_code);
            break;

        default:
            break;
    }
}
/**@snippet [Handling the data received over UART] */


/**@brief  Function for initializing the UART module.
 */
/**@snippet [UART Initialization] */
static void uart_init(void)
{
    uint32_t                     err_code;
    const app_uart_comm_params_t comm_params =
    {
        RX_PIN_NUMBER,
        TX_PIN_NUMBER,
        RTS_PIN_NUMBER,
        CTS_PIN_NUMBER,
        //APP_UART_FLOW_CONTROL_ENABLED,
			  APP_UART_FLOW_CONTROL_DISABLED,
        false,
        UART_BAUDRATE_BAUDRATE_Baud115200
    };

    APP_UART_FIFO_INIT( &comm_params,
                       UART_RX_BUF_SIZE,
                       UART_TX_BUF_SIZE,
                       uart_event_handle,
                       APP_IRQ_PRIORITY_LOW,
                       err_code);
    APP_ERROR_CHECK(err_code);
}
/**@snippet [UART Initialization] */


/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;
    ble_advdata_t scanrsp;

    // Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata, 0, sizeof(advdata));
    advdata.name_type          = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = false;
    advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;

    memset(&scanrsp, 0, sizeof(scanrsp));
    scanrsp.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    scanrsp.uuids_complete.p_uuids  = m_adv_uuids;

    ble_adv_modes_config_t options = {0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, &scanrsp, &options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    bsp_event_t startup_event;

    uint32_t err_code = bsp_init(BSP_INIT_LED | BSP_INIT_BUTTONS,
                                 APP_TIMER_TICKS(100, APP_TIMER_PRESCALER), 
                                 bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}


/**@brief Function for placing the application in low power state while waiting for events.
 */
static void power_manage(void)
{
    uint32_t err_code = sd_app_evt_wait();
    APP_ERROR_CHECK(err_code);
}


/**@brief Application main function.
 */
int main(void)
{
    uint32_t err_code;
    bool erase_bonds;

    // Initialize.
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
    uart_init();
    
    buttons_leds_init(&erase_bonds);
    ble_stack_init();
    gap_params_init();
    services_init();
    advertising_init();
    conn_params_init();

    printf("\r\nUART Start!\r\n");
    err_code = ble_advertising_start(BLE_ADV_MODE_FAST);
    APP_ERROR_CHECK(err_code);
    
    // Enter main loop.
    for (;;)
    {
        power_manage();
    }
}


/** 
 * @}
 */
		
		
		


I am working with a custom hardware. Below is the schematics diagram for the BLE:

The above schematics is based on Raytac MDBT40-P (BT4.1 Module with printed Antenna). UART RX pin is connected to pin P0.5 and UART TX to P0.24, as shown in the circuit diagram above. To mach the UART pins I changed definitions in the pca10028.h file.

#define RX_PIN_NUMBER 5
#define TX_PIN_NUMBER 24
#define CTS_PIN_NUMBER 10
#define RTS_PIN_NUMBER 8
#define HWFC true

I have found Adafruit Bluefruit LE Friend developmet kit. I believe it has all the hardware needed to start with NRF51822. You will need to get a separate programmer or use over the air firmware updates.

As you can see from the schematics above RXD pin is connected to P0.11 and TXD is connected to P0.19. There is no need to change defenitions in pca10028.h file for the Adafruit hardware.



The UART is tested and works fine. UART over BLE allows to send text messages over BLE connection from a mobile device to serial terminal on a PC. The app can be downloaded from the Google Play. I have fond Github UART app code Nordic UART App Looking at the source code file, main.c has a function for handling the data from the Nordic UART Service.

 
	/**@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_nus    Nordic UART Service structure.
 	* @param[in] p_data   Data to be send to UART module.
 	* @param[in] length   Length of the data.
 	*/
	/**@snippet [Handling the data received over BLE] */
	static void nus_data_handler(ble_nus_t * p_nus, uint8_t * p_data, uint16_t length)
	{
    	for (uint32_t i = 0; i < length; i++)
    	{
        	while(app_uart_put(p_data[i]) != NRF_SUCCESS);
    	}
    	while(app_uart_put('\n') != NRF_SUCCESS);
	}
	

Length of the received data, "length", over BLE is limited to 20 bytes. Function app_uart_put(p_data[i]) shown above gets data from a serial terminal and places it to p_data[] buffer one byte at a time. As far as I can tell this function works with a serial terminal only. When user enters string into a serial terminal app_uart_put copies this string to p_data[]. Then this data gets sent over BLE connection to a mobile device.

I have added nrf_drv_spi.c project file to my project. This file handles all SPI configuration and data transfer. Once SPI is enabled:

nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
spi_config.ss_pin = SPI_CS_PIN; //CS is pin 19
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));

the following code snippet can be used to send data over SPI (download the latest code from Github):

	
	static const uint8_t adc_idac_config[]	= {0x4A,0x01,0x03,0x2F};
	main{
		......
		spi_tx_buff_ptr = adc_idac_config;		// Initialize pointer to the buffer to be sent over SPI	
		memset(m_rx_buf, 0, m_length_exc);		// Reset rx buffer and transfer done flag
		spi_xfer_done = false;	
		// Send data over SPI. Load received data from the SPI periferal into m_rx_buf
		APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, (uint8_t const *)spi_tx_buff_ptr, m_length_exc, m_rx_buf, m_length_exc));
		while (!spi_xfer_done)
		{
			__WFE();
		}
		...
	}
	
	

To send an array of data collected by ADS1247 SPI 24 bit converter, I use ble_nus_string_send(&m_nus, m_rx_buf, length); function. The m_rx_buf buffer contains data to be sent over BLE link to a mobile device, length is the number of bytes being sent. The m_rx_buf has data received over SPI from ADC converter. Currently 10 byte character value is transmitted every 0.5 sec (nrf_delay_ms(500)).


	
// The main loop.
for (;;)
{
	if(!nrf_gpio_pin_read (23)){//!DRDY
		spibuff = adc_data;	
		// Reset rx buffer and transfer done flag
		memset(m_rx_buf, 0, m_length_conv);
		spi_xfer_done = false;					
		APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, (uint8_t const *)spibuff, m_length_conv, m_rx_buf, m_length_conv));
		while (!spi_xfer_done)
		{
			__WFE();
		}		
		ble_nus_string_send(&m_nus, m_rx_buf, 10);
		LEDS_INVERT(BSP_LED_0_MASK);
		nrf_delay_ms(500);
	}			
	power_manage();
}
	
	

nrf_drv_spi_transfer(&spi, (uint8_t const *)spibuff, m_length_conv, m_rx_buf, m_length_conv) function for starting the SPI data transfer. If an event handler was provided in nrf_drv_spi_init() call, this function returns immediately and the handler is called when the transfer is done. Otherwise, the transfer is performed in blocking mode, i.e. this function returns when the transfer is finished. SPI Master Nordic Doc Reference


APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, (uint8_t const *)spi_tx_buff_ptr, m_length_conv, m_rx_buf, m_length_conv));
APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler)); //spi_event_handler_init
SPI event handler:
On the complition of SPI transfer m_rx_buf has data received from SPI periferqal.

/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const * p_event)
{
	spi_xfer_done = true;	
	uint32_t Result = 0;
	//ADC result is 3 byte long. 
	Result = (uint32_t) m_rx_buf[0];		// Save byte 0 of the ADC result in uint32_t Result
	Result = (Result << 8);				// Shift left MSB byte of the ADC result 
	Result = (Result + (uint32_t)m_rx_buf[1]);	// Save byte 1 of the ADC result in uint32_t Result
	Result = (Result << 8);				// Shift left MSB and NSB byte of the ADC result
	Result = (Result + (uint32_t)m_rx_buf[2]);	// Save byte 2 of the ADC result in uint32_t Result
	hexdec_long( Result );				// Convert Result into character string. Fills up Rx_buf
	//#define UART_SPI
#ifdef UART_SPI	
	if (m_rx_buf[0] != 0)
	{  
		NRF_LOG_PRINTF("SPI DATA Rx: %s\r\n", m_rx_buf);		
		NRF_LOG_PRINTF("SPI DATA Rx: %s\r\n", Rx_buf);
		NRF_LOG_PRINTF("   SPI Rx bytes: %d\r\n", m_length_conv);
	}
	else
	{
		//printf("SPI Received: No Data\r\n"); // UART print
	}
#endif			
}



Adding NRF51822 ADC

I would like to add NRF51822 built in ADC to measure the battery status. Nordic provides an example of ADC with a SoftDevice enabled. You can donload the example file in zip format from my Github I have added nrf_drv_adc.c file from the example project to my project. Then I have made the following changes to the "main_Rev01.c" file:


/**
 * @brief ADC interrupt handler.
 * Prints ADC results on hardware UART and over BLE via the NUS service.
 * The 10 bit incremental Analog to Digital Converter (ADC) enables sampling of up to 8 external signals
 * through a front-end multiplexer. The ADC has configurable input and reference prescaling, and sample
 * resolution (8, 9, and 10 bit)
 * Full battery at 1.68V
 *
 * Resolution = 10 bit, 
 * scaling = 1/3, VREF = 1.2V; 
 * Battery voltage = 3.7V
 * 3.7*2/4.4 = 1.68V; 1.68/3 = 0.56V
 * 1.2/2^10 = 0.001172 V, 0.56 / 0.001172 = 478 steps
 */
static void adc_event_handler(nrf_drv_adc_evt_t const * p_event)
{
	uint8_t adc_result[ADC_BUFFER_SIZE*2];
	uint8_t adc_str1[] = "Battery level"; 		//battery level reading indicator
	uint32_t adc_data_to_char = 0;
			
	if (p_event->type == NRF_DRV_ADC_EVT_DONE)
	{
	// Send BLE message "Battery level"
	ble_nus_string_send(&m_nus, adc_str1, sizeof(adc_str1));
	uint32_t i;
	//done is in nrf_drv_adc.h size = ADC_BUFFER_SIZE set 
	//in nrf_drv_adc_buffer_convert(adc_buffer,ADC_BUFFER_SIZE)) below
	for (i = 0; i < p_event->data.done.size; i++)
	{
		//p_event is 16 bit value. Print ADC result on hardware UART
		printf("Sample value %d: %d\r\n", i+1, p_event->data.done.p_buffer[i]);	
		adc_result[(i*2)] = p_event->data.done.p_buffer[i] >> 8;	//LSB byte of the result
		adc_result[(i*2)+1] = p_event->data.done.p_buffer[i];		//MSB byte of the result	
											
		adc_data_to_char = (uint32_t) (p_event->data.done.p_buffer[i]);//Load
		hexdec_long( adc_data_to_char );//Convert
		ble_nus_string_send(&m_nus, Rx_buf, 10);//Send
	}       	
	LEDS_INVERT(BSP_LED_3_MASK);	//Indicate sampling complete on LED 4
	}
}

#define _PRIO_APP_LOW       3

#define ADC_CONFIG_IRQ_PRIORITY APP_IRQ_PRIORITY_LOW

/**
 * @brief ADC initialization.
 */
static void adc_config(void)
{
    ret_code_t ret_code;
    nrf_drv_adc_config_t config = NRF_DRV_ADC_DEFAULT_CONFIG;

    ret_code = nrf_drv_adc_init(&config, adc_event_handler);
    APP_ERROR_CHECK(ret_code);

    nrf_drv_adc_channel_enable(&m_channel_config);
}
#endif

.....

#ifdef adc
	adc_config();
#endif

.....

main(){

...

#ifdef adc			
APP_ERROR_CHECK(nrf_drv_adc_buffer_convert(adc_buffer,ADC_BUFFER_SIZE));   //Allocate buffer for ADC
for (uint32_t i = 0; i < ADC_BUFFER_SIZE; i++)
{
	nrf_drv_adc_sample();           // manually trigger ADC conversion
// CPU enter sleep mode during sampling. CPU will be enabled again when ADC interrupt occurs and adc_event_handler is called
	power_manage();                 
	LEDS_INVERT(BSP_LED_1_MASK);    // Indicate sampling complete
	nrf_delay_ms(250);              // Slow down sampling frequency with 250ms blocking delay
}
#endif

...
}


Sleep and unit wakeup

In main:

/**@brief Function for putting the chip into sleep mode.
 *
 * @note This function will not return.
 */
static void sleep_mode_enter(void)
{
    uint32_t err_code = bsp_indication_set(BSP_INDICATE_IDLE);
    APP_ERROR_CHECK(err_code);

    // Prepare wakeup buttons.
    err_code = bsp_btn_ble_sleep_mode_prepare();
    APP_ERROR_CHECK(err_code);

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    err_code = sd_power_system_off();
    APP_ERROR_CHECK(err_code);
}
	

You can get the complete source code for this project here: Source Code Files

Android-nRF-UART