Writing a BLE server and client for Nordic UART Service using PolyMCU and GattLib by Olivier on 27/02/2017 15:16

BLE Peripheral Firmware

Nordic already wrote the example for BLE NUS (Nordic UART Service) examples/ble_peripheral/ble_app_uart in their SDK. As PolyMCU uses Nordic SDK we will see how easy it is to port it to the PolyMCU framework.
We added the Github commits to help the reader to see the changes.

  1. Clone PolyMCU repository: git clone https://github.com/labapart/polymcu.git
  2. Create folder under <polymcu_root>/Application/<vendor_name>/<application_name> for instance <polymcu_root>/Application/Nordic/ble_app_uart
  3. Copy the C file of the NUS example to this directory. In the case of this example, I copied examples/ble_peripheral/ble_app_uart/main.c as <polymcu_root>/Application/Nordic/ble_app_uart/ble_app_uart.c (Github commit)
  4. Create Application.cmake. This file defines the global settings and dependencies for the application. It uses Cmake language.

    # This application is only supported on Nordic boards
    # List of modules needed by the board support and the application
    # This example requires BLE Peripheral support
    # RAM Memory requires by Nordic SoftDevice 
  5. Create the CMake CMakeLists.txt. This file defines which application specific files to use and their local settings.

    cmake_minimum_required(VERSION 2.6)
    # Export the board specific settings and include directories
    # List application specific sources
    set(Firmware_SRCS ble_app_uart.c)
    # List required libraries
    set(Firmware_LIBS ${Board_LIBRARIES} ${PolyMCU_LIBRARIES})
    BUILD_FIRMWARE(Firmware BLEPeripheralUart "${Firmware_SRCS}" "${Firmware_LIBS}")

    See Github commit

  6. Prior building your application, you can define your toolchain path if a arm-none-eabi- toolchain is not already available in your PATH. For instance: export CROSS_COMPILE=~/Toolchains/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabi-

    PolyMCU supports building firmware out-of-tree (ie: the generated files do not mix with the source file).

    cd <polymcu_root>
    mkdir Build && cd Build

    To build our PolyMCU application for the Nordic NRF52 Development Kit:

    cmake -DBOARD=Nordic/nRF52DK -DAPPLICATION=Nordic/ble_app_uart ..

    A build error shows up:

    ../../../Board/Nordic/libboard_nordic.a(board.c.o): In function `assert_nrf_callback':
    /home/olivier/labapart/polymcu/Board/Nordic/nRF52DK/board.c:101: multiple definition of `assert_nrf_callback'

    We already defined assert_nrf_callback int board file Board/Nordic/nRF52DK/board.c to avoid to duplicate this code around.

    Github commit

  7. Let’s deploy the firmware on the Nordic Board with sudo make install

  8. The firmware does not seem to work. We will debug it using GDB. Start in one terminal the GDB server JLinkGDBServer -if SWD -device nrf52 and in another one the GDB client:

    ${CROSS_COMPILE}gdb Application/Nordic/ble_app_uart/BLEPeripheralUart.elf 
    (gdb) target remote localhost:2331
    Remote debugging using localhost:2331
    0x0002a558 in _exit ()
    (gdb) where
    #0  0x0002a558 in _exit ()
    #1  0x000268a6 in abort ()
    #2  0x00023c2a in __assert_func ()
    #3  0x0001db8a in app_error_handler (error_code=8, line_num=436, 
        p_file_name=0x2a580 <_fini+24> "/home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c")
        at /home/olivier/labapart/polymcu/Board/Nordic/nRF52DK/board.c:87
    #4  0x0001cb16 in uart_init () at /home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c:436
    #5  0x0001cc8c in main () at /home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c:507

    The issue is the PolyMCU board support already initializes UART support for debug purposes. Either we discard PolyMCU UART support specifically for this example or we remove the UART initialization in the main() function. We went for this second solution to reduce the size of the main().

    Github commit

    After make the change and rebuilding and deploying with make && sudo make install. New runtime error:

    Remote debugging using localhost:2331
    0x0002a3d8 in _exit ()
    (gdb) where
    #0  0x0002a3d8 in _exit ()
    #1  0x00026726 in abort ()
    #2  0x00023a7a in __assert_func ()
    #3  0x0001da62 in app_error_handler (error_code=4, line_num=451, 
        p_file_name=0x2a400 <_fini+24> "/home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c")
        at /home/olivier/labapart/polymcu/Board/Nordic/nRF52DK/board.c:87
    #4  0x0001caca in buttons_leds_init (p_erase_bonds=0x20007fe7) at /home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c:451
    #5  0x0001cb6e in main () at /home/olivier/labapart/polymcu/Application/Nordic/ble_app_uart/ble_app_uart.c:479

    After investigation, we found the default value for GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS does not fit the requirements of this application. We increase this value to 4 and add this settings to Application.cmake

    Github commit

  9. Let's be polite and say "thank you!" when receiving a string over NUS. This change demonstrates how to send characters through NUS.

    Github commit

BLE Client Application

Now we have our firmware, let’s have a look at the BLE client side on the host machine.

We will write an application that will communicate with NUS (Nordic UART Service) using gattlib (https://github.com/labapart/gattlib).

First let’s see if our previously built firmware correctly exposes the NUS. We will use GattLib example ble_scan (https://github.com/labapart/gattlib/tree/master/examples/ble_scan) to detect BLE services available around us.

To build gattlib:

git clone https://github.com/labapart/gattlib.git
cd gattlib
mkdir build && cd build

Let's run ble_scan

sudo ./examples/ble_scan/ble_scan 
Discovered DA:94:40:95:E0:87
Scan completed
------------START DA:94:40:95:E0:87 ---------------
Succeeded to connect to the bluetooth device with random address.
service[0] start_handle:01 end_handle:07 uuid:0x1800
service[1] start_handle:08 end_handle:08 uuid:0x1801
service[2] start_handle:09 end_handle:ffff uuid:0x6e400001-b5a3-f393-e0a9-e50e24dcca9
characteristic[0] properties:0a value_handle:0003 uuid:0x2a00
characteristic[1] properties:02 value_handle:0005 uuid:0x2a01
characteristic[2] properties:02 value_handle:0007 uuid:0x2a04
characteristic[3] properties:10 value_handle:000b uuid:0x6e400003-b5a3-f393-e0a9-e50e24dcca9
characteristic[4] properties:0c value_handle:000e uuid:0x6e400002-b5a3-f393-e0a9-e50e24dcca9
------------DONE DA:94:40:95:E0:87 ---------------

Nordic UART Services is correctly advertised:

Both characteristics have a 20-byte buffer.

NUS RX characteristic can also notify when there is data to read. We register a handler with gattlib_register_notification() on NUS RX characteristic.

The entire source file is available here: https://github.com/labapart/gattlib/blob/master/examples/nordic_uart/nordic_uart.c

Now we can communicate with our Nordic board using NUS with ./examples/nrt/nordic_uart DA:94:40:95:E0:87.

No comment yet

Your reply