Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitBLEManager.cpp Source File

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 "MicroBitStorage.h"
00029 #include "MicroBitFiber.h"
00030 
00031 
00032 /* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
00033  * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
00034  * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
00035  * as a compatability option, but does not support the options used...
00036  */
00037 #if !defined(__arm)
00038 #pragma GCC diagnostic ignored "-Wunused-function"
00039 #pragma GCC diagnostic push
00040 #pragma GCC diagnostic ignored "-Wunused-parameter"
00041 #endif
00042 
00043 #include "ble.h"
00044 
00045 extern "C"
00046 {
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 const char* MICROBIT_BLE_MANUFACTURER = NULL;
00061 const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
00062 const char* MICROBIT_BLE_HARDWARE_VERSION = NULL;
00063 const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
00064 const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
00065 const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
00066 
00067 /*
00068  * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods.
00069  * So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit
00070  * whilst keeping the code modular.
00071  */
00072 static MicroBitBLEManager *manager = NULL;                      // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-(
00073 static uint8_t deviceID = 255;                                  // Unique ID for the peer that has connected to us.
00074 static Gap::Handle_t pairingHandle = 0;                         // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly.
00075 
00076 static void storeSystemAttributes(Gap::Handle_t handle)
00077 {
00078     if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
00079     {
00080         ManagedString key("bleSysAttrs");
00081 
00082         KeyValuePair* bleSysAttrs = manager->storage->get(key);
00083 
00084         BLESysAttribute attrib;
00085         BLESysAttributeStore attribStore;
00086 
00087         uint16_t len = sizeof(attrib.sys_attr);
00088 
00089         sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
00090 
00091         //copy our stored sysAttrs
00092         if(bleSysAttrs != NULL)
00093         {
00094             memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
00095             delete bleSysAttrs;
00096         }
00097 
00098         //check if we need to update
00099         if(memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0)
00100         {
00101             attribStore.sys_attrs[deviceID] = attrib;
00102             manager->storage->put(key, (uint8_t *)&attribStore);
00103         }
00104     }
00105 }
00106 
00107 /**
00108   * Callback when a BLE GATT disconnect occurs.
00109   */
00110 static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
00111 {
00112     storeSystemAttributes(reason->handle);
00113 
00114     if (manager)
00115         manager->advertise();
00116 }
00117 
00118 /**
00119   * Callback when a BLE SYS_ATTR_MISSING.
00120   */
00121 static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *params)
00122 {
00123     int complete = 0;
00124     deviceID = 255;
00125 
00126     dm_handle_t dm_handle = {0,0,0,0};
00127 
00128     int ret = dm_handle_get(params->connHandle, &dm_handle);
00129 
00130     if (ret == 0)
00131         deviceID = dm_handle.device_id;
00132 
00133     if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
00134     {
00135         ManagedString key("bleSysAttrs");
00136 
00137         KeyValuePair* bleSysAttrs = manager->storage->get(key);
00138 
00139         BLESysAttributeStore attribStore;
00140         BLESysAttribute attrib;
00141 
00142         if(bleSysAttrs != NULL)
00143         {
00144             //restore our sysAttrStore
00145             memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
00146             delete bleSysAttrs;
00147 
00148             attrib = attribStore.sys_attrs[deviceID];
00149 
00150             ret = sd_ble_gatts_sys_attr_set(params->connHandle, attrib.sys_attr, sizeof(attrib.sys_attr), BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
00151 
00152             complete = 1;
00153 
00154             if(ret == 0)
00155                 ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
00156         }
00157     }
00158 
00159     if (!complete)
00160         sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
00161 
00162 }
00163 
00164 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
00165 {
00166     (void) handle; /* -Wunused-param */
00167 
00168     ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
00169 
00170     if (manager)
00171         manager->pairingRequested(passKey);
00172 }
00173 
00174 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
00175 {
00176     (void) handle; /* -Wunused-param */
00177 
00178     dm_handle_t dm_handle = {0,0,0,0};
00179     int ret = dm_handle_get(handle, &dm_handle);
00180 
00181     if (ret == 0)
00182         deviceID = dm_handle.device_id;
00183 
00184     if (manager)
00185     {
00186         pairingHandle = handle;
00187         manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
00188     }
00189 }
00190 
00191 /**
00192  * Constructor.
00193  *
00194  * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
00195  *
00196  * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS).
00197  *
00198  * @note The BLE stack *cannot*  be brought up in a static context (the software simply hangs or corrupts itself).
00199  * Hence, the init() member function should be used to initialise the BLE stack.
00200  */
00201 MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage& _storage) :
00202     storage(&_storage)
00203 {
00204     manager = this;
00205     this->ble = NULL;
00206     this->pairingStatus = 0;
00207 }
00208 
00209 /**
00210  * Constructor.
00211  *
00212  * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
00213  *
00214  * @note The BLE stack *cannot*  be brought up in a static context (the software simply hangs or corrupts itself).
00215  * Hence, the init() member function should be used to initialise the BLE stack.
00216  */
00217 MicroBitBLEManager::MicroBitBLEManager() :
00218     storage(NULL)
00219 {
00220     manager = this;
00221     this->ble = NULL;
00222     this->pairingStatus = 0;
00223 }
00224 
00225 /**
00226  * When called, the micro:bit will begin advertising for a predefined period,
00227  * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
00228  */
00229 void MicroBitBLEManager::advertise()
00230 {
00231     if(ble)
00232         ble->gap().startAdvertising();
00233 }
00234 
00235 /**
00236   * Post constructor initialisation method as the BLE stack cannot be brought
00237   * up in a static context.
00238   *
00239   * @param deviceName The name used when advertising
00240   * @param serialNumber The serial number exposed by the device information service
00241   * @param messageBus An instance of an EventModel, used during pairing.
00242   * @param enableBonding If true, the security manager enabled bonding.
00243   *
00244   * @code
00245   * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
00246   * @endcode
00247   */
00248 void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding)
00249 {
00250     ManagedString BLEName("BBC micro:bit");
00251     this->deviceName = deviceName;
00252 
00253 #if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
00254     ManagedString namePrefix(" [");
00255     ManagedString namePostfix("]");
00256     BLEName = BLEName + namePrefix + deviceName + namePostfix;
00257 #endif
00258 
00259     // Start the BLE stack.
00260 #if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
00261     btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
00262 #endif
00263 
00264     ble = new BLEDevice();
00265     ble->init();
00266 
00267     // automatically restart advertising after a device disconnects.
00268     ble->gap().onDisconnection(bleDisconnectionCallback);
00269     ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback);
00270 
00271     // Configure the stack to hold onto the CPU during critical timing events.
00272     // mbed-classic performs __disable_irq() calls in its timers that can cause
00273     // MIC failures on secure BLE channels...
00274     ble_common_opt_radio_cpu_mutex_t opt;
00275     opt.enable = 1;
00276     sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
00277 
00278 #if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
00279     // Configure for private addresses, so kids' behaviour can't be easily tracked.
00280     ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
00281 #endif
00282 
00283     // Setup our security requirements.
00284     ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
00285     ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
00286     ble->securityManager().init(enableBonding, (SecurityManager::MICROBIT_BLE_SECURITY_LEVEL == SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM), SecurityManager::IO_CAPS_DISPLAY_ONLY);
00287 
00288     if (enableBonding)
00289     {
00290         // If we're in pairing mode, review the size of the bond table.
00291         int bonds = getBondCount();
00292 
00293         // TODO: It would be much better to implement some sort of LRU/NFU policy here,
00294         // but this isn't currently supported in mbed, so we'd need to layer break...
00295 
00296         // If we're full, empty the bond table.
00297         if (bonds >= MICROBIT_BLE_MAXIMUM_BONDS)
00298             ble->securityManager().purgeAllBondingState();
00299     }
00300 
00301 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
00302     // Configure a whitelist to filter all connection requetss from unbonded devices.
00303     // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks.
00304     BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
00305     Gap::Whitelist_t whitelist;
00306     whitelist.addresses = bondedAddresses;
00307     whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
00308 
00309     ble->securityManager().getAddressesFromBondTable(whitelist);
00310 
00311     ble->gap().setWhitelist(whitelist);
00312     ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST);
00313     ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS);
00314 #endif
00315 
00316     // Configure the radio at our default power level
00317     setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
00318 
00319     // Bring up core BLE services.
00320     new MicroBitDFUService(*ble);
00321     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);
00322     new MicroBitEventService(*ble, messageBus);
00323 
00324 
00325     // Configure for high speed mode where possible.
00326     Gap::ConnectionParams_t fast;
00327     ble->getPreferredConnectionParams(&fast);
00328     fast.minConnectionInterval = 8; // 10 ms
00329     fast.maxConnectionInterval = 16; // 20 ms
00330     fast.slaveLatency = 0;
00331     ble->setPreferredConnectionParams(&fast);
00332 
00333     // Setup advertising.
00334 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
00335     ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
00336 #else
00337     ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00338 #endif
00339 
00340     ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
00341     ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00342     ble->setAdvertisingInterval(200);
00343 
00344 #if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
00345     ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
00346 #endif
00347 
00348     // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
00349     // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable.
00350     // If whiltelisting is disabled, then we always advertise.
00351 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
00352     if (whitelist.size > 0)
00353 #endif
00354         ble->startAdvertising();
00355 }
00356 
00357 /**
00358  * Change the output power level of the transmitter to the given value.
00359  *
00360  * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
00361  *
00362  * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
00363  *
00364  * @code
00365  * // maximum transmission power.
00366  * bleManager.setTransmitPower(7);
00367  * @endcode
00368  */
00369 int MicroBitBLEManager::setTransmitPower(int power)
00370 {
00371     if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS)
00372         return MICROBIT_INVALID_PARAMETER;
00373 
00374     if (ble->gap().setTxPower(MICROBIT_BLE_POWER_LEVEL[power]) != NRF_SUCCESS)
00375         return MICROBIT_NOT_SUPPORTED;
00376 
00377     return MICROBIT_OK;
00378 }
00379 
00380 /**
00381  * Determines the number of devices currently bonded with this micro:bit.
00382  * @return The number of active bonds.
00383  */
00384 int MicroBitBLEManager::getBondCount()
00385 {
00386     BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
00387     Gap::Whitelist_t whitelist;
00388     whitelist.addresses = bondedAddresses;
00389     whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
00390     ble->securityManager().getAddressesFromBondTable(whitelist);
00391 
00392     return whitelist.bonds;
00393 }
00394 
00395 /**
00396  * A request to pair has been received from a BLE device.
00397  * If we're in pairing mode, display the passkey to the user.
00398  * Also, purge the bonding table if it has reached capacity.
00399  *
00400  * @note for internal use only.
00401  */
00402 void MicroBitBLEManager::pairingRequested(ManagedString passKey)
00403 {
00404     // Update our mode to display the passkey.
00405     this->passKey = passKey;
00406     this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
00407 }
00408 
00409 /**
00410  * A pairing request has been sucessfully completed.
00411  * If we're in pairing mode, display a success or failure message.
00412  *
00413  * @note for internal use only.
00414  */
00415 void MicroBitBLEManager::pairingComplete(bool success)
00416 {
00417     this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
00418 
00419     if(success)
00420     {
00421         this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
00422         fiber_add_idle_component(this);
00423     }
00424 }
00425 
00426 /**
00427  * Periodic callback in thread context.
00428  * We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
00429  */
00430 void MicroBitBLEManager::idleTick()
00431 {
00432     if (ble)
00433         ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
00434 
00435     fiber_remove_idle_component(this);
00436 }
00437 
00438 /**
00439  * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
00440  * of the micro:bit in cases where BLE is disabled during normal operation.
00441  *
00442  * @param display An instance of MicroBitDisplay used when displaying pairing information.
00443  * @param authorizationButton The button to use to authorise a pairing request.
00444  *
00445  * @code
00446  * // initiate pairing mode
00447  * bleManager.pairingMode(uBit.display, uBit.buttonA);
00448  * @endcode
00449  */
00450 void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& authorisationButton)
00451 {
00452     ManagedString namePrefix("BBC micro:bit [");
00453     ManagedString namePostfix("]");
00454     ManagedString BLEName = namePrefix + deviceName + namePostfix;
00455 
00456     ManagedString msg("PAIRING MODE!");
00457 
00458     int timeInPairingMode = 0;
00459     int brightness = 255;
00460     int fadeDirection = 0;
00461 
00462     ble->gap().stopAdvertising();
00463 
00464     // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices.
00465 #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
00466     BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
00467     Gap::Whitelist_t whitelist;
00468     whitelist.addresses = addresses;
00469     whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
00470     whitelist.size = 0;
00471     ble->gap().setWhitelist(whitelist);
00472     ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
00473 #endif
00474 
00475     // Update the advertised name of this micro:bit to include the device name
00476     ble->clearAdvertisingPayload();
00477 
00478     ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00479     ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
00480     ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00481     ble->setAdvertisingInterval(200);
00482 
00483     ble->gap().setAdvertisingTimeout(0);
00484     ble->gap().startAdvertising();
00485 
00486     // Stop any running animations on the display
00487     display.stopAnimation();
00488     display.scroll(msg);
00489 
00490     // Display our name, visualised as a histogram in the display to aid identification.
00491     showNameHistogram(display);
00492 
00493     while(1)
00494     {
00495         if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
00496         {
00497             timeInPairingMode = 0;
00498             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");
00499             display.print(arrow,0,0,0);
00500 
00501             if (fadeDirection == 0)
00502                 brightness -= MICROBIT_PAIRING_FADE_SPEED;
00503             else
00504                 brightness += MICROBIT_PAIRING_FADE_SPEED;
00505 
00506             if (brightness <= 40)
00507                 display.clear();
00508 
00509             if (brightness <= 0)
00510                 fadeDirection = 1;
00511 
00512             if (brightness >= 255)
00513                 fadeDirection = 0;
00514 
00515             if (authorisationButton.isPressed())
00516             {
00517                 pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
00518                 pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
00519             }
00520         }
00521 
00522         if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
00523         {
00524             timeInPairingMode = 0;
00525             display.setBrightness(255);
00526             for (int i=0; i<passKey.length(); i++)
00527             {
00528                 display.image.print(passKey.charAt(i),0,0);
00529                 fiber_sleep(800);
00530                 display.clear();
00531                 fiber_sleep(200);
00532 
00533                 if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
00534                     break;
00535             }
00536 
00537             fiber_sleep(1000);
00538         }
00539 
00540         if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
00541         {
00542             if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
00543             {
00544                 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");
00545                 display.print(tick,0,0,0);
00546                 fiber_sleep(15000);
00547                 timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30;
00548 
00549                 /*
00550                  * Disabled, as the API to return the number of active bonds is not reliable at present...
00551                  *
00552                 display.clear();
00553                 ManagedString c(getBondCount());
00554                 ManagedString c2("/");
00555                 ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS);
00556                 ManagedString c4("USED");
00557 
00558                 display.scroll(c+c2+c3+c4);
00559                 *
00560                 *
00561                 */
00562             }
00563             else
00564             {
00565                 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");
00566                 display.print(cross,0,0,0);
00567             }
00568         }
00569 
00570         fiber_sleep(100);
00571         timeInPairingMode++;
00572 
00573         if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
00574             microbit_reset();
00575     }
00576 }
00577 
00578 /**
00579  * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
00580  *
00581  * @param display The display instance used for displaying the histogram.
00582  */
00583 void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
00584 {
00585     uint32_t n = NRF_FICR->DEVICEID[1];
00586     int ld = 1;
00587     int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
00588     int h;
00589 
00590     display.clear();
00591     for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
00592     {
00593         h = (n % d) / ld;
00594 
00595         n -= h;
00596         d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
00597         ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
00598 
00599         for (int j=0; j<h+1; j++)
00600             display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
00601     }
00602 }