7.02.16 Getting Started with Bluetoe

A Bluetooth Low Energy Blinky

In the embedded world, blinky is what elsewhere is known as Hello World; a small example that shows how to blink an LED on embedded hardware. In Bluetooth Low Energy, the simplest example is a GATT server with a single service containing one characteristic.

My target platform is a PCA10003 eval board with a nrf51422 and I want to switch one of the LEDs on the board remotely, using any generic GATT client.

Definition of the GATT Server

As one design goal of Bluetoe was "Make Easy Things Easy", this should be a fairly easy task to be done.

#include <bluetoe/server.hpp>

using namespace bluetoe;

static std::uint8_t io_pin_write_handler( bool state );

typedef server<
    service<
        service_uuid<
            0xC11169E1, 0x6252, 0x4450, 0x931C, 0x1B43A318783B >,
        characteristic<
            free_write_handler< bool, io_pin_write_handler >
        >
    >
> blinky_server;

The examples shows the definition of a GATT server, with a single service. The service has a 128 bit random UUID (C11169E1-6252-4450-931C-1B43A318783B), that I generated by using uuidgen (rolling a 16 sided dice 32 times would do the job too). Unfortunately there is no way for a library to generate such pseudo random numbers at compile time.

But, in situations where the service has a 128 bit random UUID, it is very likely that the characteristics within that service will be identified by 128 bit random UUIDs too. If in such situation, the characteristic<> declaration contains no definition of an UUID, Bluetoe will generate a UUID for the characteristic based on the UUID of the service. (Bluetoe does so by xor-ing the lowest two bytes of the service UUID with the consecutive number of the characteristic.)

The only argument to the definition of the characteristic, is a free_write_handler definition. With this definition, Bluetoe knows, that the characteristic is writable (and as a read handler is missing, it is write-only).

From the argument type bool, it's clear, that every attempt to write more than a single byte to the device is an error and that writing other values then 0 or 1, is an error too. Bluetoe handles all of these error situations.

Finally a correct write request from a connected GATT client will result in io_pin_write_handler() being called with either true or false. The return value of the function could be used to communicate error conditions that occured, but in the case of switching an LED on and off, there is hardly anything that could go wrong. The implementation of the handler to switch the LED looks like this:

static constexpr int io_pin = 19;

static std::uint8_t io_pin_write_handler( bool state )
{
    NRF_GPIO->OUT = state
        ? NRF_GPIO->OUT | ( 1 << io_pin )
        : NRF_GPIO->OUT & ~( 1 << io_pin );

    return error_codes::success;
}

And finally this have to run on real hardware with a concrete target platform (nrf51 is the only supported plattform at the moment):

blinky_server gatt;

nrf51< blinky_server > gatt_srv;

int main()
{
    // Init GPIO pin
    NRF_GPIO->PIN_CNF[ io_pin ] =
        ( GPIO_PIN_CNF_DRIVE_S0H1 << GPIO_PIN_CNF_DRIVE_Pos ) |
        ( GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos );

    for ( ;; )
        gatt_srv.run( gatt );
}

The whole example (including build files) can be found on Github.

The relevant entries in the documentation are server, service, characteristic, and free_write_handler.