Experimental BLE Blinky Application using a LED and button service server. This is a small custom service that is used to toggle LEDs and receive button status from wirelessly connected Bluetooth Smart development boards.

For information on the GATT service UUID, characteristic UUIDs, or what to expect when manipulating them, please refer to:

http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v13.0.0%2Fble_sdk_app_blinky.html http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fgroup__ble__sdk__srv__lbs.html http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fgroup__ble__sdk__srv__lbs__c.html

Good luck with that.

For an easy 5 minute tour of how Bluetooth Smart works, program a developer kit (nRF52-DK for example) with this application. Once it starts to run it will indicate advertising by blinking LED1. At this time, install a corresponding client application like the nRF Blinky Android app [1] and watch it detect the developer kit. Click on the 'Lightsw' identifier to connect with the GATT service and wait until the developer kit LED1 stops blinking to indicate a connected state. Now, illuminate LED2 by clicking on the lightbulb. Pressing one of the developer kit's buttons will cause the Android app's background to illuminate illustrating bidirectional communication over Bluetooth Smart.

[1] https://play.google.com/store/apps/details?id=no.nordicsemi.android.nrfblinky

Committer:
michaesc
Date:
Tue Mar 28 15:57:46 2017 +0000
Revision:
2:204381095967
Implemented Blinkyserv class and GATT derived service and characteristic logic.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
michaesc 2:204381095967 1 /* Blinky service application
michaesc 2:204381095967 2 *
michaesc 2:204381095967 3 * This Bluetooth Smart application intends to serve as a substitute
michaesc 2:204381095967 4 * for existing applications implemented using the Nordic Semiconductor
michaesc 2:204381095967 5 * proprietary nRF 5 SDK. This application interfaces with example
michaesc 2:204381095967 6 * client applications such as the preexisting 'nRF Blinky' and
michaesc 2:204381095967 7 * 'nRF52 Blinky Web App', while complying with proprietary Nordic
michaesc 2:204381095967 8 * Semiconductor service and characteristic UUIDs.
michaesc 2:204381095967 9 *
michaesc 2:204381095967 10 * Licensed under the Apache License, Version 2.0 (the "License");
michaesc 2:204381095967 11 * you may not use this file except in compliance with the License.
michaesc 2:204381095967 12 * You may obtain a copy of the License at
michaesc 2:204381095967 13 *
michaesc 2:204381095967 14 * http://www.apache.org/licenses/LICENSE-2.0
michaesc 2:204381095967 15 *
michaesc 2:204381095967 16 * Unless required by applicable law or agreed to in writing, software
michaesc 2:204381095967 17 * distributed under the License is distributed on an "AS IS" BASIS,
michaesc 2:204381095967 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michaesc 2:204381095967 19 * See the License for the specific language governing permissions and
michaesc 2:204381095967 20 * limitations under the License.
michaesc 2:204381095967 21 */
michaesc 2:204381095967 22
michaesc 2:204381095967 23 #include <mbed.h>
michaesc 2:204381095967 24 #include "ble/BLE.h"
michaesc 2:204381095967 25 #include "ble/Gap.h"
michaesc 2:204381095967 26 #include "Service.h"
michaesc 2:204381095967 27
michaesc 2:204381095967 28 // FIXME: initializing has no effect!
michaesc 2:204381095967 29 DigitalOut alivenessLED(LED1);
michaesc 2:204381095967 30 DigitalOut actuatedLED(LED2);
michaesc 2:204381095967 31 InterruptIn button(BUTTON1);
michaesc 2:204381095967 32
michaesc 2:204381095967 33 enum { // Button!
michaesc 2:204381095967 34 RELEASED = 0,
michaesc 2:204381095967 35 PRESSED,
michaesc 2:204381095967 36 IDLE
michaesc 2:204381095967 37 };
michaesc 2:204381095967 38
michaesc 2:204381095967 39 // Stores the state of button sensor
michaesc 2:204381095967 40 static uint8_t buttonState = IDLE;
michaesc 2:204381095967 41
michaesc 2:204381095967 42 const static char DEVICE_NAME[] = "Lightsw"; // Seems this string has a limited maximum length?
michaesc 2:204381095967 43 //static const uint16_t uuid16_list[] = {Blinkyserv::LED_SERVICE_UUID}; // Only works on short URLs
michaesc 2:204381095967 44 //static const uint8_t uuid128_list[] = {0x0000, 0x1523, 0x1212, 0xefde, 0x1523, 0x785f, 0xeabc, 0xd123};
michaesc 2:204381095967 45
michaesc 2:204381095967 46 Blinkyserv *blinkySvcptr;
michaesc 2:204381095967 47
michaesc 2:204381095967 48 static EventQueue eventQueue(
michaesc 2:204381095967 49 /* event count */ 16 * /* event size */ 32
michaesc 2:204381095967 50 );
michaesc 2:204381095967 51
michaesc 2:204381095967 52 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
michaesc 2:204381095967 53 {
michaesc 2:204381095967 54 printf("Disconnected handle %u!\n", params->handle);
michaesc 2:204381095967 55 printf("Restarting the advertising process\n");
michaesc 2:204381095967 56 BLE::Instance().gap().startAdvertising();
michaesc 2:204381095967 57 actuatedLED = 1; // Reinitialize actuator,
michaesc 2:204381095967 58 // according to svc value
michaesc 2:204381095967 59 // initialValueForLEDCharacteristic
michaesc 2:204381095967 60 }
michaesc 2:204381095967 61
michaesc 2:204381095967 62 void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
michaesc 2:204381095967 63 {
michaesc 2:204381095967 64 printf("Connected handle %u!\n", params->handle);
michaesc 2:204381095967 65 printf("Stopping the advertising process\n");
michaesc 2:204381095967 66 BLE::Instance().gap().stopAdvertising();
michaesc 2:204381095967 67 alivenessLED = 1; // Indicate end of adverts
michaesc 2:204381095967 68 }
michaesc 2:204381095967 69
michaesc 2:204381095967 70 void blinkCallback(void)
michaesc 2:204381095967 71 {
michaesc 2:204381095967 72 BLE &ble = BLE::Instance();
michaesc 2:204381095967 73
michaesc 2:204381095967 74 // Hack to avoid hung connections
michaesc 2:204381095967 75 if (!ble.gap().getState().connected
michaesc 2:204381095967 76 && !ble.gap().getState().advertising) {
michaesc 2:204381095967 77 BLE::Instance().gap().startAdvertising();
michaesc 2:204381095967 78 }
michaesc 2:204381095967 79
michaesc 2:204381095967 80 // Indicate adverts by blinking a LED
michaesc 2:204381095967 81 if (ble.gap().getState().advertising) {
michaesc 2:204381095967 82 alivenessLED = !alivenessLED;
michaesc 2:204381095967 83 }
michaesc 2:204381095967 84 }
michaesc 2:204381095967 85
michaesc 2:204381095967 86 /**
michaesc 2:204381095967 87 * Allows the LED service to receive updates to the ledState characteristic.
michaesc 2:204381095967 88 *
michaesc 2:204381095967 89 * @param[in] params
michaesc 2:204381095967 90 * Information about the characterisitc being updated.
michaesc 2:204381095967 91 */
michaesc 2:204381095967 92 void onDataWrittenCallback(const GattWriteCallbackParams *params) {
michaesc 2:204381095967 93 if ((params->handle == blinkySvcptr->getValueHandle()) && (params->len == 1)) {
michaesc 2:204381095967 94 printf("Going to set the LED actuator!\n");
michaesc 2:204381095967 95 actuatedLED = !*(params->data); // Inverse
michaesc 2:204381095967 96 }
michaesc 2:204381095967 97 }
michaesc 2:204381095967 98
michaesc 2:204381095967 99 /**
michaesc 2:204381095967 100 * Allows the LED service to receive updates to the ledState characteristic.
michaesc 2:204381095967 101 */
michaesc 2:204381095967 102 void onUpdatesCallback(Gap::Handle_t handle) {
michaesc 2:204381095967 103 printf("Notifications enabled for %d\n!", handle);
michaesc 2:204381095967 104 }
michaesc 2:204381095967 105
michaesc 2:204381095967 106 /* Callback user space helper method */
michaesc 2:204381095967 107 void fall_handler_user_context(void) {
michaesc 2:204381095967 108 // Fall handler is called on button actions
michaesc 2:204381095967 109 blinkySvcptr->updateButtonState(buttonState);
michaesc 2:204381095967 110 buttonState = IDLE;
michaesc 2:204381095967 111 }
michaesc 2:204381095967 112
michaesc 2:204381095967 113 /* Callback user space helper method */
michaesc 2:204381095967 114 void rise_handler_user_context(void) {
michaesc 2:204381095967 115 // Fall handler is called on button actions
michaesc 2:204381095967 116 blinkySvcptr->updateButtonState(buttonState);
michaesc 2:204381095967 117 buttonState = IDLE;
michaesc 2:204381095967 118 }
michaesc 2:204381095967 119
michaesc 2:204381095967 120 /* Note that the buttonPressedCallback() executes in interrupt context,
michaesc 2:204381095967 121 * so it is safer to access BLE device API from the main thread. For
michaesc 2:204381095967 122 * example, do not try to call printf(3) inside interrupt context! */
michaesc 2:204381095967 123 void buttonPressedCallback(void)
michaesc 2:204381095967 124 {
michaesc 2:204381095967 125 buttonState = PRESSED;
michaesc 2:204381095967 126 eventQueue.call(fall_handler_user_context);
michaesc 2:204381095967 127 }
michaesc 2:204381095967 128
michaesc 2:204381095967 129 /* Note that the buttonReleasedCallback() executes in interrupt context,
michaesc 2:204381095967 130 * so it is safer to access BLE device API from the main thread. For
michaesc 2:204381095967 131 * example, do not try to call printf(3) inside interrupt context! */
michaesc 2:204381095967 132 void buttonReleasedCallback(void)
michaesc 2:204381095967 133 {
michaesc 2:204381095967 134 buttonState = RELEASED;
michaesc 2:204381095967 135 eventQueue.call(rise_handler_user_context);
michaesc 2:204381095967 136 }
michaesc 2:204381095967 137
michaesc 2:204381095967 138 /**
michaesc 2:204381095967 139 * This function is called when the ble initialization process has failed
michaesc 2:204381095967 140 */
michaesc 2:204381095967 141 void onBleInitError(BLE &ble, ble_error_t error)
michaesc 2:204381095967 142 {
michaesc 2:204381095967 143 /* Initialization error handling should go here */
michaesc 2:204381095967 144 }
michaesc 2:204381095967 145
michaesc 2:204381095967 146 /**
michaesc 2:204381095967 147 * Callback triggered when the ble initialization process has finished
michaesc 2:204381095967 148 */
michaesc 2:204381095967 149 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
michaesc 2:204381095967 150 {
michaesc 2:204381095967 151 BLE& ble = params->ble;
michaesc 2:204381095967 152 ble_error_t error = params->error;
michaesc 2:204381095967 153
michaesc 2:204381095967 154 if (error != BLE_ERROR_NONE) {
michaesc 2:204381095967 155 /* In case of error, forward the error handling to onBleInitError */
michaesc 2:204381095967 156 onBleInitError(ble, error);
michaesc 2:204381095967 157 return;
michaesc 2:204381095967 158 }
michaesc 2:204381095967 159
michaesc 2:204381095967 160 /* Ensure that it is the default instance of BLE */
michaesc 2:204381095967 161 if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
michaesc 2:204381095967 162 return;
michaesc 2:204381095967 163 }
michaesc 2:204381095967 164
michaesc 2:204381095967 165 ble.gap().onConnection(connectionCallback);
michaesc 2:204381095967 166 ble.gap().onDisconnection(disconnectionCallback);
michaesc 2:204381095967 167 ble.gattServer().onDataWritten(onDataWrittenCallback);
michaesc 2:204381095967 168 ble.gattServer().onUpdatesEnabled(onUpdatesCallback);
michaesc 2:204381095967 169
michaesc 2:204381095967 170 /* Setup primary service */
michaesc 2:204381095967 171 bool initialValueForLEDCharacteristic = false;
michaesc 2:204381095967 172 bool initialValueForButCharacteristic = false;
michaesc 2:204381095967 173 blinkySvcptr = new Blinkyserv(ble, initialValueForLEDCharacteristic, initialValueForButCharacteristic);
michaesc 2:204381095967 174
michaesc 2:204381095967 175 /* Setup advertising */
michaesc 2:204381095967 176 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
michaesc 2:204381095967 177 //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *) uuid16_list, sizeof(uuid16_list));
michaesc 2:204381095967 178 //ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *) uuid128_list, sizeof(uuid128_list));
michaesc 2:204381095967 179 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, blinkySvcptr->LED_SERVICE_UUID->getBaseUUID(), blinkySvcptr->LED_SERVICE_UUID->getLen());
michaesc 2:204381095967 180 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME));
michaesc 2:204381095967 181 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
michaesc 2:204381095967 182 ble.gap().setAdvertisingInterval(1000); /* 1000ms */
michaesc 2:204381095967 183 ble.gap().startAdvertising();
michaesc 2:204381095967 184 }
michaesc 2:204381095967 185
michaesc 2:204381095967 186 void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
michaesc 2:204381095967 187 BLE &ble = BLE::Instance();
michaesc 2:204381095967 188
michaesc 2:204381095967 189 // Finally redirect call to BLE callback handler
michaesc 2:204381095967 190 eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
michaesc 2:204381095967 191 }
michaesc 2:204381095967 192
michaesc 2:204381095967 193 int main()
michaesc 2:204381095967 194 {
michaesc 2:204381095967 195 printf("Starting application main entry point...\n");
michaesc 2:204381095967 196 eventQueue.call_every(200, blinkCallback);
michaesc 2:204381095967 197
michaesc 2:204381095967 198 BLE &ble = BLE::Instance();
michaesc 2:204381095967 199 ble.onEventsToProcess(scheduleBleEventsProcessing);
michaesc 2:204381095967 200 ble.init(bleInitComplete);
michaesc 2:204381095967 201
michaesc 2:204381095967 202 actuatedLED = 1; // initialize off
michaesc 2:204381095967 203 button.fall(buttonPressedCallback);
michaesc 2:204381095967 204 button.rise(buttonReleasedCallback);
michaesc 2:204381095967 205
michaesc 2:204381095967 206 eventQueue.dispatch_forever();
michaesc 2:204381095967 207
michaesc 2:204381095967 208 return 0;
michaesc 2:204381095967 209 }