BLE Client UART function

Dependencies:   RingBuffer

This is a BLE Client (Central) program for nRF51.
You can communicate with mbed BLE using "BLE_Uart_Server" program as follows.
/users/kenjiArai/code/BLE_Uart_Server/
Please refer following my notebook.
/users/kenjiArai/notebook/ble-client-and-peripheral-using-switch-sience-ty51/#

Files at this revision

API Documentation at this revision

Comitter:
kenjiArai
Date:
Sat Apr 16 07:32:21 2016 +0000
Child:
1:f68a5e55a60e
Commit message:
BLE Client UART function

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show annotated file Show diff for this revision Revisions of this file
original.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_API.lib	Sat Apr 16 07:32:21 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#66159681aa21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Apr 16 07:32:21 2016 +0000
@@ -0,0 +1,422 @@
+/*
+ *  ------- BLE Client UART function --------------------------------
+ *      --- Tested on Switch Science mbed TY51822r3 ---
+ *
+ *  Modified by Kenji Arai
+ *      http://www.page.sannet.ne.jp/kenjia/index.html
+ *      http://mbed.org/users/kenjiArai/
+ *
+ *      Started:  April     8th, 2016
+ *      Revised:  April    16th, 2016
+ *
+ *  Original program (see original.cpp file):
+ *      S130 potential unstability case [closed] by Fabien Comte
+ *      https://devzone.nordicsemi.com/question/49705/s130-potential-unstability-case/
+ *      GitHub Q&A by Fabien COMTE
+ *      https://github.com/ARMmbed/ble/issues/69
+ *  Tested Peripheral Device:
+ *      BLE_Uart_Peripheral
+ *      https://developer.mbed.org/users/kenjiArai/code/BLE_Uart_Peripheral/
+ *
+ */
+
+//  Include ---------------------------------------------------------------------------------------
+#include "mbed.h"
+#include "BLE.h"
+#include "UARTService.h"
+#include "ble/DiscoveredCharacteristic.h"
+#include "ble/DiscoveredService.h"
+
+//  Definition ------------------------------------------------------------------------------------
+#define NEED_CONSOLE_OUTPUT         0
+
+#if NEED_CONSOLE_OUTPUT
+#define DEBUG(...) { printf(__VA_ARGS__); }
+#else
+#define DEBUG(...)
+#endif
+
+#define SOFT_DEVICE_FATHER_HANDLE   3
+#define BOARDS_COUNT                3
+
+//  Object ----------------------------------------------------------------------------------------
+BLE          ble;
+DigitalOut   alivenessLED(LED1, 1);
+DigitalOut   connectedLED(D10, 0);
+DigitalIn    sw(D12, PullUp);
+InterruptIn  signal_sw(D12);         // duplicated!!
+Serial       pc(USBTX, USBRX);
+Ticker       ticker;
+
+//  ROM / Constant data ---------------------------------------------------------------------------
+#warning "You need to modify below value based on your board."
+const Gap::Address_t    mac_board_0   = {0x59, 0x2c, 0xa8, 0x0e, 0xe2, 0xef};
+const Gap::Address_t    mac_board_1   = {0x50, 0x2b, 0xea, 0x14, 0x95, 0xd2};
+const Gap::Address_t    mac_board_2   = {0x30, 0x74, 0x6d, 0xbd, 0x83, 0xf4};
+#warning "You need to confirm your device name."
+const static char       DEVICE_NAME[] = "UART_C";
+static const uint16_t   uuid16_list[] = {UARTServiceShortUUID};
+
+//  RAM -------------------------------------------------------------------------------------------
+Gap::Handle_t   connectionHandle          = 0xFFFF;
+DiscoveredCharacteristic uartTXCharacteristic;
+DiscoveredCharacteristic uartRXCharacteristic;
+bool            foundUartRXCharacteristic = false;
+bool            connected2peripheral      = false;
+UARTService *   uartServicePtr            = NULL;
+Gap::Address_t  my_mac;
+int             my_board_index            = -1;
+bool            recieved_uart_dat0        = false;
+int8_t          uart_buffer0[24];
+uint8_t         uart_bf0_len;
+bool            recieved_uart_dat1        = false;
+int8_t          uart_buffer1[24];
+uint8_t         uart_bf1_len;
+bool            line_input                = false;
+uint8_t         linebuf_irq[24];
+int             linebf_irq_len            = 0;
+uint8_t         linebuf[24];
+int             linebf_len                = 0;
+
+//  Function prototypes ---------------------------------------------------------------------------
+//      BLE
+void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params);
+void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params);
+void serviceDiscoveryCallback(const DiscoveredService *service);
+void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP);
+void discoveryTerminationCallback(Gap::Handle_t connectionHandle);
+void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params);
+void connectionCallback(const Gap::ConnectionCallbackParams_t *params);
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params);
+//      Interrupt related
+void periodicCallback(void);
+void serialRxCallback(void);
+//      Pre-check
+bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2);
+int  get_board_index(const Gap::Address_t mac);
+void adjust_line(uint8_t *bf);
+
+//-------------------------------------------------------------------------------------------------
+//  Control Program
+//-------------------------------------------------------------------------------------------------
+int main(void){
+    alivenessLED = 0;
+    pc.attach(&serialRxCallback, Serial::RxIrq);
+    ticker.attach(periodicCallback, 1);
+    for (int k = 0; k < 20; k++) { pc.printf("\r\n");}          // clear terminal output
+    pc.printf("UART Communication / Client side\r\n");          // opening message
+    pc.printf("  Client(Central) and Peripheral(device)\r\n");  // opening message
+    // Mixed role *************************************************************
+    ble.init();
+    Gap::AddressType_t my_mac_type;
+    ble.gap().getAddress(&my_mac_type, my_mac);
+    my_board_index = get_board_index(my_mac);
+    DEBUG(
+        "me %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n",
+        my_mac[5], my_mac[4], my_mac[3], my_mac[2], my_mac[1], my_mac[0],
+        (my_mac_type == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random"
+    );
+    ble.gap().onConnection(connectionCallback);
+    ble.gap().onDisconnection(disconnectionCallback);
+    // Peripheral(Device) role ************************************************
+    ble.gattServer().onDataWritten(onReceivedDataFromCentralCallback);
+    UARTService uartService(ble);
+    uartServicePtr = &uartService;
+    // setup advertising
+    ble.accumulateAdvertisingPayload(
+        GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE
+    );
+    ble.accumulateAdvertisingPayload(
+        GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)
+    );
+    ble.accumulateAdvertisingPayload(
+        GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
+        (uint8_t *)uuid16_list,
+        sizeof(uuid16_list)
+    ); // UUID's broadcast in advertising packet (set advertising type)
+    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.setAdvertisingInterval(100);
+    // Client(Central) role ***************************************************
+    ble.gattClient().onHVX(onReceivedDataFromDeviceCallback);
+    ble.gap().setScanParams(500, 450);
+    ble.startAdvertising();                     // start advertising and scan
+    ble.gap().startScan(advertisementCallback);
+    while(true) {
+        // allow notifications from Peripheral(device)
+        if (foundUartRXCharacteristic && !ble.gattClient().isServiceDiscoveryActive()){
+            foundUartRXCharacteristic = false; // need to do the following only once
+            uint16_t value = BLE_HVX_NOTIFICATION;
+            ble.gattClient().write(
+                GattClient::GATT_OP_WRITE_REQ,
+                connectionHandle,
+                uartRXCharacteristic.getValueHandle() + 1,
+                sizeof(uint16_t),
+                reinterpret_cast<const uint8_t *>(&value)
+            );
+        }
+        if (recieved_uart_dat0 == true){
+            recieved_uart_dat0 = false;
+            for(int i = 0; i < uart_bf0_len; i++){
+                pc.printf("%c", uart_buffer0[i]);
+            }
+            pc.printf("\r\n");
+        }
+        if (recieved_uart_dat1 == true){
+            recieved_uart_dat1 = false;
+            for(int i = 0; i < uart_bf1_len; i++){
+                pc.printf("%c", uart_buffer1[i]);
+            }
+            pc.printf("\r\n");
+        }
+        if (line_input == true){
+            line_input = false;
+            // Client to Peripheral(central to device)
+            adjust_line(linebuf);
+            int ret = uartTXCharacteristic.write(20, linebuf);
+            if (ret == BLE_ERROR_NONE){
+                DEBUG("\r\ndone (c2p)\r\n");
+            } else if (ret == BLE_STACK_BUSY){
+                DEBUG("\r\nbusy (c2p)\r\n");
+            } else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED) {
+                DEBUG("\r\nnot connected (c2d)\r\n");
+            } else  {
+                DEBUG("\r\ncode %d (c2d)\r\n", ret);
+            }
+            // Peripheral to Client(device to central)
+            if (ble.gap().getState().connected){
+                int ret = ble.gattServer().write(
+                    uartServicePtr->getRXCharacteristicHandle(),
+                    linebuf,
+                    linebf_len
+                );
+                if (ret == BLE_ERROR_NONE){
+                    DEBUG("\r\ndone (p2c)\r\n");
+                }  else if (ret == BLE_STACK_BUSY){
+                    DEBUG("\r\nbusy (p2c)\r\n");
+                }  else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED){
+                    DEBUG("\r\nnot connected (p2c)\r\n");
+                }  else {
+                    DEBUG("\r\ncode %d (p2c)\r\n", ret);
+                }
+            }
+        }
+        ble.waitForEvent(); // save power
+    }
+}
+
+void serialRxCallback(){
+    char c = pc.getc();
+    if (c == '\r') {
+        linebuf_irq[linebf_irq_len++] = c;
+        pc.printf("\r\n");
+        linebf_len = linebf_irq_len;
+        strcpy((char *)linebuf, (char *)linebuf_irq);
+        linebf_irq_len = 0;
+        line_input = true;
+    } else if ((c == '\b') && linebf_irq_len) {
+        linebf_irq_len--;
+        pc.putc(c);
+        pc.putc(' ');
+        pc.putc(c);
+    } else if (((uint8_t)c >= ' ') && (linebf_irq_len < 20)) {
+        linebuf_irq[linebf_irq_len++] = c;
+        pc.putc(c);
+    } else if ( c == 0x1f ){    // Control+?
+        SCB->AIRCR = 0x05fa0004;    // System RESET!!
+    }
+    linebuf_irq[linebf_irq_len] = 0; 
+}
+
+void adjust_line(uint8_t *bf){
+uint8_t i, c;
+
+    for (i = 0; i <20; bf++, i++){
+        c = *bf;
+        if ( (c == '\r') || (c == '\n') || (c == 0)){
+            break;
+        }
+    }
+    for (; i < 20; bf++, i++){
+        *bf = ' ';
+    }
+    *(bf + 1) = 0;
+}
+
+// Peripheral(Device) role ****************************************************
+void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params){
+    if (uartServicePtr != NULL){
+        if ((params->handle ==
+                uartServicePtr->getTXCharacteristicHandle()) && (params->len >= 1))
+        {
+            uart_bf0_len = params->len;
+            strcpy((char *)uart_buffer0, (char *)params->data);
+            recieved_uart_dat0 = true;
+        }
+    }
+}
+
+void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params){
+    DEBUG(
+        "received HVX callback for handle %u; type %s\r\r\n",
+        params->handle,
+        (params->type == BLE_HVX_NOTIFICATION) ? "notification" : "indication"
+    );
+    if (params->type == BLE_HVX_NOTIFICATION){
+        if ((params->handle
+                == uartRXCharacteristic.getValueHandle()) && (params->len > 0))
+        {
+            uart_bf1_len = params->len;
+            strcpy((char *)uart_buffer1, (char *)params->data);
+            recieved_uart_dat1 = true;
+         }
+    }
+}
+
+void periodicCallback(void){
+    alivenessLED = !alivenessLED; // Do blinky on alivenessLED to indicate system aliveness
+    if (connected2peripheral){
+        connectedLED = 1;
+    } else {
+        connectedLED = 0;
+    }
+}
+
+bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2){
+    DEBUG("Address: ");
+    for (int i = 0; i < 6; i++){
+        DEBUG("0x%02x ", mac_1[i]);
+    }
+    DEBUG("\r\n");
+    for (int i = 0; i < 6; i++){
+        if (mac_1[i] != mac_2[i]){
+            DEBUG("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i);
+            return false;
+        } else {
+            DEBUG("0x%02x == 0x%02x at %d\r\n", mac_1[i], mac_2[i], i);
+        }
+    }
+    return true;
+}
+
+int get_board_index(const Gap::Address_t mac){
+    if (mac_equals(mac, mac_board_0)) {     return 0;}
+    if (mac_equals(mac, mac_board_1)) {     return 1;}
+    if (mac_equals(mac, mac_board_2)) {     return 2;}
+    return -1;
+}
+
+// Client(Central) role *******************************************************
+void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params){
+    // connections
+    int peer_board_index = get_board_index(params->peerAddr);
+    if (peer_board_index != -1){
+        pc.printf("adv peerAddr");
+        pc.printf(
+            "[%02x %02x %02x %02x %02x %02x] rssi %+4d, isScanResponse %u, AdvertisementType %u",
+            params->peerAddr[5], params->peerAddr[4], params->peerAddr[3],
+            params->peerAddr[2], params->peerAddr[1], params->peerAddr[0],
+            params->rssi,
+            params->isScanResponse,
+            params->type
+        );
+        pc.printf("\r\n");
+        ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
+    }
+}
+
+void serviceDiscoveryCallback(const DiscoveredService *service) {
+    DEBUG("service found...\r\n");
+    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
+        DEBUG(
+            "Service UUID-%x attrs[%u %u]\r\n",
+            service->getUUID().getShortUUID(),
+            service->getStartHandle(),
+            service->getEndHandle()
+        );
+    } else {
+        DEBUG("Service UUID-");
+        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
+        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
+            DEBUG("%02x", longUUIDBytes[i]);
+        }
+        DEBUG(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
+    }
+}
+
+void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP){
+    DEBUG(
+        " C UUID-%x valueAttr[%u] props[%x]\r\n",
+        characteristicP->getUUID().getShortUUID(),
+        characteristicP->getValueHandle(),
+        (uint8_t)characteristicP->getProperties().broadcast()
+    );
+    if (characteristicP->getUUID().getShortUUID()
+                == UARTServiceTXCharacteristicShortUUID)
+    {
+        pc.printf("Sevice TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID);
+        uartTXCharacteristic = *characteristicP;
+    } else if (characteristicP->getUUID().getShortUUID()
+                == UARTServiceRXCharacteristicShortUUID)
+    {
+        pc.printf("Sevice RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID);
+        uartRXCharacteristic = *characteristicP;
+        foundUartRXCharacteristic = true;
+    }
+}
+
+void discoveryTerminationCallback(Gap::Handle_t connectionHandle){
+    DEBUG("terminated SD for handle=%u\r\n", connectionHandle);
+}
+
+// Mixed role *****************************************************************
+void connectionCallback(const Gap::ConnectionCallbackParams_t *params){
+    if (params->role == Gap::CENTRAL) {
+        DEBUG("connected as Client(central) (handle = %d)\r\n\r", params->handle);
+        connected2peripheral = true;
+        connectionHandle = params->handle;
+        ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
+        ble.gattClient().launchServiceDiscovery(
+                params->handle,
+                serviceDiscoveryCallback,
+                characteristicDiscoveryCallback/*,
+                0xa000, 0xa001*/
+        );
+    } else {
+        DEBUG("connected as device (handle = %d)\r\n\r", params->handle);
+        DEBUG(
+            "Conn. params => min=%d, max=%d, slave=%d, supervision=%d\r\n",
+            params->connectionParams->minConnectionInterval,
+            params->connectionParams->maxConnectionInterval,
+            params->connectionParams->slaveLatency,
+            params->connectionParams->connectionSupervisionTimeout
+        );
+        Gap::ConnectionParams_t connectionParams;
+        connectionParams.minConnectionInterval        = 6;
+        connectionParams.maxConnectionInterval        = 12;
+        connectionParams.slaveLatency                 = 0;
+        connectionParams.connectionSupervisionTimeout = 500;
+        if (ble.updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE){
+            DEBUG("failed to update connection parameter\r\n");
+        }
+    }
+    DEBUG(
+        "own %02x:%02x:%02x:%02x:%02x:%02x (%s), peer %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n",
+        params->ownAddr[5], params->ownAddr[4], params->ownAddr[3],
+        params->ownAddr[2], params->ownAddr[1], params->ownAddr[0],
+        (params->ownAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random",
+        params->peerAddr[5], params->peerAddr[4], params->peerAddr[3],
+        params->peerAddr[2], params->peerAddr[1], params->peerAddr[0],
+        (params->peerAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random"
+    );
+}
+
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params){
+    DEBUG("handle = %d ", params->handle);
+    pc.printf(" -> disconnected\r\n", params->handle);
+    connected2peripheral = false;
+    if (params->handle == SOFT_DEVICE_FATHER_HANDLE) {
+        ble.startAdvertising();                         // restart advertising
+    } else {
+        ble.gap().startScan(advertisementCallback);     // restart scan
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Apr 16 07:32:21 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/082adc85693f
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF51822.lib	Sat Apr 16 07:32:21 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#f7faad332abc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/original.cpp	Sat Apr 16 07:32:21 2016 +0000
@@ -0,0 +1,613 @@
+#if 0
+//-------------------------------------------------------------------------------------------------
+//  ORIGINAL PROGRAM
+//      S130 potential unstability case [closed] by Fabien Comte
+//      https://devzone.nordicsemi.com/question/49705/s130-potential-unstability-case/
+//
+#include "mbed.h"
+#include "BLE.h"
+#include "UARTService.h"
+#include "ble/DiscoveredCharacteristic.h"
+#include "ble/DiscoveredService.h"
+#include "UARTService.h"
+
+#define SOFT_DEVICE_FATHER_HANDLE 3
+
+#define BOARDS_COUNT 3
+
+const Gap::Address_t mac_board_0 = {0xb8, 0xac, 0x4e, 0x8d, 0x8b, 0xeb}; 
+const Gap::Address_t mac_board_1 = {0x9c, 0x43, 0x62, 0x30, 0xaf, 0xd2}; 
+const Gap::Address_t mac_board_2 = {0x5f, 0x1a, 0x9e, 0x6a, 0x63, 0xdd}; 
+
+
+// tiny ble board
+#define LED_GREEN   p21
+#define LED_RED     p22
+#define LED_BLUE    p23
+#define BUTTON_PIN  p17
+#define BATTERY_PIN p1
+
+#define MPU6050_SDA p12
+#define MPU6050_SCL p13
+
+#define UART_TX     p9
+#define UART_RX     p11
+#define UART_CTS    p8
+#define UART_RTS    p10
+
+DigitalOut led(LED_RED);
+DigitalOut alivenessLED(LED_GREEN);
+InterruptIn button(BUTTON_PIN);
+AnalogIn battery(BATTERY_PIN);
+Serial pc(UART_TX, UART_RX);
+
+bool mac_equals(const Gap::Address_t mac_1, const Gap::Address_t mac_2)
+{
+    #if 0
+        if (mac_1[0] != mac_2[0])
+        {
+            return false;
+        }
+        if (mac_1[1] != mac_2[1])
+        {
+            return false;
+        }
+        if (mac_1[2] != mac_2[2])
+        {
+            return false;
+        }
+        if (mac_1[3] != mac_2[3])
+        {
+            return false;
+        }
+        if (mac_1[4] != mac_2[4])
+        {
+            return false;
+        }
+        if (mac_1[5] != mac_2[5])
+        {
+            return false;
+        }
+    #else
+        for (int i = 0; i < 6; i++)
+        {
+            if (mac_1[i] != mac_2[i])
+            {
+                //pc.printf("0x%02x != 0x%02x at %d\r\n", mac_1[i], mac_2[i], i);
+                return false;
+            }
+            else
+            {
+                //pc.printf("0x%02x == 0x%02x at %d\r\n", mac_1[i], mac_2[i], i);
+            }
+        }
+    #endif
+    return true;
+}
+
+int get_board_index(const Gap::Address_t mac)
+{
+    if (mac_equals(mac, mac_board_0))
+    {
+        return 0;
+    }
+    if (mac_equals(mac, mac_board_1))
+    {
+        return 1;
+    }
+    if (mac_equals(mac, mac_board_2))
+    {
+        return 2;
+    }
+    
+    return -1;
+}
+
+void periodicCallback(void) 
+{
+    alivenessLED = !alivenessLED; /* do blinky on alivenessLED while we're waiting for BLE events */
+}
+
+
+// Mixed role ****************************************************
+BLE ble;
+Gap::Address_t my_mac;
+int my_board_index = -1;
+
+// Device role ****************************************************
+UARTService * uartServicePtr = NULL;
+const static char     DEVICE_NAME[]        = "ChangeMe!!"; // change this
+static const uint16_t uuid16_list[] = {UARTServiceShortUUID};
+volatile int central_handle = -1;
+
+
+// Central role ****************************************************
+Gap::Handle_t connectionHandle = 0xFFFF;
+DiscoveredCharacteristic uartTXCharacteristic;
+DiscoveredCharacteristic uartRXCharacteristic;
+bool foundUartRXCharacteristic = false;
+volatile int device_handle = -1;
+
+
+// Device role ****************************************************
+void onReceivedDataFromCentralCallback(const GattWriteCallbackParams *params) 
+{
+    if (uartServicePtr != NULL)
+    {
+        if ((params->handle == uartServicePtr->getTXCharacteristicHandle()) && (params->len >= 1)) 
+        {
+            if (params->data[0] != '0')
+            {
+                led = 1;
+            }
+            else
+            {
+                led = 0;
+            }
+            
+            for(int i = 0; i < params->len; i++) 
+            {
+                pc.printf("%c", params->data[i]);
+            }
+            
+            pc.printf(" (%d, %d)\r\n", params->handle, params->connHandle);
+        }
+    }
+}
+
+// Central role ****************************************************
+void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) 
+{
+    // do connections like a triangle
+    int peer_board_index = get_board_index(params->peerAddr);
+    
+    int next_board_index = my_board_index + 1;
+    if (next_board_index >= BOARDS_COUNT)
+    {
+        next_board_index = 0;
+    }
+    
+    //pc.printf("adv %d, %d, %d\r\n", peer_board_index, my_board_index, next_board_index);
+    
+    // force order
+    if ((central_handle != -1) || (peer_board_index == 0))
+    {
+        if (peer_board_index == next_board_index)
+        {
+            //pc.printf("adv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n",
+            //       params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0],
+            //       params->rssi, params->isScanResponse, params->type);
+        
+            ble.gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
+        }
+    }
+}
+
+void serviceDiscoveryCallback(const DiscoveredService *service) 
+{
+    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) 
+    {
+        pc.printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
+    } 
+    else
+    {
+        //pc.printf("S UUID-");
+        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
+        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) 
+        {
+            pc.printf("%02x", longUUIDBytes[i]);
+        }
+        pc.printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
+    }
+}
+
+void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) 
+{
+    //pc.printf("  C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast());
+    if (characteristicP->getUUID().getShortUUID() == UARTServiceTXCharacteristicShortUUID) 
+    { 
+        pc.printf("fit TX 0x%04x\r\n", UARTServiceTXCharacteristicShortUUID);
+        /* !ALERT! Alter this filter to suit your device. */
+        uartTXCharacteristic        = *characteristicP;
+    }
+    else if (characteristicP->getUUID().getShortUUID() == UARTServiceRXCharacteristicShortUUID) 
+    { 
+        pc.printf("fit RX 0x%04x\r\n", UARTServiceRXCharacteristicShortUUID);
+        /* !ALERT! Alter this filter to suit your device. */
+        uartRXCharacteristic        = *characteristicP;
+        foundUartRXCharacteristic = true;
+    }
+}
+
+void discoveryTerminationCallback(Gap::Handle_t connectionHandle) 
+{
+    pc.printf("terminated SD for handle %u\r\n", connectionHandle);
+}
+
+void onReceivedDataFromDeviceCallback(const GattHVXCallbackParams *params) 
+{
+    //pc.printf("received HVX callback for handle %u; type %s\r\r\n", params->handle, (params->type == BLE_HVX_NOTIFICATION) ? "notification" : "indication");
+    if (params->type == BLE_HVX_NOTIFICATION)
+    {
+        if ((params->handle == uartRXCharacteristic.getValueHandle()) && (params->len > 0))
+        {
+            for (int i = 0; i < params->len; i++) 
+            {
+                pc.printf("%c", params->data[i]);
+            }
+            
+            pc.printf(" (%d, %d)\r\n", params->handle, params->connHandle);
+        }
+    }
+    else
+    {
+        pc.printf("%d\r\n", params->type);
+    }
+}
+
+// Mixed role ****************************************************
+void connectionCallback(const Gap::ConnectionCallbackParams_t *params) 
+{
+    if (params->role == Gap::CENTRAL) 
+    {
+        if (central_handle == -1)
+        {
+            ble.stopAdvertising(); // stop advertising during discovery, incoming connection breaks discovery
+        }
+        
+        device_handle = params->handle;
+        pc.printf("connected as central (handle = %d)\r\n\r", params->handle);
+        connectionHandle = params->handle;
+        ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
+        int ret = ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, UARTServiceShortUUID/*, 0xa001*/);
+        
+        if (ret != BLE_ERROR_NONE)
+        {
+            pc.printf("launchServiceDiscovery failed error = %d\r\n\r", ret);
+        }
+    }
+    else
+    {
+        central_handle = params->handle;
+        pc.printf("connected as device (handle = %d)\r\n\r", params->handle);
+        
+        //pc.printf("Conn. params => min=%d, max=%d, slave=%d, supervision=%d\r\n", params->connectionParams->minConnectionInterval, params->connectionParams->maxConnectionInterval, params->connectionParams->slaveLatency, params->connectionParams->connectionSupervisionTimeout);
+        /*
+        Gap::ConnectionParams_t connectionParams;
+        connectionParams.minConnectionInterval        = 6;
+        connectionParams.maxConnectionInterval        = 12;
+        connectionParams.slaveLatency                 = 40;
+        connectionParams.connectionSupervisionTimeout = 500;
+        
+        int ret = ble.updateConnectionParams(params->handle, &connectionParams);
+        if (ret != BLE_ERROR_NONE) 
+        {
+            pc.printf("failed to update connection parameter\r\n");
+        }
+        */
+    }
+    pc.printf("own %02x:%02x:%02x:%02x:%02x:%02x (%s), peer %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n", params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], params->ownAddr[2], params->ownAddr[1], params->ownAddr[0], (params->ownAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random", params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], (params->peerAddrType == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random");
+}
+
+void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) 
+{
+    char * ascii_reason = "?";
+    switch (reason)
+    {
+        case Gap::CONNECTION_TIMEOUT:
+            ascii_reason = "connection timeout";
+            break;    
+        case Gap::REMOTE_USER_TERMINATED_CONNECTION:
+            ascii_reason = "user terminated connection";
+            break;    
+        case Gap::REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES:
+            ascii_reason = "low resources";
+            break;    
+        case Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF:
+            ascii_reason = "power off";
+            break;    
+        case Gap::LOCAL_HOST_TERMINATED_CONNECTION:
+            ascii_reason = "host terminated connection";
+            break;    
+        case Gap::CONN_INTERVAL_UNACCEPTABLE:
+            ascii_reason = "interval unacceptable";
+            break;    
+        default:
+            ascii_reason = "unknown";
+            break;
+    }
+    
+    pc.printf("disconnected (reason = %s, handle = %d)\r\n", ascii_reason, handle);
+    
+    
+    if (handle == SOFT_DEVICE_FATHER_HANDLE)
+    {
+        central_handle = -1;
+        // restart advertising
+        ble.startAdvertising(); 
+    }
+    else
+    {
+        device_handle = -1;
+        // restart scan
+        ble.gap().startScan(advertisementCallback);
+    }
+}
+
+
+
+void serialTxCallback() 
+{
+
+}
+ 
+int rx_char = -1;
+void serialRxCallback() 
+{   
+    if (rx_char != -1)
+    {
+        pc.printf("overflow\r\n");
+    }
+     
+     //computer.putc(computer.getc());
+     rx_char = pc.getc();
+}
+
+/*
+void gattServerOnDataSent(unsigned count)
+{
+ 
+}
+*/
+
+
+int main(void) 
+{
+    alivenessLED = 0;
+    
+    pc.baud(115200);
+    //pc.attach(&serialTxCallback, Serial::TxIrq);
+    pc.attach(&serialRxCallback, Serial::RxIrq);
+    
+    // clear terminal output
+    for (int k = 0; k < 32; k++)
+    {
+        pc.printf("\r\n");    
+    }
+    
+    pc.printf("Central and device\r\n");
+   
+    Ticker ticker;
+    ticker.attach(periodicCallback, 1);
+    
+    
+    // Mixed role ****************************************************
+    ble.init();
+    
+    Gap::AddressType_t my_mac_type;
+    ble.gap().getAddress(&my_mac_type, my_mac);
+    my_board_index = get_board_index(my_mac);
+    pc.printf("me %02x:%02x:%02x:%02x:%02x:%02x (%s)\r\n", my_mac[5], my_mac[4], my_mac[3], my_mac[2], my_mac[1], my_mac[0], (my_mac_type == Gap::ADDR_TYPE_PUBLIC) ? "public" : "random");
+    
+
+    // try to speed up but looks like if it was ignored
+    Gap::ConnectionParams_t fast;
+    if (ble.getPreferredConnectionParams(&fast) != BLE_ERROR_NONE) 
+    {
+        pc.printf("getPreferredConnectionParams failed\r\n");
+    }
+    else
+    {
+        fast.minConnectionInterval = 16; // 20 ms
+        fast.maxConnectionInterval = 32; // 40 ms
+        fast.slaveLatency = 0;
+        if (ble.gap().setPreferredConnectionParams(&fast) != BLE_ERROR_NONE) 
+        {
+            pc.printf("setPreferredConnectionParams failed\r\n");
+        }
+    }
+    ble.gap().onConnection(connectionCallback);
+    ble.gap().onDisconnection(disconnectionCallback);
+    
+    // Device role ****************************************************
+    ble.gattServer().onDataWritten(onReceivedDataFromCentralCallback);
+    //ble.gattServer().onDataSent(gattServerOnDataSent);
+
+    UARTService uartService(ble);
+    uartServicePtr = &uartService;
+
+    // setup advertising
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); // BLE only, no classic BT
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); // add name
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // UUID's broadcast in advertising packet
+    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // advertising type
+    ble.setAdvertisingInterval(100); 
+    
+    // Central role ****************************************************
+    ble.gattClient().onHVX(onReceivedDataFromDeviceCallback);
+    ble.gap().setScanParams(500, 450);
+    
+    
+    // start advertising and scan
+    ble.startAdvertising(); 
+    ble.gap().startScan(advertisementCallback);
+
+    while (true) 
+    {
+          // allow notifications from device
+          if (foundUartRXCharacteristic && !ble.gattClient().isServiceDiscoveryActive()) 
+          {
+            foundUartRXCharacteristic = false; /* need to do the following only once */
+ 
+            uint16_t value = BLE_HVX_NOTIFICATION;
+            int ret = ble.gattClient().write(GattClient::GATT_OP_WRITE_REQ,
+                                   connectionHandle,
+                                   uartRXCharacteristic.getValueHandle() + 1, /* HACK Alert. We're assuming that CCCD descriptor immediately follows the value attribute. */
+                                   sizeof(uint16_t),                          /* HACK Alert! size should be made into a BLE_API constant. */
+                                   reinterpret_cast<const uint8_t *>(&value));
+                                   
+            if (ret == BLE_ERROR_NONE) 
+            {
+                pc.printf("\r\ndevice notifications enabled\r\n");
+            }
+            else
+            {
+                switch (ret)
+                {
+                    case BLE_STACK_BUSY:
+                        foundUartRXCharacteristic = true; // retry later
+                        break;
+                    case BLE_ERROR_NO_MEM:
+                        foundUartRXCharacteristic = true; // retry later
+                        break;
+                    case BLE_ERROR_INVALID_STATE:
+                        pc.printf("\r\ndevice notifications enable failed\r\n");
+                        break;
+                    default:
+                        break;
+                }
+            }
+            
+            if (!foundUartRXCharacteristic)
+            {
+                if (central_handle == -1)
+                {
+                    ble.startAdvertising();        
+                }
+            }
+        }
+        
+        // while a new char from computer is available
+        while (rx_char != -1)
+        { 
+            uint8_t temp[20];
+            int length = 1;
+            
+            uint8_t command = rx_char;
+            rx_char = -1;
+        
+            // if special char to test a 20 bytes frame
+            /*
+            if (command == '*')
+            {
+                pc.printf("20 chars\r\n");
+                
+                int c = 0;
+                for (c = 0; c < 20; c++)
+                {
+                    temp[c] = 'a' + c;
+                }
+                length = 20;
+            }
+            else
+            {
+                temp[0] = command;
+            }
+            */
+            temp[0] = command;
+       
+            // to central
+            //if (command == '1')     
+            {
+                if (central_handle != -1)
+                {
+                    // device to central
+                    while (1)
+                    {             
+                        if (central_handle == -1)
+                        {
+                            pc.printf("\r\ndisconnected 1 (to central)\r\n");
+                            break;
+                        }              
+                        if (!ble.gap().getState().connected) 
+                        {
+                            pc.printf("\r\ndisconnected 2 (to central)\r\n");
+                            break;
+                        }
+                        int ret = ble.gattServer().write(uartServicePtr->getRXCharacteristicHandle(), temp, length);
+                        
+                        if (ret == BLE_ERROR_NONE) 
+                        {
+                            //pc.printf("\r\nok (to central)\r\n");
+                            break;
+                        }
+                        else if (ret == BLE_STACK_BUSY)
+                        {
+                            //pc.printf("\r\nbusy (to central)\r\n");
+                            //break;
+                        }
+                        else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED)
+                        {
+                            pc.printf("\r\nnot permitted (to central)\r\n");
+                            break;
+                        }
+                        else if (ret == BLE_ERROR_INVALID_STATE)
+                        {
+                            pc.printf("\r\ninvalid state (to central)\r\n");
+                            break;
+                        }
+                        else
+                        {
+                            pc.printf("\r\ncode %d (to central)\r\n", ret);
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    pc.printf("\r\nnot connected with central\r\n");
+                }
+            }
+             
+            // to device
+            //if (command == '2')   
+            {
+                if (device_handle != -1)
+                {
+                    // central to device 
+                    while (1)
+                    {               
+                        if (device_handle == -1)
+                        {
+                            pc.printf("\r\ndisconnected (to device)\r\n");
+                            break;
+                        }     
+                        int ret = uartTXCharacteristic.write(length, temp);
+                        if (ret == BLE_ERROR_NONE) 
+                        {
+                            //pc.printf("\r\nok (to device)\r\n");
+                            break;
+                        }
+                        else if (ret == BLE_STACK_BUSY) 
+                        {
+                            //pc.printf("\r\nbusy (to device)\r\n");
+                            //break;
+                        }
+                        else if (ret == BLE_ERROR_OPERATION_NOT_PERMITTED)
+                        {
+                            pc.printf("\r\nnot permitted (to device)\r\n");
+                            break;
+                        }
+                        else if (ret == BLE_ERROR_INVALID_STATE)
+                        {
+                            pc.printf("\r\ninvalid state (to device)\r\n");
+                            break;
+                        }
+                        else
+                        {
+                            pc.printf("\r\ncode %d (to device)\r\n", ret);
+                            break;
+                        }
+                    }
+                }
+                else
+                {
+                    pc.printf("\r\nnot connected with device\r\n");
+                }
+            }
+        }
+        
+        ble.waitForEvent(); // save power
+    }
+}
+#endif