Eddystone test using modified DAL

Dependencies:   BLE_API mbed-dev-bin nRF51822

Dependents:   microbit-eddystone

Fork of microbit-dal by Lancaster University

Committer:
bluetooth_mdw
Date:
Wed Feb 08 07:49:17 2017 +0000
Revision:
74:a8f5674a0079
Parent:
66:2fc7d7c2fffc
Eddystone URL test using modified DAL

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jonathan Austin 1:8aa5cdb4ab67 1 /*
Jonathan Austin 1:8aa5cdb4ab67 2 The MIT License (MIT)
Jonathan Austin 1:8aa5cdb4ab67 3
Jonathan Austin 1:8aa5cdb4ab67 4 Copyright (c) 2016 British Broadcasting Corporation.
Jonathan Austin 1:8aa5cdb4ab67 5 This software is provided by Lancaster University by arrangement with the BBC.
Jonathan Austin 1:8aa5cdb4ab67 6
Jonathan Austin 1:8aa5cdb4ab67 7 Permission is hereby granted, free of charge, to any person obtaining a
Jonathan Austin 1:8aa5cdb4ab67 8 copy of this software and associated documentation files (the "Software"),
Jonathan Austin 1:8aa5cdb4ab67 9 to deal in the Software without restriction, including without limitation
Jonathan Austin 1:8aa5cdb4ab67 10 the rights to use, copy, modify, merge, publish, distribute, sublicense,
Jonathan Austin 1:8aa5cdb4ab67 11 and/or sell copies of the Software, and to permit persons to whom the
Jonathan Austin 1:8aa5cdb4ab67 12 Software is furnished to do so, subject to the following conditions:
Jonathan Austin 1:8aa5cdb4ab67 13
Jonathan Austin 1:8aa5cdb4ab67 14 The above copyright notice and this permission notice shall be included in
Jonathan Austin 1:8aa5cdb4ab67 15 all copies or substantial portions of the Software.
Jonathan Austin 1:8aa5cdb4ab67 16
Jonathan Austin 1:8aa5cdb4ab67 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Jonathan Austin 1:8aa5cdb4ab67 18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Jonathan Austin 1:8aa5cdb4ab67 19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Jonathan Austin 1:8aa5cdb4ab67 20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Jonathan Austin 1:8aa5cdb4ab67 22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Jonathan Austin 1:8aa5cdb4ab67 23 DEALINGS IN THE SOFTWARE.
Jonathan Austin 1:8aa5cdb4ab67 24 */
Jonathan Austin 1:8aa5cdb4ab67 25
Jonathan Austin 1:8aa5cdb4ab67 26 #include "MicroBitConfig.h"
Jonathan Austin 1:8aa5cdb4ab67 27 #include "MicroBitBLEManager.h"
bluetooth_mdw 74:a8f5674a0079 28 #include "MicroBitEddystone.h"
Jonathan Austin 1:8aa5cdb4ab67 29 #include "MicroBitStorage.h"
Jonathan Austin 1:8aa5cdb4ab67 30 #include "MicroBitFiber.h"
bluetooth_mdw 74:a8f5674a0079 31 #include "MicroBitSystemTimer.h"
Jonathan Austin 1:8aa5cdb4ab67 32
Jonathan Austin 1:8aa5cdb4ab67 33 /* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
Jonathan Austin 1:8aa5cdb4ab67 34 * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
Jonathan Austin 1:8aa5cdb4ab67 35 * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
Jonathan Austin 1:8aa5cdb4ab67 36 * as a compatability option, but does not support the options used...
Jonathan Austin 1:8aa5cdb4ab67 37 */
Jonathan Austin 1:8aa5cdb4ab67 38 #if !defined(__arm)
Jonathan Austin 1:8aa5cdb4ab67 39 #pragma GCC diagnostic ignored "-Wunused-function"
Jonathan Austin 1:8aa5cdb4ab67 40 #pragma GCC diagnostic push
Jonathan Austin 1:8aa5cdb4ab67 41 #pragma GCC diagnostic ignored "-Wunused-parameter"
Jonathan Austin 1:8aa5cdb4ab67 42 #endif
Jonathan Austin 1:8aa5cdb4ab67 43
Jonathan Austin 1:8aa5cdb4ab67 44 #include "ble.h"
Jonathan Austin 1:8aa5cdb4ab67 45
bluetooth_mdw 74:a8f5674a0079 46 extern "C" {
Jonathan Austin 1:8aa5cdb4ab67 47 #include "device_manager.h"
Jonathan Austin 1:8aa5cdb4ab67 48 uint32_t btle_set_gatt_table_size(uint32_t size);
Jonathan Austin 1:8aa5cdb4ab67 49 }
Jonathan Austin 1:8aa5cdb4ab67 50
Jonathan Austin 1:8aa5cdb4ab67 51 /*
Jonathan Austin 1:8aa5cdb4ab67 52 * Return to our predefined compiler settings.
Jonathan Austin 1:8aa5cdb4ab67 53 */
Jonathan Austin 1:8aa5cdb4ab67 54 #if !defined(__arm)
Jonathan Austin 1:8aa5cdb4ab67 55 #pragma GCC diagnostic pop
Jonathan Austin 1:8aa5cdb4ab67 56 #endif
Jonathan Austin 1:8aa5cdb4ab67 57
bluetooth_mdw 74:a8f5674a0079 58 #define MICROBIT_PAIRING_FADE_SPEED 4
Jonathan Austin 1:8aa5cdb4ab67 59
bluetooth_mdw 74:a8f5674a0079 60 //
bluetooth_mdw 74:a8f5674a0079 61 // Local enumeration of valid security modes. Used only to optimise pre‐processor comparisons.
bluetooth_mdw 74:a8f5674a0079 62 //
bluetooth_mdw 74:a8f5674a0079 63 #define __SECURITY_MODE_ENCRYPTION_OPEN_LINK 0
bluetooth_mdw 74:a8f5674a0079 64 #define __SECURITY_MODE_ENCRYPTION_NO_MITM 1
bluetooth_mdw 74:a8f5674a0079 65 #define __SECURITY_MODE_ENCRYPTION_WITH_MITM 2
bluetooth_mdw 74:a8f5674a0079 66 //
bluetooth_mdw 74:a8f5674a0079 67 // Some Black Magic to compare the definition of our security mode in MicroBitConfig with a given parameter.
bluetooth_mdw 74:a8f5674a0079 68 // Required as the MicroBitConfig option is actually an mbed enum, that is not normally comparable at compile time.
bluetooth_mdw 74:a8f5674a0079 69 //
bluetooth_mdw 74:a8f5674a0079 70
bluetooth_mdw 74:a8f5674a0079 71 #define __CAT(a, ...) a##__VA_ARGS__
bluetooth_mdw 74:a8f5674a0079 72 #define SECURITY_MODE(x) __CAT(__, x)
bluetooth_mdw 74:a8f5674a0079 73 #define SECURITY_MODE_IS(x) (SECURITY_MODE(MICROBIT_BLE_SECURITY_LEVEL) == SECURITY_MODE(x))
bluetooth_mdw 74:a8f5674a0079 74
bluetooth_mdw 74:a8f5674a0079 75 const char *MICROBIT_BLE_MANUFACTURER = NULL;
bluetooth_mdw 74:a8f5674a0079 76 const char *MICROBIT_BLE_MODEL = "BBC micro:bit";
bluetooth_mdw 74:a8f5674a0079 77 const char *MICROBIT_BLE_HARDWARE_VERSION = NULL;
bluetooth_mdw 74:a8f5674a0079 78 const char *MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
bluetooth_mdw 74:a8f5674a0079 79 const char *MICROBIT_BLE_SOFTWARE_VERSION = NULL;
Jonathan Austin 1:8aa5cdb4ab67 80 const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
Jonathan Austin 1:8aa5cdb4ab67 81
Jonathan Austin 1:8aa5cdb4ab67 82 /*
Jonathan Austin 1:8aa5cdb4ab67 83 * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods.
Jonathan Austin 1:8aa5cdb4ab67 84 * So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit
Jonathan Austin 1:8aa5cdb4ab67 85 * whilst keeping the code modular.
Jonathan Austin 1:8aa5cdb4ab67 86 */
bluetooth_mdw 74:a8f5674a0079 87 MicroBitBLEManager *MicroBitBLEManager::manager = NULL; // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-(
bluetooth_mdw 74:a8f5674a0079 88
bluetooth_mdw 74:a8f5674a0079 89 static uint8_t deviceID = 255; // Unique ID for the peer that has connected to us.
bluetooth_mdw 74:a8f5674a0079 90 static Gap::Handle_t pairingHandle = 0; // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly.
Jonathan Austin 1:8aa5cdb4ab67 91
Jonathan Austin 1:8aa5cdb4ab67 92 static void storeSystemAttributes(Gap::Handle_t handle)
Jonathan Austin 1:8aa5cdb4ab67 93 {
bluetooth_mdw 74:a8f5674a0079 94 if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
Jonathan Austin 1:8aa5cdb4ab67 95 {
Jonathan Austin 1:8aa5cdb4ab67 96 ManagedString key("bleSysAttrs");
Jonathan Austin 1:8aa5cdb4ab67 97
bluetooth_mdw 74:a8f5674a0079 98 KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
Jonathan Austin 1:8aa5cdb4ab67 99
Jonathan Austin 1:8aa5cdb4ab67 100 BLESysAttribute attrib;
Jonathan Austin 1:8aa5cdb4ab67 101 BLESysAttributeStore attribStore;
Jonathan Austin 1:8aa5cdb4ab67 102
Jonathan Austin 1:8aa5cdb4ab67 103 uint16_t len = sizeof(attrib.sys_attr);
Jonathan Austin 1:8aa5cdb4ab67 104
Jonathan Austin 1:8aa5cdb4ab67 105 sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
Jonathan Austin 1:8aa5cdb4ab67 106
Jonathan Austin 1:8aa5cdb4ab67 107 //copy our stored sysAttrs
bluetooth_mdw 74:a8f5674a0079 108 if (bleSysAttrs != NULL)
Jonathan Austin 1:8aa5cdb4ab67 109 {
Jonathan Austin 1:8aa5cdb4ab67 110 memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
Jonathan Austin 1:8aa5cdb4ab67 111 delete bleSysAttrs;
Jonathan Austin 1:8aa5cdb4ab67 112 }
Jonathan Austin 1:8aa5cdb4ab67 113
Jonathan Austin 1:8aa5cdb4ab67 114 //check if we need to update
bluetooth_mdw 74:a8f5674a0079 115 if (memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0)
Jonathan Austin 1:8aa5cdb4ab67 116 {
Jonathan Austin 1:8aa5cdb4ab67 117 attribStore.sys_attrs[deviceID] = attrib;
bluetooth_mdw 74:a8f5674a0079 118 MicroBitBLEManager::manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore));
Jonathan Austin 1:8aa5cdb4ab67 119 }
Jonathan Austin 1:8aa5cdb4ab67 120 }
Jonathan Austin 1:8aa5cdb4ab67 121 }
Jonathan Austin 1:8aa5cdb4ab67 122
Jonathan Austin 1:8aa5cdb4ab67 123 /**
Jonathan Austin 1:8aa5cdb4ab67 124 * Callback when a BLE GATT disconnect occurs.
Jonathan Austin 1:8aa5cdb4ab67 125 */
Jonathan Austin 1:8aa5cdb4ab67 126 static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
Jonathan Austin 1:8aa5cdb4ab67 127 {
bluetooth_mdw 74:a8f5674a0079 128 MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED);
LancasterUniversity 26:493daf8966fd 129
Jonathan Austin 1:8aa5cdb4ab67 130 storeSystemAttributes(reason->handle);
Jonathan Austin 1:8aa5cdb4ab67 131
bluetooth_mdw 74:a8f5674a0079 132 if (MicroBitBLEManager::manager)
bluetooth_mdw 74:a8f5674a0079 133 MicroBitBLEManager::manager->advertise();
Jonathan Austin 1:8aa5cdb4ab67 134 }
Jonathan Austin 1:8aa5cdb4ab67 135
Jonathan Austin 1:8aa5cdb4ab67 136 /**
LancasterUniversity 23:6055f6c19fa6 137 * Callback when a BLE connection is established.
LancasterUniversity 23:6055f6c19fa6 138 */
bluetooth_mdw 74:a8f5674a0079 139 static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t *)
LancasterUniversity 23:6055f6c19fa6 140 {
bluetooth_mdw 74:a8f5674a0079 141 MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED);
LancasterUniversity 23:6055f6c19fa6 142 }
LancasterUniversity 23:6055f6c19fa6 143
LancasterUniversity 23:6055f6c19fa6 144 /**
Jonathan Austin 1:8aa5cdb4ab67 145 * Callback when a BLE SYS_ATTR_MISSING.
Jonathan Austin 1:8aa5cdb4ab67 146 */
Jonathan Austin 1:8aa5cdb4ab67 147 static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *params)
Jonathan Austin 1:8aa5cdb4ab67 148 {
Jonathan Austin 1:8aa5cdb4ab67 149 int complete = 0;
Jonathan Austin 1:8aa5cdb4ab67 150 deviceID = 255;
Jonathan Austin 1:8aa5cdb4ab67 151
bluetooth_mdw 74:a8f5674a0079 152 dm_handle_t dm_handle = {0, 0, 0, 0};
Jonathan Austin 1:8aa5cdb4ab67 153
Jonathan Austin 1:8aa5cdb4ab67 154 int ret = dm_handle_get(params->connHandle, &dm_handle);
Jonathan Austin 1:8aa5cdb4ab67 155
Jonathan Austin 1:8aa5cdb4ab67 156 if (ret == 0)
Jonathan Austin 1:8aa5cdb4ab67 157 deviceID = dm_handle.device_id;
Jonathan Austin 1:8aa5cdb4ab67 158
bluetooth_mdw 74:a8f5674a0079 159 if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
Jonathan Austin 1:8aa5cdb4ab67 160 {
Jonathan Austin 1:8aa5cdb4ab67 161 ManagedString key("bleSysAttrs");
Jonathan Austin 1:8aa5cdb4ab67 162
bluetooth_mdw 74:a8f5674a0079 163 KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
Jonathan Austin 1:8aa5cdb4ab67 164
Jonathan Austin 1:8aa5cdb4ab67 165 BLESysAttributeStore attribStore;
Jonathan Austin 1:8aa5cdb4ab67 166 BLESysAttribute attrib;
Jonathan Austin 1:8aa5cdb4ab67 167
bluetooth_mdw 74:a8f5674a0079 168 if (bleSysAttrs != NULL)
Jonathan Austin 1:8aa5cdb4ab67 169 {
Jonathan Austin 1:8aa5cdb4ab67 170 //restore our sysAttrStore
Jonathan Austin 1:8aa5cdb4ab67 171 memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
Jonathan Austin 1:8aa5cdb4ab67 172 delete bleSysAttrs;
Jonathan Austin 1:8aa5cdb4ab67 173
Jonathan Austin 1:8aa5cdb4ab67 174 attrib = attribStore.sys_attrs[deviceID];
Jonathan Austin 1:8aa5cdb4ab67 175
Jonathan Austin 1:8aa5cdb4ab67 176 ret = sd_ble_gatts_sys_attr_set(params->connHandle, attrib.sys_attr, sizeof(attrib.sys_attr), BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
Jonathan Austin 1:8aa5cdb4ab67 177
Jonathan Austin 1:8aa5cdb4ab67 178 complete = 1;
Jonathan Austin 1:8aa5cdb4ab67 179
bluetooth_mdw 74:a8f5674a0079 180 if (ret == 0)
Jonathan Austin 1:8aa5cdb4ab67 181 ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
Jonathan Austin 1:8aa5cdb4ab67 182 }
Jonathan Austin 1:8aa5cdb4ab67 183 }
Jonathan Austin 1:8aa5cdb4ab67 184
Jonathan Austin 1:8aa5cdb4ab67 185 if (!complete)
Jonathan Austin 1:8aa5cdb4ab67 186 sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
Jonathan Austin 1:8aa5cdb4ab67 187 }
Jonathan Austin 1:8aa5cdb4ab67 188
Jonathan Austin 1:8aa5cdb4ab67 189 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
Jonathan Austin 1:8aa5cdb4ab67 190 {
bluetooth_mdw 74:a8f5674a0079 191 (void)handle; /* -Wunused-param */
Jonathan Austin 1:8aa5cdb4ab67 192
bluetooth_mdw 74:a8f5674a0079 193 ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
Jonathan Austin 1:8aa5cdb4ab67 194
bluetooth_mdw 74:a8f5674a0079 195 if (MicroBitBLEManager::manager)
bluetooth_mdw 74:a8f5674a0079 196 MicroBitBLEManager::manager->pairingRequested(passKey);
Jonathan Austin 1:8aa5cdb4ab67 197 }
Jonathan Austin 1:8aa5cdb4ab67 198
Jonathan Austin 1:8aa5cdb4ab67 199 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
Jonathan Austin 1:8aa5cdb4ab67 200 {
bluetooth_mdw 74:a8f5674a0079 201 (void)handle; /* -Wunused-param */
Jonathan Austin 1:8aa5cdb4ab67 202
bluetooth_mdw 74:a8f5674a0079 203 dm_handle_t dm_handle = {0, 0, 0, 0};
Jonathan Austin 1:8aa5cdb4ab67 204 int ret = dm_handle_get(handle, &dm_handle);
Jonathan Austin 1:8aa5cdb4ab67 205
Jonathan Austin 1:8aa5cdb4ab67 206 if (ret == 0)
Jonathan Austin 1:8aa5cdb4ab67 207 deviceID = dm_handle.device_id;
Jonathan Austin 1:8aa5cdb4ab67 208
bluetooth_mdw 74:a8f5674a0079 209 if (MicroBitBLEManager::manager)
Jonathan Austin 1:8aa5cdb4ab67 210 {
Jonathan Austin 1:8aa5cdb4ab67 211 pairingHandle = handle;
bluetooth_mdw 74:a8f5674a0079 212 MicroBitBLEManager::manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
Jonathan Austin 1:8aa5cdb4ab67 213 }
Jonathan Austin 1:8aa5cdb4ab67 214 }
Jonathan Austin 1:8aa5cdb4ab67 215
Jonathan Austin 1:8aa5cdb4ab67 216 /**
Jonathan Austin 1:8aa5cdb4ab67 217 * Constructor.
Jonathan Austin 1:8aa5cdb4ab67 218 *
Jonathan Austin 1:8aa5cdb4ab67 219 * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
Jonathan Austin 1:8aa5cdb4ab67 220 *
Jonathan Austin 1:8aa5cdb4ab67 221 * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS).
Jonathan Austin 1:8aa5cdb4ab67 222 *
Jonathan Austin 1:8aa5cdb4ab67 223 * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
Jonathan Austin 1:8aa5cdb4ab67 224 * Hence, the init() member function should be used to initialise the BLE stack.
Jonathan Austin 1:8aa5cdb4ab67 225 */
bluetooth_mdw 74:a8f5674a0079 226 MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage &_storage) : storage(&_storage)
Jonathan Austin 1:8aa5cdb4ab67 227 {
Jonathan Austin 1:8aa5cdb4ab67 228 manager = this;
bluetooth_mdw 74:a8f5674a0079 229 this->ble = NULL;
bluetooth_mdw 74:a8f5674a0079 230 this->pairingStatus = 0;
Jonathan Austin 1:8aa5cdb4ab67 231 }
Jonathan Austin 1:8aa5cdb4ab67 232
Jonathan Austin 1:8aa5cdb4ab67 233 /**
Jonathan Austin 1:8aa5cdb4ab67 234 * Constructor.
Jonathan Austin 1:8aa5cdb4ab67 235 *
Jonathan Austin 1:8aa5cdb4ab67 236 * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
Jonathan Austin 1:8aa5cdb4ab67 237 *
Jonathan Austin 1:8aa5cdb4ab67 238 * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
Jonathan Austin 1:8aa5cdb4ab67 239 * Hence, the init() member function should be used to initialise the BLE stack.
Jonathan Austin 1:8aa5cdb4ab67 240 */
bluetooth_mdw 74:a8f5674a0079 241 MicroBitBLEManager::MicroBitBLEManager() : storage(NULL)
Jonathan Austin 1:8aa5cdb4ab67 242 {
Jonathan Austin 1:8aa5cdb4ab67 243 manager = this;
bluetooth_mdw 74:a8f5674a0079 244 this->ble = NULL;
bluetooth_mdw 74:a8f5674a0079 245 this->pairingStatus = 0;
bluetooth_mdw 74:a8f5674a0079 246 }
bluetooth_mdw 74:a8f5674a0079 247
bluetooth_mdw 74:a8f5674a0079 248 /**
bluetooth_mdw 74:a8f5674a0079 249 * When called, the micro:bit will begin advertising for a predefined period,
bluetooth_mdw 74:a8f5674a0079 250 * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
bluetooth_mdw 74:a8f5674a0079 251 */
bluetooth_mdw 74:a8f5674a0079 252 MicroBitBLEManager *MicroBitBLEManager::getInstance()
bluetooth_mdw 74:a8f5674a0079 253 {
bluetooth_mdw 74:a8f5674a0079 254 if (manager == 0)
bluetooth_mdw 74:a8f5674a0079 255 {
bluetooth_mdw 74:a8f5674a0079 256 manager = new MicroBitBLEManager;
bluetooth_mdw 74:a8f5674a0079 257 }
bluetooth_mdw 74:a8f5674a0079 258 return manager;
Jonathan Austin 1:8aa5cdb4ab67 259 }
Jonathan Austin 1:8aa5cdb4ab67 260
Jonathan Austin 1:8aa5cdb4ab67 261 /**
Jonathan Austin 1:8aa5cdb4ab67 262 * When called, the micro:bit will begin advertising for a predefined period,
Jonathan Austin 1:8aa5cdb4ab67 263 * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
Jonathan Austin 1:8aa5cdb4ab67 264 */
Jonathan Austin 1:8aa5cdb4ab67 265 void MicroBitBLEManager::advertise()
Jonathan Austin 1:8aa5cdb4ab67 266 {
bluetooth_mdw 74:a8f5674a0079 267 if (ble)
Jonathan Austin 1:8aa5cdb4ab67 268 ble->gap().startAdvertising();
Jonathan Austin 1:8aa5cdb4ab67 269 }
Jonathan Austin 1:8aa5cdb4ab67 270
Jonathan Austin 1:8aa5cdb4ab67 271 /**
Jonathan Austin 1:8aa5cdb4ab67 272 * Post constructor initialisation method as the BLE stack cannot be brought
Jonathan Austin 1:8aa5cdb4ab67 273 * up in a static context.
Jonathan Austin 1:8aa5cdb4ab67 274 *
Jonathan Austin 1:8aa5cdb4ab67 275 * @param deviceName The name used when advertising
Jonathan Austin 1:8aa5cdb4ab67 276 * @param serialNumber The serial number exposed by the device information service
Jonathan Austin 1:8aa5cdb4ab67 277 * @param messageBus An instance of an EventModel, used during pairing.
Jonathan Austin 1:8aa5cdb4ab67 278 * @param enableBonding If true, the security manager enabled bonding.
Jonathan Austin 1:8aa5cdb4ab67 279 *
Jonathan Austin 1:8aa5cdb4ab67 280 * @code
Jonathan Austin 1:8aa5cdb4ab67 281 * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
Jonathan Austin 1:8aa5cdb4ab67 282 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 283 */
bluetooth_mdw 74:a8f5674a0079 284 void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel &messageBus, bool enableBonding)
Jonathan Austin 1:8aa5cdb4ab67 285 {
bluetooth_mdw 74:a8f5674a0079 286 ManagedString BLEName("BBC micro:bit");
bluetooth_mdw 74:a8f5674a0079 287 this->deviceName = deviceName;
Jonathan Austin 1:8aa5cdb4ab67 288
Jonathan Austin 1:8aa5cdb4ab67 289 #if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
bluetooth_mdw 74:a8f5674a0079 290 ManagedString namePrefix(" [");
bluetooth_mdw 74:a8f5674a0079 291 ManagedString namePostfix("]");
bluetooth_mdw 74:a8f5674a0079 292 BLEName = BLEName + namePrefix + deviceName + namePostfix;
Jonathan Austin 1:8aa5cdb4ab67 293 #endif
Jonathan Austin 1:8aa5cdb4ab67 294
bluetooth_mdw 74:a8f5674a0079 295 // Start the BLE stack.
Jonathan Austin 1:8aa5cdb4ab67 296 #if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
Jonathan Austin 1:8aa5cdb4ab67 297 btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
Jonathan Austin 1:8aa5cdb4ab67 298 #endif
Jonathan Austin 1:8aa5cdb4ab67 299
Jonathan Austin 1:8aa5cdb4ab67 300 ble = new BLEDevice();
Jonathan Austin 1:8aa5cdb4ab67 301 ble->init();
Jonathan Austin 1:8aa5cdb4ab67 302
Jonathan Austin 1:8aa5cdb4ab67 303 // automatically restart advertising after a device disconnects.
Jonathan Austin 1:8aa5cdb4ab67 304 ble->gap().onDisconnection(bleDisconnectionCallback);
Jonathan Austin 1:8aa5cdb4ab67 305 ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback);
LancasterUniversity 26:493daf8966fd 306
LancasterUniversity 24:3373f1fb0353 307 // generate an event when a Bluetooth connection is established
LancasterUniversity 23:6055f6c19fa6 308 ble->gap().onConnection(bleConnectionCallback);
Jonathan Austin 1:8aa5cdb4ab67 309
Jonathan Austin 1:8aa5cdb4ab67 310 // Configure the stack to hold onto the CPU during critical timing events.
Jonathan Austin 1:8aa5cdb4ab67 311 // mbed-classic performs __disable_irq() calls in its timers that can cause
Jonathan Austin 1:8aa5cdb4ab67 312 // MIC failures on secure BLE channels...
Jonathan Austin 1:8aa5cdb4ab67 313 ble_common_opt_radio_cpu_mutex_t opt;
Jonathan Austin 1:8aa5cdb4ab67 314 opt.enable = 1;
Jonathan Austin 1:8aa5cdb4ab67 315 sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
Jonathan Austin 1:8aa5cdb4ab67 316
Jonathan Austin 1:8aa5cdb4ab67 317 #if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
bluetooth_mdw 74:a8f5674a0079 318 // Configure for private addresses, so kids' behaviour can't be easily tracked.
bluetooth_mdw 74:a8f5674a0079 319 ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
Jonathan Austin 1:8aa5cdb4ab67 320 #endif
Jonathan Austin 1:8aa5cdb4ab67 321
Jonathan Austin 1:8aa5cdb4ab67 322 // Setup our security requirements.
Jonathan Austin 1:8aa5cdb4ab67 323 ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
Jonathan Austin 1:8aa5cdb4ab67 324 ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
bluetooth_mdw 74:a8f5674a0079 325 // @bluetooth_mdw: select either passkey pairing (more secure), "just works" pairing (less secure but nice and simple for the user)
bluetooth_mdw 74:a8f5674a0079 326 // or no security
bluetooth_mdw 74:a8f5674a0079 327 // Default to passkey pairing with MITM protection
bluetooth_mdw 74:a8f5674a0079 328 #if (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_NO_MITM))
bluetooth_mdw 74:a8f5674a0079 329 // Just Works
bluetooth_mdw 74:a8f5674a0079 330 ble->securityManager().init(enableBonding, false, SecurityManager::IO_CAPS_NONE);
bluetooth_mdw 74:a8f5674a0079 331 #elif (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_OPEN_LINK))
bluetooth_mdw 74:a8f5674a0079 332 // no security
bluetooth_mdw 74:a8f5674a0079 333 ble->securityManager().init(false, false, SecurityManager::IO_CAPS_DISPLAY_ONLY);
bluetooth_mdw 74:a8f5674a0079 334 #else
bluetooth_mdw 74:a8f5674a0079 335 // passkey
bluetooth_mdw 74:a8f5674a0079 336 ble->securityManager().init(enableBonding, true, SecurityManager::IO_CAPS_DISPLAY_ONLY);
bluetooth_mdw 74:a8f5674a0079 337 #endif
Jonathan Austin 1:8aa5cdb4ab67 338
Jonathan Austin 1:8aa5cdb4ab67 339 if (enableBonding)
Jonathan Austin 1:8aa5cdb4ab67 340 {
Jonathan Austin 1:8aa5cdb4ab67 341 // If we're in pairing mode, review the size of the bond table.
Jonathan Austin 1:8aa5cdb4ab67 342 int bonds = getBondCount();
Jonathan Austin 1:8aa5cdb4ab67 343
Jonathan Austin 1:8aa5cdb4ab67 344 // TODO: It would be much better to implement some sort of LRU/NFU policy here,
Jonathan Austin 1:8aa5cdb4ab67 345 // but this isn't currently supported in mbed, so we'd need to layer break...
Jonathan Austin 1:8aa5cdb4ab67 346
Jonathan Austin 1:8aa5cdb4ab67 347 // If we're full, empty the bond table.
Jonathan Austin 1:8aa5cdb4ab67 348 if (bonds >= MICROBIT_BLE_MAXIMUM_BONDS)
Jonathan Austin 1:8aa5cdb4ab67 349 ble->securityManager().purgeAllBondingState();
Jonathan Austin 1:8aa5cdb4ab67 350 }
Jonathan Austin 1:8aa5cdb4ab67 351
Jonathan Austin 1:8aa5cdb4ab67 352 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
Jonathan Austin 1:8aa5cdb4ab67 353 // Configure a whitelist to filter all connection requetss from unbonded devices.
Jonathan Austin 1:8aa5cdb4ab67 354 // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks.
Jonathan Austin 1:8aa5cdb4ab67 355 BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
Jonathan Austin 1:8aa5cdb4ab67 356 Gap::Whitelist_t whitelist;
Jonathan Austin 1:8aa5cdb4ab67 357 whitelist.addresses = bondedAddresses;
Jonathan Austin 1:8aa5cdb4ab67 358 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
Jonathan Austin 1:8aa5cdb4ab67 359
Jonathan Austin 1:8aa5cdb4ab67 360 ble->securityManager().getAddressesFromBondTable(whitelist);
Jonathan Austin 1:8aa5cdb4ab67 361
Jonathan Austin 1:8aa5cdb4ab67 362 ble->gap().setWhitelist(whitelist);
Jonathan Austin 1:8aa5cdb4ab67 363 ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST);
Jonathan Austin 1:8aa5cdb4ab67 364 ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS);
Jonathan Austin 1:8aa5cdb4ab67 365 #endif
Jonathan Austin 1:8aa5cdb4ab67 366
Jonathan Austin 1:8aa5cdb4ab67 367 // Configure the radio at our default power level
Jonathan Austin 1:8aa5cdb4ab67 368 setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
Jonathan Austin 1:8aa5cdb4ab67 369
bluetooth_mdw 74:a8f5674a0079 370 // Bring up core BLE services.
LancasterUniversity 29:62f8b007debf 371 #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
Jonathan Austin 1:8aa5cdb4ab67 372 new MicroBitDFUService(*ble);
LancasterUniversity 29:62f8b007debf 373 #endif
LancasterUniversity 29:62f8b007debf 374
LancasterUniversity 29:62f8b007debf 375 #if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
bluetooth_mdw 74:a8f5674a0079 376 DeviceInformationService ble_device_information_service(*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, serialNumber.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
LancasterUniversity 66:2fc7d7c2fffc 377 #else
LancasterUniversity 66:2fc7d7c2fffc 378 (void)serialNumber;
LancasterUniversity 29:62f8b007debf 379 #endif
LancasterUniversity 29:62f8b007debf 380
LancasterUniversity 29:62f8b007debf 381 #if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE)
Jonathan Austin 1:8aa5cdb4ab67 382 new MicroBitEventService(*ble, messageBus);
LancasterUniversity 29:62f8b007debf 383 #else
LancasterUniversity 29:62f8b007debf 384 (void)messageBus;
LancasterUniversity 29:62f8b007debf 385 #endif
Jonathan Austin 1:8aa5cdb4ab67 386
Jonathan Austin 1:8aa5cdb4ab67 387 // Configure for high speed mode where possible.
Jonathan Austin 1:8aa5cdb4ab67 388 Gap::ConnectionParams_t fast;
Jonathan Austin 1:8aa5cdb4ab67 389 ble->getPreferredConnectionParams(&fast);
bluetooth_mdw 74:a8f5674a0079 390 fast.minConnectionInterval = 8; // 10 ms
Jonathan Austin 1:8aa5cdb4ab67 391 fast.maxConnectionInterval = 16; // 20 ms
Jonathan Austin 1:8aa5cdb4ab67 392 fast.slaveLatency = 0;
Jonathan Austin 1:8aa5cdb4ab67 393 ble->setPreferredConnectionParams(&fast);
Jonathan Austin 1:8aa5cdb4ab67 394
bluetooth_mdw 74:a8f5674a0079 395 // Setup advertising.
Jonathan Austin 1:8aa5cdb4ab67 396 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
Jonathan Austin 1:8aa5cdb4ab67 397 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
Jonathan Austin 1:8aa5cdb4ab67 398 #else
Jonathan Austin 1:8aa5cdb4ab67 399 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
Jonathan Austin 1:8aa5cdb4ab67 400 #endif
Jonathan Austin 1:8aa5cdb4ab67 401
Jonathan Austin 1:8aa5cdb4ab67 402 ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
Jonathan Austin 1:8aa5cdb4ab67 403 ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
Jonathan Austin 1:8aa5cdb4ab67 404 ble->setAdvertisingInterval(200);
Jonathan Austin 1:8aa5cdb4ab67 405
Jonathan Austin 1:8aa5cdb4ab67 406 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
Jonathan Austin 1:8aa5cdb4ab67 407 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
Jonathan Austin 1:8aa5cdb4ab67 408 #endif
Jonathan Austin 1:8aa5cdb4ab67 409
bluetooth_mdw 74:a8f5674a0079 410 // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
bluetooth_mdw 74:a8f5674a0079 411 // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable.
bluetooth_mdw 74:a8f5674a0079 412 // If whiltelisting is disabled, then we always advertise.
Jonathan Austin 1:8aa5cdb4ab67 413 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
Jonathan Austin 1:8aa5cdb4ab67 414 if (whitelist.size > 0)
Jonathan Austin 1:8aa5cdb4ab67 415 #endif
Jonathan Austin 1:8aa5cdb4ab67 416 ble->startAdvertising();
Jonathan Austin 1:8aa5cdb4ab67 417 }
Jonathan Austin 1:8aa5cdb4ab67 418
Jonathan Austin 1:8aa5cdb4ab67 419 /**
Jonathan Austin 1:8aa5cdb4ab67 420 * Change the output power level of the transmitter to the given value.
Jonathan Austin 1:8aa5cdb4ab67 421 *
Jonathan Austin 1:8aa5cdb4ab67 422 * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
Jonathan Austin 1:8aa5cdb4ab67 423 *
Jonathan Austin 1:8aa5cdb4ab67 424 * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
Jonathan Austin 1:8aa5cdb4ab67 425 *
Jonathan Austin 1:8aa5cdb4ab67 426 * @code
Jonathan Austin 1:8aa5cdb4ab67 427 * // maximum transmission power.
Jonathan Austin 1:8aa5cdb4ab67 428 * bleManager.setTransmitPower(7);
Jonathan Austin 1:8aa5cdb4ab67 429 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 430 */
Jonathan Austin 1:8aa5cdb4ab67 431 int MicroBitBLEManager::setTransmitPower(int power)
Jonathan Austin 1:8aa5cdb4ab67 432 {
Jonathan Austin 1:8aa5cdb4ab67 433 if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS)
Jonathan Austin 1:8aa5cdb4ab67 434 return MICROBIT_INVALID_PARAMETER;
Jonathan Austin 1:8aa5cdb4ab67 435
Jonathan Austin 1:8aa5cdb4ab67 436 if (ble->gap().setTxPower(MICROBIT_BLE_POWER_LEVEL[power]) != NRF_SUCCESS)
Jonathan Austin 1:8aa5cdb4ab67 437 return MICROBIT_NOT_SUPPORTED;
Jonathan Austin 1:8aa5cdb4ab67 438
Jonathan Austin 1:8aa5cdb4ab67 439 return MICROBIT_OK;
Jonathan Austin 1:8aa5cdb4ab67 440 }
Jonathan Austin 1:8aa5cdb4ab67 441
Jonathan Austin 1:8aa5cdb4ab67 442 /**
Jonathan Austin 1:8aa5cdb4ab67 443 * Determines the number of devices currently bonded with this micro:bit.
Jonathan Austin 1:8aa5cdb4ab67 444 * @return The number of active bonds.
Jonathan Austin 1:8aa5cdb4ab67 445 */
Jonathan Austin 1:8aa5cdb4ab67 446 int MicroBitBLEManager::getBondCount()
Jonathan Austin 1:8aa5cdb4ab67 447 {
Jonathan Austin 1:8aa5cdb4ab67 448 BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
Jonathan Austin 1:8aa5cdb4ab67 449 Gap::Whitelist_t whitelist;
Jonathan Austin 1:8aa5cdb4ab67 450 whitelist.addresses = bondedAddresses;
Jonathan Austin 1:8aa5cdb4ab67 451 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
Jonathan Austin 1:8aa5cdb4ab67 452 ble->securityManager().getAddressesFromBondTable(whitelist);
Jonathan Austin 1:8aa5cdb4ab67 453
Jonathan Austin 1:8aa5cdb4ab67 454 return whitelist.bonds;
Jonathan Austin 1:8aa5cdb4ab67 455 }
Jonathan Austin 1:8aa5cdb4ab67 456
Jonathan Austin 1:8aa5cdb4ab67 457 /**
Jonathan Austin 1:8aa5cdb4ab67 458 * A request to pair has been received from a BLE device.
Jonathan Austin 1:8aa5cdb4ab67 459 * If we're in pairing mode, display the passkey to the user.
Jonathan Austin 1:8aa5cdb4ab67 460 * Also, purge the bonding table if it has reached capacity.
Jonathan Austin 1:8aa5cdb4ab67 461 *
Jonathan Austin 1:8aa5cdb4ab67 462 * @note for internal use only.
Jonathan Austin 1:8aa5cdb4ab67 463 */
Jonathan Austin 1:8aa5cdb4ab67 464 void MicroBitBLEManager::pairingRequested(ManagedString passKey)
Jonathan Austin 1:8aa5cdb4ab67 465 {
Jonathan Austin 1:8aa5cdb4ab67 466 // Update our mode to display the passkey.
bluetooth_mdw 74:a8f5674a0079 467 this->passKey = passKey;
bluetooth_mdw 74:a8f5674a0079 468 this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
Jonathan Austin 1:8aa5cdb4ab67 469 }
Jonathan Austin 1:8aa5cdb4ab67 470
Jonathan Austin 1:8aa5cdb4ab67 471 /**
Jonathan Austin 1:8aa5cdb4ab67 472 * A pairing request has been sucessfully completed.
Jonathan Austin 1:8aa5cdb4ab67 473 * If we're in pairing mode, display a success or failure message.
Jonathan Austin 1:8aa5cdb4ab67 474 *
Jonathan Austin 1:8aa5cdb4ab67 475 * @note for internal use only.
Jonathan Austin 1:8aa5cdb4ab67 476 */
Jonathan Austin 1:8aa5cdb4ab67 477 void MicroBitBLEManager::pairingComplete(bool success)
Jonathan Austin 1:8aa5cdb4ab67 478 {
bluetooth_mdw 74:a8f5674a0079 479 this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
bluetooth_mdw 74:a8f5674a0079 480
bluetooth_mdw 74:a8f5674a0079 481 pairing_completed_at_time = system_timer_current_time();
Jonathan Austin 1:8aa5cdb4ab67 482
bluetooth_mdw 74:a8f5674a0079 483 if (success)
Jonathan Austin 1:8aa5cdb4ab67 484 {
bluetooth_mdw 74:a8f5674a0079 485 this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
Jonathan Austin 1:8aa5cdb4ab67 486 fiber_add_idle_component(this);
Jonathan Austin 1:8aa5cdb4ab67 487 }
Jonathan Austin 1:8aa5cdb4ab67 488 }
Jonathan Austin 1:8aa5cdb4ab67 489
Jonathan Austin 1:8aa5cdb4ab67 490 /**
Jonathan Austin 1:8aa5cdb4ab67 491 * Periodic callback in thread context.
Jonathan Austin 1:8aa5cdb4ab67 492 * We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
Jonathan Austin 1:8aa5cdb4ab67 493 */
Jonathan Austin 1:8aa5cdb4ab67 494 void MicroBitBLEManager::idleTick()
Jonathan Austin 1:8aa5cdb4ab67 495 {
bluetooth_mdw 74:a8f5674a0079 496 if((system_timer_current_time() - pairing_completed_at_time) >= MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY) {
bluetooth_mdw 74:a8f5674a0079 497 if (ble)
bluetooth_mdw 74:a8f5674a0079 498 ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
bluetooth_mdw 74:a8f5674a0079 499 fiber_remove_idle_component(this);
bluetooth_mdw 74:a8f5674a0079 500 }
bluetooth_mdw 74:a8f5674a0079 501
bluetooth_mdw 74:a8f5674a0079 502 }
bluetooth_mdw 74:a8f5674a0079 503
bluetooth_mdw 74:a8f5674a0079 504
bluetooth_mdw 74:a8f5674a0079 505 /**
bluetooth_mdw 74:a8f5674a0079 506 * Stops any currently running BLE advertisements
bluetooth_mdw 74:a8f5674a0079 507 */
bluetooth_mdw 74:a8f5674a0079 508 void MicroBitBLEManager::stopAdvertising()
bluetooth_mdw 74:a8f5674a0079 509 {
bluetooth_mdw 74:a8f5674a0079 510 ble->gap().stopAdvertising();
bluetooth_mdw 74:a8f5674a0079 511 }
bluetooth_mdw 74:a8f5674a0079 512
bluetooth_mdw 74:a8f5674a0079 513 #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
bluetooth_mdw 74:a8f5674a0079 514 /**
bluetooth_mdw 74:a8f5674a0079 515 * Set the content of Eddystone URL frames
bluetooth_mdw 74:a8f5674a0079 516 *
bluetooth_mdw 74:a8f5674a0079 517 * @param url The url to broadcast
bluetooth_mdw 74:a8f5674a0079 518 *
bluetooth_mdw 74:a8f5674a0079 519 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
bluetooth_mdw 74:a8f5674a0079 520 *
bluetooth_mdw 74:a8f5674a0079 521 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
bluetooth_mdw 74:a8f5674a0079 522 *
bluetooth_mdw 74:a8f5674a0079 523 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
bluetooth_mdw 74:a8f5674a0079 524 *
bluetooth_mdw 74:a8f5674a0079 525 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
bluetooth_mdw 74:a8f5674a0079 526 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
bluetooth_mdw 74:a8f5674a0079 527 */
bluetooth_mdw 74:a8f5674a0079 528 int MicroBitBLEManager::advertiseEddystoneUrl(const char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
bluetooth_mdw 74:a8f5674a0079 529 {
bluetooth_mdw 74:a8f5674a0079 530 ble->gap().stopAdvertising();
bluetooth_mdw 74:a8f5674a0079 531 ble->clearAdvertisingPayload();
bluetooth_mdw 74:a8f5674a0079 532
bluetooth_mdw 74:a8f5674a0079 533 ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
bluetooth_mdw 74:a8f5674a0079 534 ble->setAdvertisingInterval(interval);
bluetooth_mdw 74:a8f5674a0079 535
bluetooth_mdw 74:a8f5674a0079 536 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
bluetooth_mdw 74:a8f5674a0079 537
bluetooth_mdw 74:a8f5674a0079 538 int ret = MicroBitEddystone::getInstance()->setURL(ble, url, calibratedPower);
bluetooth_mdw 74:a8f5674a0079 539
bluetooth_mdw 74:a8f5674a0079 540 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
bluetooth_mdw 74:a8f5674a0079 541 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
bluetooth_mdw 74:a8f5674a0079 542 #endif
bluetooth_mdw 74:a8f5674a0079 543 ble->gap().startAdvertising();
bluetooth_mdw 74:a8f5674a0079 544
bluetooth_mdw 74:a8f5674a0079 545 return ret;
bluetooth_mdw 74:a8f5674a0079 546 }
Jonathan Austin 1:8aa5cdb4ab67 547
bluetooth_mdw 74:a8f5674a0079 548 /**
bluetooth_mdw 74:a8f5674a0079 549 * Set the content of Eddystone URL frames, but accepts a ManagedString as a url.
bluetooth_mdw 74:a8f5674a0079 550 *
bluetooth_mdw 74:a8f5674a0079 551 * @param url The url to broadcast
bluetooth_mdw 74:a8f5674a0079 552 *
bluetooth_mdw 74:a8f5674a0079 553 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
bluetooth_mdw 74:a8f5674a0079 554 *
bluetooth_mdw 74:a8f5674a0079 555 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
bluetooth_mdw 74:a8f5674a0079 556 *
bluetooth_mdw 74:a8f5674a0079 557 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
bluetooth_mdw 74:a8f5674a0079 558 *
bluetooth_mdw 74:a8f5674a0079 559 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
bluetooth_mdw 74:a8f5674a0079 560 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
bluetooth_mdw 74:a8f5674a0079 561 */
bluetooth_mdw 74:a8f5674a0079 562 int MicroBitBLEManager::advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval)
bluetooth_mdw 74:a8f5674a0079 563 {
bluetooth_mdw 74:a8f5674a0079 564 return advertiseEddystoneUrl((char *)url.toCharArray(), calibratedPower, connectable, interval);
Jonathan Austin 1:8aa5cdb4ab67 565 }
bluetooth_mdw 74:a8f5674a0079 566 #endif
bluetooth_mdw 74:a8f5674a0079 567
bluetooth_mdw 74:a8f5674a0079 568 #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
bluetooth_mdw 74:a8f5674a0079 569 /**
bluetooth_mdw 74:a8f5674a0079 570 * Set the content of Eddystone UID frames
bluetooth_mdw 74:a8f5674a0079 571 *
bluetooth_mdw 74:a8f5674a0079 572 * @param uid_namespace: the uid namespace. Must 10 bytes long.
bluetooth_mdw 74:a8f5674a0079 573 *
bluetooth_mdw 74:a8f5674a0079 574 * @param uid_instance: the uid instance value. Must 6 bytes long.
bluetooth_mdw 74:a8f5674a0079 575 *
bluetooth_mdw 74:a8f5674a0079 576 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
bluetooth_mdw 74:a8f5674a0079 577 *
bluetooth_mdw 74:a8f5674a0079 578 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
bluetooth_mdw 74:a8f5674a0079 579 *
bluetooth_mdw 74:a8f5674a0079 580 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
bluetooth_mdw 74:a8f5674a0079 581 *
bluetooth_mdw 74:a8f5674a0079 582 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
bluetooth_mdw 74:a8f5674a0079 583 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
bluetooth_mdw 74:a8f5674a0079 584 */
bluetooth_mdw 74:a8f5674a0079 585 int MicroBitBLEManager::advertiseEddystoneUid(const char* uid_namespace, const char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
bluetooth_mdw 74:a8f5674a0079 586 {
bluetooth_mdw 74:a8f5674a0079 587 ble->gap().stopAdvertising();
bluetooth_mdw 74:a8f5674a0079 588 ble->clearAdvertisingPayload();
bluetooth_mdw 74:a8f5674a0079 589
bluetooth_mdw 74:a8f5674a0079 590 ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
bluetooth_mdw 74:a8f5674a0079 591 ble->setAdvertisingInterval(interval);
bluetooth_mdw 74:a8f5674a0079 592
bluetooth_mdw 74:a8f5674a0079 593 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
bluetooth_mdw 74:a8f5674a0079 594
bluetooth_mdw 74:a8f5674a0079 595 int ret = MicroBitEddystone::getInstance()->setUID(ble, uid_namespace, uid_instance, calibratedPower);
bluetooth_mdw 74:a8f5674a0079 596
bluetooth_mdw 74:a8f5674a0079 597 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
bluetooth_mdw 74:a8f5674a0079 598 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
bluetooth_mdw 74:a8f5674a0079 599 #endif
bluetooth_mdw 74:a8f5674a0079 600 ble->gap().startAdvertising();
bluetooth_mdw 74:a8f5674a0079 601
bluetooth_mdw 74:a8f5674a0079 602 return ret;
bluetooth_mdw 74:a8f5674a0079 603 }
bluetooth_mdw 74:a8f5674a0079 604 #endif
Jonathan Austin 1:8aa5cdb4ab67 605
Jonathan Austin 1:8aa5cdb4ab67 606 /**
Jonathan Austin 1:8aa5cdb4ab67 607 * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
Jonathan Austin 1:8aa5cdb4ab67 608 * of the micro:bit in cases where BLE is disabled during normal operation.
Jonathan Austin 1:8aa5cdb4ab67 609 *
Jonathan Austin 1:8aa5cdb4ab67 610 * @param display An instance of MicroBitDisplay used when displaying pairing information.
Jonathan Austin 1:8aa5cdb4ab67 611 * @param authorizationButton The button to use to authorise a pairing request.
Jonathan Austin 1:8aa5cdb4ab67 612 *
Jonathan Austin 1:8aa5cdb4ab67 613 * @code
Jonathan Austin 1:8aa5cdb4ab67 614 * // initiate pairing mode
Jonathan Austin 1:8aa5cdb4ab67 615 * bleManager.pairingMode(uBit.display, uBit.buttonA);
Jonathan Austin 1:8aa5cdb4ab67 616 * @endcode
Jonathan Austin 1:8aa5cdb4ab67 617 */
bluetooth_mdw 74:a8f5674a0079 618 void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &authorisationButton)
Jonathan Austin 1:8aa5cdb4ab67 619 {
bluetooth_mdw 74:a8f5674a0079 620 ManagedString namePrefix("BBC micro:bit [");
bluetooth_mdw 74:a8f5674a0079 621 ManagedString namePostfix("]");
bluetooth_mdw 74:a8f5674a0079 622 ManagedString BLEName = namePrefix + deviceName + namePostfix;
Jonathan Austin 1:8aa5cdb4ab67 623
bluetooth_mdw 74:a8f5674a0079 624 ManagedString msg("PAIRING MODE!");
Jonathan Austin 1:8aa5cdb4ab67 625
bluetooth_mdw 74:a8f5674a0079 626 int timeInPairingMode = 0;
bluetooth_mdw 74:a8f5674a0079 627 int brightness = 255;
bluetooth_mdw 74:a8f5674a0079 628 int fadeDirection = 0;
Jonathan Austin 1:8aa5cdb4ab67 629
Jonathan Austin 1:8aa5cdb4ab67 630 ble->gap().stopAdvertising();
Jonathan Austin 1:8aa5cdb4ab67 631
bluetooth_mdw 74:a8f5674a0079 632 // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices.
Jonathan Austin 1:8aa5cdb4ab67 633 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
Jonathan Austin 1:8aa5cdb4ab67 634 BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
Jonathan Austin 1:8aa5cdb4ab67 635 Gap::Whitelist_t whitelist;
Jonathan Austin 1:8aa5cdb4ab67 636 whitelist.addresses = addresses;
Jonathan Austin 1:8aa5cdb4ab67 637 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
Jonathan Austin 1:8aa5cdb4ab67 638 whitelist.size = 0;
Jonathan Austin 1:8aa5cdb4ab67 639 ble->gap().setWhitelist(whitelist);
Jonathan Austin 1:8aa5cdb4ab67 640 ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
Jonathan Austin 1:8aa5cdb4ab67 641 #endif
Jonathan Austin 1:8aa5cdb4ab67 642
bluetooth_mdw 74:a8f5674a0079 643 // Update the advertised name of this micro:bit to include the device name
Jonathan Austin 1:8aa5cdb4ab67 644 ble->clearAdvertisingPayload();
Jonathan Austin 1:8aa5cdb4ab67 645
Jonathan Austin 1:8aa5cdb4ab67 646 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
Jonathan Austin 1:8aa5cdb4ab67 647 ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
Jonathan Austin 1:8aa5cdb4ab67 648 ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
Jonathan Austin 1:8aa5cdb4ab67 649 ble->setAdvertisingInterval(200);
Jonathan Austin 1:8aa5cdb4ab67 650
Jonathan Austin 1:8aa5cdb4ab67 651 ble->gap().setAdvertisingTimeout(0);
Jonathan Austin 1:8aa5cdb4ab67 652 ble->gap().startAdvertising();
Jonathan Austin 1:8aa5cdb4ab67 653
bluetooth_mdw 74:a8f5674a0079 654 // Stop any running animations on the display
bluetooth_mdw 74:a8f5674a0079 655 display.stopAnimation();
bluetooth_mdw 74:a8f5674a0079 656 display.scroll(msg);
Jonathan Austin 1:8aa5cdb4ab67 657
bluetooth_mdw 74:a8f5674a0079 658 // Display our name, visualised as a histogram in the display to aid identification.
bluetooth_mdw 74:a8f5674a0079 659 showNameHistogram(display);
Jonathan Austin 1:8aa5cdb4ab67 660
bluetooth_mdw 74:a8f5674a0079 661 while (1)
bluetooth_mdw 74:a8f5674a0079 662 {
bluetooth_mdw 74:a8f5674a0079 663 if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
bluetooth_mdw 74:a8f5674a0079 664 {
bluetooth_mdw 74:a8f5674a0079 665 timeInPairingMode = 0;
bluetooth_mdw 74:a8f5674a0079 666 MicroBitImage arrow("0,0,255,0,0\n0,255,0,0,0\n255,255,255,255,255\n0,255,0,0,0\n0,0,255,0,0\n");
bluetooth_mdw 74:a8f5674a0079 667 display.print(arrow, 0, 0, 0);
Jonathan Austin 1:8aa5cdb4ab67 668
bluetooth_mdw 74:a8f5674a0079 669 if (fadeDirection == 0)
bluetooth_mdw 74:a8f5674a0079 670 brightness -= MICROBIT_PAIRING_FADE_SPEED;
bluetooth_mdw 74:a8f5674a0079 671 else
bluetooth_mdw 74:a8f5674a0079 672 brightness += MICROBIT_PAIRING_FADE_SPEED;
Jonathan Austin 1:8aa5cdb4ab67 673
bluetooth_mdw 74:a8f5674a0079 674 if (brightness <= 40)
bluetooth_mdw 74:a8f5674a0079 675 display.clear();
Jonathan Austin 1:8aa5cdb4ab67 676
bluetooth_mdw 74:a8f5674a0079 677 if (brightness <= 0)
bluetooth_mdw 74:a8f5674a0079 678 fadeDirection = 1;
Jonathan Austin 1:8aa5cdb4ab67 679
bluetooth_mdw 74:a8f5674a0079 680 if (brightness >= 255)
bluetooth_mdw 74:a8f5674a0079 681 fadeDirection = 0;
Jonathan Austin 1:8aa5cdb4ab67 682
bluetooth_mdw 74:a8f5674a0079 683 if (authorisationButton.isPressed())
bluetooth_mdw 74:a8f5674a0079 684 {
bluetooth_mdw 74:a8f5674a0079 685 pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
bluetooth_mdw 74:a8f5674a0079 686 pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
bluetooth_mdw 74:a8f5674a0079 687 }
bluetooth_mdw 74:a8f5674a0079 688 }
Jonathan Austin 1:8aa5cdb4ab67 689
bluetooth_mdw 74:a8f5674a0079 690 if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
bluetooth_mdw 74:a8f5674a0079 691 {
bluetooth_mdw 74:a8f5674a0079 692 timeInPairingMode = 0;
bluetooth_mdw 74:a8f5674a0079 693 display.setBrightness(255);
bluetooth_mdw 74:a8f5674a0079 694 for (int i = 0; i < passKey.length(); i++)
bluetooth_mdw 74:a8f5674a0079 695 {
bluetooth_mdw 74:a8f5674a0079 696 display.image.print(passKey.charAt(i), 0, 0);
bluetooth_mdw 74:a8f5674a0079 697 fiber_sleep(800);
bluetooth_mdw 74:a8f5674a0079 698 display.clear();
bluetooth_mdw 74:a8f5674a0079 699 fiber_sleep(200);
Jonathan Austin 1:8aa5cdb4ab67 700
bluetooth_mdw 74:a8f5674a0079 701 if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
bluetooth_mdw 74:a8f5674a0079 702 break;
bluetooth_mdw 74:a8f5674a0079 703 }
Jonathan Austin 1:8aa5cdb4ab67 704
bluetooth_mdw 74:a8f5674a0079 705 fiber_sleep(1000);
bluetooth_mdw 74:a8f5674a0079 706 }
Jonathan Austin 1:8aa5cdb4ab67 707
bluetooth_mdw 74:a8f5674a0079 708 if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
bluetooth_mdw 74:a8f5674a0079 709 {
bluetooth_mdw 74:a8f5674a0079 710 if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
bluetooth_mdw 74:a8f5674a0079 711 {
bluetooth_mdw 74:a8f5674a0079 712 MicroBitImage tick("0,0,0,0,0\n0,0,0,0,255\n0,0,0,255,0\n255,0,255,0,0\n0,255,0,0,0\n");
bluetooth_mdw 74:a8f5674a0079 713 display.print(tick, 0, 0, 0);
Jonathan Austin 1:8aa5cdb4ab67 714 fiber_sleep(15000);
bluetooth_mdw 74:a8f5674a0079 715 timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30;
Jonathan Austin 1:8aa5cdb4ab67 716
Jonathan Austin 1:8aa5cdb4ab67 717 /*
Jonathan Austin 1:8aa5cdb4ab67 718 * Disabled, as the API to return the number of active bonds is not reliable at present...
Jonathan Austin 1:8aa5cdb4ab67 719 *
Jonathan Austin 1:8aa5cdb4ab67 720 display.clear();
Jonathan Austin 1:8aa5cdb4ab67 721 ManagedString c(getBondCount());
Jonathan Austin 1:8aa5cdb4ab67 722 ManagedString c2("/");
Jonathan Austin 1:8aa5cdb4ab67 723 ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS);
Jonathan Austin 1:8aa5cdb4ab67 724 ManagedString c4("USED");
Jonathan Austin 1:8aa5cdb4ab67 725
Jonathan Austin 1:8aa5cdb4ab67 726 display.scroll(c+c2+c3+c4);
Jonathan Austin 1:8aa5cdb4ab67 727 *
Jonathan Austin 1:8aa5cdb4ab67 728 *
Jonathan Austin 1:8aa5cdb4ab67 729 */
bluetooth_mdw 74:a8f5674a0079 730 }
bluetooth_mdw 74:a8f5674a0079 731 else
bluetooth_mdw 74:a8f5674a0079 732 {
bluetooth_mdw 74:a8f5674a0079 733 MicroBitImage cross("255,0,0,0,255\n0,255,0,255,0\n0,0,255,0,0\n0,255,0,255,0\n255,0,0,0,255\n");
bluetooth_mdw 74:a8f5674a0079 734 display.print(cross, 0, 0, 0);
bluetooth_mdw 74:a8f5674a0079 735 }
bluetooth_mdw 74:a8f5674a0079 736 }
Jonathan Austin 1:8aa5cdb4ab67 737
bluetooth_mdw 74:a8f5674a0079 738 fiber_sleep(100);
bluetooth_mdw 74:a8f5674a0079 739 timeInPairingMode++;
Jonathan Austin 1:8aa5cdb4ab67 740
bluetooth_mdw 74:a8f5674a0079 741 if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
bluetooth_mdw 74:a8f5674a0079 742 microbit_reset();
bluetooth_mdw 74:a8f5674a0079 743 }
Jonathan Austin 1:8aa5cdb4ab67 744 }
Jonathan Austin 1:8aa5cdb4ab67 745
Jonathan Austin 1:8aa5cdb4ab67 746 /**
Jonathan Austin 1:8aa5cdb4ab67 747 * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
Jonathan Austin 1:8aa5cdb4ab67 748 *
Jonathan Austin 1:8aa5cdb4ab67 749 * @param display The display instance used for displaying the histogram.
Jonathan Austin 1:8aa5cdb4ab67 750 */
Jonathan Austin 1:8aa5cdb4ab67 751 void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
Jonathan Austin 1:8aa5cdb4ab67 752 {
Jonathan Austin 1:8aa5cdb4ab67 753 uint32_t n = NRF_FICR->DEVICEID[1];
Jonathan Austin 1:8aa5cdb4ab67 754 int ld = 1;
Jonathan Austin 1:8aa5cdb4ab67 755 int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
Jonathan Austin 1:8aa5cdb4ab67 756 int h;
Jonathan Austin 1:8aa5cdb4ab67 757
Jonathan Austin 1:8aa5cdb4ab67 758 display.clear();
bluetooth_mdw 74:a8f5674a0079 759 for (int i = 0; i < MICROBIT_DFU_HISTOGRAM_WIDTH; i++)
Jonathan Austin 1:8aa5cdb4ab67 760 {
Jonathan Austin 1:8aa5cdb4ab67 761 h = (n % d) / ld;
Jonathan Austin 1:8aa5cdb4ab67 762
Jonathan Austin 1:8aa5cdb4ab67 763 n -= h;
Jonathan Austin 1:8aa5cdb4ab67 764 d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
Jonathan Austin 1:8aa5cdb4ab67 765 ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
Jonathan Austin 1:8aa5cdb4ab67 766
bluetooth_mdw 74:a8f5674a0079 767 for (int j = 0; j < h + 1; j++)
bluetooth_mdw 74:a8f5674a0079 768 display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1, MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1, 255);
Jonathan Austin 1:8aa5cdb4ab67 769 }
bluetooth_mdw 74:a8f5674a0079 770 }