Merging two BLE examples together

This is a follow-up to my previous blog post; adding custom commands to a BLE example.

I hope it was interesting to see an example of adding the vendor specific Nordic UART service (NUS) into another project.

For completeness, I wanted to share steps needed to keep all the functionality of the BLE UART example (ble_app_uart), instead of just using part of the service functionality like we did in the previous blog post.

This leads to a few gotchas that we will explore now. As with the previous blog post, I uploaded the files to GitHub. Looking at the diff of the original ble_app_hts and the merged result ble_app_hts_uart is probably just as educational as reading the points below.

The following steps assume you have completed the steps in the previous blog post. We can continue with the same ble_app_hts main.c and sdk_config.h file.

1. Restore nus_data_handler() to how it looks in ble_app_uart.

Posted below for convenience.

static void nus_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);

        for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
                err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
                if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
                    NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
            } while (err_code == NRF_ERROR_BUSY);
        if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
            while (app_uart_put('\n') == NRF_ERROR_BUSY);


2. Changes to sdk_config.h to add UART.

UART uses app_uart and app_fifo but these are actually missing in the sdk_config used by ble_app_hts. we have to add them.

// <e> APP_UART_ENABLED - app_uart - UART driver
// <o> APP_UART_DRIVER_INSTANCE  - UART instance used
// <0=> 0 


// <q> APP_FIFO_ENABLED  - app_fifo - Software FIFO implementation


// </e>

ble_app_hts was using the UART to print logs from the app.
ble_app_uart, on the other hand, was using RTT to print these logs since the UART was (fittingly enough) being used to print UART data coming over BLE from NUS.
We want that same behavior as the ble_app_uart example. Change NRF_LOG_BACKEND_UART_ENABLED form 1 to 0, and NRF_LOG_BACKEND_RTT_ENABLED from 0 to 1.


3. Changes to project and main.c to add uart

Include app_uart module and the nrf_uart driver.

#include "app_uart.h"
#include "nrf_uart.h" //or #include "nrf_uarte.h" to use EasyDMA

Add app_uart_fifo and app_fifo source file to you project and the header file to your include path.
(app_uart.h and app_uart_fifo.c are located in <SDK>/components/libraries/uart)
(app_fifo.h and app_fifo.h are located in <SDK>/components/libraries/fifo)

Copy the entire uart_init() and uart_event_handle() from ble_app_uart. Posted below for convenience.

static void uart_init(void)
    uint32_t                     err_code;
    app_uart_comm_params_t const comm_params =
        .rx_pin_no    = RX_PIN_NUMBER,
        .tx_pin_no    = TX_PIN_NUMBER,
        .rts_pin_no   = RTS_PIN_NUMBER,
        .cts_pin_no   = CTS_PIN_NUMBER,
        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity   = false,
#if defined (UART_PRESENT)
        .baud_rate    = NRF_UART_BAUDRATE_115200
        .baud_rate    = NRF_UARTE_BAUDRATE_115200


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:

            if ((data_array[index - 1] == '\n') || (index >= (m_ble_nus_max_data_len)))
                NRF_LOG_DEBUG("Ready to send data over BLE NUS");
                NRF_LOG_HEXDUMP_DEBUG(data_array, index);

                    uint16_t length = (uint16_t)index;
                    err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                    if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_BUSY) &&
                         (err_code != NRF_ERROR_NOT_FOUND) )
                } while (err_code == NRF_ERROR_BUSY);

                index = 0;


        case APP_UART_FIFO_ERROR:


Add uart_init(); to your main function.

Add buffer defines for UART close to the top of your main file.

#define UART_TX_BUF_SIZE 256
#define UART_RX_BUF_SIZE 256

4. (optional) Enable long MTU

uart_event_handle() uses m_ble_nus_max_data_len. We have to add it near the top of main next to the other global defines.

static uint16_t m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;

Replace gatt_init() of ble_app_hts with the gatt_init() found in ble_app_uart.

void gatt_init(void)
    ret_code_t err_code;

    err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);

    err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);

In sdk_config.h, change the NRF_SDH_BLE_GATT_MAX_MTU_SIZE from 23 to 247.


5. Advertising data

By now the application should be able to run. You can connect to it using nRF Toolbox on iOS or Android or nRF Connect. Notice that logs are printed on the RTT and whatever you send with the UART app in nRF Toolbox is printed over UART on our pc.
The app connects to nRF toolbox without problems, but if you observe the nRF Connect app, you notice that it does not advertise with the NUS UUID. Let's add it.

If we try to add it to the m_adv_uuids, we will get the error NRF_ERROR_DATA_SIZE returned from advertising_init() because we don't have enough bytes left in our 29 bytes of advertising data to fit the 16 bytes of NUS UUID.
But we can add it to the scan response instead since that lets us send 29 additional payload bytes to any active scanner.

We do have space to change the advertising name:

#define DEVICE_NAME "Nordic_HTS_UART"

Add the UUID define from ble_app_uart, and the static list of scan response UUIDs.


static ble_uuid_t m_sr_uuids[] =

in advertising_init() add

init.srdata.uuids_complete.uuid_cnt = sizeof(m_sr_uuids) / sizeof(m_sr_uuids[0]);
init.srdata.uuids_complete.p_uuids = m_sr_uuids;

Thats it! You should be able to test your application according to the steps described for ble_app_hts and ble_app_uart. Especially the the uart should be interesting to test since it should now work the same way as in ble_app_uart, with respect to input and output of UART data over terminal (unlike my last blog post).

  • I know this is one of the most meaningful information for me. And I'm animated reading your article. But it's a good thing, the website is perfect; the articles are great. Thanks for the tone of tangible and possible help.

    <a href="">>

  • In my opinion, I would have to agree with this writer on the information given in the article. The writer was very convincing in the way the article was written. I will read this again. Economics Help Online

  • I really impress your content its very interesting content provide thank you so much dude accounting homework help

  • I really impress these stuff your article is very nice content post keep doing great job Essay Help

  • It is estimated that customers make a decision related to a product or service within the first few second of the encounter whether it is online or offline. Most of the times, a company’s logo or name is the first things prospects see so it is better to get a Custom Logo Design to improved brand reputation.