This example demonstrates using the GattClient API to control BLE client devices. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-ble/tree/master/BLE_LEDBlinker
BLE LED Blinker
This example demonstrates using the ``GattClient`` API to control BLE client devices.
The example uses two applications running on two different devices:
- The first device - the central - runs the application ``BLE_LEDBlinker`` from this repository. This application sends an on/off toggle over BLE.
- The second device - the peripheral - runs the application ``BLE_LED`` to respond to the toggle.
The toggle simply turns the LED on the peripheral device on and off.
Running the application
Requirements
Hardware requirements are in the main readme.
This example requires *two* devices.
Building instructions
You will need to build both applications and flash each one to a different board.
Please note: The application BLE_LEDBlinker
in this repository initiate a connection to all ble devices which advertise "LED" as complete local name. By default, the application BLE_LED
advertises "LED" as complete local name. If you change the local name advertised by the application BLE_LED
you should reflect your change in this application by changing the value of the constant PEER_NAME
in main.cpp
.
Tip: You may notice that the application also checks the LED characteristic's UUID; you don't need to change this parameter's value, because it already matches the UUID provided by the second application, BLE_LED
.
Building with mbed CLI
If you'd like to use mbed CLI to build this, then you should refer to the main readme. The instructions here relate to using the developer.mbed.org Online Compiler
In order to build this example in the mbed Online Compiler, first import the example using the ‘Import’ button on the right hand side.
Next, select a platform to build for. This must either be a platform that supports BLE, for example the NRF51-DK, or one of the following:
List of platforms supporting Bluetooth Low Energy
Or you must also add a piece of hardware and the supporting library that includes a Bluetooth Low Energy driver for that hardware, for example the K64F or NUCLEO_F401RE with the X-NUCLEO-IDB05A1
List of components supporting Bluetooth Low Energy.
Once you have selected your platform, compile the example and drag and drop the resulting binary onto your board.
For general instructions on using the mbed Online Compiler, please see the mbed Handbook
Checking for success
- Build both applications and install one on each device, as explained in the building instructions.
- The LED number two of the device running ``BLE_LED`` should blink.
Monitoring the application through a serial port
You can run ``BLE_LEDBlinker`` and see that it works properly by monitoring its serial output.
You need a terminal program to listen to the output through a serial port. You can download one, for example:
- Tera Term for Windows.
- CoolTerm for Mac OS X.
- GNU Screen for Linux.
To see the application's output:
- Check which serial port your device is connected to.
- Run a terminal program with the correct serial port and set the baud rate to 9600. For example, to use GNU Screen, run: ``screen /dev/tty.usbmodem1412 9600``.
- The application should start printing the toggle's value to the terminal.
Note: ``BLE_LEDBlinker`` will not run properly if the ``BLE_LED`` application is not running on a second device. The terminal will show a few print statements, but you will not be able to see the application in full operation.
source/main.cpp@77:85a0d3cdd896, 2019-01-25 (annotated)
- Committer:
- mbed_official
- Date:
- Fri Jan 25 15:15:20 2019 +0000
- Revision:
- 77:85a0d3cdd896
- Parent:
- 75:1a8d19363522
Fix SafeEnum comparison.
.
Commit copied from https://github.com/ARMmbed/mbed-os-example-ble
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Vincent Coubard |
0:86bf1d2040b3 | 1 | /* mbed Microcontroller Library |
Vincent Coubard |
0:86bf1d2040b3 | 2 | * Copyright (c) 2006-2015 ARM Limited |
Vincent Coubard |
0:86bf1d2040b3 | 3 | * |
Vincent Coubard |
0:86bf1d2040b3 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
Vincent Coubard |
0:86bf1d2040b3 | 5 | * you may not use this file except in compliance with the License. |
Vincent Coubard |
0:86bf1d2040b3 | 6 | * You may obtain a copy of the License at |
Vincent Coubard |
0:86bf1d2040b3 | 7 | * |
Vincent Coubard |
0:86bf1d2040b3 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
Vincent Coubard |
0:86bf1d2040b3 | 9 | * |
Vincent Coubard |
0:86bf1d2040b3 | 10 | * Unless required by applicable law or agreed to in writing, software |
Vincent Coubard |
0:86bf1d2040b3 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
Vincent Coubard |
0:86bf1d2040b3 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
Vincent Coubard |
0:86bf1d2040b3 | 13 | * See the License for the specific language governing permissions and |
Vincent Coubard |
0:86bf1d2040b3 | 14 | * limitations under the License. |
Vincent Coubard |
0:86bf1d2040b3 | 15 | */ |
Vincent Coubard |
0:86bf1d2040b3 | 16 | |
mbed_official | 12:059c7b7fb18a | 17 | #include <events/mbed_events.h> |
Vincent Coubard |
0:86bf1d2040b3 | 18 | #include <mbed.h> |
Vincent Coubard |
0:86bf1d2040b3 | 19 | #include "ble/BLE.h" |
Vincent Coubard |
0:86bf1d2040b3 | 20 | #include "ble/DiscoveredCharacteristic.h" |
Vincent Coubard |
0:86bf1d2040b3 | 21 | #include "ble/DiscoveredService.h" |
mbed_official | 75:1a8d19363522 | 22 | #include "ble/gap/Gap.h" |
mbed_official | 75:1a8d19363522 | 23 | #include "ble/gap/AdvertisingDataParser.h" |
mbed_official | 75:1a8d19363522 | 24 | #include "pretty_printer.h" |
Vincent Coubard |
0:86bf1d2040b3 | 25 | |
mbed_official | 75:1a8d19363522 | 26 | const static char PEER_NAME[] = "LED"; |
Vincent Coubard |
0:86bf1d2040b3 | 27 | |
mbed_official | 75:1a8d19363522 | 28 | static EventQueue event_queue(/* event count */ 10 * EVENTS_EVENT_SIZE); |
Vincent Coubard |
0:86bf1d2040b3 | 29 | |
mbed_official | 75:1a8d19363522 | 30 | static DiscoveredCharacteristic led_characteristic; |
mbed_official | 75:1a8d19363522 | 31 | static bool trigger_led_characteristic = false; |
Vincent Coubard |
0:86bf1d2040b3 | 32 | |
mbed_official | 75:1a8d19363522 | 33 | void service_discovery(const DiscoveredService *service) { |
Vincent Coubard |
0:86bf1d2040b3 | 34 | if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) { |
Vincent Coubard |
0:86bf1d2040b3 | 35 | printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle()); |
Vincent Coubard |
0:86bf1d2040b3 | 36 | } else { |
Vincent Coubard |
0:86bf1d2040b3 | 37 | printf("S UUID-"); |
Vincent Coubard |
0:86bf1d2040b3 | 38 | const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID(); |
Vincent Coubard |
0:86bf1d2040b3 | 39 | for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) { |
Vincent Coubard |
0:86bf1d2040b3 | 40 | printf("%02x", longUUIDBytes[i]); |
Vincent Coubard |
0:86bf1d2040b3 | 41 | } |
Vincent Coubard |
0:86bf1d2040b3 | 42 | printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle()); |
Vincent Coubard |
0:86bf1d2040b3 | 43 | } |
Vincent Coubard |
0:86bf1d2040b3 | 44 | } |
Vincent Coubard |
0:86bf1d2040b3 | 45 | |
mbed_official | 75:1a8d19363522 | 46 | void update_led_characteristic(void) { |
Vincent Coubard |
0:86bf1d2040b3 | 47 | if (!BLE::Instance().gattClient().isServiceDiscoveryActive()) { |
mbed_official | 75:1a8d19363522 | 48 | led_characteristic.read(); |
Vincent Coubard |
0:86bf1d2040b3 | 49 | } |
Vincent Coubard |
0:86bf1d2040b3 | 50 | } |
Vincent Coubard |
0:86bf1d2040b3 | 51 | |
mbed_official | 75:1a8d19363522 | 52 | void characteristic_discovery(const DiscoveredCharacteristic *characteristicP) { |
mbed_official | 75:1a8d19363522 | 53 | printf(" C UUID-%x valueAttr[%u] props[%x]\r\n", characteristicP->getUUID().getShortUUID(), characteristicP->getValueHandle(), (uint8_t)characteristicP->getProperties().broadcast()); |
mbed_official | 75:1a8d19363522 | 54 | if (characteristicP->getUUID().getShortUUID() == 0xa001) { /* !ALERT! Alter this filter to suit your device. */ |
mbed_official | 75:1a8d19363522 | 55 | led_characteristic = *characteristicP; |
mbed_official | 75:1a8d19363522 | 56 | trigger_led_characteristic = true; |
Vincent Coubard |
0:86bf1d2040b3 | 57 | } |
Vincent Coubard |
0:86bf1d2040b3 | 58 | } |
Vincent Coubard |
0:86bf1d2040b3 | 59 | |
mbed_official | 75:1a8d19363522 | 60 | void discovery_termination(Gap::Handle_t connectionHandle) { |
mbed_official | 75:1a8d19363522 | 61 | printf("terminated SD for handle %u\r\n", connectionHandle); |
mbed_official | 75:1a8d19363522 | 62 | if (trigger_led_characteristic) { |
mbed_official | 75:1a8d19363522 | 63 | trigger_led_characteristic = false; |
mbed_official | 75:1a8d19363522 | 64 | event_queue.call(update_led_characteristic); |
Vincent Coubard |
0:86bf1d2040b3 | 65 | } |
Vincent Coubard |
0:86bf1d2040b3 | 66 | } |
Vincent Coubard |
0:86bf1d2040b3 | 67 | |
mbed_official | 75:1a8d19363522 | 68 | void trigger_toggled_write(const GattReadCallbackParams *response) { |
mbed_official | 75:1a8d19363522 | 69 | if (response->handle == led_characteristic.getValueHandle()) { |
mbed_official | 75:1a8d19363522 | 70 | printf("trigger_toggled_write: handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len); |
Vincent Coubard |
0:86bf1d2040b3 | 71 | for (unsigned index = 0; index < response->len; index++) { |
Vincent Coubard |
0:86bf1d2040b3 | 72 | printf("%c[%02x]", response->data[index], response->data[index]); |
Vincent Coubard |
0:86bf1d2040b3 | 73 | } |
Vincent Coubard |
0:86bf1d2040b3 | 74 | printf("\r\n"); |
Vincent Coubard |
0:86bf1d2040b3 | 75 | |
Vincent Coubard |
0:86bf1d2040b3 | 76 | uint8_t toggledValue = response->data[0] ^ 0x1; |
mbed_official | 75:1a8d19363522 | 77 | led_characteristic.write(1, &toggledValue); |
Vincent Coubard |
0:86bf1d2040b3 | 78 | } |
Vincent Coubard |
0:86bf1d2040b3 | 79 | } |
Vincent Coubard |
0:86bf1d2040b3 | 80 | |
mbed_official | 75:1a8d19363522 | 81 | void trigger_read(const GattWriteCallbackParams *response) { |
mbed_official | 75:1a8d19363522 | 82 | if (response->handle == led_characteristic.getValueHandle()) { |
mbed_official | 75:1a8d19363522 | 83 | led_characteristic.read(); |
Vincent Coubard |
0:86bf1d2040b3 | 84 | } |
Vincent Coubard |
0:86bf1d2040b3 | 85 | } |
Vincent Coubard |
0:86bf1d2040b3 | 86 | |
mbed_official | 75:1a8d19363522 | 87 | class LEDBlinkerDemo : ble::Gap::EventHandler { |
mbed_official | 75:1a8d19363522 | 88 | public: |
mbed_official | 75:1a8d19363522 | 89 | LEDBlinkerDemo(BLE &ble, events::EventQueue &event_queue) : |
mbed_official | 75:1a8d19363522 | 90 | _ble(ble), |
mbed_official | 75:1a8d19363522 | 91 | _event_queue(event_queue), |
mbed_official | 75:1a8d19363522 | 92 | _alive_led(LED1, 1), |
mbed_official | 75:1a8d19363522 | 93 | _actuated_led(LED2, 0), |
mbed_official | 75:1a8d19363522 | 94 | _is_connecting(false) { } |
Vincent Coubard |
0:86bf1d2040b3 | 95 | |
mbed_official | 75:1a8d19363522 | 96 | ~LEDBlinkerDemo() { } |
mbed_official | 75:1a8d19363522 | 97 | |
mbed_official | 75:1a8d19363522 | 98 | void start() { |
mbed_official | 75:1a8d19363522 | 99 | _ble.gap().setEventHandler(this); |
mbed_official | 75:1a8d19363522 | 100 | |
mbed_official | 75:1a8d19363522 | 101 | _ble.init(this, &LEDBlinkerDemo::on_init_complete); |
mbed_official | 75:1a8d19363522 | 102 | |
mbed_official | 75:1a8d19363522 | 103 | _event_queue.call_every(500, this, &LEDBlinkerDemo::blink); |
mbed_official | 75:1a8d19363522 | 104 | |
mbed_official | 75:1a8d19363522 | 105 | _event_queue.dispatch_forever(); |
mbed_official | 75:1a8d19363522 | 106 | } |
Vincent Coubard |
0:86bf1d2040b3 | 107 | |
mbed_official | 75:1a8d19363522 | 108 | private: |
mbed_official | 75:1a8d19363522 | 109 | /** Callback triggered when the ble initialization process has finished */ |
mbed_official | 75:1a8d19363522 | 110 | void on_init_complete(BLE::InitializationCompleteCallbackContext *params) { |
mbed_official | 75:1a8d19363522 | 111 | if (params->error != BLE_ERROR_NONE) { |
mbed_official | 75:1a8d19363522 | 112 | printf("Ble initialization failed."); |
mbed_official | 75:1a8d19363522 | 113 | return; |
mbed_official | 75:1a8d19363522 | 114 | } |
mbed_official | 75:1a8d19363522 | 115 | |
mbed_official | 75:1a8d19363522 | 116 | print_mac_address(); |
mbed_official | 75:1a8d19363522 | 117 | |
mbed_official | 75:1a8d19363522 | 118 | _ble.gattClient().onDataRead(trigger_toggled_write); |
mbed_official | 75:1a8d19363522 | 119 | _ble.gattClient().onDataWritten(trigger_read); |
mbed_official | 75:1a8d19363522 | 120 | |
mbed_official | 75:1a8d19363522 | 121 | ble::ScanParameters scan_params; |
mbed_official | 75:1a8d19363522 | 122 | _ble.gap().setScanParameters(scan_params); |
mbed_official | 75:1a8d19363522 | 123 | _ble.gap().startScan(); |
mbed_official | 45:9fe6d1e21b8a | 124 | } |
mbed_official | 75:1a8d19363522 | 125 | |
mbed_official | 75:1a8d19363522 | 126 | void blink() { |
mbed_official | 75:1a8d19363522 | 127 | _alive_led = !_alive_led; |
mbed_official | 75:1a8d19363522 | 128 | } |
mbed_official | 45:9fe6d1e21b8a | 129 | |
mbed_official | 75:1a8d19363522 | 130 | private: |
mbed_official | 75:1a8d19363522 | 131 | /* Event handler */ |
Vincent Coubard |
0:86bf1d2040b3 | 132 | |
mbed_official | 75:1a8d19363522 | 133 | void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) { |
mbed_official | 75:1a8d19363522 | 134 | _ble.gap().startScan(); |
mbed_official | 75:1a8d19363522 | 135 | _is_connecting = false; |
Vincent Coubard |
0:86bf1d2040b3 | 136 | } |
Vincent Coubard |
0:86bf1d2040b3 | 137 | |
mbed_official | 75:1a8d19363522 | 138 | void onConnectionComplete(const ble::ConnectionCompleteEvent& event) { |
mbed_official | 77:85a0d3cdd896 | 139 | if (event.getOwnRole() == ble::connection_role_t::CENTRAL) { |
mbed_official | 75:1a8d19363522 | 140 | _ble.gattClient().onServiceDiscoveryTermination(discovery_termination); |
mbed_official | 75:1a8d19363522 | 141 | _ble.gattClient().launchServiceDiscovery( |
mbed_official | 75:1a8d19363522 | 142 | event.getConnectionHandle(), |
mbed_official | 75:1a8d19363522 | 143 | service_discovery, |
mbed_official | 75:1a8d19363522 | 144 | characteristic_discovery, |
mbed_official | 75:1a8d19363522 | 145 | 0xa000, |
mbed_official | 75:1a8d19363522 | 146 | 0xa001 |
mbed_official | 75:1a8d19363522 | 147 | ); |
mbed_official | 75:1a8d19363522 | 148 | } else { |
mbed_official | 75:1a8d19363522 | 149 | _ble.gap().startScan(); |
mbed_official | 75:1a8d19363522 | 150 | } |
mbed_official | 75:1a8d19363522 | 151 | _is_connecting = false; |
Vincent Coubard |
0:86bf1d2040b3 | 152 | } |
Vincent Coubard |
0:86bf1d2040b3 | 153 | |
mbed_official | 75:1a8d19363522 | 154 | void onAdvertisingReport(const ble::AdvertisingReportEvent &event) { |
mbed_official | 75:1a8d19363522 | 155 | /* don't bother with analysing scan result if we're already connecting */ |
mbed_official | 75:1a8d19363522 | 156 | if (_is_connecting) { |
mbed_official | 75:1a8d19363522 | 157 | return; |
mbed_official | 75:1a8d19363522 | 158 | } |
mbed_official | 75:1a8d19363522 | 159 | |
mbed_official | 75:1a8d19363522 | 160 | ble::AdvertisingDataParser adv_data(event.getPayload()); |
mbed_official | 75:1a8d19363522 | 161 | |
mbed_official | 75:1a8d19363522 | 162 | /* parse the advertising payload, looking for a discoverable device */ |
mbed_official | 75:1a8d19363522 | 163 | while (adv_data.hasNext()) { |
mbed_official | 75:1a8d19363522 | 164 | ble::AdvertisingDataParser::element_t field = adv_data.next(); |
Vincent Coubard |
0:86bf1d2040b3 | 165 | |
mbed_official | 75:1a8d19363522 | 166 | /* connect to a discoverable device */ |
mbed_official | 75:1a8d19363522 | 167 | if (field.type == ble::adv_data_type_t::COMPLETE_LOCAL_NAME && |
mbed_official | 75:1a8d19363522 | 168 | field.value.size() == strlen(PEER_NAME) && |
mbed_official | 75:1a8d19363522 | 169 | (memcmp(field.value.data(), PEER_NAME, field.value.size()) == 0)) { |
mbed_official | 75:1a8d19363522 | 170 | |
mbed_official | 75:1a8d19363522 | 171 | printf("Adv from: "); |
mbed_official | 75:1a8d19363522 | 172 | print_address(event.getPeerAddress().data()); |
mbed_official | 75:1a8d19363522 | 173 | printf(" rssi: %d, scan response: %u, connectable: %u\r\n", |
mbed_official | 75:1a8d19363522 | 174 | event.getRssi(), event.getType().scan_response(), event.getType().connectable()); |
mbed_official | 75:1a8d19363522 | 175 | |
mbed_official | 75:1a8d19363522 | 176 | ble_error_t error = _ble.gap().stopScan(); |
mbed_official | 75:1a8d19363522 | 177 | |
mbed_official | 75:1a8d19363522 | 178 | if (error) { |
mbed_official | 75:1a8d19363522 | 179 | print_error(error, "Error caused by Gap::stopScan"); |
mbed_official | 75:1a8d19363522 | 180 | return; |
mbed_official | 75:1a8d19363522 | 181 | } |
mbed_official | 75:1a8d19363522 | 182 | |
mbed_official | 75:1a8d19363522 | 183 | const ble::ConnectionParameters connection_params; |
Vincent Coubard |
0:86bf1d2040b3 | 184 | |
mbed_official | 75:1a8d19363522 | 185 | error = _ble.gap().connect( |
mbed_official | 75:1a8d19363522 | 186 | event.getPeerAddressType(), |
mbed_official | 75:1a8d19363522 | 187 | event.getPeerAddress(), |
mbed_official | 75:1a8d19363522 | 188 | connection_params |
mbed_official | 75:1a8d19363522 | 189 | ); |
mbed_official | 75:1a8d19363522 | 190 | |
mbed_official | 75:1a8d19363522 | 191 | if (error) { |
mbed_official | 75:1a8d19363522 | 192 | _ble.gap().startScan(); |
mbed_official | 75:1a8d19363522 | 193 | return; |
mbed_official | 75:1a8d19363522 | 194 | } |
mbed_official | 75:1a8d19363522 | 195 | |
mbed_official | 75:1a8d19363522 | 196 | /* we may have already scan events waiting |
mbed_official | 75:1a8d19363522 | 197 | * to be processed so we need to remember |
mbed_official | 75:1a8d19363522 | 198 | * that we are already connecting and ignore them */ |
mbed_official | 75:1a8d19363522 | 199 | _is_connecting = true; |
mbed_official | 45:9fe6d1e21b8a | 200 | |
mbed_official | 75:1a8d19363522 | 201 | return; |
mbed_official | 75:1a8d19363522 | 202 | } |
mbed_official | 75:1a8d19363522 | 203 | } |
mbed_official | 75:1a8d19363522 | 204 | } |
Vincent Coubard |
0:86bf1d2040b3 | 205 | |
mbed_official | 75:1a8d19363522 | 206 | private: |
mbed_official | 75:1a8d19363522 | 207 | BLE &_ble; |
mbed_official | 75:1a8d19363522 | 208 | events::EventQueue &_event_queue; |
mbed_official | 75:1a8d19363522 | 209 | DigitalOut _alive_led; |
mbed_official | 75:1a8d19363522 | 210 | DigitalOut _actuated_led; |
mbed_official | 75:1a8d19363522 | 211 | bool _is_connecting; |
mbed_official | 75:1a8d19363522 | 212 | }; |
mbed_official | 75:1a8d19363522 | 213 | |
mbed_official | 75:1a8d19363522 | 214 | /** Schedule processing of events from the BLE middleware in the event queue. */ |
mbed_official | 75:1a8d19363522 | 215 | void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) { |
mbed_official | 75:1a8d19363522 | 216 | event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents)); |
Vincent Coubard |
0:86bf1d2040b3 | 217 | } |
Vincent Coubard |
0:86bf1d2040b3 | 218 | |
Vincent Coubard |
0:86bf1d2040b3 | 219 | int main() |
Vincent Coubard |
0:86bf1d2040b3 | 220 | { |
mbed_official | 75:1a8d19363522 | 221 | BLE &ble = BLE::Instance(); |
mbed_official | 75:1a8d19363522 | 222 | ble.onEventsToProcess(schedule_ble_events); |
Vincent Coubard |
0:86bf1d2040b3 | 223 | |
mbed_official | 75:1a8d19363522 | 224 | LEDBlinkerDemo demo(ble, event_queue); |
mbed_official | 75:1a8d19363522 | 225 | demo.start(); |
Vincent Coubard |
0:86bf1d2040b3 | 226 | |
Vincent Coubard |
0:86bf1d2040b3 | 227 | return 0; |
Vincent Coubard |
0:86bf1d2040b3 | 228 | } |