.

Demo for periodic advertising. This requires two devices to run. Both devices run the same program. They attempt to find each other after which they adopt complementary roles. One sets up periodic advertising. The other attempts to scan and sync with the periodic advertising.

The role of the scanner device can also be performed by a BLE scanner on a smartphone. Connect to the advertiser. This will establish it as the advertiser. After you disconnect the device will begin periodic advertising. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-ble/tree/master/BLE_PeriodicAdvertising

Running the application

Requirements

Devices must support extended advertising and periodic advertising (Bluetooth version 5+).

The sample application can also be monitored by any BLE scanner on a smartphone.

If you don't have a scanner on your phone, please install:

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

Building instructions

Building instructions for all samples are in the https:github.com/ARMmbed/mbed-os-example-ble/blob/master/README.md.

Committer:
mbed_official
Date:
Wed Feb 13 18:35:09 2019 +0000
Revision:
0:c736663c1bcc
Updating mbed-os to mbed-os-5.11.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 0:c736663c1bcc 1 /* mbed Microcontroller Library
mbed_official 0:c736663c1bcc 2 * Copyright (c) 2006-2018 ARM Limited
mbed_official 0:c736663c1bcc 3 *
mbed_official 0:c736663c1bcc 4 * Licensed under the Apache License, Version 2.0 (the "License");
mbed_official 0:c736663c1bcc 5 * you may not use this file except in compliance with the License.
mbed_official 0:c736663c1bcc 6 * You may obtain a copy of the License at
mbed_official 0:c736663c1bcc 7 *
mbed_official 0:c736663c1bcc 8 * http://www.apache.org/licenses/LICENSE-2.0
mbed_official 0:c736663c1bcc 9 *
mbed_official 0:c736663c1bcc 10 * Unless required by applicable law or agreed to in writing, software
mbed_official 0:c736663c1bcc 11 * distributed under the License is distributed on an "AS IS" BASIS,
mbed_official 0:c736663c1bcc 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
mbed_official 0:c736663c1bcc 13 * See the License for the specific language governing permissions and
mbed_official 0:c736663c1bcc 14 * limitations under the License.
mbed_official 0:c736663c1bcc 15 */
mbed_official 0:c736663c1bcc 16
mbed_official 0:c736663c1bcc 17 #include <events/mbed_events.h>
mbed_official 0:c736663c1bcc 18 #include <mbed.h>
mbed_official 0:c736663c1bcc 19 #include "ble/BLE.h"
mbed_official 0:c736663c1bcc 20 #include "gap/Gap.h"
mbed_official 0:c736663c1bcc 21 #include "gap/AdvertisingDataParser.h"
mbed_official 0:c736663c1bcc 22 #include "pretty_printer.h"
mbed_official 0:c736663c1bcc 23 #include "BatteryService.h"
mbed_official 0:c736663c1bcc 24
mbed_official 0:c736663c1bcc 25 /** This example demonstrates extended and periodic advertising
mbed_official 0:c736663c1bcc 26 */
mbed_official 0:c736663c1bcc 27
mbed_official 0:c736663c1bcc 28 events::EventQueue event_queue;
mbed_official 0:c736663c1bcc 29
mbed_official 0:c736663c1bcc 30 static const char DEVICE_NAME[] = "Periodic";
mbed_official 0:c736663c1bcc 31
mbed_official 0:c736663c1bcc 32 static const uint16_t MAX_ADVERTISING_PAYLOAD_SIZE = 50;
mbed_official 0:c736663c1bcc 33 static const uint16_t SCAN_TIME = 5000;
mbed_official 0:c736663c1bcc 34 static const uint8_t CONNECTION_DURATION = 2;
mbed_official 0:c736663c1bcc 35
mbed_official 0:c736663c1bcc 36 /** Demonstrate periodic advertising and scanning and syncing with the advertising
mbed_official 0:c736663c1bcc 37 */
mbed_official 0:c736663c1bcc 38 class PeriodicDemo : private mbed::NonCopyable<PeriodicDemo>, public ble::Gap::EventHandler
mbed_official 0:c736663c1bcc 39 {
mbed_official 0:c736663c1bcc 40 public:
mbed_official 0:c736663c1bcc 41 PeriodicDemo(BLE& ble, events::EventQueue& event_queue) :
mbed_official 0:c736663c1bcc 42 _ble(ble),
mbed_official 0:c736663c1bcc 43 _gap(ble.gap()),
mbed_official 0:c736663c1bcc 44 _event_queue(event_queue),
mbed_official 0:c736663c1bcc 45 _led1(LED1, 0),
mbed_official 0:c736663c1bcc 46 _is_scanner(false),
mbed_official 0:c736663c1bcc 47 _is_connecting_or_syncing(false),
mbed_official 0:c736663c1bcc 48 _role_established(false),
mbed_official 0:c736663c1bcc 49 _battery_uuid(GattService::UUID_BATTERY_SERVICE),
mbed_official 0:c736663c1bcc 50 _battery_level(100),
mbed_official 0:c736663c1bcc 51 _battery_service(ble, _battery_level),
mbed_official 0:c736663c1bcc 52 _adv_data_builder(_adv_buffer),
mbed_official 0:c736663c1bcc 53 _adv_handle(ble::INVALID_ADVERTISING_HANDLE),
mbed_official 0:c736663c1bcc 54 _sync_handle(ble::INVALID_ADVERTISING_HANDLE)
mbed_official 0:c736663c1bcc 55 {
mbed_official 0:c736663c1bcc 56 }
mbed_official 0:c736663c1bcc 57
mbed_official 0:c736663c1bcc 58 ~PeriodicDemo()
mbed_official 0:c736663c1bcc 59 {
mbed_official 0:c736663c1bcc 60 if (_ble.hasInitialized()) {
mbed_official 0:c736663c1bcc 61 _ble.shutdown();
mbed_official 0:c736663c1bcc 62 }
mbed_official 0:c736663c1bcc 63 }
mbed_official 0:c736663c1bcc 64
mbed_official 0:c736663c1bcc 65 /** Start BLE interface initialisation */
mbed_official 0:c736663c1bcc 66 void run()
mbed_official 0:c736663c1bcc 67 {
mbed_official 0:c736663c1bcc 68 if (_ble.hasInitialized()) {
mbed_official 0:c736663c1bcc 69 printf("Ble instance already initialised.\r\n");
mbed_official 0:c736663c1bcc 70 return;
mbed_official 0:c736663c1bcc 71 }
mbed_official 0:c736663c1bcc 72
mbed_official 0:c736663c1bcc 73 /* handle gap events */
mbed_official 0:c736663c1bcc 74 _gap.setEventHandler(this);
mbed_official 0:c736663c1bcc 75
mbed_official 0:c736663c1bcc 76 ble_error_t error = _ble.init(this, &PeriodicDemo::on_init_complete);
mbed_official 0:c736663c1bcc 77 if (error) {
mbed_official 0:c736663c1bcc 78 print_error(error, "Error returned by BLE::init\r\n");
mbed_official 0:c736663c1bcc 79 return;
mbed_official 0:c736663c1bcc 80 }
mbed_official 0:c736663c1bcc 81
mbed_official 0:c736663c1bcc 82 /* to show we're running we'll blink every 500ms */
mbed_official 0:c736663c1bcc 83 _event_queue.call_every(500, this, &PeriodicDemo::blink);
mbed_official 0:c736663c1bcc 84
mbed_official 0:c736663c1bcc 85 /* this will not return until shutdown */
mbed_official 0:c736663c1bcc 86 _event_queue.dispatch_forever();
mbed_official 0:c736663c1bcc 87 }
mbed_official 0:c736663c1bcc 88
mbed_official 0:c736663c1bcc 89 private:
mbed_official 0:c736663c1bcc 90 /** This is called when BLE interface is initialised and starts the first mode */
mbed_official 0:c736663c1bcc 91 void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
mbed_official 0:c736663c1bcc 92 {
mbed_official 0:c736663c1bcc 93 if (event->error) {
mbed_official 0:c736663c1bcc 94 print_error(event->error, "Error during the initialisation\r\n");
mbed_official 0:c736663c1bcc 95 return;
mbed_official 0:c736663c1bcc 96 }
mbed_official 0:c736663c1bcc 97
mbed_official 0:c736663c1bcc 98 if (!_gap.isFeatureSupported(ble::controller_supported_features_t::LE_EXTENDED_ADVERTISING) ||
mbed_official 0:c736663c1bcc 99 !_gap.isFeatureSupported(ble::controller_supported_features_t::LE_PERIODIC_ADVERTISING)) {
mbed_official 0:c736663c1bcc 100 printf("Periodic advertising not supported, cannot run example.\r\n");
mbed_official 0:c736663c1bcc 101 return;
mbed_official 0:c736663c1bcc 102 }
mbed_official 0:c736663c1bcc 103
mbed_official 0:c736663c1bcc 104 print_mac_address();
mbed_official 0:c736663c1bcc 105
mbed_official 0:c736663c1bcc 106 /* all calls are serialised on the user thread through the event queue */
mbed_official 0:c736663c1bcc 107 _event_queue.call(this, &PeriodicDemo::start_role);
mbed_official 0:c736663c1bcc 108 }
mbed_official 0:c736663c1bcc 109
mbed_official 0:c736663c1bcc 110 void start_role()
mbed_official 0:c736663c1bcc 111 {
mbed_official 0:c736663c1bcc 112 /* This example is designed to be run on two boards at the same time,
mbed_official 0:c736663c1bcc 113 * depending on our role we will either be the advertiser or scanner,
mbed_official 0:c736663c1bcc 114 * until the roles are established we will cycle the roles until we find each other */
mbed_official 0:c736663c1bcc 115 if (_role_established) {
mbed_official 0:c736663c1bcc 116 if (_is_scanner) {
mbed_official 0:c736663c1bcc 117 _event_queue.call(this, &PeriodicDemo::scan_periodic);
mbed_official 0:c736663c1bcc 118 } else {
mbed_official 0:c736663c1bcc 119 _event_queue.call(this, &PeriodicDemo::advertise_periodic);
mbed_official 0:c736663c1bcc 120 }
mbed_official 0:c736663c1bcc 121 } else {
mbed_official 0:c736663c1bcc 122 _is_scanner = !_is_scanner;
mbed_official 0:c736663c1bcc 123
mbed_official 0:c736663c1bcc 124 if (_is_scanner) {
mbed_official 0:c736663c1bcc 125 _event_queue.call(this, &PeriodicDemo::scan);
mbed_official 0:c736663c1bcc 126 } else {
mbed_official 0:c736663c1bcc 127 _event_queue.call(this, &PeriodicDemo::advertise);
mbed_official 0:c736663c1bcc 128 }
mbed_official 0:c736663c1bcc 129 }
mbed_official 0:c736663c1bcc 130 }
mbed_official 0:c736663c1bcc 131
mbed_official 0:c736663c1bcc 132 /** Set up and start advertising */
mbed_official 0:c736663c1bcc 133 void advertise()
mbed_official 0:c736663c1bcc 134 {
mbed_official 0:c736663c1bcc 135 ble_error_t error;
mbed_official 0:c736663c1bcc 136
mbed_official 0:c736663c1bcc 137 ble::AdvertisingParameters adv_params;
mbed_official 0:c736663c1bcc 138 adv_params.setUseLegacyPDU(false);
mbed_official 0:c736663c1bcc 139 adv_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
mbed_official 0:c736663c1bcc 140
mbed_official 0:c736663c1bcc 141 /* create the advertising set with its parameter if we haven't yet */
mbed_official 0:c736663c1bcc 142 if (_adv_handle == ble::INVALID_ADVERTISING_HANDLE) {
mbed_official 0:c736663c1bcc 143 error = _gap.createAdvertisingSet(
mbed_official 0:c736663c1bcc 144 &_adv_handle,
mbed_official 0:c736663c1bcc 145 adv_params
mbed_official 0:c736663c1bcc 146 );
mbed_official 0:c736663c1bcc 147
mbed_official 0:c736663c1bcc 148 if (error) {
mbed_official 0:c736663c1bcc 149 print_error(error, "Gap::createAdvertisingSet() failed\r\n");
mbed_official 0:c736663c1bcc 150 return;
mbed_official 0:c736663c1bcc 151 }
mbed_official 0:c736663c1bcc 152 } else {
mbed_official 0:c736663c1bcc 153 _gap.setAdvertisingParameters(_adv_handle, adv_params);
mbed_official 0:c736663c1bcc 154 }
mbed_official 0:c736663c1bcc 155
mbed_official 0:c736663c1bcc 156 _adv_data_builder.clear();
mbed_official 0:c736663c1bcc 157 _adv_data_builder.setFlags();
mbed_official 0:c736663c1bcc 158 _adv_data_builder.setName(DEVICE_NAME);
mbed_official 0:c736663c1bcc 159
mbed_official 0:c736663c1bcc 160 /* Set payload for the set */
mbed_official 0:c736663c1bcc 161 error = _gap.setAdvertisingPayload(
mbed_official 0:c736663c1bcc 162 _adv_handle,
mbed_official 0:c736663c1bcc 163 _adv_data_builder.getAdvertisingData()
mbed_official 0:c736663c1bcc 164 );
mbed_official 0:c736663c1bcc 165
mbed_official 0:c736663c1bcc 166 if (error) {
mbed_official 0:c736663c1bcc 167 print_error(error, "Gap::setAdvertisingPayload() failed\r\n");
mbed_official 0:c736663c1bcc 168 return;
mbed_official 0:c736663c1bcc 169 }
mbed_official 0:c736663c1bcc 170
mbed_official 0:c736663c1bcc 171 /* since we have two boards which might start running this example at the same time
mbed_official 0:c736663c1bcc 172 * we randomise the interval of advertising to have them meet when one is advertising
mbed_official 0:c736663c1bcc 173 * and the other one is scanning (we use their random address as source of randomness) */
mbed_official 0:c736663c1bcc 174 ble::millisecond_t random_duration_ms((3 + rand() % 5) * 1000);
mbed_official 0:c736663c1bcc 175 ble::adv_duration_t random_duration(random_duration_ms);
mbed_official 0:c736663c1bcc 176
mbed_official 0:c736663c1bcc 177 error = _ble.gap().startAdvertising(
mbed_official 0:c736663c1bcc 178 _adv_handle,
mbed_official 0:c736663c1bcc 179 random_duration
mbed_official 0:c736663c1bcc 180 );
mbed_official 0:c736663c1bcc 181
mbed_official 0:c736663c1bcc 182 if (error) {
mbed_official 0:c736663c1bcc 183 print_error(error, "Gap::startAdvertising() failed\r\n");
mbed_official 0:c736663c1bcc 184 return;
mbed_official 0:c736663c1bcc 185 }
mbed_official 0:c736663c1bcc 186
mbed_official 0:c736663c1bcc 187 printf("Advertising started for %dms\r\n", random_duration_ms);
mbed_official 0:c736663c1bcc 188 }
mbed_official 0:c736663c1bcc 189
mbed_official 0:c736663c1bcc 190 void advertise_periodic()
mbed_official 0:c736663c1bcc 191 {
mbed_official 0:c736663c1bcc 192 ble::AdvertisingParameters adv_params;
mbed_official 0:c736663c1bcc 193 adv_params.setType(ble::advertising_type_t::NON_CONNECTABLE_UNDIRECTED);
mbed_official 0:c736663c1bcc 194 adv_params.setUseLegacyPDU(false);
mbed_official 0:c736663c1bcc 195 adv_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
mbed_official 0:c736663c1bcc 196
mbed_official 0:c736663c1bcc 197 ble_error_t error = _gap.setAdvertisingParameters(_adv_handle, adv_params);
mbed_official 0:c736663c1bcc 198
mbed_official 0:c736663c1bcc 199 if (error) {
mbed_official 0:c736663c1bcc 200 print_error(error, "Gap::setAdvertisingParameters() failed\r\n");
mbed_official 0:c736663c1bcc 201 return;
mbed_official 0:c736663c1bcc 202 }
mbed_official 0:c736663c1bcc 203
mbed_official 0:c736663c1bcc 204 /* Start advertising the set as the advertising needs to be active
mbed_official 0:c736663c1bcc 205 * before we start periodic advertising */
mbed_official 0:c736663c1bcc 206 error = _gap.startAdvertising(_adv_handle);
mbed_official 0:c736663c1bcc 207
mbed_official 0:c736663c1bcc 208 if (error) {
mbed_official 0:c736663c1bcc 209 print_error(error, "Gap::startAdvertising() failed\r\n");
mbed_official 0:c736663c1bcc 210 return;
mbed_official 0:c736663c1bcc 211 }
mbed_official 0:c736663c1bcc 212
mbed_official 0:c736663c1bcc 213 error = _gap.setPeriodicAdvertisingParameters(
mbed_official 0:c736663c1bcc 214 _adv_handle,
mbed_official 0:c736663c1bcc 215 ble::periodic_interval_t(50),
mbed_official 0:c736663c1bcc 216 ble::periodic_interval_t(500)
mbed_official 0:c736663c1bcc 217 );
mbed_official 0:c736663c1bcc 218
mbed_official 0:c736663c1bcc 219 if (error) {
mbed_official 0:c736663c1bcc 220 print_error(error, "Gap::setPeriodicAdvertisingParameters() failed\r\n");
mbed_official 0:c736663c1bcc 221 return;
mbed_official 0:c736663c1bcc 222 }
mbed_official 0:c736663c1bcc 223
mbed_official 0:c736663c1bcc 224 /* we will put the battery level data in there and update it every second */
mbed_official 0:c736663c1bcc 225 update_payload();
mbed_official 0:c736663c1bcc 226
mbed_official 0:c736663c1bcc 227 error = _gap.startPeriodicAdvertising(_adv_handle);
mbed_official 0:c736663c1bcc 228
mbed_official 0:c736663c1bcc 229 if (error) {
mbed_official 0:c736663c1bcc 230 print_error(error, "Gap::startPeriodicAdvertising() failed\r\n");
mbed_official 0:c736663c1bcc 231 return;
mbed_official 0:c736663c1bcc 232 }
mbed_official 0:c736663c1bcc 233
mbed_official 0:c736663c1bcc 234 printf("Periodic advertising started\r\n");
mbed_official 0:c736663c1bcc 235
mbed_official 0:c736663c1bcc 236 /* tick over our fake battery data, this will also update the advertising payload */
mbed_official 0:c736663c1bcc 237 _event_queue.call_every(1000, this, &PeriodicDemo::update_sensor_value);
mbed_official 0:c736663c1bcc 238 }
mbed_official 0:c736663c1bcc 239
mbed_official 0:c736663c1bcc 240 void update_payload()
mbed_official 0:c736663c1bcc 241 {
mbed_official 0:c736663c1bcc 242 /* advertising payload will have the battery level which we will update */
mbed_official 0:c736663c1bcc 243 ble_error_t error = _adv_data_builder.setServiceData(
mbed_official 0:c736663c1bcc 244 _battery_uuid,
mbed_official 0:c736663c1bcc 245 mbed::make_Span(&_battery_level, 1)
mbed_official 0:c736663c1bcc 246 );
mbed_official 0:c736663c1bcc 247
mbed_official 0:c736663c1bcc 248 if (error) {
mbed_official 0:c736663c1bcc 249 print_error(error, "AdvertisingDataBuilder::setServiceData() failed\r\n");
mbed_official 0:c736663c1bcc 250 return;
mbed_official 0:c736663c1bcc 251 }
mbed_official 0:c736663c1bcc 252
mbed_official 0:c736663c1bcc 253 /* the data in the local host buffer has been updated but now
mbed_official 0:c736663c1bcc 254 * we have to update the data in the controller */
mbed_official 0:c736663c1bcc 255 error = _gap.setPeriodicAdvertisingPayload(
mbed_official 0:c736663c1bcc 256 _adv_handle,
mbed_official 0:c736663c1bcc 257 _adv_data_builder.getAdvertisingData()
mbed_official 0:c736663c1bcc 258 );
mbed_official 0:c736663c1bcc 259
mbed_official 0:c736663c1bcc 260 if (error) {
mbed_official 0:c736663c1bcc 261 print_error(error, "Gap::setPeriodicAdvertisingPayload() failed\r\n");
mbed_official 0:c736663c1bcc 262 return;
mbed_official 0:c736663c1bcc 263 }
mbed_official 0:c736663c1bcc 264 }
mbed_official 0:c736663c1bcc 265
mbed_official 0:c736663c1bcc 266 /** Set up and start scanning */
mbed_official 0:c736663c1bcc 267 void scan()
mbed_official 0:c736663c1bcc 268 {
mbed_official 0:c736663c1bcc 269 _is_connecting_or_syncing = false;
mbed_official 0:c736663c1bcc 270
mbed_official 0:c736663c1bcc 271 ble::ScanParameters scan_params;
mbed_official 0:c736663c1bcc 272 scan_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
mbed_official 0:c736663c1bcc 273
mbed_official 0:c736663c1bcc 274 ble_error_t error = _gap.setScanParameters(scan_params);
mbed_official 0:c736663c1bcc 275
mbed_official 0:c736663c1bcc 276 if (error) {
mbed_official 0:c736663c1bcc 277 print_error(error, "Error caused by Gap::setScanParameters\r\n");
mbed_official 0:c736663c1bcc 278 return;
mbed_official 0:c736663c1bcc 279 }
mbed_official 0:c736663c1bcc 280
mbed_official 0:c736663c1bcc 281 error = _gap.startScan(ble::scan_duration_t(500));
mbed_official 0:c736663c1bcc 282
mbed_official 0:c736663c1bcc 283 if (error) {
mbed_official 0:c736663c1bcc 284 print_error(error, "Error caused by Gap::startScan\r\n");
mbed_official 0:c736663c1bcc 285 return;
mbed_official 0:c736663c1bcc 286 }
mbed_official 0:c736663c1bcc 287
mbed_official 0:c736663c1bcc 288 printf("Scanning started\r\n");
mbed_official 0:c736663c1bcc 289 }
mbed_official 0:c736663c1bcc 290
mbed_official 0:c736663c1bcc 291 void scan_periodic()
mbed_official 0:c736663c1bcc 292 {
mbed_official 0:c736663c1bcc 293 _is_connecting_or_syncing = false;
mbed_official 0:c736663c1bcc 294
mbed_official 0:c736663c1bcc 295 ble_error_t error = _gap.startScan();
mbed_official 0:c736663c1bcc 296
mbed_official 0:c736663c1bcc 297 if (error) {
mbed_official 0:c736663c1bcc 298 print_error(error, "Error caused by Gap::startScan\r\n");
mbed_official 0:c736663c1bcc 299 return;
mbed_official 0:c736663c1bcc 300 }
mbed_official 0:c736663c1bcc 301
mbed_official 0:c736663c1bcc 302 printf("Scanning for periodic advertising started\r\n");
mbed_official 0:c736663c1bcc 303 }
mbed_official 0:c736663c1bcc 304
mbed_official 0:c736663c1bcc 305 private:
mbed_official 0:c736663c1bcc 306 /* Gap::EventHandler */
mbed_official 0:c736663c1bcc 307
mbed_official 0:c736663c1bcc 308 /** Look at scan payload to find a peer device and connect to it */
mbed_official 0:c736663c1bcc 309 virtual void onAdvertisingReport(
mbed_official 0:c736663c1bcc 310 const ble::AdvertisingReportEvent &event
mbed_official 0:c736663c1bcc 311 ) {
mbed_official 0:c736663c1bcc 312 /* don't bother with analysing scan result if we're already connecting */
mbed_official 0:c736663c1bcc 313 if (_is_connecting_or_syncing) {
mbed_official 0:c736663c1bcc 314 return;
mbed_official 0:c736663c1bcc 315 }
mbed_official 0:c736663c1bcc 316
mbed_official 0:c736663c1bcc 317 /* if we're looking for periodic advertising don't bother unless it's present */
mbed_official 0:c736663c1bcc 318 if (_role_established && !event.isPeriodicIntervalPresent()) {
mbed_official 0:c736663c1bcc 319 return;
mbed_official 0:c736663c1bcc 320 }
mbed_official 0:c736663c1bcc 321
mbed_official 0:c736663c1bcc 322 ble::AdvertisingDataParser adv_parser(event.getPayload());
mbed_official 0:c736663c1bcc 323
mbed_official 0:c736663c1bcc 324 /* parse the advertising payload, looking for a discoverable device */
mbed_official 0:c736663c1bcc 325 while (adv_parser.hasNext()) {
mbed_official 0:c736663c1bcc 326 ble::AdvertisingDataParser::element_t field = adv_parser.next();
mbed_official 0:c736663c1bcc 327
mbed_official 0:c736663c1bcc 328 /* identify peer by name */
mbed_official 0:c736663c1bcc 329 if (field.type == ble::adv_data_type_t::COMPLETE_LOCAL_NAME &&
mbed_official 0:c736663c1bcc 330 field.value.size() == strlen(DEVICE_NAME) &&
mbed_official 0:c736663c1bcc 331 (memcmp(field.value.data(), DEVICE_NAME, field.value.size()) == 0)) {
mbed_official 0:c736663c1bcc 332 /* if we haven't established our roles connect, otherwise sync with advertising */
mbed_official 0:c736663c1bcc 333 if (_role_established) {
mbed_official 0:c736663c1bcc 334 printf("We found the peer, syncing with SID %d"
mbed_official 0:c736663c1bcc 335 "and periodic interval %dms\r\n",
mbed_official 0:c736663c1bcc 336 event.getSID(), event.getPeriodicInterval().valueInMs());
mbed_official 0:c736663c1bcc 337
mbed_official 0:c736663c1bcc 338 ble_error_t error = _gap.createSync(
mbed_official 0:c736663c1bcc 339 event.getPeerAddressType(),
mbed_official 0:c736663c1bcc 340 event.getPeerAddress(),
mbed_official 0:c736663c1bcc 341 event.getSID(),
mbed_official 0:c736663c1bcc 342 2,
mbed_official 0:c736663c1bcc 343 ble::sync_timeout_t(ble::millisecond_t(500))
mbed_official 0:c736663c1bcc 344 );
mbed_official 0:c736663c1bcc 345
mbed_official 0:c736663c1bcc 346 if (error) {
mbed_official 0:c736663c1bcc 347 print_error(error, "Error caused by Gap::createSync\r\n");
mbed_official 0:c736663c1bcc 348 return;
mbed_official 0:c736663c1bcc 349 }
mbed_official 0:c736663c1bcc 350 } else {
mbed_official 0:c736663c1bcc 351 printf("We found the peer, connecting\r\n");
mbed_official 0:c736663c1bcc 352
mbed_official 0:c736663c1bcc 353 ble_error_t error = _gap.connect(
mbed_official 0:c736663c1bcc 354 event.getPeerAddressType(),
mbed_official 0:c736663c1bcc 355 event.getPeerAddress(),
mbed_official 0:c736663c1bcc 356 ble::ConnectionParameters() // use the default connection parameters
mbed_official 0:c736663c1bcc 357 );
mbed_official 0:c736663c1bcc 358
mbed_official 0:c736663c1bcc 359 if (error) {
mbed_official 0:c736663c1bcc 360 print_error(error, "Error caused by Gap::connect\r\n");
mbed_official 0:c736663c1bcc 361 return;
mbed_official 0:c736663c1bcc 362 }
mbed_official 0:c736663c1bcc 363 }
mbed_official 0:c736663c1bcc 364
mbed_official 0:c736663c1bcc 365 /* we may have already scan events waiting to be processed
mbed_official 0:c736663c1bcc 366 * so we need to remember that we are already connecting
mbed_official 0:c736663c1bcc 367 * or syncing and ignore them */
mbed_official 0:c736663c1bcc 368 _is_connecting_or_syncing = true;
mbed_official 0:c736663c1bcc 369
mbed_official 0:c736663c1bcc 370 return;
mbed_official 0:c736663c1bcc 371 }
mbed_official 0:c736663c1bcc 372 }
mbed_official 0:c736663c1bcc 373 }
mbed_official 0:c736663c1bcc 374
mbed_official 0:c736663c1bcc 375 virtual void onAdvertisingEnd(
mbed_official 0:c736663c1bcc 376 const ble::AdvertisingEndEvent &event
mbed_official 0:c736663c1bcc 377 ) {
mbed_official 0:c736663c1bcc 378 if (event.isConnected()) {
mbed_official 0:c736663c1bcc 379 printf("Stopped advertising due to connection\r\n");
mbed_official 0:c736663c1bcc 380 } else {
mbed_official 0:c736663c1bcc 381 printf("Advertising ended\r\n");
mbed_official 0:c736663c1bcc 382 _event_queue.call(this, &PeriodicDemo::start_role);
mbed_official 0:c736663c1bcc 383 }
mbed_official 0:c736663c1bcc 384 }
mbed_official 0:c736663c1bcc 385
mbed_official 0:c736663c1bcc 386 virtual void onScanTimeout(
mbed_official 0:c736663c1bcc 387 const ble::ScanTimeoutEvent&
mbed_official 0:c736663c1bcc 388 ) {
mbed_official 0:c736663c1bcc 389 if (!_is_connecting_or_syncing) {
mbed_official 0:c736663c1bcc 390 printf("Scanning ended, failed to find peer\r\n");
mbed_official 0:c736663c1bcc 391 _event_queue.call(this, &PeriodicDemo::start_role);
mbed_official 0:c736663c1bcc 392 }
mbed_official 0:c736663c1bcc 393 }
mbed_official 0:c736663c1bcc 394
mbed_official 0:c736663c1bcc 395 /** This is called by Gap to notify the application we connected */
mbed_official 0:c736663c1bcc 396 virtual void onConnectionComplete(
mbed_official 0:c736663c1bcc 397 const ble::ConnectionCompleteEvent &event
mbed_official 0:c736663c1bcc 398 ) {
mbed_official 0:c736663c1bcc 399 if (event.getStatus() == BLE_ERROR_NONE) {
mbed_official 0:c736663c1bcc 400 printf("Connected to: ");
mbed_official 0:c736663c1bcc 401 print_address(event.getPeerAddress().data());
mbed_official 0:c736663c1bcc 402 printf("Roles established\r\n");
mbed_official 0:c736663c1bcc 403 _role_established = true;
mbed_official 0:c736663c1bcc 404
mbed_official 0:c736663c1bcc 405 /* we have to specify the disconnect call because of ambiguous overloads */
mbed_official 0:c736663c1bcc 406 typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t);
mbed_official 0:c736663c1bcc 407 const disconnect_call_t disconnect_call = &Gap::disconnect;
mbed_official 0:c736663c1bcc 408
mbed_official 0:c736663c1bcc 409 if (_is_scanner) {
mbed_official 0:c736663c1bcc 410 _event_queue.call_in(
mbed_official 0:c736663c1bcc 411 2000,
mbed_official 0:c736663c1bcc 412 &_ble.gap(),
mbed_official 0:c736663c1bcc 413 disconnect_call,
mbed_official 0:c736663c1bcc 414 event.getConnectionHandle(),
mbed_official 0:c736663c1bcc 415 ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
mbed_official 0:c736663c1bcc 416 );
mbed_official 0:c736663c1bcc 417 }
mbed_official 0:c736663c1bcc 418 } else {
mbed_official 0:c736663c1bcc 419 printf("Failed to connect\r\n");
mbed_official 0:c736663c1bcc 420 _event_queue.call(this, &PeriodicDemo::start_role);
mbed_official 0:c736663c1bcc 421 }
mbed_official 0:c736663c1bcc 422 }
mbed_official 0:c736663c1bcc 423
mbed_official 0:c736663c1bcc 424 /** This is called by Gap to notify the application we disconnected */
mbed_official 0:c736663c1bcc 425 virtual void onDisconnectionComplete(
mbed_official 0:c736663c1bcc 426 const ble::DisconnectionCompleteEvent &event
mbed_official 0:c736663c1bcc 427 ) {
mbed_official 0:c736663c1bcc 428 printf("Disconnected\r\n");
mbed_official 0:c736663c1bcc 429 _event_queue.call(this, &PeriodicDemo::start_role);
mbed_official 0:c736663c1bcc 430 }
mbed_official 0:c736663c1bcc 431
mbed_official 0:c736663c1bcc 432 /** Called when first advertising packet in periodic advertising is received. */
mbed_official 0:c736663c1bcc 433 virtual void onPeriodicAdvertisingSyncEstablished(
mbed_official 0:c736663c1bcc 434 const ble::PeriodicAdvertisingSyncEstablishedEvent &event
mbed_official 0:c736663c1bcc 435 ) {
mbed_official 0:c736663c1bcc 436 if (event.getStatus() == BLE_ERROR_NONE) {
mbed_official 0:c736663c1bcc 437 printf("Synced with periodic advertising\r\n");
mbed_official 0:c736663c1bcc 438 _sync_handle = event.getSyncHandle();
mbed_official 0:c736663c1bcc 439 } else {
mbed_official 0:c736663c1bcc 440 printf("Sync with periodic advertising failed\r\n");
mbed_official 0:c736663c1bcc 441 }
mbed_official 0:c736663c1bcc 442 }
mbed_official 0:c736663c1bcc 443
mbed_official 0:c736663c1bcc 444 /** Called when a periodic advertising packet is received. */
mbed_official 0:c736663c1bcc 445 virtual void onPeriodicAdvertisingReport(
mbed_official 0:c736663c1bcc 446 const ble::PeriodicAdvertisingReportEvent &event
mbed_official 0:c736663c1bcc 447 ) {
mbed_official 0:c736663c1bcc 448 ble::AdvertisingDataParser adv_parser(event.getPayload());
mbed_official 0:c736663c1bcc 449
mbed_official 0:c736663c1bcc 450 /* parse the advertising payload, looking for a battery level */
mbed_official 0:c736663c1bcc 451 while (adv_parser.hasNext()) {
mbed_official 0:c736663c1bcc 452 ble::AdvertisingDataParser::element_t field = adv_parser.next();
mbed_official 0:c736663c1bcc 453
mbed_official 0:c736663c1bcc 454 if (field.type == ble::adv_data_type_t::SERVICE_DATA) {
mbed_official 0:c736663c1bcc 455 if (memcmp(field.value.data(), _battery_uuid.getBaseUUID(), _battery_uuid.getLen()) != 0) {
mbed_official 0:c736663c1bcc 456 printf("Unexpected service data\r\n");
mbed_official 0:c736663c1bcc 457 } else {
mbed_official 0:c736663c1bcc 458 /* battery level is right after the UUID */
mbed_official 0:c736663c1bcc 459 const uint8_t *battery_level = field.value.data() + _battery_uuid.getLen();
mbed_official 0:c736663c1bcc 460 printf("Peer battery level: %d\r\n", *battery_level);
mbed_official 0:c736663c1bcc 461 }
mbed_official 0:c736663c1bcc 462 }
mbed_official 0:c736663c1bcc 463 }
mbed_official 0:c736663c1bcc 464 }
mbed_official 0:c736663c1bcc 465
mbed_official 0:c736663c1bcc 466 /** Called when a periodic advertising sync has been lost. */
mbed_official 0:c736663c1bcc 467 virtual void onPeriodicAdvertisingSyncLoss(
mbed_official 0:c736663c1bcc 468 const ble::PeriodicAdvertisingSyncLoss &event
mbed_official 0:c736663c1bcc 469 ) {
mbed_official 0:c736663c1bcc 470 printf("Sync to periodic advertising lost\r\n");
mbed_official 0:c736663c1bcc 471 _sync_handle = ble::INVALID_ADVERTISING_HANDLE;
mbed_official 0:c736663c1bcc 472 _event_queue.call(this, &PeriodicDemo::scan_periodic);
mbed_official 0:c736663c1bcc 473 }
mbed_official 0:c736663c1bcc 474
mbed_official 0:c736663c1bcc 475 private:
mbed_official 0:c736663c1bcc 476 void update_sensor_value() {
mbed_official 0:c736663c1bcc 477 _battery_level--;
mbed_official 0:c736663c1bcc 478 if (_battery_level < 1) {
mbed_official 0:c736663c1bcc 479 _battery_level = 100;
mbed_official 0:c736663c1bcc 480 }
mbed_official 0:c736663c1bcc 481
mbed_official 0:c736663c1bcc 482 _battery_service.updateBatteryLevel(_battery_level);
mbed_official 0:c736663c1bcc 483 update_payload();
mbed_official 0:c736663c1bcc 484 }
mbed_official 0:c736663c1bcc 485
mbed_official 0:c736663c1bcc 486 /** Blink LED to show we're running */
mbed_official 0:c736663c1bcc 487 void blink(void)
mbed_official 0:c736663c1bcc 488 {
mbed_official 0:c736663c1bcc 489 _led1 = !_led1;
mbed_official 0:c736663c1bcc 490 }
mbed_official 0:c736663c1bcc 491
mbed_official 0:c736663c1bcc 492 private:
mbed_official 0:c736663c1bcc 493 BLE &_ble;
mbed_official 0:c736663c1bcc 494 ble::Gap &_gap;
mbed_official 0:c736663c1bcc 495 events::EventQueue &_event_queue;
mbed_official 0:c736663c1bcc 496
mbed_official 0:c736663c1bcc 497 DigitalOut _led1;
mbed_official 0:c736663c1bcc 498
mbed_official 0:c736663c1bcc 499 bool _is_scanner;
mbed_official 0:c736663c1bcc 500 bool _is_connecting_or_syncing;
mbed_official 0:c736663c1bcc 501 bool _role_established;
mbed_official 0:c736663c1bcc 502
mbed_official 0:c736663c1bcc 503 UUID _battery_uuid;
mbed_official 0:c736663c1bcc 504 uint8_t _battery_level;
mbed_official 0:c736663c1bcc 505 BatteryService _battery_service;
mbed_official 0:c736663c1bcc 506
mbed_official 0:c736663c1bcc 507 uint8_t _adv_buffer[MAX_ADVERTISING_PAYLOAD_SIZE];
mbed_official 0:c736663c1bcc 508 ble::AdvertisingDataBuilder _adv_data_builder;
mbed_official 0:c736663c1bcc 509
mbed_official 0:c736663c1bcc 510 ble::advertising_handle_t _adv_handle;
mbed_official 0:c736663c1bcc 511 ble::periodic_sync_handle_t _sync_handle;
mbed_official 0:c736663c1bcc 512 };
mbed_official 0:c736663c1bcc 513
mbed_official 0:c736663c1bcc 514 /** Schedule processing of events from the BLE middleware in the event queue. */
mbed_official 0:c736663c1bcc 515 void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
mbed_official 0:c736663c1bcc 516 event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents));
mbed_official 0:c736663c1bcc 517 }
mbed_official 0:c736663c1bcc 518
mbed_official 0:c736663c1bcc 519 int main()
mbed_official 0:c736663c1bcc 520 {
mbed_official 0:c736663c1bcc 521 BLE &ble = BLE::Instance();
mbed_official 0:c736663c1bcc 522
mbed_official 0:c736663c1bcc 523 /* this will inform us off all events so we can schedule their handling
mbed_official 0:c736663c1bcc 524 * using our event queue */
mbed_official 0:c736663c1bcc 525 ble.onEventsToProcess(schedule_ble_events);
mbed_official 0:c736663c1bcc 526
mbed_official 0:c736663c1bcc 527 /* look for other device and then settle on a role and sync periodic advertising */
mbed_official 0:c736663c1bcc 528 PeriodicDemo demo(ble, event_queue);
mbed_official 0:c736663c1bcc 529
mbed_official 0:c736663c1bcc 530 demo.run();
mbed_official 0:c736663c1bcc 531
mbed_official 0:c736663c1bcc 532 return 0;
mbed_official 0:c736663c1bcc 533 }