ENSMM / Mbed OS BLE-GAP

Fork of BLE-GAP by Joël Imbaud

Committer:
paulszczepanek
Date:
Fri Mar 09 16:41:54 2018 +0000
Revision:
0:8539ba0984da
Child:
1:d4bb1e33950e
initial commit based on examples github repo

Who changed what in which revision?

UserRevisionLine numberNew contents of line
paulszczepanek 0:8539ba0984da 1 /* mbed Microcontroller Library
paulszczepanek 0:8539ba0984da 2 * Copyright (c) 2006-2013 ARM Limited
paulszczepanek 0:8539ba0984da 3 *
paulszczepanek 0:8539ba0984da 4 * Licensed under the Apache License, Version 2.0 (the "License");
paulszczepanek 0:8539ba0984da 5 * you may not use this file except in compliance with the License.
paulszczepanek 0:8539ba0984da 6 * You may obtain a copy of the License at
paulszczepanek 0:8539ba0984da 7 *
paulszczepanek 0:8539ba0984da 8 * http://www.apache.org/licenses/LICENSE-2.0
paulszczepanek 0:8539ba0984da 9 *
paulszczepanek 0:8539ba0984da 10 * Unless required by applicable law or agreed to in writing, software
paulszczepanek 0:8539ba0984da 11 * distributed under the License is distributed on an "AS IS" BASIS,
paulszczepanek 0:8539ba0984da 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
paulszczepanek 0:8539ba0984da 13 * See the License for the specific language governing permissions and
paulszczepanek 0:8539ba0984da 14 * limitations under the License.
paulszczepanek 0:8539ba0984da 15 */
paulszczepanek 0:8539ba0984da 16
paulszczepanek 0:8539ba0984da 17 #include <events/mbed_events.h>
paulszczepanek 0:8539ba0984da 18 #include <mbed.h>
paulszczepanek 0:8539ba0984da 19 #include "ble/BLE.h"
paulszczepanek 0:8539ba0984da 20
paulszczepanek 0:8539ba0984da 21 /** This example demonstrates all the basic setup required
paulszczepanek 0:8539ba0984da 22 * to advertise, scan and connect to other devices.
paulszczepanek 0:8539ba0984da 23 *
paulszczepanek 0:8539ba0984da 24 * It contains a single class that performs both scans and advertisements.
paulszczepanek 0:8539ba0984da 25 *
paulszczepanek 0:8539ba0984da 26 * The demonstrations happens in sequence, after each "mode" ends
paulszczepanek 0:8539ba0984da 27 * the demo jumps to the next mode to continue. There are several modes
paulszczepanek 0:8539ba0984da 28 * that show scanning and several showing advertising. These are configured
paulszczepanek 0:8539ba0984da 29 * according to the two arrays containing parameters. During scanning
paulszczepanek 0:8539ba0984da 30 * a connection will be made to a connectable device upon its discovery.
paulszczepanek 0:8539ba0984da 31 */
paulszczepanek 0:8539ba0984da 32
paulszczepanek 0:8539ba0984da 33 static const uint8_t DEVICE_NAME[] = "GAP_device";
paulszczepanek 0:8539ba0984da 34
paulszczepanek 0:8539ba0984da 35 /* Duration of each mode in milliseconds */
paulszczepanek 0:8539ba0984da 36 static const size_t MODE_DURATION_MS = 6000;
paulszczepanek 0:8539ba0984da 37
paulszczepanek 0:8539ba0984da 38 /* Time between each mode in milliseconds */
paulszczepanek 0:8539ba0984da 39 static const size_t TIME_BETWEEN_MODES_MS = 2000;
paulszczepanek 0:8539ba0984da 40
paulszczepanek 0:8539ba0984da 41 /* how long to wait before disconnecting in milliseconds */
paulszczepanek 0:8539ba0984da 42 static const size_t CONNECTION_DURATION = 3000;
paulszczepanek 0:8539ba0984da 43
paulszczepanek 0:8539ba0984da 44 typedef struct {
paulszczepanek 0:8539ba0984da 45 GapAdvertisingParams::AdvertisingType_t adv_type;
paulszczepanek 0:8539ba0984da 46 uint16_t interval;
paulszczepanek 0:8539ba0984da 47 uint16_t timeout;
paulszczepanek 0:8539ba0984da 48 } AdvModeParam_t;
paulszczepanek 0:8539ba0984da 49
paulszczepanek 0:8539ba0984da 50 typedef struct {
paulszczepanek 0:8539ba0984da 51 uint16_t interval;
paulszczepanek 0:8539ba0984da 52 uint16_t window;
paulszczepanek 0:8539ba0984da 53 uint16_t timeout;
paulszczepanek 0:8539ba0984da 54 bool active;
paulszczepanek 0:8539ba0984da 55 } ScanModeParam_t;
paulszczepanek 0:8539ba0984da 56
paulszczepanek 0:8539ba0984da 57 /** the entries in this array are used to configure our advertising
paulszczepanek 0:8539ba0984da 58 * parameters for each of the modes we use in our demo */
paulszczepanek 0:8539ba0984da 59 static const AdvModeParam_t advertising_params[] = {
paulszczepanek 0:8539ba0984da 60 /* advertising type interval timeout */
paulszczepanek 0:8539ba0984da 61 { GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED, 40,/*ms*/ 3/*s*/},
paulszczepanek 0:8539ba0984da 62 { GapAdvertisingParams::ADV_SCANNABLE_UNDIRECTED, 100, 4 },
paulszczepanek 0:8539ba0984da 63 { GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED, 100, 0 }
paulszczepanek 0:8539ba0984da 64 };
paulszczepanek 0:8539ba0984da 65
paulszczepanek 0:8539ba0984da 66 /* when we cycle through all our advertising modes we will move to scanning modes */
paulszczepanek 0:8539ba0984da 67
paulszczepanek 0:8539ba0984da 68 /** the entries in this array are used to configure our scanning
paulszczepanek 0:8539ba0984da 69 * parameters for each of the modes we use in our demo */
paulszczepanek 0:8539ba0984da 70 static const ScanModeParam_t scanning_params[] = {
paulszczepanek 0:8539ba0984da 71 /* interval window timeout active */
paulszczepanek 0:8539ba0984da 72 { 4,/*ms*/ 4,/*ms*/ 0,/*s*/ false },
paulszczepanek 0:8539ba0984da 73 { 160, 100, 3, false },
paulszczepanek 0:8539ba0984da 74 { 160, 40, 0, true },
paulszczepanek 0:8539ba0984da 75 { 500, 10, 0, false }
paulszczepanek 0:8539ba0984da 76 };
paulszczepanek 0:8539ba0984da 77
paulszczepanek 0:8539ba0984da 78 /* parameters to use when attempting to connect to maximise speed of connection */
paulszczepanek 0:8539ba0984da 79 static const GapScanningParams connection_scan_params(
paulszczepanek 0:8539ba0984da 80 GapScanningParams::SCAN_INTERVAL_MAX,
paulszczepanek 0:8539ba0984da 81 GapScanningParams::SCAN_WINDOW_MAX,
paulszczepanek 0:8539ba0984da 82 3,
paulszczepanek 0:8539ba0984da 83 false
paulszczepanek 0:8539ba0984da 84 );
paulszczepanek 0:8539ba0984da 85
paulszczepanek 0:8539ba0984da 86 /* get number of items in our arrays */
paulszczepanek 0:8539ba0984da 87 static const size_t SCAN_PARAM_SET_MAX =
paulszczepanek 0:8539ba0984da 88 sizeof(scanning_params) / sizeof(GapScanningParams);
paulszczepanek 0:8539ba0984da 89 static const size_t ADV_PARAM_SET_MAX =
paulszczepanek 0:8539ba0984da 90 sizeof(advertising_params) / sizeof(GapAdvertisingParams);
paulszczepanek 0:8539ba0984da 91
paulszczepanek 0:8539ba0984da 92
paulszczepanek 0:8539ba0984da 93 /** Demonstrate advertising, scanning and connecting
paulszczepanek 0:8539ba0984da 94 */
paulszczepanek 0:8539ba0984da 95 class GAPDevice : private mbed::NonCopyable<GAPDevice>
paulszczepanek 0:8539ba0984da 96 {
paulszczepanek 0:8539ba0984da 97 public:
paulszczepanek 0:8539ba0984da 98 GAPDevice() :
paulszczepanek 0:8539ba0984da 99 _ble(BLE::Instance()),
paulszczepanek 0:8539ba0984da 100 _led1(LED1, 0),
paulszczepanek 0:8539ba0984da 101 _set_index(0),
paulszczepanek 0:8539ba0984da 102 _is_in_scanning_mode(false),
paulszczepanek 0:8539ba0984da 103 _is_connecting(false),
paulszczepanek 0:8539ba0984da 104 _on_duration_end_id(0),
paulszczepanek 0:8539ba0984da 105 _scan_count(0) { };
paulszczepanek 0:8539ba0984da 106
paulszczepanek 0:8539ba0984da 107 ~GAPDevice()
paulszczepanek 0:8539ba0984da 108 {
paulszczepanek 0:8539ba0984da 109 if (_ble.hasInitialized()) {
paulszczepanek 0:8539ba0984da 110 _ble.shutdown();
paulszczepanek 0:8539ba0984da 111 }
paulszczepanek 0:8539ba0984da 112 };
paulszczepanek 0:8539ba0984da 113
paulszczepanek 0:8539ba0984da 114 /** Start BLE interface initialisation */
paulszczepanek 0:8539ba0984da 115 void run()
paulszczepanek 0:8539ba0984da 116 {
paulszczepanek 0:8539ba0984da 117 ble_error_t error;
paulszczepanek 0:8539ba0984da 118
paulszczepanek 0:8539ba0984da 119 if (_ble.hasInitialized()) {
paulszczepanek 0:8539ba0984da 120 printf("Ble instance already initialised.\r\n");
paulszczepanek 0:8539ba0984da 121 return;
paulszczepanek 0:8539ba0984da 122 }
paulszczepanek 0:8539ba0984da 123
paulszczepanek 0:8539ba0984da 124 /* this will inform us off all events so we can schedule their handling
paulszczepanek 0:8539ba0984da 125 * using our event queue */
paulszczepanek 0:8539ba0984da 126 _ble.onEventsToProcess(
paulszczepanek 0:8539ba0984da 127 makeFunctionPointer(this, &GAPDevice::schedule_ble_events)
paulszczepanek 0:8539ba0984da 128 );
paulszczepanek 0:8539ba0984da 129
paulszczepanek 0:8539ba0984da 130 /* handle timeouts, for example when connection attempts fail */
paulszczepanek 0:8539ba0984da 131 _ble.gap().onTimeout(
paulszczepanek 0:8539ba0984da 132 makeFunctionPointer(this, &GAPDevice::on_timeout)
paulszczepanek 0:8539ba0984da 133 );
paulszczepanek 0:8539ba0984da 134
paulszczepanek 0:8539ba0984da 135 error = _ble.init(this, &GAPDevice::on_init_complete);
paulszczepanek 0:8539ba0984da 136
paulszczepanek 0:8539ba0984da 137 if (error) {
paulszczepanek 0:8539ba0984da 138 printf("Error returned by BLE::init.\r\n");
paulszczepanek 0:8539ba0984da 139 return;
paulszczepanek 0:8539ba0984da 140 }
paulszczepanek 0:8539ba0984da 141
paulszczepanek 0:8539ba0984da 142 /* to show we're running we'll blink every 500ms */
paulszczepanek 0:8539ba0984da 143 _event_queue.call_every(500, this, &GAPDevice::blink);
paulszczepanek 0:8539ba0984da 144
paulszczepanek 0:8539ba0984da 145 /* this will not return until shutdown */
paulszczepanek 0:8539ba0984da 146 _event_queue.dispatch_forever();
paulszczepanek 0:8539ba0984da 147 };
paulszczepanek 0:8539ba0984da 148
paulszczepanek 0:8539ba0984da 149 private:
paulszczepanek 0:8539ba0984da 150 /** This is called when BLE interface is initialised and starts the first mode */
paulszczepanek 0:8539ba0984da 151 void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
paulszczepanek 0:8539ba0984da 152 {
paulszczepanek 0:8539ba0984da 153 if (event->error) {
paulszczepanek 0:8539ba0984da 154 printf("Error during the initialisation\r\n");
paulszczepanek 0:8539ba0984da 155 return;
paulszczepanek 0:8539ba0984da 156 }
paulszczepanek 0:8539ba0984da 157
paulszczepanek 0:8539ba0984da 158 /* print device address */
paulszczepanek 0:8539ba0984da 159 Gap::AddressType_t addr_type;
paulszczepanek 0:8539ba0984da 160 Gap::Address_t addr;
paulszczepanek 0:8539ba0984da 161 _ble.gap().getAddress(&addr_type, addr);
paulszczepanek 0:8539ba0984da 162 printf("Device address: %02x:%02x:%02x:%02x:%02x:%02x\r\n",
paulszczepanek 0:8539ba0984da 163 addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
paulszczepanek 0:8539ba0984da 164
paulszczepanek 0:8539ba0984da 165 /* all calls are serialised on the user thread through the event queue */
paulszczepanek 0:8539ba0984da 166 _event_queue.call(this, &GAPDevice::demo_mode_start);
paulszczepanek 0:8539ba0984da 167 };
paulszczepanek 0:8539ba0984da 168
paulszczepanek 0:8539ba0984da 169 /** queue up start of the current demo mode */
paulszczepanek 0:8539ba0984da 170 void demo_mode_start()
paulszczepanek 0:8539ba0984da 171 {
paulszczepanek 0:8539ba0984da 172 if (_is_in_scanning_mode) {
paulszczepanek 0:8539ba0984da 173 /* when scanning we want to connect to a peer device so we need to
paulszczepanek 0:8539ba0984da 174 * attach callbacks that are used by Gap to notify us of events */
paulszczepanek 0:8539ba0984da 175 _ble.gap().onConnection(this, &GAPDevice::on_connect);
paulszczepanek 0:8539ba0984da 176 _ble.gap().onDisconnection(this, &GAPDevice::on_disconnect);
paulszczepanek 0:8539ba0984da 177
paulszczepanek 0:8539ba0984da 178 _event_queue.call(this, &GAPDevice::scan);
paulszczepanek 0:8539ba0984da 179 } else {
paulszczepanek 0:8539ba0984da 180 _event_queue.call(this, &GAPDevice::advertise);
paulszczepanek 0:8539ba0984da 181 }
paulszczepanek 0:8539ba0984da 182
paulszczepanek 0:8539ba0984da 183 /* for performance measurement keep track of duration of the demo mode */
paulszczepanek 0:8539ba0984da 184 _demo_duration.start();
paulszczepanek 0:8539ba0984da 185 /* keep track of our state */
paulszczepanek 0:8539ba0984da 186 _is_connecting = false;
paulszczepanek 0:8539ba0984da 187
paulszczepanek 0:8539ba0984da 188 /* queue up next demo mode */
paulszczepanek 0:8539ba0984da 189 _on_duration_end_id = _event_queue.call_in(
paulszczepanek 0:8539ba0984da 190 MODE_DURATION_MS, this, &GAPDevice::on_duration_end
paulszczepanek 0:8539ba0984da 191 );
paulszczepanek 0:8539ba0984da 192
paulszczepanek 0:8539ba0984da 193 printf("\r\n");
paulszczepanek 0:8539ba0984da 194 }
paulszczepanek 0:8539ba0984da 195
paulszczepanek 0:8539ba0984da 196 /** Set up and start advertising */
paulszczepanek 0:8539ba0984da 197 void advertise()
paulszczepanek 0:8539ba0984da 198 {
paulszczepanek 0:8539ba0984da 199 ble_error_t error;
paulszczepanek 0:8539ba0984da 200 GapAdvertisingData advertising_data;
paulszczepanek 0:8539ba0984da 201
paulszczepanek 0:8539ba0984da 202 /* add advertising flags */
paulszczepanek 0:8539ba0984da 203 advertising_data.addFlags(GapAdvertisingData::LE_GENERAL_DISCOVERABLE
paulszczepanek 0:8539ba0984da 204 | GapAdvertisingData::BREDR_NOT_SUPPORTED);
paulszczepanek 0:8539ba0984da 205
paulszczepanek 0:8539ba0984da 206 /* add device name */
paulszczepanek 0:8539ba0984da 207 advertising_data.addData(
paulszczepanek 0:8539ba0984da 208 GapAdvertisingData::COMPLETE_LOCAL_NAME,
paulszczepanek 0:8539ba0984da 209 DEVICE_NAME,
paulszczepanek 0:8539ba0984da 210 sizeof(DEVICE_NAME)
paulszczepanek 0:8539ba0984da 211 );
paulszczepanek 0:8539ba0984da 212
paulszczepanek 0:8539ba0984da 213 error = _ble.gap().setAdvertisingPayload(advertising_data);
paulszczepanek 0:8539ba0984da 214
paulszczepanek 0:8539ba0984da 215 if (error) {
paulszczepanek 0:8539ba0984da 216 printf("Error during Gap::setAdvertisingPayload\r\n");
paulszczepanek 0:8539ba0984da 217 return;
paulszczepanek 0:8539ba0984da 218 }
paulszczepanek 0:8539ba0984da 219
paulszczepanek 0:8539ba0984da 220 /* set the advertising parameters according to currently selected set,
paulszczepanek 0:8539ba0984da 221 * see @AdvertisingType_t for explanation of modes */
paulszczepanek 0:8539ba0984da 222 GapAdvertisingParams::AdvertisingType_t adv_type =
paulszczepanek 0:8539ba0984da 223 advertising_params[_set_index].adv_type;
paulszczepanek 0:8539ba0984da 224
paulszczepanek 0:8539ba0984da 225 /* how many milliseconds between advertisements, lower interval
paulszczepanek 0:8539ba0984da 226 * increases the chances of being seen at the cost of more power */
paulszczepanek 0:8539ba0984da 227 uint16_t interval = advertising_params[_set_index].interval;
paulszczepanek 0:8539ba0984da 228
paulszczepanek 0:8539ba0984da 229 /* advertising will continue for this many seconds or until connected */
paulszczepanek 0:8539ba0984da 230 uint16_t timeout = advertising_params[_set_index].timeout;
paulszczepanek 0:8539ba0984da 231
paulszczepanek 0:8539ba0984da 232 _ble.gap().setAdvertisingType(adv_type);
paulszczepanek 0:8539ba0984da 233 _ble.gap().setAdvertisingInterval(interval);
paulszczepanek 0:8539ba0984da 234 _ble.gap().setAdvertisingTimeout(timeout);
paulszczepanek 0:8539ba0984da 235
paulszczepanek 0:8539ba0984da 236 error = _ble.gap().startAdvertising();
paulszczepanek 0:8539ba0984da 237
paulszczepanek 0:8539ba0984da 238 if (error) {
paulszczepanek 0:8539ba0984da 239 printf("Error during Gap::startAdvertising.\r\n");
paulszczepanek 0:8539ba0984da 240 return;
paulszczepanek 0:8539ba0984da 241 }
paulszczepanek 0:8539ba0984da 242
paulszczepanek 0:8539ba0984da 243 printf("Advertising started (type: 0x%x, interval: %dms, timeout: %ds)\r\n",
paulszczepanek 0:8539ba0984da 244 adv_type, interval, timeout);
paulszczepanek 0:8539ba0984da 245 };
paulszczepanek 0:8539ba0984da 246
paulszczepanek 0:8539ba0984da 247 /** Set up and start scanning */
paulszczepanek 0:8539ba0984da 248 void scan()
paulszczepanek 0:8539ba0984da 249 {
paulszczepanek 0:8539ba0984da 250 ble_error_t error;
paulszczepanek 0:8539ba0984da 251
paulszczepanek 0:8539ba0984da 252 /* scanning happens repeatedly, interval is the number of milliseconds
paulszczepanek 0:8539ba0984da 253 * between each cycle of scanning */
paulszczepanek 0:8539ba0984da 254 uint16_t interval = scanning_params[_set_index].interval;
paulszczepanek 0:8539ba0984da 255
paulszczepanek 0:8539ba0984da 256 /* number of milliseconds we scan for each time we enter
paulszczepanek 0:8539ba0984da 257 * the scanning cycle after the interval set above */
paulszczepanek 0:8539ba0984da 258 uint16_t window = scanning_params[_set_index].window;
paulszczepanek 0:8539ba0984da 259
paulszczepanek 0:8539ba0984da 260 /* how long to repeat the cycles of scanning in seconds */
paulszczepanek 0:8539ba0984da 261 uint16_t timeout = scanning_params[_set_index].timeout;
paulszczepanek 0:8539ba0984da 262
paulszczepanek 0:8539ba0984da 263 /* active scanning will send a scan request to any scanable devices that
paulszczepanek 0:8539ba0984da 264 * we see advertising */
paulszczepanek 0:8539ba0984da 265 bool active = scanning_params[_set_index].active;
paulszczepanek 0:8539ba0984da 266
paulszczepanek 0:8539ba0984da 267 /* set the scanning parameters according to currently selected set */
paulszczepanek 0:8539ba0984da 268 error = _ble.gap().setScanParams(interval, window, timeout, active);
paulszczepanek 0:8539ba0984da 269
paulszczepanek 0:8539ba0984da 270 if (error) {
paulszczepanek 0:8539ba0984da 271 printf("Error during Gap::setScanParams\r\n");
paulszczepanek 0:8539ba0984da 272 return;
paulszczepanek 0:8539ba0984da 273 }
paulszczepanek 0:8539ba0984da 274
paulszczepanek 0:8539ba0984da 275 /* start scanning and attach a callback that will handle advertisements
paulszczepanek 0:8539ba0984da 276 * and scan requests responses */
paulszczepanek 0:8539ba0984da 277 error = _ble.gap().startScan(this, &GAPDevice::on_scan);
paulszczepanek 0:8539ba0984da 278
paulszczepanek 0:8539ba0984da 279 if (error) {
paulszczepanek 0:8539ba0984da 280 printf("Error during Gap::startScan\r\n");
paulszczepanek 0:8539ba0984da 281 return;
paulszczepanek 0:8539ba0984da 282 }
paulszczepanek 0:8539ba0984da 283
paulszczepanek 0:8539ba0984da 284 printf("Scanning started (interval: %dms, window: %dms, timeout: %ds).\r\n",
paulszczepanek 0:8539ba0984da 285 interval, window, timeout);
paulszczepanek 0:8539ba0984da 286 };
paulszczepanek 0:8539ba0984da 287
paulszczepanek 0:8539ba0984da 288 /** After a set duration this cycles to the next demo mode
paulszczepanek 0:8539ba0984da 289 * unless a connection happened first */
paulszczepanek 0:8539ba0984da 290 void on_duration_end()
paulszczepanek 0:8539ba0984da 291 {
paulszczepanek 0:8539ba0984da 292 print_performance();
paulszczepanek 0:8539ba0984da 293
paulszczepanek 0:8539ba0984da 294 /* alloted time has elapsed, move to next demo mode */
paulszczepanek 0:8539ba0984da 295 _event_queue.call(this, &GAPDevice::demo_mode_end);
paulszczepanek 0:8539ba0984da 296 };
paulszczepanek 0:8539ba0984da 297
paulszczepanek 0:8539ba0984da 298 /** Look at scan payload to find a peer device and connect to it */
paulszczepanek 0:8539ba0984da 299 void on_scan(const Gap::AdvertisementCallbackParams_t *params)
paulszczepanek 0:8539ba0984da 300 {
paulszczepanek 0:8539ba0984da 301 /* keep track of scan events for performance reporting */
paulszczepanek 0:8539ba0984da 302 _scan_count++;
paulszczepanek 0:8539ba0984da 303
paulszczepanek 0:8539ba0984da 304 /* don't bother with analysing scan result if we're already connecting */
paulszczepanek 0:8539ba0984da 305 if (_is_connecting) {
paulszczepanek 0:8539ba0984da 306 return;
paulszczepanek 0:8539ba0984da 307 }
paulszczepanek 0:8539ba0984da 308
paulszczepanek 0:8539ba0984da 309 /* parse the advertising payload, looking for a discoverable device */
paulszczepanek 0:8539ba0984da 310 for (uint8_t i = 0; i < params->advertisingDataLen; ++i) {
paulszczepanek 0:8539ba0984da 311 /* The advertising payload is a collection of key/value records where
paulszczepanek 0:8539ba0984da 312 * byte 0: length of the record excluding this byte
paulszczepanek 0:8539ba0984da 313 * byte 1: The key, it is the type of the data
paulszczepanek 0:8539ba0984da 314 * byte [2..N] The value. N is equal to byte0 - 1 */
paulszczepanek 0:8539ba0984da 315 const uint8_t record_length = params->advertisingData[i];
paulszczepanek 0:8539ba0984da 316 if (record_length == 0) {
paulszczepanek 0:8539ba0984da 317 continue;
paulszczepanek 0:8539ba0984da 318 }
paulszczepanek 0:8539ba0984da 319 const uint8_t type = params->advertisingData[i + 1];
paulszczepanek 0:8539ba0984da 320 const uint8_t *value = params->advertisingData + i + 2;
paulszczepanek 0:8539ba0984da 321
paulszczepanek 0:8539ba0984da 322 /* connect to a discoverable device */
paulszczepanek 0:8539ba0984da 323 if ((type == GapAdvertisingData::FLAGS)
paulszczepanek 0:8539ba0984da 324 && (*value & GapAdvertisingData::LE_GENERAL_DISCOVERABLE)) {
paulszczepanek 0:8539ba0984da 325
paulszczepanek 0:8539ba0984da 326 /* abort timeout as the mode will end on disconnection */
paulszczepanek 0:8539ba0984da 327 _event_queue.cancel(_on_duration_end_id);
paulszczepanek 0:8539ba0984da 328
paulszczepanek 0:8539ba0984da 329 printf("We found a connectable device\r\n");
paulszczepanek 0:8539ba0984da 330
paulszczepanek 0:8539ba0984da 331 ble_error_t error = _ble.gap().connect(
paulszczepanek 0:8539ba0984da 332 params->peerAddr, Gap::ADDR_TYPE_RANDOM_STATIC,
paulszczepanek 0:8539ba0984da 333 NULL, &connection_scan_params
paulszczepanek 0:8539ba0984da 334 );
paulszczepanek 0:8539ba0984da 335
paulszczepanek 0:8539ba0984da 336 if (error) {
paulszczepanek 0:8539ba0984da 337 printf("Error during Gap::connect\r\n");
paulszczepanek 0:8539ba0984da 338 /* since no connection will be attempted end the mode */
paulszczepanek 0:8539ba0984da 339 _event_queue.call(this, &GAPDevice::demo_mode_end);
paulszczepanek 0:8539ba0984da 340 return;
paulszczepanek 0:8539ba0984da 341 }
paulszczepanek 0:8539ba0984da 342
paulszczepanek 0:8539ba0984da 343 /* we may have already scan events waiting
paulszczepanek 0:8539ba0984da 344 * to be processed so we need to remember
paulszczepanek 0:8539ba0984da 345 * that we are already connecting and ignore them */
paulszczepanek 0:8539ba0984da 346 _is_connecting = true;
paulszczepanek 0:8539ba0984da 347
paulszczepanek 0:8539ba0984da 348 return;
paulszczepanek 0:8539ba0984da 349 }
paulszczepanek 0:8539ba0984da 350
paulszczepanek 0:8539ba0984da 351 i += record_length;
paulszczepanek 0:8539ba0984da 352 }
paulszczepanek 0:8539ba0984da 353 };
paulszczepanek 0:8539ba0984da 354
paulszczepanek 0:8539ba0984da 355 /** This is called by Gap to notify the application we connected,
paulszczepanek 0:8539ba0984da 356 * in our case it immediately disconnects */
paulszczepanek 0:8539ba0984da 357 void on_connect(const Gap::ConnectionCallbackParams_t *connection_event)
paulszczepanek 0:8539ba0984da 358 {
paulszczepanek 0:8539ba0984da 359 print_performance();
paulszczepanek 0:8539ba0984da 360
paulszczepanek 0:8539ba0984da 361 printf("Connected in %dms\r\n", _demo_duration.read_ms());
paulszczepanek 0:8539ba0984da 362
paulszczepanek 0:8539ba0984da 363 /* cancel the connect timeout since we connected */
paulszczepanek 0:8539ba0984da 364 _event_queue.cancel(_on_duration_end_id);
paulszczepanek 0:8539ba0984da 365
paulszczepanek 0:8539ba0984da 366 _event_queue.call_in(
paulszczepanek 0:8539ba0984da 367 CONNECTION_DURATION, &_ble.gap(), &Gap::disconnect, Gap::REMOTE_USER_TERMINATED_CONNECTION
paulszczepanek 0:8539ba0984da 368 );
paulszczepanek 0:8539ba0984da 369 };
paulszczepanek 0:8539ba0984da 370
paulszczepanek 0:8539ba0984da 371 /** This is called by Gap to notify the application we disconnected,
paulszczepanek 0:8539ba0984da 372 * in our case it calls demo_mode_end() to progress the demo */
paulszczepanek 0:8539ba0984da 373 void on_disconnect(const Gap::DisconnectionCallbackParams_t *event)
paulszczepanek 0:8539ba0984da 374 {
paulszczepanek 0:8539ba0984da 375 printf("Disconnected\r\n");
paulszczepanek 0:8539ba0984da 376
paulszczepanek 0:8539ba0984da 377 /* we have successfully disconnected ending the demo, move to next mode */
paulszczepanek 0:8539ba0984da 378 _event_queue.call(this, &GAPDevice::demo_mode_end);
paulszczepanek 0:8539ba0984da 379 };
paulszczepanek 0:8539ba0984da 380
paulszczepanek 0:8539ba0984da 381 /** called if timeout is reached during advertising, scanning
paulszczepanek 0:8539ba0984da 382 * or connection initiation */
paulszczepanek 0:8539ba0984da 383 void on_timeout(const Gap::TimeoutSource_t source)
paulszczepanek 0:8539ba0984da 384 {
paulszczepanek 0:8539ba0984da 385 _demo_duration.stop();
paulszczepanek 0:8539ba0984da 386
paulszczepanek 0:8539ba0984da 387 switch (source) {
paulszczepanek 0:8539ba0984da 388 case Gap::TIMEOUT_SRC_ADVERTISING:
paulszczepanek 0:8539ba0984da 389 printf("Stopped advertising early due to timeout parameter\r\n");
paulszczepanek 0:8539ba0984da 390 break;
paulszczepanek 0:8539ba0984da 391 case Gap::TIMEOUT_SRC_SCAN:
paulszczepanek 0:8539ba0984da 392 printf("Stopped scanning early due to timeout parameter\r\n");
paulszczepanek 0:8539ba0984da 393 break;
paulszczepanek 0:8539ba0984da 394 case Gap::TIMEOUT_SRC_CONN:
paulszczepanek 0:8539ba0984da 395 printf("Failed to connect after scanning %d advertisements\r\n", _scan_count);
paulszczepanek 0:8539ba0984da 396 _event_queue.call(this, &GAPDevice::print_performance);
paulszczepanek 0:8539ba0984da 397 _event_queue.call(this, &GAPDevice::demo_mode_end);
paulszczepanek 0:8539ba0984da 398 break;
paulszczepanek 0:8539ba0984da 399 default:
paulszczepanek 0:8539ba0984da 400 printf("Unexpected timeout\r\n");
paulszczepanek 0:8539ba0984da 401 break;
paulszczepanek 0:8539ba0984da 402 }
paulszczepanek 0:8539ba0984da 403 };
paulszczepanek 0:8539ba0984da 404
paulszczepanek 0:8539ba0984da 405 /** clean up after last run, cycle to the next mode and launch it */
paulszczepanek 0:8539ba0984da 406 void demo_mode_end()
paulszczepanek 0:8539ba0984da 407 {
paulszczepanek 0:8539ba0984da 408 /* reset the demo ready for the next mode */
paulszczepanek 0:8539ba0984da 409 _scan_count = 0;
paulszczepanek 0:8539ba0984da 410 _demo_duration.stop();
paulszczepanek 0:8539ba0984da 411 _demo_duration.reset();
paulszczepanek 0:8539ba0984da 412
paulszczepanek 0:8539ba0984da 413 /* cycle through all demo modes */
paulszczepanek 0:8539ba0984da 414 _set_index++;
paulszczepanek 0:8539ba0984da 415
paulszczepanek 0:8539ba0984da 416 /* switch between advertising and scanning when we go
paulszczepanek 0:8539ba0984da 417 * through all the params in the array */
paulszczepanek 0:8539ba0984da 418 if (_set_index >= (_is_in_scanning_mode? SCAN_PARAM_SET_MAX : ADV_PARAM_SET_MAX)) {
paulszczepanek 0:8539ba0984da 419 _set_index = 0;
paulszczepanek 0:8539ba0984da 420 _is_in_scanning_mode = !_is_in_scanning_mode;
paulszczepanek 0:8539ba0984da 421 }
paulszczepanek 0:8539ba0984da 422
paulszczepanek 0:8539ba0984da 423 _ble.shutdown();
paulszczepanek 0:8539ba0984da 424 _event_queue.break_dispatch();
paulszczepanek 0:8539ba0984da 425 };
paulszczepanek 0:8539ba0984da 426
paulszczepanek 0:8539ba0984da 427 /** print some information about our radio activity */
paulszczepanek 0:8539ba0984da 428 void print_performance()
paulszczepanek 0:8539ba0984da 429 {
paulszczepanek 0:8539ba0984da 430 /* measure time from mode start, may have been stopped by timeout */
paulszczepanek 0:8539ba0984da 431 uint16_t duration = _demo_duration.read_ms();
paulszczepanek 0:8539ba0984da 432
paulszczepanek 0:8539ba0984da 433 if (_is_in_scanning_mode) {
paulszczepanek 0:8539ba0984da 434 /* convert ms into timeslots for accurate calculation as internally
paulszczepanek 0:8539ba0984da 435 * all durations are in timeslots (0.625ms) */
paulszczepanek 0:8539ba0984da 436 uint16_t interval_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
paulszczepanek 0:8539ba0984da 437 scanning_params[_set_index].interval
paulszczepanek 0:8539ba0984da 438 );
paulszczepanek 0:8539ba0984da 439 uint16_t window_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
paulszczepanek 0:8539ba0984da 440 scanning_params[_set_index].window
paulszczepanek 0:8539ba0984da 441 );
paulszczepanek 0:8539ba0984da 442 uint16_t duration_ts = GapScanningParams::MSEC_TO_SCAN_DURATION_UNITS(
paulszczepanek 0:8539ba0984da 443 duration
paulszczepanek 0:8539ba0984da 444 );
paulszczepanek 0:8539ba0984da 445 /* this is how long we scanned for in timeslots */
paulszczepanek 0:8539ba0984da 446 uint16_t rx_ts = (duration_ts / interval_ts) * window_ts;
paulszczepanek 0:8539ba0984da 447 /* convert to milliseconds */
paulszczepanek 0:8539ba0984da 448 uint16_t rx_ms = (rx_ts * GapScanningParams::UNIT_0_625_MS) / 1000;
paulszczepanek 0:8539ba0984da 449
paulszczepanek 0:8539ba0984da 450 printf("We have scanned for %dms with an interval of %d"
paulszczepanek 0:8539ba0984da 451 " timeslot and a window of %d timeslots\r\n",
paulszczepanek 0:8539ba0984da 452 duration, interval_ts, window_ts);
paulszczepanek 0:8539ba0984da 453
paulszczepanek 0:8539ba0984da 454 printf("We have been listening on the radio for at least %dms\r\n", rx_ms);
paulszczepanek 0:8539ba0984da 455
paulszczepanek 0:8539ba0984da 456 } else /* advertising */ {
paulszczepanek 0:8539ba0984da 457
paulszczepanek 0:8539ba0984da 458 /* convert ms into timeslots for accurate calculation as internally
paulszczepanek 0:8539ba0984da 459 * all durations are in timeslots (0.625ms) */
paulszczepanek 0:8539ba0984da 460 uint16_t interval_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(
paulszczepanek 0:8539ba0984da 461 advertising_params[_set_index].interval
paulszczepanek 0:8539ba0984da 462 );
paulszczepanek 0:8539ba0984da 463 uint16_t duration_ts = GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(
paulszczepanek 0:8539ba0984da 464 duration
paulszczepanek 0:8539ba0984da 465 );
paulszczepanek 0:8539ba0984da 466 /* this is how many times we advertised */
paulszczepanek 0:8539ba0984da 467 uint16_t events = duration_ts / interval_ts;
paulszczepanek 0:8539ba0984da 468
paulszczepanek 0:8539ba0984da 469 printf("We have advertised for %dms"
paulszczepanek 0:8539ba0984da 470 " with an interval of %d timeslots\r\n",
paulszczepanek 0:8539ba0984da 471 duration, interval_ts);
paulszczepanek 0:8539ba0984da 472
paulszczepanek 0:8539ba0984da 473 /* non-scannable and non-connectable advertising
paulszczepanek 0:8539ba0984da 474 * skips rx events saving on power consumption */
paulszczepanek 0:8539ba0984da 475 if (advertising_params[_set_index].adv_type
paulszczepanek 0:8539ba0984da 476 == GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED) {
paulszczepanek 0:8539ba0984da 477 printf("We created at least %d tx events\r\n", events);
paulszczepanek 0:8539ba0984da 478 } else {
paulszczepanek 0:8539ba0984da 479 printf("We created at least %d tx and rx events\r\n", events);
paulszczepanek 0:8539ba0984da 480 }
paulszczepanek 0:8539ba0984da 481 }
paulszczepanek 0:8539ba0984da 482 };
paulszczepanek 0:8539ba0984da 483
paulszczepanek 0:8539ba0984da 484 /** Schedule processing of events from the BLE middleware in the event queue. */
paulszczepanek 0:8539ba0984da 485 void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
paulszczepanek 0:8539ba0984da 486 {
paulszczepanek 0:8539ba0984da 487 _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents));
paulszczepanek 0:8539ba0984da 488 };
paulszczepanek 0:8539ba0984da 489
paulszczepanek 0:8539ba0984da 490 /** Blink LED to show we're running */
paulszczepanek 0:8539ba0984da 491 void blink(void)
paulszczepanek 0:8539ba0984da 492 {
paulszczepanek 0:8539ba0984da 493 _led1 = !_led1;
paulszczepanek 0:8539ba0984da 494 };
paulszczepanek 0:8539ba0984da 495
paulszczepanek 0:8539ba0984da 496 private:
paulszczepanek 0:8539ba0984da 497 BLE &_ble;
paulszczepanek 0:8539ba0984da 498 events::EventQueue _event_queue;
paulszczepanek 0:8539ba0984da 499 DigitalOut _led1;
paulszczepanek 0:8539ba0984da 500
paulszczepanek 0:8539ba0984da 501 /* Keep track of our progress through demo modes */
paulszczepanek 0:8539ba0984da 502 size_t _set_index;
paulszczepanek 0:8539ba0984da 503 bool _is_in_scanning_mode;
paulszczepanek 0:8539ba0984da 504 bool _is_connecting;
paulszczepanek 0:8539ba0984da 505
paulszczepanek 0:8539ba0984da 506 /* Remember the call id of the function on _event_queue
paulszczepanek 0:8539ba0984da 507 * so we can cancel it if we need to end the mode early */
paulszczepanek 0:8539ba0984da 508 int _on_duration_end_id;
paulszczepanek 0:8539ba0984da 509
paulszczepanek 0:8539ba0984da 510 /* Measure performance of our advertising/scanning */
paulszczepanek 0:8539ba0984da 511 Timer _demo_duration;
paulszczepanek 0:8539ba0984da 512 size_t _scan_count;
paulszczepanek 0:8539ba0984da 513 };
paulszczepanek 0:8539ba0984da 514
paulszczepanek 0:8539ba0984da 515 int main()
paulszczepanek 0:8539ba0984da 516 {
paulszczepanek 0:8539ba0984da 517 GAPDevice gap_device;
paulszczepanek 0:8539ba0984da 518
paulszczepanek 0:8539ba0984da 519 while (1) {
paulszczepanek 0:8539ba0984da 520 gap_device.run();
paulszczepanek 0:8539ba0984da 521 wait_ms(TIME_BETWEEN_MODES_MS);
paulszczepanek 0:8539ba0984da 522 printf("\r\nStarting next GAP demo mode\r\n");
paulszczepanek 0:8539ba0984da 523 };
paulszczepanek 0:8539ba0984da 524
paulszczepanek 0:8539ba0984da 525 return 0;
paulszczepanek 0:8539ba0984da 526 }