ENSMM / Mbed OS BLE-GAP

Fork of BLE-GAP by Joël Imbaud

Committer:
mbed_official
Date:
Wed Sep 26 15:00:23 2018 +0100
Revision:
10:6f1c573093c1
Parent:
1:d4bb1e33950e
Child:
11:37872bf83624
Merge pull request #183 from ARMmbed/mbed-os-5.10.0-oob

Mbed os 5.10.0
.
Commit copied from https://github.com/ARMmbed/mbed-os-example-ble

Who changed what in which revision?

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