Unudge / Mbed 2 deprecated BLE_HeartRate

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_HeartRate by Bluetooth Low Energy

main.cpp

Committer:
Aeyrial
Date:
2017-06-05
Revision:
80:a2573ce708e2
Parent:
75:8128a06c0a21
Child:
81:fd97c70abbd8

File content as of revision 80:a2573ce708e2:

/* mbed Microcontroller Library
 * Copyright (c) 2006-2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 

#include "mbed.h"
#include "ble/BLE.h"
#include "ble/services/HeartRateService.h"
#include "ble/services/BatteryService.h"
#include "ble/services/DeviceInformationService.h"
#include "ble/services/UARTService.h"               // Not explictly used, I have just added this in.

//** From Simple Chat example **//
#define BLE_UUID_TXRX_SERVICE            0x0000 /**< The UUID of the Nordic UART Service. */
#define BLE_UUID_TX_CHARACTERISTIC       0x0002 /**< The UUID of the TX Characteristic. */
#define BLE_UUIDS_RX_CHARACTERISTIC      0x0003 /**< The UUID of the RX Characteristic. */

#define TXRX_BUF_LEN                     20

 /* SimpleChat */ BLE  ble; // Commented out to prevent conflict
 /* HeartRate */ /* BLE &ble = params->ble; // Defined in scope of "bleInitComplete"
                  * ble_error_t error = params->error; */
                  
Serial pc(USBTX, USBRX); // USBTX, USBRX origin unclear

// The Nordic UART Service
static const uint8_t uart_base_uuid[] = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_tx_uuid[]   = {0x71, 0x3D, 0, 3, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_rx_uuid[]   = {0x71, 0x3D, 0, 2, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E};
static const uint8_t uart_base_uuid_rev[] = {0x1E, 0x94, 0x8D, 0xF1, 0x48, 0x31, 0x94, 0xBA, 0x75, 0x4C, 0x3E, 0x50, 0, 0, 0x3D, 0x71};

uint8_t txPayload[TXRX_BUF_LEN] = {0,};
uint8_t rxPayload[TXRX_BUF_LEN] = {0,};

static uint8_t rx_buf[TXRX_BUF_LEN];
static uint8_t rx_len=0;

GattCharacteristic  txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
GattCharacteristic  rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic};
GattService         uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));

void WrittenHandler(const GattWriteCallbackParams *Handler)
{   
    uint8_t buf[TXRX_BUF_LEN];
    uint16_t bytesRead, index;
    
    if (Handler->handle == txCharacteristic.getValueAttribute().getHandle()) 
    {
        ble.readCharacteristicValue(txCharacteristic.getValueAttribute().getHandle(), buf, &bytesRead);
        memset(txPayload, 0, TXRX_BUF_LEN);
        memcpy(txPayload, buf, TXRX_BUF_LEN);       
        pc.printf("WriteHandler \r\n");
        pc.printf("Length: ");
        pc.putc(bytesRead);
        pc.printf("\r\n");
        pc.printf("Data: ");
        for(index=0; index<bytesRead; index++)
        {
            pc.putc(txPayload[index]);        
        }
        pc.printf("\r\n");
    }
}

void uartCB(void)
{   
    while(pc.readable())    
    {
        rx_buf[rx_len++] = pc.getc();    
        if(rx_len>=20 || rx_buf[rx_len-1]=='\0' || rx_buf[rx_len-1]=='\n')
        {
            ble.updateCharacteristicValue(rxCharacteristic.getValueAttribute().getHandle(), rx_buf, rx_len); 
            pc.printf("RecHandler \r\n");
            pc.printf("Length: ");
            pc.putc(rx_len);
            pc.printf("\r\n");
            rx_len = 0;
            break;
        }
    }
}
//** From Heart Rate example **//
DigitalOut led1(LED1);

const static char     DEVICE_NAME[]        = "Unudge_Prototype";
static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,
                                              GattService::UUID_DEVICE_INFORMATION_SERVICE};

// Alternately to include the UART , I could use a different unit size ("uint128_list[]" rather than "uuid16_list[]")

static volatile bool  triggerSensorPolling = false;

uint8_t hrmCounter = 100; // init HRM to 100bps

// These services are classes: "HeartRateService" and "DeviceInformationService" are defined in the BLE_API library under classes
// The names of the objects are brought in as pointers
HeartRateService         *hrService;
DeviceInformationService *deviceInfo;

