Eddystone test using modified DAL

Dependencies:   BLE_API mbed-dev-bin nRF51822

Dependents:   microbit-eddystone

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 "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 }