Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
source/main.cpp@0:8539ba0984da, 2018-03-09 (annotated)
- 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?
| User | Revision | Line number | New 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 | } |