// Specifics not understood, purpose clear: When the device is disconnected,
// start advertising again immediately
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
{
    BLE::Instance(BLE::DEFAULT_INSTANCE).startAdvertising(); // restart advertising
    // Alternate Content
    /*pc.printf("Disconnected \r\n");
    pc.printf("Restart advertising \r\n");
    ble.startAdvertising();*/
}

// This function switches the led ON/OFF and
// sets a flag value which is used in int main ()
void periodicCallback(void)
{
    led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */

    /* Note that the periodicCallback() executes in interrupt context, so it is safer to do
     * heavy-weight sensor polling from the main thread. */
    triggerSensorPolling = true;
    
    // ".available" is guesswork. I need something to check
    // If there's any incoming message - onDataWritten?
    /*
    if(ble.getGapState().available) {
        // I anticipate using a single char input to flag
        // The intended purpose of the communication
        
        // read in "someFlag" and store
        string someFlag = /syntax for reading ble input/
        
        switch(someFlag){
            case 'S':
            inBoundSettingChange = true;
            break;
            case 'R':
            inBoundRhythm = true;
            break;
            default:
            break;
        }
        //Flush someFlag
        someFlag = '';
    }
    */
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    // BLE is the class, &ble is the address of the declared object
    // "params->" purpose unknown
    BLE &ble          = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {
        return;
    }

    ble.onDisconnection(disconnectionCallback);

    /* Setup primary service. */
    hrService = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);

    /* Setup auxiliary service. */
    // 2nd parameter - the manufacturer's name // rev - revision
    deviceInfo = new DeviceInformationService(ble, "ARM", "Model1", "SerialNo#", "hw-rev1", "fw-rev1", "soft-rev1");

    /* Setup advertising. */
    // This line below is mandatory for bluetooth smart
    // It indicates the device only supports Bluetooth Low Energy
    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    // I believe this advertises the service list
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
    
    // I realise that this isn't efficient but I would rather keep the code as
    // Is for now, so that it actually works. Hence why a 2nd "COMPLETE_LIST"
    /* SimpleChat */ ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
                     (const uint8_t *)uart_base_uuid_rev, sizeof(uart_base_uuid));
    
    ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
    // Advertise the device name
    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    // Appears in all examples I have seen
    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    
    // Setup Advertising Interval
    ble.setAdvertisingInterval(1000); /* 1000ms */
    
    /* SimpleChat */ ble.addService(uartService);
    // Start Advertising
    ble.startAdvertising();
    
    /* SimpleChat */ pc.printf("Advertising Start \r\n");
}

int main(void)
{
    pc.baud(9600);
    pc.printf("Baud Rate set to 9600 \r\n");
    
    pc.attach( uartCB , pc.RxIrq);
    
    led1 = 1;
    Ticker ticker;
    ticker.attach(periodicCallback, 1); // blink LED every second

    BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
    ble.init(bleInitComplete);

    /* SpinWait for initialization to complete. This is necessary because the
     * BLE object is used in the main loop below. */
    while (ble.hasInitialized()  == false) { /* spin loop */ }

    /* SimpleChat */ // Places nothing in the while (1) {} loop
                     // It's relying on interrupts rather than
                     // a periodic callback via a Ticker
    // infinite loop
    while (1) {
        // check for trigger from periodicCallback()
        if (triggerSensorPolling && ble.getGapState().connected) {
            triggerSensorPolling = false;

            // Do blocking calls or whatever is necessary for sensor polling.
            // In our case, we simply update the HRM measurement.
            hrmCounter++;
            if (hrmCounter == 175) { //  100 <= HRM bps <=175
                hrmCounter = 100;
            }
            hrService->updateHeartRate(hrmCounter);
        // Code relating to storing/playing inbound rhythm
        // Into "Rhythm" Class (Yet to be defined)
        /*} else if (inBoundRhythm && ble.getGapState().connected) {
            inBoundRhythm = false;
            Rhythm inRhythm;
            inRythm += syntax for getting the incoming string
            inRhythm.play(); // Create Member function which 
                             // Plays Rhythms
            
        */
        // Code relating to storing/sending outbound rhythm
        /*} else if (inBoundRhythm && ble.getGapState().connected) {
            inBoundRhythm = false;
            Rhythm inRhythm;
            inRythm += syntax for getting the incoming string
            inRhythm.play(); // Create Member function which 
                             // Plays Rhythms
            
        */
        
        // How will I set inBoundRhythm = true; ?
        } else {
            ble.waitForEvent(); // low power wait for event
        }
    }
}