Demonstration of the GAP profile. It shows advertising, scanning and connecting. The demo will cycle through several modes and print over the serial connection information about current activity.

GAP - Advertising, Scanning, Connecting

Demonstration of GAP API usage. It shows advertising, scanning and connecting. The demo will cycle through several modes and print over the serial connection information about current activity.

Running the application

Requirements

The sample application can be seen on any BLE scanner on a smartphone. If you don't have a scanner on your phone, please install :

- nRF Master Control Panel for Android.

- LightBlue for iPhone.

Information about activity is printed over the serial connection - please have a client open. You may use:

- Tera Term

Hardware requirements are in the main readme.

Building instructions

Building instructions for all samples are in the main readme.

Committer:
mbed_official
Date:
Mon Oct 22 12:30:28 2018 +0100
Revision:
12:3993e2608615
Parent:
11:37872bf83624
Child:
16:4dd4ecbc8efb
Merge pull request #187 from cmonr/master

Updating mbed-os to mbed-os-5.10.2
.
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 11:37872bf83624 181 Gap::PhySet_t tx_phys(/* 1M */ false, /* 2M */ true, /* coded */ false);
mbed_official 11:37872bf83624 182 Gap::PhySet_t rx_phys(/* 1M */ false, /* 2M */ true, /* coded */ false);
mbed_official 11:37872bf83624 183 ble_error_t err = _ble.gap().setPreferredPhys(&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 }