Demo BLE Central function

Fork of BLE_HeartRate_DELTA by Delta

This example demonstrates BLE central that to interact with the HRM peripheral.

The example uses two applications running on two different devices:

The first device - the central - runs the application BLE_Central_HeartRate_DELTA from this repository.

The second device - the peripheral - runs the application BLE_HeartRate_DELTA.

Committer:
silviaChen
Date:
Fri Mar 24 08:30:59 2017 +0000
Revision:
2:504d4bd0e684
Parent:
1:82331af3e4c9
Support BLE Central function

Who changed what in which revision?

UserRevisionLine numberNew contents of line
silviaChen 0:c7bcc0b36b5e 1 /* mbed Microcontroller Library
silviaChen 0:c7bcc0b36b5e 2 * Copyright (c) 2006-2015 ARM Limited
silviaChen 0:c7bcc0b36b5e 3 *
silviaChen 0:c7bcc0b36b5e 4 * Licensed under the Apache License, Version 2.0 (the "License");
silviaChen 0:c7bcc0b36b5e 5 * you may not use this file except in compliance with the License.
silviaChen 0:c7bcc0b36b5e 6 * You may obtain a copy of the License at
silviaChen 0:c7bcc0b36b5e 7 *
silviaChen 0:c7bcc0b36b5e 8 * http://www.apache.org/licenses/LICENSE-2.0
silviaChen 0:c7bcc0b36b5e 9 *
silviaChen 0:c7bcc0b36b5e 10 * Unless required by applicable law or agreed to in writing, software
silviaChen 0:c7bcc0b36b5e 11 * distributed under the License is distributed on an "AS IS" BASIS,
silviaChen 0:c7bcc0b36b5e 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
silviaChen 0:c7bcc0b36b5e 13 * See the License for the specific language governing permissions and
silviaChen 0:c7bcc0b36b5e 14 * limitations under the License.
silviaChen 0:c7bcc0b36b5e 15 */
silviaChen 0:c7bcc0b36b5e 16
silviaChen 2:504d4bd0e684 17 #include "Gap.h"
silviaChen 2:504d4bd0e684 18 #include "ble/GattCharacteristic.h"
silviaChen 2:504d4bd0e684 19 #include "ble/UUID.h"
silviaChen 2:504d4bd0e684 20 #include "ble/DiscoveredCharacteristic.h"
silviaChen 2:504d4bd0e684 21 #include "ble/DiscoveredService.h"
silviaChen 0:c7bcc0b36b5e 22 #include "mbed.h"
silviaChen 0:c7bcc0b36b5e 23 #include "ble/BLE.h"
silviaChen 0:c7bcc0b36b5e 24 #include "ble/services/HeartRateService.h"
silviaChen 0:c7bcc0b36b5e 25 #include "ble/services/BatteryService.h"
silviaChen 0:c7bcc0b36b5e 26 #include "ble/services/DeviceInformationService.h"
silviaChen 0:c7bcc0b36b5e 27
silviaChen 0:c7bcc0b36b5e 28 DigitalOut led1(LED1);
silviaChen 2:504d4bd0e684 29 Serial uart(USBTX, USBRX, 115200);
silviaChen 0:c7bcc0b36b5e 30
silviaChen 2:504d4bd0e684 31 const static char DEVICE_NAME[] = "DELTA_HRM_CEN";
silviaChen 0:c7bcc0b36b5e 32 static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE,
silviaChen 0:c7bcc0b36b5e 33 GattService::UUID_DEVICE_INFORMATION_SERVICE};
silviaChen 0:c7bcc0b36b5e 34 static volatile bool triggerSensorPolling = false;
silviaChen 0:c7bcc0b36b5e 35
silviaChen 0:c7bcc0b36b5e 36 uint8_t hrmCounter = 100; // init HRM to 100bps
silviaChen 2:504d4bd0e684 37 const static char PEER_NAME[] = "DELTA_HRM1";
silviaChen 0:c7bcc0b36b5e 38
silviaChen 0:c7bcc0b36b5e 39 HeartRateService *hrService;
silviaChen 0:c7bcc0b36b5e 40 DeviceInformationService *deviceInfo;
silviaChen 0:c7bcc0b36b5e 41
silviaChen 2:504d4bd0e684 42 void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {
silviaChen 2:504d4bd0e684 43 for (uint8_t i = 0; i < params->advertisingDataLen; ++i) {
silviaChen 2:504d4bd0e684 44 const uint8_t record_length = params->advertisingData[i];
silviaChen 2:504d4bd0e684 45 if (record_length == 0) continue;
silviaChen 2:504d4bd0e684 46 const uint8_t type = params->advertisingData[i + 1];
silviaChen 2:504d4bd0e684 47 const uint8_t* value = params->advertisingData + i + 2;
silviaChen 2:504d4bd0e684 48 const uint8_t value_length = record_length - 1;
silviaChen 2:504d4bd0e684 49
silviaChen 2:504d4bd0e684 50 if(type == GapAdvertisingData::COMPLETE_LOCAL_NAME) {
silviaChen 2:504d4bd0e684 51 if ((value_length == sizeof(PEER_NAME)) && (memcmp(value, PEER_NAME, value_length) == 0)) {
silviaChen 2:504d4bd0e684 52 uart.printf("%s ", value);
silviaChen 2:504d4bd0e684 53 uart.printf(", adv peerAddr [%02x %02x %02x %02x %02x %02x], rssi %d, isScanResponse %u, AdvertisementType %u\r\n",
silviaChen 2:504d4bd0e684 54 params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2],
silviaChen 2:504d4bd0e684 55 params->peerAddr[1], params->peerAddr[0], params->rssi, params->isScanResponse, params->type
silviaChen 2:504d4bd0e684 56 );
silviaChen 2:504d4bd0e684 57 BLE::Instance(BLE::DEFAULT_INSTANCE).gap().stopScan();
silviaChen 2:504d4bd0e684 58 BLE::Instance(BLE::DEFAULT_INSTANCE).gap().connect(params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
silviaChen 2:504d4bd0e684 59 break;
silviaChen 2:504d4bd0e684 60 }
silviaChen 2:504d4bd0e684 61 }
silviaChen 2:504d4bd0e684 62 i += record_length;
silviaChen 2:504d4bd0e684 63 }
silviaChen 2:504d4bd0e684 64 }
silviaChen 2:504d4bd0e684 65
silviaChen 2:504d4bd0e684 66 void serviceDiscoveryCallback(const DiscoveredService *service) {
silviaChen 2:504d4bd0e684 67 uart.printf("serviceDiscoveryCallback\r\n");
silviaChen 2:504d4bd0e684 68 if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
silviaChen 2:504d4bd0e684 69 uart.printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
silviaChen 2:504d4bd0e684 70 } else {
silviaChen 2:504d4bd0e684 71 uart.printf("S UUID-");
silviaChen 2:504d4bd0e684 72 const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
silviaChen 2:504d4bd0e684 73 for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
silviaChen 2:504d4bd0e684 74 uart.printf("%02x", longUUIDBytes[i]);
silviaChen 2:504d4bd0e684 75 }
silviaChen 2:504d4bd0e684 76 uart.printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
silviaChen 2:504d4bd0e684 77 }
silviaChen 2:504d4bd0e684 78 }
silviaChen 2:504d4bd0e684 79
silviaChen 2:504d4bd0e684 80 void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) {
silviaChen 2:504d4bd0e684 81 if (characteristicP->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
silviaChen 2:504d4bd0e684 82 uart.printf(" C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast());
silviaChen 2:504d4bd0e684 83 if (characteristicP->getUUID().getShortUUID() == 0x2a37) { /* !ALERT! Alter this filter to suit your device. */
silviaChen 2:504d4bd0e684 84 uint16_t value = BLE_HVX_NOTIFICATION;
silviaChen 2:504d4bd0e684 85 BLE::Instance(BLE::DEFAULT_INSTANCE).gattClient().write(GattClient::GATT_OP_WRITE_REQ, characteristicP->getConnectionHandle(), characteristicP->getValueHandle() + 1, 2, (uint8_t *)&value);
silviaChen 2:504d4bd0e684 86 }
silviaChen 2:504d4bd0e684 87 }
silviaChen 2:504d4bd0e684 88 else {
silviaChen 2:504d4bd0e684 89 uart.printf(" C UUID-");
silviaChen 2:504d4bd0e684 90 const uint8_t *longUUIDBytes = characteristicP->getUUID().getBaseUUID();
silviaChen 2:504d4bd0e684 91 for (unsigned i = (UUID::LENGTH_OF_LONG_UUID) - 1; i < UUID::LENGTH_OF_LONG_UUID; i--) {
silviaChen 2:504d4bd0e684 92 uart.printf("%02x ", longUUIDBytes[i]);
silviaChen 2:504d4bd0e684 93 }
silviaChen 2:504d4bd0e684 94 uart.printf(" valueAttr[%u] props[%x]\r\n", characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast());
silviaChen 2:504d4bd0e684 95 }
silviaChen 2:504d4bd0e684 96 }
silviaChen 2:504d4bd0e684 97
silviaChen 2:504d4bd0e684 98 void discoveryTerminationCallback(Gap::Handle_t connectionHandle) {
silviaChen 2:504d4bd0e684 99 uart.printf("terminated SD for handle %u\r\n", connectionHandle);
silviaChen 2:504d4bd0e684 100 }
silviaChen 2:504d4bd0e684 101
silviaChen 2:504d4bd0e684 102 void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
silviaChen 2:504d4bd0e684 103 uart.printf("connectionCallback\r\n");
silviaChen 2:504d4bd0e684 104 if (params->role == Gap::CENTRAL) {
silviaChen 2:504d4bd0e684 105 BLE::Instance(BLE::DEFAULT_INSTANCE).gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
silviaChen 2:504d4bd0e684 106 BLE::Instance(BLE::DEFAULT_INSTANCE).gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback);
silviaChen 2:504d4bd0e684 107 }
silviaChen 2:504d4bd0e684 108 }
silviaChen 2:504d4bd0e684 109
silviaChen 2:504d4bd0e684 110 void dataReadCallback(const GattReadCallbackParams *response) {
silviaChen 2:504d4bd0e684 111
silviaChen 2:504d4bd0e684 112 }
silviaChen 2:504d4bd0e684 113
silviaChen 2:504d4bd0e684 114 void dataWriteCallback(const GattWriteCallbackParams *response) {
silviaChen 2:504d4bd0e684 115
silviaChen 2:504d4bd0e684 116 }
silviaChen 2:504d4bd0e684 117
silviaChen 2:504d4bd0e684 118 void hvxCallback(const GattHVXCallbackParams *response) {
silviaChen 2:504d4bd0e684 119 uart.printf("hvxCallback\r\n");
silviaChen 2:504d4bd0e684 120 for (unsigned index = 0; index < response->len; index++) {
silviaChen 2:504d4bd0e684 121 uart.printf("[%02x]", response->data[index]);
silviaChen 2:504d4bd0e684 122 }
silviaChen 2:504d4bd0e684 123 uart.printf("\r\n");
silviaChen 2:504d4bd0e684 124 }
silviaChen 2:504d4bd0e684 125
silviaChen 0:c7bcc0b36b5e 126 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
silviaChen 0:c7bcc0b36b5e 127 {
silviaChen 0:c7bcc0b36b5e 128 BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising(); // restart advertising
silviaChen 0:c7bcc0b36b5e 129 }
silviaChen 0:c7bcc0b36b5e 130
silviaChen 0:c7bcc0b36b5e 131 void periodicCallback(void)
silviaChen 0:c7bcc0b36b5e 132 {
silviaChen 0:c7bcc0b36b5e 133 led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
silviaChen 0:c7bcc0b36b5e 134
silviaChen 0:c7bcc0b36b5e 135 /* Note that the periodicCallback() executes in interrupt context, so it is safer to do
silviaChen 0:c7bcc0b36b5e 136 * heavy-weight sensor polling from the main thread. */
silviaChen 0:c7bcc0b36b5e 137 triggerSensorPolling = true;
silviaChen 0:c7bcc0b36b5e 138 }
silviaChen 0:c7bcc0b36b5e 139
silviaChen 0:c7bcc0b36b5e 140 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
silviaChen 0:c7bcc0b36b5e 141 {
silviaChen 0:c7bcc0b36b5e 142 BLE &ble = params->ble;
silviaChen 0:c7bcc0b36b5e 143 ble_error_t error = params->error;
silviaChen 0:c7bcc0b36b5e 144
silviaChen 0:c7bcc0b36b5e 145 if (error != BLE_ERROR_NONE) {
silviaChen 0:c7bcc0b36b5e 146 return;
silviaChen 0:c7bcc0b36b5e 147 }
silviaChen 0:c7bcc0b36b5e 148
silviaChen 2:504d4bd0e684 149 //Register callback function
silviaChen 2:504d4bd0e684 150 ble.gap().onConnection(connectionCallback);
silviaChen 0:c7bcc0b36b5e 151 ble.gap().onDisconnection(disconnectionCallback);
silviaChen 2:504d4bd0e684 152 ble.gattClient().onDataRead(dataReadCallback);
silviaChen 2:504d4bd0e684 153 ble.gattClient().onDataWrite(dataWriteCallback);
silviaChen 2:504d4bd0e684 154 ble.gattClient().onHVX(hvxCallback);
silviaChen 0:c7bcc0b36b5e 155
silviaChen 0:c7bcc0b36b5e 156 /* Setup primary service. */
silviaChen 0:c7bcc0b36b5e 157 hrService = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
silviaChen 0:c7bcc0b36b5e 158
silviaChen 0:c7bcc0b36b5e 159 /* Setup auxiliary service. */
silviaChen 0:c7bcc0b36b5e 160 deviceInfo = new DeviceInformationService(ble, "DELTA", "NQ620", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
silviaChen 0:c7bcc0b36b5e 161
silviaChen 0:c7bcc0b36b5e 162 /* Setup advertising. */
silviaChen 0:c7bcc0b36b5e 163 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
silviaChen 0:c7bcc0b36b5e 164 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
silviaChen 0:c7bcc0b36b5e 165 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
silviaChen 0:c7bcc0b36b5e 166 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
silviaChen 0:c7bcc0b36b5e 167 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
silviaChen 2:504d4bd0e684 168 ble.gap().setAdvertisingInterval(160); /* 160ms */
silviaChen 0:c7bcc0b36b5e 169 ble.gap().startAdvertising();
silviaChen 2:504d4bd0e684 170
silviaChen 2:504d4bd0e684 171 //Central - start scan
silviaChen 2:504d4bd0e684 172 ble.gap().setScanParams(500, 400);
silviaChen 2:504d4bd0e684 173 ble.gap().startScan(advertisementCallback);
silviaChen 0:c7bcc0b36b5e 174 }
silviaChen 0:c7bcc0b36b5e 175
silviaChen 0:c7bcc0b36b5e 176 int main(void)
silviaChen 0:c7bcc0b36b5e 177 {
silviaChen 0:c7bcc0b36b5e 178 led1 = 1;
silviaChen 0:c7bcc0b36b5e 179 Ticker ticker;
silviaChen 0:c7bcc0b36b5e 180 ticker.attach(periodicCallback, 1); // blink LED every second
silviaChen 0:c7bcc0b36b5e 181
silviaChen 0:c7bcc0b36b5e 182 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
silviaChen 0:c7bcc0b36b5e 183 ble.init(bleInitComplete);
silviaChen 0:c7bcc0b36b5e 184
silviaChen 0:c7bcc0b36b5e 185 /* SpinWait for initialization to complete. This is necessary because the
silviaChen 0:c7bcc0b36b5e 186 * BLE object is used in the main loop below. */
silviaChen 0:c7bcc0b36b5e 187 while (ble.hasInitialized() == false) { /* spin loop */ }
silviaChen 0:c7bcc0b36b5e 188
tsungta 1:82331af3e4c9 189 ble.setTxPower(0);
tsungta 1:82331af3e4c9 190
silviaChen 0:c7bcc0b36b5e 191 // infinite loop
silviaChen 0:c7bcc0b36b5e 192 while (1) {
silviaChen 0:c7bcc0b36b5e 193 // check for trigger from periodicCallback()
silviaChen 0:c7bcc0b36b5e 194 if (triggerSensorPolling && ble.getGapState().connected) {
silviaChen 0:c7bcc0b36b5e 195 triggerSensorPolling = false;
silviaChen 0:c7bcc0b36b5e 196
silviaChen 0:c7bcc0b36b5e 197 // Do blocking calls or whatever is necessary for sensor polling.
silviaChen 0:c7bcc0b36b5e 198 // In our case, we simply update the HRM measurement.
silviaChen 0:c7bcc0b36b5e 199 hrmCounter++;
silviaChen 0:c7bcc0b36b5e 200 if (hrmCounter == 175) { // 100 <= HRM bps <=175
silviaChen 0:c7bcc0b36b5e 201 hrmCounter = 100;
silviaChen 0:c7bcc0b36b5e 202 }
silviaChen 0:c7bcc0b36b5e 203
silviaChen 0:c7bcc0b36b5e 204 hrService->updateHeartRate(hrmCounter);
silviaChen 0:c7bcc0b36b5e 205 } else {
silviaChen 0:c7bcc0b36b5e 206 ble.waitForEvent(); // low power wait for event
silviaChen 0:c7bcc0b36b5e 207 }
silviaChen 0:c7bcc0b36b5e 208 }
silviaChen 0:c7bcc0b36b5e 209 }