A GattClient example based on ST BlueNRG driving an LED service exported by a BLE_LED peripheral. Shows scanning, connections, service-discovery, and reads/writes.
Dependencies: BLE_API X_NUCLEO_IDB0XA1 mbed
main.cpp@2:c944cfe08e79, 2016-02-19 (annotated)
- Committer:
- apalmieri
- Date:
- Fri Feb 19 15:32:04 2016 +0000
- Revision:
- 2:c944cfe08e79
- Parent:
- 1:e0f4bcce540c
- Child:
- 3:b2bf47485f85
Sync w/ rev114 of mbed lib
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
apalmieri | 2:c944cfe08e79 | 1 | #if 0 |
apalmieri | 0:2e972e7d3f64 | 2 | /* mbed Microcontroller Library |
apalmieri | 0:2e972e7d3f64 | 3 | * Copyright (c) 2006-2013 ARM Limited |
apalmieri | 0:2e972e7d3f64 | 4 | * |
apalmieri | 0:2e972e7d3f64 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
apalmieri | 0:2e972e7d3f64 | 6 | * you may not use this file except in compliance with the License. |
apalmieri | 0:2e972e7d3f64 | 7 | * You may obtain a copy of the License at |
apalmieri | 0:2e972e7d3f64 | 8 | * |
apalmieri | 0:2e972e7d3f64 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
apalmieri | 0:2e972e7d3f64 | 10 | * |
apalmieri | 0:2e972e7d3f64 | 11 | * Unless required by applicable law or agreed to in writing, software |
apalmieri | 0:2e972e7d3f64 | 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
apalmieri | 0:2e972e7d3f64 | 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
apalmieri | 0:2e972e7d3f64 | 14 | * See the License for the specific language governing permissions and |
apalmieri | 0:2e972e7d3f64 | 15 | * limitations under the License. |
apalmieri | 0:2e972e7d3f64 | 16 | */ |
apalmieri | 0:2e972e7d3f64 | 17 | |
apalmieri | 0:2e972e7d3f64 | 18 | #include "mbed.h" |
apalmieri | 0:2e972e7d3f64 | 19 | #include "ble/BLE.h" |
apalmieri | 0:2e972e7d3f64 | 20 | #include "ble/DiscoveredCharacteristic.h" |
apalmieri | 0:2e972e7d3f64 | 21 | #include "ble/DiscoveredService.h" |
apalmieri | 0:2e972e7d3f64 | 22 | |
apalmieri | 0:2e972e7d3f64 | 23 | #define SCAN_INT 0x30 // 30 ms = 48 * 0.625 ms |
apalmieri | 0:2e972e7d3f64 | 24 | #define SCAN_WIND 0x30 // 30 ms = 48 * 0.625 ms |
apalmieri | 0:2e972e7d3f64 | 25 | |
apalmieri | 0:2e972e7d3f64 | 26 | |
apalmieri | 0:2e972e7d3f64 | 27 | const Gap::Address_t BLE_address_BE = {0xCC, 0x00, 0x00, 0xE1, 0x80, 0x02}; |
apalmieri | 0:2e972e7d3f64 | 28 | const Gap::Address_t BLE_peer_address_BE = {0xFD, 0x66, 0x05, 0x13, 0xBE, 0xBA}; |
apalmieri | 0:2e972e7d3f64 | 29 | |
apalmieri | 0:2e972e7d3f64 | 30 | DiscoveredCharacteristic ledCharacteristic; |
apalmieri | 0:2e972e7d3f64 | 31 | |
apalmieri | 0:2e972e7d3f64 | 32 | uint8_t toggledValue = 0; |
apalmieri | 0:2e972e7d3f64 | 33 | enum { |
apalmieri | 0:2e972e7d3f64 | 34 | READ = 0, |
apalmieri | 0:2e972e7d3f64 | 35 | WRITE, |
apalmieri | 0:2e972e7d3f64 | 36 | IDLE |
apalmieri | 0:2e972e7d3f64 | 37 | }; |
apalmieri | 0:2e972e7d3f64 | 38 | static volatile unsigned int triggerOp = IDLE; |
apalmieri | 0:2e972e7d3f64 | 39 | |
apalmieri | 0:2e972e7d3f64 | 40 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) |
apalmieri | 0:2e972e7d3f64 | 41 | { |
apalmieri | 0:2e972e7d3f64 | 42 | (void)params; |
apalmieri | 0:2e972e7d3f64 | 43 | printf("disconnected\n\r"); |
apalmieri | 0:2e972e7d3f64 | 44 | } |
apalmieri | 0:2e972e7d3f64 | 45 | |
apalmieri | 0:2e972e7d3f64 | 46 | void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) { |
apalmieri | 0:2e972e7d3f64 | 47 | if (params->peerAddr[0] != BLE_peer_address_BE[0]) { |
apalmieri | 0:2e972e7d3f64 | 48 | return; |
apalmieri | 0:2e972e7d3f64 | 49 | } |
apalmieri | 0:2e972e7d3f64 | 50 | |
apalmieri | 0:2e972e7d3f64 | 51 | printf("adv peerAddr[%02x %02x %02x %02x %02x %02x] rssi %d, isScanResponse %u, AdvertisementType %u\r\n", |
apalmieri | 0:2e972e7d3f64 | 52 | params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0], |
apalmieri | 0:2e972e7d3f64 | 53 | params->rssi, params->isScanResponse, params->type); |
apalmieri | 0:2e972e7d3f64 | 54 | |
apalmieri | 0:2e972e7d3f64 | 55 | if(!params->isScanResponse) { |
apalmieri | 0:2e972e7d3f64 | 56 | BLE::Instance().gap().connect(params->peerAddr, Gap::ADDR_TYPE_PUBLIC, NULL, NULL); |
apalmieri | 0:2e972e7d3f64 | 57 | } |
apalmieri | 0:2e972e7d3f64 | 58 | } |
apalmieri | 0:2e972e7d3f64 | 59 | |
apalmieri | 0:2e972e7d3f64 | 60 | void discoveryTerminationCallback(Gap::Handle_t connectionHandle) { |
apalmieri | 0:2e972e7d3f64 | 61 | printf("terminated SD for handle %u\r\n", connectionHandle); |
apalmieri | 0:2e972e7d3f64 | 62 | } |
apalmieri | 0:2e972e7d3f64 | 63 | |
apalmieri | 0:2e972e7d3f64 | 64 | void serviceDiscoveryCallback(const DiscoveredService *service) { |
apalmieri | 0:2e972e7d3f64 | 65 | if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) { |
apalmieri | 0:2e972e7d3f64 | 66 | printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle()); |
apalmieri | 0:2e972e7d3f64 | 67 | } else { |
apalmieri | 0:2e972e7d3f64 | 68 | printf("S UUID-"); |
apalmieri | 0:2e972e7d3f64 | 69 | const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); |
apalmieri | 0:2e972e7d3f64 | 70 | for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { |
apalmieri | 0:2e972e7d3f64 | 71 | printf("%02X", longUUIDBytes[i]); |
apalmieri | 0:2e972e7d3f64 | 72 | } |
apalmieri | 0:2e972e7d3f64 | 73 | printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); |
apalmieri | 0:2e972e7d3f64 | 74 | } |
apalmieri | 0:2e972e7d3f64 | 75 | } |
apalmieri | 0:2e972e7d3f64 | 76 | |
apalmieri | 0:2e972e7d3f64 | 77 | void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) { |
apalmieri | 0:2e972e7d3f64 | 78 | //printf(" C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast()); |
apalmieri | 0:2e972e7d3f64 | 79 | if (characteristicP->getUUID().getShortUUID() == 0xa001) { /* !ALERT! Alter this filter to suit your device. */ |
apalmieri | 0:2e972e7d3f64 | 80 | //printf(" C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast()); |
apalmieri | 0:2e972e7d3f64 | 81 | ledCharacteristic = *characteristicP; |
apalmieri | 0:2e972e7d3f64 | 82 | triggerOp = READ; |
apalmieri | 0:2e972e7d3f64 | 83 | } |
apalmieri | 0:2e972e7d3f64 | 84 | } |
apalmieri | 0:2e972e7d3f64 | 85 | |
apalmieri | 0:2e972e7d3f64 | 86 | void connectionCallback(const Gap::ConnectionCallbackParams_t *params) { |
apalmieri | 0:2e972e7d3f64 | 87 | uint16_t LED_SERVICE_UUID = 0xA000; |
apalmieri | 0:2e972e7d3f64 | 88 | uint16_t LED_STATE_CHARACTERISTIC_UUID = 0xA001; |
apalmieri | 0:2e972e7d3f64 | 89 | |
apalmieri | 0:2e972e7d3f64 | 90 | if (params->role == Gap::CENTRAL) { |
apalmieri | 0:2e972e7d3f64 | 91 | BLE &ble = BLE::Instance(); |
apalmieri | 0:2e972e7d3f64 | 92 | ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback); |
apalmieri | 0:2e972e7d3f64 | 93 | ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, LED_SERVICE_UUID, LED_STATE_CHARACTERISTIC_UUID); |
apalmieri | 0:2e972e7d3f64 | 94 | } |
apalmieri | 0:2e972e7d3f64 | 95 | } |
apalmieri | 0:2e972e7d3f64 | 96 | |
apalmieri | 0:2e972e7d3f64 | 97 | void triggerToggledWrite(const GattReadCallbackParams *response) { |
apalmieri | 0:2e972e7d3f64 | 98 | if (response->handle == ledCharacteristic.getValueHandle()) { |
apalmieri | 0:2e972e7d3f64 | 99 | #if 0 |
apalmieri | 0:2e972e7d3f64 | 100 | printf("triggerToggledWrite: handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len); |
apalmieri | 0:2e972e7d3f64 | 101 | for (unsigned index = 0; index < response->len; index++) { |
apalmieri | 0:2e972e7d3f64 | 102 | printf("%c[%02x]", response->data[index], response->data[index]); |
apalmieri | 0:2e972e7d3f64 | 103 | } |
apalmieri | 0:2e972e7d3f64 | 104 | printf("\r\n"); |
apalmieri | 0:2e972e7d3f64 | 105 | #endif |
apalmieri | 0:2e972e7d3f64 | 106 | |
apalmieri | 0:2e972e7d3f64 | 107 | toggledValue = response->data[0] ^ 0x1; |
apalmieri | 0:2e972e7d3f64 | 108 | triggerOp = WRITE; |
apalmieri | 0:2e972e7d3f64 | 109 | } |
apalmieri | 0:2e972e7d3f64 | 110 | } |
apalmieri | 0:2e972e7d3f64 | 111 | |
apalmieri | 0:2e972e7d3f64 | 112 | void triggerRead(const GattWriteCallbackParams *response) { |
apalmieri | 0:2e972e7d3f64 | 113 | if (response->handle == ledCharacteristic.getValueHandle()) { |
apalmieri | 0:2e972e7d3f64 | 114 | triggerOp = READ; |
apalmieri | 0:2e972e7d3f64 | 115 | } |
apalmieri | 0:2e972e7d3f64 | 116 | } |
apalmieri | 0:2e972e7d3f64 | 117 | |
apalmieri | 0:2e972e7d3f64 | 118 | /** |
apalmieri | 0:2e972e7d3f64 | 119 | * This function is called when the ble initialization process has failled |
apalmieri | 0:2e972e7d3f64 | 120 | */ |
apalmieri | 0:2e972e7d3f64 | 121 | void onBleInitError(BLE &ble, ble_error_t error) |
apalmieri | 0:2e972e7d3f64 | 122 | { |
apalmieri | 0:2e972e7d3f64 | 123 | /* Initialization error handling should go here */ |
apalmieri | 0:2e972e7d3f64 | 124 | } |
apalmieri | 0:2e972e7d3f64 | 125 | |
apalmieri | 0:2e972e7d3f64 | 126 | /** |
apalmieri | 0:2e972e7d3f64 | 127 | * Callback triggered when the ble initialization process has finished |
apalmieri | 0:2e972e7d3f64 | 128 | */ |
apalmieri | 0:2e972e7d3f64 | 129 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
apalmieri | 0:2e972e7d3f64 | 130 | { |
apalmieri | 0:2e972e7d3f64 | 131 | BLE& ble = params->ble; |
apalmieri | 0:2e972e7d3f64 | 132 | ble_error_t error = params->error; |
apalmieri | 0:2e972e7d3f64 | 133 | |
apalmieri | 0:2e972e7d3f64 | 134 | if (error != BLE_ERROR_NONE) { |
apalmieri | 0:2e972e7d3f64 | 135 | /* In case of error, forward the error handling to onBleInitError */ |
apalmieri | 0:2e972e7d3f64 | 136 | onBleInitError(ble, error); |
apalmieri | 0:2e972e7d3f64 | 137 | return; |
apalmieri | 0:2e972e7d3f64 | 138 | } |
apalmieri | 0:2e972e7d3f64 | 139 | |
apalmieri | 0:2e972e7d3f64 | 140 | /* Ensure that it is the default instance of BLE */ |
apalmieri | 0:2e972e7d3f64 | 141 | if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
apalmieri | 0:2e972e7d3f64 | 142 | return; |
apalmieri | 0:2e972e7d3f64 | 143 | } |
apalmieri | 0:2e972e7d3f64 | 144 | |
apalmieri | 0:2e972e7d3f64 | 145 | // Set BT Address |
apalmieri | 1:e0f4bcce540c | 146 | ble.gap().setAddress(BLEProtocol::AddressType::PUBLIC, BLE_address_BE); |
apalmieri | 0:2e972e7d3f64 | 147 | |
apalmieri | 0:2e972e7d3f64 | 148 | ble.gap().onConnection(connectionCallback); |
apalmieri | 0:2e972e7d3f64 | 149 | ble.onDisconnection(disconnectionCallback); |
apalmieri | 0:2e972e7d3f64 | 150 | |
apalmieri | 0:2e972e7d3f64 | 151 | ble.gattClient().onDataRead(triggerToggledWrite); |
apalmieri | 0:2e972e7d3f64 | 152 | ble.gattClient().onDataWrite(triggerRead); |
apalmieri | 0:2e972e7d3f64 | 153 | |
apalmieri | 0:2e972e7d3f64 | 154 | ble.gap().setScanParams(SCAN_INT, SCAN_WIND); |
apalmieri | 0:2e972e7d3f64 | 155 | ble.gap().startScan(advertisementCallback); |
apalmieri | 0:2e972e7d3f64 | 156 | |
apalmieri | 0:2e972e7d3f64 | 157 | // infinite loop |
apalmieri | 0:2e972e7d3f64 | 158 | while (1) { |
apalmieri | 0:2e972e7d3f64 | 159 | if (!ble.gattClient().isServiceDiscoveryActive()) { |
apalmieri | 0:2e972e7d3f64 | 160 | switch(triggerOp) { |
apalmieri | 0:2e972e7d3f64 | 161 | case READ: |
apalmieri | 0:2e972e7d3f64 | 162 | triggerOp = IDLE; |
apalmieri | 0:2e972e7d3f64 | 163 | ledCharacteristic.read(); |
apalmieri | 0:2e972e7d3f64 | 164 | break; |
apalmieri | 0:2e972e7d3f64 | 165 | case WRITE: |
apalmieri | 0:2e972e7d3f64 | 166 | triggerOp = IDLE; |
apalmieri | 0:2e972e7d3f64 | 167 | ledCharacteristic.write(1, &toggledValue); |
apalmieri | 0:2e972e7d3f64 | 168 | break; |
apalmieri | 0:2e972e7d3f64 | 169 | } |
apalmieri | 0:2e972e7d3f64 | 170 | } |
apalmieri | 0:2e972e7d3f64 | 171 | ble.waitForEvent(); |
apalmieri | 0:2e972e7d3f64 | 172 | } |
apalmieri | 0:2e972e7d3f64 | 173 | } |
apalmieri | 0:2e972e7d3f64 | 174 | |
apalmieri | 0:2e972e7d3f64 | 175 | int main(void) |
apalmieri | 0:2e972e7d3f64 | 176 | { |
apalmieri | 0:2e972e7d3f64 | 177 | BLE::Instance().init(bleInitComplete); |
apalmieri | 0:2e972e7d3f64 | 178 | } |
apalmieri | 0:2e972e7d3f64 | 179 | |
apalmieri | 2:c944cfe08e79 | 180 | #endif |
apalmieri | 2:c944cfe08e79 | 181 | |
apalmieri | 2:c944cfe08e79 | 182 | |
apalmieri | 2:c944cfe08e79 | 183 | |
apalmieri | 2:c944cfe08e79 | 184 | |
apalmieri | 2:c944cfe08e79 | 185 | |
apalmieri | 2:c944cfe08e79 | 186 | /* mbed Microcontroller Library |
apalmieri | 2:c944cfe08e79 | 187 | * Copyright (c) 2006-2015 ARM Limited |
apalmieri | 2:c944cfe08e79 | 188 | * |
apalmieri | 2:c944cfe08e79 | 189 | * Licensed under the Apache License, Version 2.0 (the "License"); |
apalmieri | 2:c944cfe08e79 | 190 | * you may not use this file except in compliance with the License. |
apalmieri | 2:c944cfe08e79 | 191 | * You may obtain a copy of the License at |
apalmieri | 2:c944cfe08e79 | 192 | * |
apalmieri | 2:c944cfe08e79 | 193 | * http://www.apache.org/licenses/LICENSE-2.0 |
apalmieri | 2:c944cfe08e79 | 194 | * |
apalmieri | 2:c944cfe08e79 | 195 | * Unless required by applicable law or agreed to in writing, software |
apalmieri | 2:c944cfe08e79 | 196 | * distributed under the License is distributed on an "AS IS" BASIS, |
apalmieri | 2:c944cfe08e79 | 197 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
apalmieri | 2:c944cfe08e79 | 198 | * See the License for the specific language governing permissions and |
apalmieri | 2:c944cfe08e79 | 199 | * limitations under the License. |
apalmieri | 2:c944cfe08e79 | 200 | */ |
apalmieri | 2:c944cfe08e79 | 201 | |
apalmieri | 2:c944cfe08e79 | 202 | #include "mbed.h" |
apalmieri | 2:c944cfe08e79 | 203 | #include "ble/Gap.h" |
apalmieri | 2:c944cfe08e79 | 204 | #include "ble/BLE.h" |
apalmieri | 2:c944cfe08e79 | 205 | #include "ble/DiscoveredCharacteristic.h" |
apalmieri | 2:c944cfe08e79 | 206 | #include "ble/DiscoveredService.h" |
apalmieri | 2:c944cfe08e79 | 207 | #include "ble/GapAdvertisingParams.h" |
apalmieri | 2:c944cfe08e79 | 208 | |
apalmieri | 2:c944cfe08e79 | 209 | |
apalmieri | 2:c944cfe08e79 | 210 | #define PRINTF(...) printf(__VA_ARGS__) |
apalmieri | 2:c944cfe08e79 | 211 | |
apalmieri | 2:c944cfe08e79 | 212 | BLE ble; |
apalmieri | 2:c944cfe08e79 | 213 | DigitalOut led1(LED1); |
apalmieri | 2:c944cfe08e79 | 214 | |
apalmieri | 2:c944cfe08e79 | 215 | |
apalmieri | 2:c944cfe08e79 | 216 | bool _connected = false; |
apalmieri | 2:c944cfe08e79 | 217 | |
apalmieri | 2:c944cfe08e79 | 218 | DiscoveredCharacteristic hrmCharacteristic; |
apalmieri | 2:c944cfe08e79 | 219 | static volatile bool triggerHrmCharacteristic = false; |
apalmieri | 2:c944cfe08e79 | 220 | //PROTOTYPES |
apalmieri | 2:c944cfe08e79 | 221 | void PrintScanResult(const Gap::AdvertisementCallbackParams_t *params); |
apalmieri | 2:c944cfe08e79 | 222 | void periodicCallback(void); |
apalmieri | 2:c944cfe08e79 | 223 | void connectToDevice(const Gap::Address_t peerAddr); |
apalmieri | 2:c944cfe08e79 | 224 | |
apalmieri | 2:c944cfe08e79 | 225 | void serviceDiscoveryCallback(const DiscoveredService *service); |
apalmieri | 2:c944cfe08e79 | 226 | void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP); |
apalmieri | 2:c944cfe08e79 | 227 | void discoveryTerminationCallback(Gap::Handle_t connectionHandle); |
apalmieri | 2:c944cfe08e79 | 228 | |
apalmieri | 2:c944cfe08e79 | 229 | |
apalmieri | 2:c944cfe08e79 | 230 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) { |
apalmieri | 2:c944cfe08e79 | 231 | _connected = false; |
apalmieri | 2:c944cfe08e79 | 232 | PRINTF("disconnected\r\n"); |
apalmieri | 2:c944cfe08e79 | 233 | } |
apalmieri | 2:c944cfe08e79 | 234 | |
apalmieri | 2:c944cfe08e79 | 235 | void periodicCallback(void) |
apalmieri | 2:c944cfe08e79 | 236 | { |
apalmieri | 2:c944cfe08e79 | 237 | led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */ |
apalmieri | 2:c944cfe08e79 | 238 | /* Note that the periodicCallback() executes in interrupt context, so it is safer to do |
apalmieri | 2:c944cfe08e79 | 239 | * heavy-weight sensor polling from the main thread. */ |
apalmieri | 2:c944cfe08e79 | 240 | triggerHrmCharacteristic = true; |
apalmieri | 2:c944cfe08e79 | 241 | } |
apalmieri | 2:c944cfe08e79 | 242 | |
apalmieri | 2:c944cfe08e79 | 243 | void PrintScanResult(const Gap::AdvertisementCallbackParams_t *params) |
apalmieri | 2:c944cfe08e79 | 244 | { |
apalmieri | 2:c944cfe08e79 | 245 | for(int8_t i = 5; i>=0; i--) |
apalmieri | 2:c944cfe08e79 | 246 | { |
apalmieri | 2:c944cfe08e79 | 247 | PRINTF("%02x",params->peerAddr[i] ); |
apalmieri | 2:c944cfe08e79 | 248 | if(i != 0)PRINTF("-"); |
apalmieri | 2:c944cfe08e79 | 249 | } |
apalmieri | 2:c944cfe08e79 | 250 | PRINTF("\r\n"); |
apalmieri | 2:c944cfe08e79 | 251 | |
apalmieri | 2:c944cfe08e79 | 252 | PRINTF("RSSI: %d\r\n",params->rssi ); |
apalmieri | 2:c944cfe08e79 | 253 | |
apalmieri | 2:c944cfe08e79 | 254 | if(params->isScanResponse){ |
apalmieri | 2:c944cfe08e79 | 255 | PRINTF("isScanResponse=true\r\n"); |
apalmieri | 2:c944cfe08e79 | 256 | } else { |
apalmieri | 2:c944cfe08e79 | 257 | PRINTF("isScanResponse=false\r\n"); |
apalmieri | 2:c944cfe08e79 | 258 | } |
apalmieri | 2:c944cfe08e79 | 259 | |
apalmieri | 2:c944cfe08e79 | 260 | switch(params->type) |
apalmieri | 2:c944cfe08e79 | 261 | { |
apalmieri | 2:c944cfe08e79 | 262 | case GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED: |
apalmieri | 2:c944cfe08e79 | 263 | PRINTF("ADV_CONNECTABLE_UNDIRECTED\r\n"); |
apalmieri | 2:c944cfe08e79 | 264 | break; |
apalmieri | 2:c944cfe08e79 | 265 | case GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED: |
apalmieri | 2:c944cfe08e79 | 266 | PRINTF("ADV_CONNECTABLE_DIRECTED\r\n"); |
apalmieri | 2:c944cfe08e79 | 267 | break; |
apalmieri | 2:c944cfe08e79 | 268 | case GapAdvertisingParams::ADV_SCANNABLE_UNDIRECTED: |
apalmieri | 2:c944cfe08e79 | 269 | PRINTF("ADV_SCANNABLE_UNDIRECTED\r\n"); |
apalmieri | 2:c944cfe08e79 | 270 | break; |
apalmieri | 2:c944cfe08e79 | 271 | case GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED: |
apalmieri | 2:c944cfe08e79 | 272 | PRINTF("ADV_NON_CONNECTABLE_UNDIRECTED\r\n"); |
apalmieri | 2:c944cfe08e79 | 273 | break; |
apalmieri | 2:c944cfe08e79 | 274 | |
apalmieri | 2:c944cfe08e79 | 275 | } |
apalmieri | 2:c944cfe08e79 | 276 | |
apalmieri | 2:c944cfe08e79 | 277 | PRINTF("advertisingDataLen: %d\r\n",params->advertisingDataLen ); |
apalmieri | 2:c944cfe08e79 | 278 | |
apalmieri | 2:c944cfe08e79 | 279 | PRINTF("advertisingData: "); |
apalmieri | 2:c944cfe08e79 | 280 | for(int i=0; i < params->advertisingDataLen; i++) |
apalmieri | 2:c944cfe08e79 | 281 | { |
apalmieri | 2:c944cfe08e79 | 282 | PRINTF("%02x",params->advertisingData[i]); |
apalmieri | 2:c944cfe08e79 | 283 | if(i != params->advertisingDataLen - 1)PRINTF("-"); |
apalmieri | 2:c944cfe08e79 | 284 | } |
apalmieri | 2:c944cfe08e79 | 285 | PRINTF("\r\n"); |
apalmieri | 2:c944cfe08e79 | 286 | } |
apalmieri | 2:c944cfe08e79 | 287 | |
apalmieri | 2:c944cfe08e79 | 288 | |
apalmieri | 2:c944cfe08e79 | 289 | void scanResultReceived(const Gap::AdvertisementCallbackParams_t *params) |
apalmieri | 2:c944cfe08e79 | 290 | { |
apalmieri | 2:c944cfe08e79 | 291 | PRINTF("\r\nscanResultReceived\r\n"); |
apalmieri | 2:c944cfe08e79 | 292 | |
apalmieri | 2:c944cfe08e79 | 293 | //print advertsing values |
apalmieri | 2:c944cfe08e79 | 294 | PrintScanResult(params); |
apalmieri | 2:c944cfe08e79 | 295 | |
apalmieri | 2:c944cfe08e79 | 296 | PRINTF("scan result ... try connect\r\n"); |
apalmieri | 2:c944cfe08e79 | 297 | connectToDevice(params->peerAddr); |
apalmieri | 2:c944cfe08e79 | 298 | } |
apalmieri | 2:c944cfe08e79 | 299 | |
apalmieri | 2:c944cfe08e79 | 300 | void connectToDevice(const Gap::Address_t peerAddr) |
apalmieri | 2:c944cfe08e79 | 301 | { |
apalmieri | 2:c944cfe08e79 | 302 | ble_error_t ret = ble.gap().connect(peerAddr, Gap::ADDR_TYPE_PUBLIC, NULL, NULL); |
apalmieri | 2:c944cfe08e79 | 303 | |
apalmieri | 2:c944cfe08e79 | 304 | if(ret == BLE_ERROR_NONE) { |
apalmieri | 2:c944cfe08e79 | 305 | PRINTF("SUCCESS: connect\r\n"); |
apalmieri | 2:c944cfe08e79 | 306 | } else { |
apalmieri | 2:c944cfe08e79 | 307 | PRINTF("ERROR : connect\r\n"); |
apalmieri | 2:c944cfe08e79 | 308 | } |
apalmieri | 2:c944cfe08e79 | 309 | } |
apalmieri | 2:c944cfe08e79 | 310 | |
apalmieri | 2:c944cfe08e79 | 311 | void serviceDiscoveryCallback(const DiscoveredService *service) { |
apalmieri | 2:c944cfe08e79 | 312 | PRINTF("Service discovery callback\r\n"); |
apalmieri | 2:c944cfe08e79 | 313 | |
apalmieri | 2:c944cfe08e79 | 314 | if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) { |
apalmieri | 2:c944cfe08e79 | 315 | PRINTF("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle()); |
apalmieri | 2:c944cfe08e79 | 316 | } else { |
apalmieri | 2:c944cfe08e79 | 317 | printf("S UUID-"); |
apalmieri | 2:c944cfe08e79 | 318 | const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); |
apalmieri | 2:c944cfe08e79 | 319 | for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { |
apalmieri | 2:c944cfe08e79 | 320 | printf("%02x", longUUIDBytes[i]); |
apalmieri | 2:c944cfe08e79 | 321 | } |
apalmieri | 2:c944cfe08e79 | 322 | PRINTF(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); |
apalmieri | 2:c944cfe08e79 | 323 | } |
apalmieri | 2:c944cfe08e79 | 324 | } |
apalmieri | 2:c944cfe08e79 | 325 | |
apalmieri | 2:c944cfe08e79 | 326 | void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) { |
apalmieri | 2:c944cfe08e79 | 327 | PRINTF("Characteristic discovery callback\r\n"); |
apalmieri | 2:c944cfe08e79 | 328 | |
apalmieri | 2:c944cfe08e79 | 329 | PRINTF(" C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast()); |
apalmieri | 2:c944cfe08e79 | 330 | //TODO: use hear rate characteristic |
apalmieri | 2:c944cfe08e79 | 331 | if (characteristicP->getUUID().getShortUUID() == 0x2A00) { |
apalmieri | 2:c944cfe08e79 | 332 | hrmCharacteristic = *characteristicP; |
apalmieri | 2:c944cfe08e79 | 333 | triggerHrmCharacteristic = true; |
apalmieri | 2:c944cfe08e79 | 334 | PRINTF("device name characteristic found\r\n"); |
apalmieri | 2:c944cfe08e79 | 335 | } |
apalmieri | 2:c944cfe08e79 | 336 | } |
apalmieri | 2:c944cfe08e79 | 337 | |
apalmieri | 2:c944cfe08e79 | 338 | void discoveryTerminationCallback(Gap::Handle_t connectionHandle) { |
apalmieri | 2:c944cfe08e79 | 339 | PRINTF("terminated SD for handle %u\r\n", connectionHandle); |
apalmieri | 2:c944cfe08e79 | 340 | } |
apalmieri | 2:c944cfe08e79 | 341 | |
apalmieri | 2:c944cfe08e79 | 342 | void timeoutCallback(Gap::TimeoutSource_t param) { |
apalmieri | 2:c944cfe08e79 | 343 | |
apalmieri | 2:c944cfe08e79 | 344 | switch(param) |
apalmieri | 2:c944cfe08e79 | 345 | { |
apalmieri | 2:c944cfe08e79 | 346 | case Gap::TIMEOUT_SRC_ADVERTISING: |
apalmieri | 2:c944cfe08e79 | 347 | PRINTF("Timeout: Advertising\r\n"); |
apalmieri | 2:c944cfe08e79 | 348 | break; |
apalmieri | 2:c944cfe08e79 | 349 | case Gap::TIMEOUT_SRC_SECURITY_REQUEST: |
apalmieri | 2:c944cfe08e79 | 350 | PRINTF("Timeout: Security access\r\n"); |
apalmieri | 2:c944cfe08e79 | 351 | break; |
apalmieri | 2:c944cfe08e79 | 352 | case Gap::TIMEOUT_SRC_SCAN: |
apalmieri | 2:c944cfe08e79 | 353 | PRINTF("Timeout: Scan\r\n"); |
apalmieri | 2:c944cfe08e79 | 354 | break; |
apalmieri | 2:c944cfe08e79 | 355 | case Gap::TIMEOUT_SRC_CONN: |
apalmieri | 2:c944cfe08e79 | 356 | PRINTF("Timeout: Conn\r\n"); |
apalmieri | 2:c944cfe08e79 | 357 | break; |
apalmieri | 2:c944cfe08e79 | 358 | } |
apalmieri | 2:c944cfe08e79 | 359 | } |
apalmieri | 2:c944cfe08e79 | 360 | |
apalmieri | 2:c944cfe08e79 | 361 | void connectionCallback(const Gap::ConnectionCallbackParams_t *params) { |
apalmieri | 2:c944cfe08e79 | 362 | if (params->role == Gap::CENTRAL) { |
apalmieri | 2:c944cfe08e79 | 363 | PRINTF("Launch service discovery\r\n"); |
apalmieri | 2:c944cfe08e79 | 364 | //TODO: use heart rate service |
apalmieri | 2:c944cfe08e79 | 365 | ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback); |
apalmieri | 2:c944cfe08e79 | 366 | ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, 0x1800, 0x2A00); |
apalmieri | 2:c944cfe08e79 | 367 | _connected = true; |
apalmieri | 2:c944cfe08e79 | 368 | // ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, GattService::UUID_HEART_RATE_SERVICE, GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR); //BLE_UUID_UNKNOWN |
apalmieri | 2:c944cfe08e79 | 369 | } |
apalmieri | 2:c944cfe08e79 | 370 | else { |
apalmieri | 2:c944cfe08e79 | 371 | PRINTF("No central role\r\n"); |
apalmieri | 2:c944cfe08e79 | 372 | } |
apalmieri | 2:c944cfe08e79 | 373 | } |
apalmieri | 2:c944cfe08e79 | 374 | |
apalmieri | 2:c944cfe08e79 | 375 | |
apalmieri | 2:c944cfe08e79 | 376 | void readCallback(const GattReadCallbackParams *response) |
apalmieri | 2:c944cfe08e79 | 377 | { |
apalmieri | 2:c944cfe08e79 | 378 | PRINTF("readCallback\r\n"); |
apalmieri | 2:c944cfe08e79 | 379 | |
apalmieri | 2:c944cfe08e79 | 380 | if (response->handle == hrmCharacteristic.getValueHandle()) { |
apalmieri | 2:c944cfe08e79 | 381 | PRINTF("readCallback: handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len); |
apalmieri | 2:c944cfe08e79 | 382 | for (unsigned index = 0; index < response->len; index++) { |
apalmieri | 2:c944cfe08e79 | 383 | PRINTF("%c[%02x]", response->data[index], response->data[index]); |
apalmieri | 2:c944cfe08e79 | 384 | } |
apalmieri | 2:c944cfe08e79 | 385 | PRINTF("\r\n"); |
apalmieri | 2:c944cfe08e79 | 386 | } |
apalmieri | 2:c944cfe08e79 | 387 | } |
apalmieri | 2:c944cfe08e79 | 388 | |
apalmieri | 2:c944cfe08e79 | 389 | /** |
apalmieri | 2:c944cfe08e79 | 390 | * This function is called when the ble initialization process has failled |
apalmieri | 2:c944cfe08e79 | 391 | */ |
apalmieri | 2:c944cfe08e79 | 392 | void onBleInitError(BLE &ble, ble_error_t error) |
apalmieri | 2:c944cfe08e79 | 393 | { |
apalmieri | 2:c944cfe08e79 | 394 | PRINTF("BLE init error"); |
apalmieri | 2:c944cfe08e79 | 395 | } |
apalmieri | 2:c944cfe08e79 | 396 | |
apalmieri | 2:c944cfe08e79 | 397 | /** |
apalmieri | 2:c944cfe08e79 | 398 | * Callback triggered when the ble initialization process has finished |
apalmieri | 2:c944cfe08e79 | 399 | */ |
apalmieri | 2:c944cfe08e79 | 400 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
apalmieri | 2:c944cfe08e79 | 401 | { |
apalmieri | 2:c944cfe08e79 | 402 | |
apalmieri | 2:c944cfe08e79 | 403 | PRINTF("init complete\r\n"); |
apalmieri | 2:c944cfe08e79 | 404 | |
apalmieri | 2:c944cfe08e79 | 405 | BLE& ble = params->ble; |
apalmieri | 2:c944cfe08e79 | 406 | ble_error_t error = params->error; |
apalmieri | 2:c944cfe08e79 | 407 | |
apalmieri | 2:c944cfe08e79 | 408 | if (error != BLE_ERROR_NONE) { |
apalmieri | 2:c944cfe08e79 | 409 | /* In case of error, forward the error handling to onBleInitError */ |
apalmieri | 2:c944cfe08e79 | 410 | PRINTF("Error init\r\n"); |
apalmieri | 2:c944cfe08e79 | 411 | onBleInitError(ble, error); |
apalmieri | 2:c944cfe08e79 | 412 | return; |
apalmieri | 2:c944cfe08e79 | 413 | } |
apalmieri | 2:c944cfe08e79 | 414 | |
apalmieri | 2:c944cfe08e79 | 415 | /* Ensure that it is the default instance of BLE */ |
apalmieri | 2:c944cfe08e79 | 416 | if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
apalmieri | 2:c944cfe08e79 | 417 | PRINTF("Error default instance\r\n"); |
apalmieri | 2:c944cfe08e79 | 418 | return; |
apalmieri | 2:c944cfe08e79 | 419 | } |
apalmieri | 2:c944cfe08e79 | 420 | |
apalmieri | 2:c944cfe08e79 | 421 | ble.onTimeout(timeoutCallback); |
apalmieri | 2:c944cfe08e79 | 422 | ble.onConnection(connectionCallback); |
apalmieri | 2:c944cfe08e79 | 423 | ble.onDisconnection(disconnectionCallback); |
apalmieri | 2:c944cfe08e79 | 424 | ble.gattClient().onDataRead(readCallback); |
apalmieri | 2:c944cfe08e79 | 425 | |
apalmieri | 2:c944cfe08e79 | 426 | ble.gap().setScanParams(300, 300); |
apalmieri | 2:c944cfe08e79 | 427 | ble.gap().startScan(scanResultReceived); |
apalmieri | 2:c944cfe08e79 | 428 | PRINTF("scan started...\r\n"); |
apalmieri | 2:c944cfe08e79 | 429 | |
apalmieri | 2:c944cfe08e79 | 430 | |
apalmieri | 2:c944cfe08e79 | 431 | // infinite loop |
apalmieri | 2:c944cfe08e79 | 432 | while (1) { |
apalmieri | 2:c944cfe08e79 | 433 | // check for trigger from periodicCallback() |
apalmieri | 2:c944cfe08e79 | 434 | if (triggerHrmCharacteristic && !ble.gattClient().isServiceDiscoveryActive() && _connected) { |
apalmieri | 2:c944cfe08e79 | 435 | |
apalmieri | 2:c944cfe08e79 | 436 | triggerHrmCharacteristic = false; |
apalmieri | 2:c944cfe08e79 | 437 | PRINTF("START READ\r\n"); |
apalmieri | 2:c944cfe08e79 | 438 | hrmCharacteristic.read(); |
apalmieri | 2:c944cfe08e79 | 439 | |
apalmieri | 2:c944cfe08e79 | 440 | } else { |
apalmieri | 2:c944cfe08e79 | 441 | ble.waitForEvent(); // low power wait for event |
apalmieri | 2:c944cfe08e79 | 442 | } |
apalmieri | 2:c944cfe08e79 | 443 | } |
apalmieri | 2:c944cfe08e79 | 444 | |
apalmieri | 2:c944cfe08e79 | 445 | |
apalmieri | 2:c944cfe08e79 | 446 | |
apalmieri | 2:c944cfe08e79 | 447 | |
apalmieri | 2:c944cfe08e79 | 448 | } |
apalmieri | 2:c944cfe08e79 | 449 | |
apalmieri | 2:c944cfe08e79 | 450 | int main(void) |
apalmieri | 2:c944cfe08e79 | 451 | { |
apalmieri | 2:c944cfe08e79 | 452 | PRINTF("started...\r\n"); |
apalmieri | 2:c944cfe08e79 | 453 | |
apalmieri | 2:c944cfe08e79 | 454 | led1 = 1; |
apalmieri | 2:c944cfe08e79 | 455 | Ticker ticker; |
apalmieri | 2:c944cfe08e79 | 456 | ticker.attach(periodicCallback, 1); // blink LED every second |
apalmieri | 2:c944cfe08e79 | 457 | |
apalmieri | 2:c944cfe08e79 | 458 | _connected = false; |
apalmieri | 2:c944cfe08e79 | 459 | |
apalmieri | 2:c944cfe08e79 | 460 | PRINTF("start init...\r\n"); |
apalmieri | 2:c944cfe08e79 | 461 | |
apalmieri | 2:c944cfe08e79 | 462 | ble.init(bleInitComplete); |
apalmieri | 2:c944cfe08e79 | 463 | } |