This is an example Heart Rate Service created using a C-like approach. Rather than creating the service as a class and then using an instance of it, the service is entirely done with simple functions and variables.

Fork of nRF5-DK-HeartRateDemo by Bill Siever

source/main.cpp

Committer:
bsiever
Date:
2016-11-11
Revision:
2:b850666f3c8f
Parent:
0:3a7b472313d7
Child:
3:f593ad98fe21

File content as of revision 2:b850666f3c8f:

#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "ble/Gap.h"

// Uses the provided Service Classes:  
// (Local copies with comments are used rather than "ble/services/NAME" versions
#include "DeviceInformationService.h"
#include "HeartRateService.h"

// If debug is "true", include code to print debugging messages to the "console"
#define DEBUG 1
#if DEBUG
#define LOG_PRINTF(...) eventQueue.call(printf, __VA_ARGS__);
#else
#define LOG_PRINTF(...) ;
#endif

/***********************************************************************
I. Global Variables: 
   This section declares "global variable" (variables that are used 
   in multiple functions in this file)
************************************************************************/

// A. A way to keep track of the heart rate service object
// Declare a pointer to a heart rate service object.  (This variable isn't
// an actual object.  It's used like Java Reference Variables --- it can refer
// to a heart rate service object, but doesn't until it is initialized)
// Pointers are used because the heart rate service constructor requires a BLE 
// object in its constructors, which isn't available when the program first starts
// The Object won't exist until created with "new" (
 HeartRateService *pHrs;  
 
// B. A way to create new "events" 
/* Total size of space for eventQueue = event count * event size  */
EventQueue eventQueue( 16 * 32);

// C. Setup the interrupt pins for any buttons
InterruptIn buttons[4] = { InterruptIn(P0_13), InterruptIn(P0_14), InterruptIn(P0_15), InterruptIn(P0_16) };

// D. Variable to contain the heart rate value
uint8_t heartRate = 0;


/***********************************************************************
II. Event "Call backs": 
   This section contains functions that are called to respond to events.
   (Each of these is "set" as a callback somewhere in the following code too)
************************************************************************/

// A. Callback for things to do when a device disconnects
void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) {
    LOG_PRINTF(__func__);  // Print the function's name
    
    // Start advertising for a new connection
    BLE::Instance().gap().startAdvertising();
}


// B. Callback for things to do when the BLE object is ready
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
    LOG_PRINTF(__func__);  // Print the function's name
    
    // Get the BLE object
    BLE&        ble   = BLE::Instance();

    // Create the HeartRateService Object with initial value of 0 and "OTHER" location 
    // (The object's constructor automatically adds the service)
    pHrs = new HeartRateService(ble, heartRate, HeartRateService::LOCATION_OTHER);

    // Add in a Device Information Service object too.
    // Argument order:                "Manufacturer", "Model No", "Serial No", "Hardware Rev", "Firmware Rev", "Software Rev"
    new DeviceInformationService(ble, "Acme",         "1A",        "123",      "v1.1",         "r2a",          "r3");

    // Setup the "onDisconnection()" callback 
    // Connection info is handeled by GAP (Generic ACCESS Protocol), hence the ble.gap() part
    ble.gap().onDisconnection(bleDisconnectionCallback);


    // Setup advertising: Build the "payload" 
    // Add in the mode (discoverable / low energy only)
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    // Include a list of services in the advertising packet
    uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *) uuid16_list, sizeof(uuid16_list));

    // Include the device name in the advertising packet
    char     DEVICE_NAME[] = "HRM";
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME));
    // Add the fact that it's "connectable" to the advertising packet (and that its not directed to a specific device)
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    // Wait 1S between advertising packets.  Shorter will lead to faster connections but more power consumption
    // Longer will usually lead to slower times-to-connect, but lower power consumption
    ble.gap().setAdvertisingInterval(1000); /* 1000ms */  

    // Also set the device name in the GAP level 
    ble.setDeviceName((uint8_t*)DEVICE_NAME);  // Set the "GAP Name" too.

    // Start advertising
    ble.gap().startAdvertising();
}

void buttonsPress() {
    LOG_PRINTF(__func__);  // Print the function's name
    // Display the buttons that are pressed
    
    // Do NOT use printf() in callbacks.  Put all printing on tasks handled by the event queue. 
    // (This uses the debug macro provided to call printf() with arguments)
    for(int i=0;i<sizeof(buttons)/sizeof(InterruptIn);i++) {
        if(buttons[i].read()==0)
          LOG_PRINTF("\t%d down\r\n",i+1);
    }
    if(buttons[0].read()==0) {
        heartRate++;
        pHrs->updateHeartRate(heartRate);
    } else 
    if(buttons[1].read()==0) {
        heartRate--;
        pHrs->updateHeartRate(heartRate);
    }
}

// C. Callback to respond to BLE events.  This will add a function call to the 
// Event Queue to process the event. 
void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
}

/***********************************************************************
III. main() thread 
   This section will run initially and configure/start everything else.
   ************************************************************************/


int main() {
    LOG_PRINTF(__func__);  // Print the function's name
    // Configure the buttons.  
    for(int i=0;i<sizeof(buttons)/sizeof(InterruptIn); i++) {
        // Pull the button voltages "up" to 3v by default
        buttons[i].mode(PullUp);     
        // Callback for when the button falls to a 0 (when the button is pressed)
        buttons[i].fall(&buttonsPress);  
    }
    
    // Get access to the BLE object
    BLE &ble = BLE::Instance();
    // Set the BLE object to use the event queue to process any BLE events
    // (So when a BLE event occurs, a function to respond to it will be placed in the event queue)
    ble.onEventsToProcess(scheduleBleEventsProcessing);
 
    // Initialize the BLE object
    ble.init(bleInitComplete);

    // Let the "event queue" take over
    eventQueue.dispatch_forever();

    return 0;
}