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/#

Revision:
0:0ef6455cbb4d
Child:
1:f68a5e55a60e
--- /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
+    }
+}