Eddystone test using modified DAL
Dependencies: BLE_API mbed-dev-bin nRF51822
Dependents: microbit-eddystone
Fork of microbit-dal by
MicroBitBLEManager.cpp
00001 /* 00002 The MIT License (MIT) 00003 00004 Copyright (c) 2016 British Broadcasting Corporation. 00005 This software is provided by Lancaster University by arrangement with the BBC. 00006 00007 Permission is hereby granted, free of charge, to any person obtaining a 00008 copy of this software and associated documentation files (the "Software"), 00009 to deal in the Software without restriction, including without limitation 00010 the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 and/or sell copies of the Software, and to permit persons to whom the 00012 Software is furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00023 DEALINGS IN THE SOFTWARE. 00024 */ 00025 00026 #include "MicroBitConfig.h" 00027 #include "MicroBitBLEManager.h" 00028 #include "MicroBitEddystone.h" 00029 #include "MicroBitStorage.h" 00030 #include "MicroBitFiber.h" 00031 #include "MicroBitSystemTimer.h" 00032 00033 /* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ. 00034 * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL) 00035 * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this 00036 * as a compatability option, but does not support the options used... 00037 */ 00038 #if !defined(__arm) 00039 #pragma GCC diagnostic ignored "-Wunused-function" 00040 #pragma GCC diagnostic push 00041 #pragma GCC diagnostic ignored "-Wunused-parameter" 00042 #endif 00043 00044 #include "ble.h" 00045 00046 extern "C" { 00047 #include "device_manager.h" 00048 uint32_t btle_set_gatt_table_size(uint32_t size); 00049 } 00050 00051 /* 00052 * Return to our predefined compiler settings. 00053 */ 00054 #if !defined(__arm) 00055 #pragma GCC diagnostic pop 00056 #endif 00057 00058 #define MICROBIT_PAIRING_FADE_SPEED 4 00059 00060 // 00061 // Local enumeration of valid security modes. Used only to optimise pre‐processor comparisons. 00062 // 00063 #define __SECURITY_MODE_ENCRYPTION_OPEN_LINK 0 00064 #define __SECURITY_MODE_ENCRYPTION_NO_MITM 1 00065 #define __SECURITY_MODE_ENCRYPTION_WITH_MITM 2 00066 // 00067 // Some Black Magic to compare the definition of our security mode in MicroBitConfig with a given parameter. 00068 // Required as the MicroBitConfig option is actually an mbed enum, that is not normally comparable at compile time. 00069 // 00070 00071 #define __CAT(a, ...) a##__VA_ARGS__ 00072 #define SECURITY_MODE(x) __CAT(__, x) 00073 #define SECURITY_MODE_IS(x) (SECURITY_MODE(MICROBIT_BLE_SECURITY_LEVEL) == SECURITY_MODE(x)) 00074 00075 const char *MICROBIT_BLE_MANUFACTURER = NULL; 00076 const char *MICROBIT_BLE_MODEL = "BBC micro:bit"; 00077 const char *MICROBIT_BLE_HARDWARE_VERSION = NULL; 00078 const char *MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION; 00079 const char *MICROBIT_BLE_SOFTWARE_VERSION = NULL; 00080 const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4}; 00081 00082 /* 00083 * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods. 00084 * So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit 00085 * whilst keeping the code modular. 00086 */ 00087 MicroBitBLEManager *MicroBitBLEManager::manager = NULL; // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-( 00088 00089 static uint8_t deviceID = 255; // Unique ID for the peer that has connected to us. 00090 static Gap::Handle_t pairingHandle = 0; // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly. 00091 00092 static void storeSystemAttributes(Gap::Handle_t handle) 00093 { 00094 if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS) 00095 { 00096 ManagedString key("bleSysAttrs"); 00097 00098 KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key); 00099 00100 BLESysAttribute attrib; 00101 BLESysAttributeStore attribStore; 00102 00103 uint16_t len = sizeof(attrib.sys_attr); 00104 00105 sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS); 00106 00107 //copy our stored sysAttrs 00108 if (bleSysAttrs != NULL) 00109 { 00110 memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore)); 00111 delete bleSysAttrs; 00112 } 00113 00114 //check if we need to update 00115 if (memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0) 00116 { 00117 attribStore.sys_attrs[deviceID] = attrib; 00118 MicroBitBLEManager::manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore)); 00119 } 00120 } 00121 } 00122 00123 /** 00124 * Callback when a BLE GATT disconnect occurs. 00125 */ 00126 static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason) 00127 { 00128 MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED); 00129 00130 storeSystemAttributes(reason->handle); 00131 00132 if (MicroBitBLEManager::manager) 00133 MicroBitBLEManager::manager->advertise(); 00134 } 00135 00136 /** 00137 * Callback when a BLE connection is established. 00138 */ 00139 static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t *) 00140 { 00141 MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED); 00142 } 00143 00144 /** 00145 * Callback when a BLE SYS_ATTR_MISSING. 00146 */ 00147 static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *params) 00148 { 00149 int complete = 0; 00150 deviceID = 255; 00151 00152 dm_handle_t dm_handle = {0, 0, 0, 0}; 00153 00154 int ret = dm_handle_get(params->connHandle, &dm_handle); 00155 00156 if (ret == 0) 00157 deviceID = dm_handle.device_id; 00158 00159 if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS) 00160 { 00161 ManagedString key("bleSysAttrs"); 00162 00163 KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key); 00164 00165 BLESysAttributeStore attribStore; 00166 BLESysAttribute attrib; 00167 00168 if (bleSysAttrs != NULL) 00169 { 00170 //restore our sysAttrStore 00171 memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore)); 00172 delete bleSysAttrs; 00173 00174 attrib = attribStore.sys_attrs[deviceID]; 00175 00176 ret = sd_ble_gatts_sys_attr_set(params->connHandle, attrib.sys_attr, sizeof(attrib.sys_attr), BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS); 00177 00178 complete = 1; 00179 00180 if (ret == 0) 00181 ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff); 00182 } 00183 } 00184 00185 if (!complete) 00186 sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0); 00187 } 00188 00189 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) 00190 { 00191 (void)handle; /* -Wunused-param */ 00192 00193 ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN); 00194 00195 if (MicroBitBLEManager::manager) 00196 MicroBitBLEManager::manager->pairingRequested(passKey); 00197 } 00198 00199 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) 00200 { 00201 (void)handle; /* -Wunused-param */ 00202 00203 dm_handle_t dm_handle = {0, 0, 0, 0}; 00204 int ret = dm_handle_get(handle, &dm_handle); 00205 00206 if (ret == 0) 00207 deviceID = dm_handle.device_id; 00208 00209 if (MicroBitBLEManager::manager) 00210 { 00211 pairingHandle = handle; 00212 MicroBitBLEManager::manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS); 00213 } 00214 } 00215 00216 /** 00217 * Constructor. 00218 * 00219 * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack. 00220 * 00221 * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS). 00222 * 00223 * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself). 00224 * Hence, the init() member function should be used to initialise the BLE stack. 00225 */ 00226 MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage &_storage) : storage(&_storage) 00227 { 00228 manager = this; 00229 this->ble = NULL; 00230 this->pairingStatus = 0; 00231 } 00232 00233 /** 00234 * Constructor. 00235 * 00236 * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack. 00237 * 00238 * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself). 00239 * Hence, the init() member function should be used to initialise the BLE stack. 00240 */ 00241 MicroBitBLEManager::MicroBitBLEManager() : storage(NULL) 00242 { 00243 manager = this; 00244 this->ble = NULL; 00245 this->pairingStatus = 0; 00246 } 00247 00248 /** 00249 * When called, the micro:bit will begin advertising for a predefined period, 00250 * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices. 00251 */ 00252 MicroBitBLEManager *MicroBitBLEManager::getInstance() 00253 { 00254 if (manager == 0) 00255 { 00256 manager = new MicroBitBLEManager; 00257 } 00258 return manager; 00259 } 00260 00261 /** 00262 * When called, the micro:bit will begin advertising for a predefined period, 00263 * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices. 00264 */ 00265 void MicroBitBLEManager::advertise() 00266 { 00267 if (ble) 00268 ble->gap().startAdvertising(); 00269 } 00270 00271 /** 00272 * Post constructor initialisation method as the BLE stack cannot be brought 00273 * up in a static context. 00274 * 00275 * @param deviceName The name used when advertising 00276 * @param serialNumber The serial number exposed by the device information service 00277 * @param messageBus An instance of an EventModel, used during pairing. 00278 * @param enableBonding If true, the security manager enabled bonding. 00279 * 00280 * @code 00281 * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true); 00282 * @endcode 00283 */ 00284 void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel &messageBus, bool enableBonding) 00285 { 00286 ManagedString BLEName("BBC micro:bit"); 00287 this->deviceName = deviceName; 00288 00289 #if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)) 00290 ManagedString namePrefix(" ["); 00291 ManagedString namePostfix("]"); 00292 BLEName = BLEName + namePrefix + deviceName + namePostfix; 00293 #endif 00294 00295 // Start the BLE stack. 00296 #if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD) 00297 btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE); 00298 #endif 00299 00300 ble = new BLEDevice(); 00301 ble->init(); 00302 00303 // automatically restart advertising after a device disconnects. 00304 ble->gap().onDisconnection(bleDisconnectionCallback); 00305 ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback); 00306 00307 // generate an event when a Bluetooth connection is established 00308 ble->gap().onConnection(bleConnectionCallback); 00309 00310 // Configure the stack to hold onto the CPU during critical timing events. 00311 // mbed-classic performs __disable_irq() calls in its timers that can cause 00312 // MIC failures on secure BLE channels... 00313 ble_common_opt_radio_cpu_mutex_t opt; 00314 opt.enable = 1; 00315 sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt); 00316 00317 #if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES) 00318 // Configure for private addresses, so kids' behaviour can't be easily tracked. 00319 ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0}); 00320 #endif 00321 00322 // Setup our security requirements. 00323 ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback); 00324 ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); 00325 // @bluetooth_mdw: select either passkey pairing (more secure), "just works" pairing (less secure but nice and simple for the user) 00326 // or no security 00327 // Default to passkey pairing with MITM protection 00328 #if (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_NO_MITM)) 00329 // Just Works 00330 ble->securityManager().init(enableBonding, false, SecurityManager::IO_CAPS_NONE); 00331 #elif (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_OPEN_LINK)) 00332 // no security 00333 ble->securityManager().init(false, false, SecurityManager::IO_CAPS_DISPLAY_ONLY); 00334 #else 00335 // passkey 00336 ble->securityManager().init(enableBonding, true, SecurityManager::IO_CAPS_DISPLAY_ONLY); 00337 #endif 00338 00339 if (enableBonding) 00340 { 00341 // If we're in pairing mode, review the size of the bond table. 00342 int bonds = getBondCount(); 00343 00344 // TODO: It would be much better to implement some sort of LRU/NFU policy here, 00345 // but this isn't currently supported in mbed, so we'd need to layer break... 00346 00347 // If we're full, empty the bond table. 00348 if (bonds >= MICROBIT_BLE_MAXIMUM_BONDS) 00349 ble->securityManager().purgeAllBondingState(); 00350 } 00351 00352 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) 00353 // Configure a whitelist to filter all connection requetss from unbonded devices. 00354 // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks. 00355 BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS]; 00356 Gap::Whitelist_t whitelist; 00357 whitelist.addresses = bondedAddresses; 00358 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; 00359 00360 ble->securityManager().getAddressesFromBondTable(whitelist); 00361 00362 ble->gap().setWhitelist(whitelist); 00363 ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST); 00364 ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS); 00365 #endif 00366 00367 // Configure the radio at our default power level 00368 setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER); 00369 00370 // Bring up core BLE services. 00371 #if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE) 00372 new MicroBitDFUService(*ble); 00373 #endif 00374 00375 #if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE) 00376 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); 00377 #else 00378 (void)serialNumber; 00379 #endif 00380 00381 #if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE) 00382 new MicroBitEventService(*ble, messageBus); 00383 #else 00384 (void)messageBus; 00385 #endif 00386 00387 // Configure for high speed mode where possible. 00388 Gap::ConnectionParams_t fast; 00389 ble->getPreferredConnectionParams(&fast); 00390 fast.minConnectionInterval = 8; // 10 ms 00391 fast.maxConnectionInterval = 16; // 20 ms 00392 fast.slaveLatency = 0; 00393 ble->setPreferredConnectionParams(&fast); 00394 00395 // Setup advertising. 00396 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) 00397 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED); 00398 #else 00399 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00400 #endif 00401 00402 ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); 00403 ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); 00404 ble->setAdvertisingInterval(200); 00405 00406 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0) 00407 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT); 00408 #endif 00409 00410 // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices... 00411 // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable. 00412 // If whiltelisting is disabled, then we always advertise. 00413 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) 00414 if (whitelist.size > 0) 00415 #endif 00416 ble->startAdvertising(); 00417 } 00418 00419 /** 00420 * Change the output power level of the transmitter to the given value. 00421 * 00422 * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest. 00423 * 00424 * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range. 00425 * 00426 * @code 00427 * // maximum transmission power. 00428 * bleManager.setTransmitPower(7); 00429 * @endcode 00430 */ 00431 int MicroBitBLEManager::setTransmitPower(int power) 00432 { 00433 if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS) 00434 return MICROBIT_INVALID_PARAMETER; 00435 00436 if (ble->gap().setTxPower(MICROBIT_BLE_POWER_LEVEL[power]) != NRF_SUCCESS) 00437 return MICROBIT_NOT_SUPPORTED; 00438 00439 return MICROBIT_OK; 00440 } 00441 00442 /** 00443 * Determines the number of devices currently bonded with this micro:bit. 00444 * @return The number of active bonds. 00445 */ 00446 int MicroBitBLEManager::getBondCount() 00447 { 00448 BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS]; 00449 Gap::Whitelist_t whitelist; 00450 whitelist.addresses = bondedAddresses; 00451 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; 00452 ble->securityManager().getAddressesFromBondTable(whitelist); 00453 00454 return whitelist.bonds; 00455 } 00456 00457 /** 00458 * A request to pair has been received from a BLE device. 00459 * If we're in pairing mode, display the passkey to the user. 00460 * Also, purge the bonding table if it has reached capacity. 00461 * 00462 * @note for internal use only. 00463 */ 00464 void MicroBitBLEManager::pairingRequested(ManagedString passKey) 00465 { 00466 // Update our mode to display the passkey. 00467 this->passKey = passKey; 00468 this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST; 00469 } 00470 00471 /** 00472 * A pairing request has been sucessfully completed. 00473 * If we're in pairing mode, display a success or failure message. 00474 * 00475 * @note for internal use only. 00476 */ 00477 void MicroBitBLEManager::pairingComplete(bool success) 00478 { 00479 this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE; 00480 00481 pairing_completed_at_time = system_timer_current_time(); 00482 00483 if (success) 00484 { 00485 this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL; 00486 fiber_add_idle_component(this); 00487 } 00488 } 00489 00490 /** 00491 * Periodic callback in thread context. 00492 * We use this here purely to safely issue a disconnect operation after a pairing operation is complete. 00493 */ 00494 void MicroBitBLEManager::idleTick() 00495 { 00496 if((system_timer_current_time() - pairing_completed_at_time) >= MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY) { 00497 if (ble) 00498 ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF); 00499 fiber_remove_idle_component(this); 00500 } 00501 00502 } 00503 00504 00505 /** 00506 * Stops any currently running BLE advertisements 00507 */ 00508 void MicroBitBLEManager::stopAdvertising() 00509 { 00510 ble->gap().stopAdvertising(); 00511 } 00512 00513 #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL) 00514 /** 00515 * Set the content of Eddystone URL frames 00516 * 00517 * @param url The url to broadcast 00518 * 00519 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m). 00520 * 00521 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true) 00522 * 00523 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL) 00524 * 00525 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded. 00526 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power 00527 */ 00528 int MicroBitBLEManager::advertiseEddystoneUrl(const char* url, int8_t calibratedPower, bool connectable, uint16_t interval) 00529 { 00530 ble->gap().stopAdvertising(); 00531 ble->clearAdvertisingPayload(); 00532 00533 ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); 00534 ble->setAdvertisingInterval(interval); 00535 00536 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00537 00538 int ret = MicroBitEddystone::getInstance()->setURL(ble, url, calibratedPower); 00539 00540 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0) 00541 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT); 00542 #endif 00543 ble->gap().startAdvertising(); 00544 00545 return ret; 00546 } 00547 00548 /** 00549 * Set the content of Eddystone URL frames, but accepts a ManagedString as a url. 00550 * 00551 * @param url The url to broadcast 00552 * 00553 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m). 00554 * 00555 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true) 00556 * 00557 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL) 00558 * 00559 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded. 00560 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power 00561 */ 00562 int MicroBitBLEManager::advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval) 00563 { 00564 return advertiseEddystoneUrl((char *)url.toCharArray(), calibratedPower, connectable, interval); 00565 } 00566 #endif 00567 00568 #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID) 00569 /** 00570 * Set the content of Eddystone UID frames 00571 * 00572 * @param uid_namespace: the uid namespace. Must 10 bytes long. 00573 * 00574 * @param uid_instance: the uid instance value. Must 6 bytes long. 00575 * 00576 * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m). 00577 * 00578 * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true) 00579 * 00580 * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL) 00581 * 00582 * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded. 00583 * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power 00584 */ 00585 int MicroBitBLEManager::advertiseEddystoneUid(const char* uid_namespace, const char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval) 00586 { 00587 ble->gap().stopAdvertising(); 00588 ble->clearAdvertisingPayload(); 00589 00590 ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); 00591 ble->setAdvertisingInterval(interval); 00592 00593 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00594 00595 int ret = MicroBitEddystone::getInstance()->setUID(ble, uid_namespace, uid_instance, calibratedPower); 00596 00597 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0) 00598 ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT); 00599 #endif 00600 ble->gap().startAdvertising(); 00601 00602 return ret; 00603 } 00604 #endif 00605 00606 /** 00607 * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming 00608 * of the micro:bit in cases where BLE is disabled during normal operation. 00609 * 00610 * @param display An instance of MicroBitDisplay used when displaying pairing information. 00611 * @param authorizationButton The button to use to authorise a pairing request. 00612 * 00613 * @code 00614 * // initiate pairing mode 00615 * bleManager.pairingMode(uBit.display, uBit.buttonA); 00616 * @endcode 00617 */ 00618 void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &authorisationButton) 00619 { 00620 ManagedString namePrefix("BBC micro:bit ["); 00621 ManagedString namePostfix("]"); 00622 ManagedString BLEName = namePrefix + deviceName + namePostfix; 00623 00624 ManagedString msg("PAIRING MODE!"); 00625 00626 int timeInPairingMode = 0; 00627 int brightness = 255; 00628 int fadeDirection = 0; 00629 00630 ble->gap().stopAdvertising(); 00631 00632 // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices. 00633 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) 00634 BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS]; 00635 Gap::Whitelist_t whitelist; 00636 whitelist.addresses = addresses; 00637 whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; 00638 whitelist.size = 0; 00639 ble->gap().setWhitelist(whitelist); 00640 ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); 00641 #endif 00642 00643 // Update the advertised name of this micro:bit to include the device name 00644 ble->clearAdvertisingPayload(); 00645 00646 ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00647 ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); 00648 ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); 00649 ble->setAdvertisingInterval(200); 00650 00651 ble->gap().setAdvertisingTimeout(0); 00652 ble->gap().startAdvertising(); 00653 00654 // Stop any running animations on the display 00655 display.stopAnimation(); 00656 display.scroll(msg); 00657 00658 // Display our name, visualised as a histogram in the display to aid identification. 00659 showNameHistogram(display); 00660 00661 while (1) 00662 { 00663 if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST) 00664 { 00665 timeInPairingMode = 0; 00666 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"); 00667 display.print(arrow, 0, 0, 0); 00668 00669 if (fadeDirection == 0) 00670 brightness -= MICROBIT_PAIRING_FADE_SPEED; 00671 else 00672 brightness += MICROBIT_PAIRING_FADE_SPEED; 00673 00674 if (brightness <= 40) 00675 display.clear(); 00676 00677 if (brightness <= 0) 00678 fadeDirection = 1; 00679 00680 if (brightness >= 255) 00681 fadeDirection = 0; 00682 00683 if (authorisationButton.isPressed()) 00684 { 00685 pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST; 00686 pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE; 00687 } 00688 } 00689 00690 if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE) 00691 { 00692 timeInPairingMode = 0; 00693 display.setBrightness(255); 00694 for (int i = 0; i < passKey.length(); i++) 00695 { 00696 display.image.print(passKey.charAt(i), 0, 0); 00697 fiber_sleep(800); 00698 display.clear(); 00699 fiber_sleep(200); 00700 00701 if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE) 00702 break; 00703 } 00704 00705 fiber_sleep(1000); 00706 } 00707 00708 if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE) 00709 { 00710 if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL) 00711 { 00712 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"); 00713 display.print(tick, 0, 0, 0); 00714 fiber_sleep(15000); 00715 timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30; 00716 00717 /* 00718 * Disabled, as the API to return the number of active bonds is not reliable at present... 00719 * 00720 display.clear(); 00721 ManagedString c(getBondCount()); 00722 ManagedString c2("/"); 00723 ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS); 00724 ManagedString c4("USED"); 00725 00726 display.scroll(c+c2+c3+c4); 00727 * 00728 * 00729 */ 00730 } 00731 else 00732 { 00733 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"); 00734 display.print(cross, 0, 0, 0); 00735 } 00736 } 00737 00738 fiber_sleep(100); 00739 timeInPairingMode++; 00740 00741 if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30) 00742 microbit_reset(); 00743 } 00744 } 00745 00746 /** 00747 * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance. 00748 * 00749 * @param display The display instance used for displaying the histogram. 00750 */ 00751 void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display) 00752 { 00753 uint32_t n = NRF_FICR->DEVICEID[1]; 00754 int ld = 1; 00755 int d = MICROBIT_DFU_HISTOGRAM_HEIGHT; 00756 int h; 00757 00758 display.clear(); 00759 for (int i = 0; i < MICROBIT_DFU_HISTOGRAM_WIDTH; i++) 00760 { 00761 h = (n % d) / ld; 00762 00763 n -= h; 00764 d *= MICROBIT_DFU_HISTOGRAM_HEIGHT; 00765 ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT; 00766 00767 for (int j = 0; j < h + 1; j++) 00768 display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1, MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1, 255); 00769 } 00770 }
Generated on Tue Jul 12 2022 21:07:39 by 1.7.2