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

main.cpp

Committer:
kenjiArai
Date:
2016-04-21
Revision:
1:f68a5e55a60e
Parent:
0:0ef6455cbb4d
Child:
2:6fb0b87b041d

File content as of revision 1:f68a5e55a60e:

/*
 *  ------- 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    21st, 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 Server Device:
 *      BLE_Uart_Server
 *      https://developer.mbed.org/users/kenjiArai/code/BLE_Uart_Server/
 *
 */

//  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};
const Gap::Address_t    mac_board_3   = {0x0f, 0x72, 0xbf, 0x43, 0xbc, 0xd0};
const Gap::Address_t    mac_board_4   = {0x98, 0x1a, 0x92, 0x10, 0x7f, 0xd5};
#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            connected2server      = 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 Server(Peripheral)\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);
    // Server(Peripheral) 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 Server(Peripheral)
        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 Server(central to peripheral)
            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);
            }
            // Server to Client(peripheral 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;
}

// Server(Peripheral) 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 (connected2server){
        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;}
    if (mac_equals(mac, mac_board_3)) {     return 3;}
    if (mac_equals(mac, mac_board_4)) {     return 4;}
    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);
        connected2server = true;
        connectionHandle = params->handle;
        ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
        ble.gattClient().launchServiceDiscovery(
                params->handle,
                serviceDiscoveryCallback,
                characteristicDiscoveryCallback/*,
                0xa000, 0xa001*/
        );
    } else {
        DEBUG("connected as Server (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);
    connected2server = false;
    if (params->handle == SOFT_DEVICE_FATHER_HANDLE) {
        ble.startAdvertising();                         // restart advertising
    } else {
        ble.gap().startScan(advertisementCallback);     // restart scan
    }
}