Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API mbed-dev-bin nRF51822
Dependents: microbit-eddystone
Fork of microbit-dal by
Revision 74:a8f5674a0079, committed 2017-02-08
- Comitter:
- bluetooth_mdw
- Date:
- Wed Feb 08 07:49:17 2017 +0000
- Parent:
- 73:eb91bba49623
- Commit message:
- Eddystone URL test using modified DAL
Changed in this revision
--- a/inc/bluetooth/MicroBitBLEManager.h Wed Jul 13 14:32:54 2016 +0000
+++ b/inc/bluetooth/MicroBitBLEManager.h Wed Feb 08 07:49:17 2017 +0000
@@ -35,7 +35,7 @@
* The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
* as a compatability option, but does not support the options used...
*/
-#if !defined (__arm)
+#if !defined(__arm)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
@@ -44,7 +44,7 @@
/*
* Return to our predefined compiler settings.
*/
-#if !defined (__arm)
+#if !defined(__arm)
#pragma GCC diagnostic pop
#endif
@@ -61,21 +61,24 @@
#include "MicroBitButton.h"
#include "MicroBitStorage.h"
-#define MICROBIT_BLE_PAIR_REQUEST 0x01
-#define MICROBIT_BLE_PAIR_COMPLETE 0x02
-#define MICROBIT_BLE_PAIR_PASSCODE 0x04
-#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08
+#define MICROBIT_BLE_PAIR_REQUEST 0x01
+#define MICROBIT_BLE_PAIR_COMPLETE 0x02
+#define MICROBIT_BLE_PAIR_PASSCODE 0x04
+#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08
-#define MICROBIT_BLE_PAIRING_TIMEOUT 90
-#define MICROBIT_BLE_POWER_LEVELS 8
-#define MICROBIT_BLE_MAXIMUM_BONDS 4
-#define MICROBIT_BLE_ENABLE_BONDING true
+#define MICROBIT_BLE_PAIRING_TIMEOUT 90
+#define MICROBIT_BLE_POWER_LEVELS 8
+#define MICROBIT_BLE_MAXIMUM_BONDS 4
+#define MICROBIT_BLE_ENABLE_BONDING true
+
+#define MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL 400
+#define MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER 0xF0
extern const int8_t MICROBIT_BLE_POWER_LEVEL[];
struct BLESysAttribute
{
- uint8_t sys_attr[8];
+ uint8_t sys_attr[8];
};
struct BLESysAttributeStore
@@ -89,13 +92,14 @@
*/
class MicroBitBLEManager : MicroBitComponent
{
- public:
+ public:
+ static MicroBitBLEManager *manager;
- // The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
- BLEDevice *ble;
+ // The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
+ BLEDevice *ble;
//an instance of MicroBitStorage used to store sysAttrs from softdevice
- MicroBitStorage* storage;
+ MicroBitStorage *storage;
/**
* Constructor.
@@ -107,7 +111,7 @@
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
* Hence, the init() member function should be used to initialise the BLE stack.
*/
- MicroBitBLEManager(MicroBitStorage& _storage);
+ MicroBitBLEManager(MicroBitStorage &_storage);
/**
* Constructor.
@@ -120,6 +124,15 @@
MicroBitBLEManager();
/**
+ * getInstance
+ *
+ * Allows other objects to easily obtain a pointer to the single instance of this object. By rights the constructor should be made
+ * private to properly implement the singleton pattern.
+ *
+ */
+ static MicroBitBLEManager *getInstance();
+
+ /**
* Post constructor initialisation method as the BLE stack cannot be brought
* up in a static context.
*
@@ -132,7 +145,7 @@
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
* @endcode
*/
- void init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding);
+ void init(ManagedString deviceName, ManagedString serialNumber, EventModel &messageBus, bool enableBonding);
/**
* Change the output power level of the transmitter to the given value.
@@ -174,42 +187,102 @@
*/
int getBondCount();
- /**
+ /**
* A request to pair has been received from a BLE device.
* If we're in pairing mode, display the passkey to the user.
* Also, purge the bonding table if it has reached capacity.
*
* @note for internal use only.
*/
- void pairingRequested(ManagedString passKey);
+ void pairingRequested(ManagedString passKey);
- /**
+ /**
* A pairing request has been sucessfully completed.
* If we're in pairing mode, display a success or failure message.
*
* @note for internal use only.
*/
- void pairingComplete(bool success);
+ void pairingComplete(bool success);
- /**
+ /**
* Periodic callback in thread context.
* We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
*/
- void idleTick();
+ void idleTick();
+
+ /**
+ * Stops any currently running BLE advertisements
+ */
+ void stopAdvertising();
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
- private:
+ /**
+ * Set the content of Eddystone URL frames
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int advertiseEddystoneUrl(const char *url, int8_t calibratedPower = MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER, bool connectable = true, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
- /**
- * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
- *
- * @param display The display instance used for displaying the histogram.
- */
- void showNameHistogram(MicroBitDisplay &display);
+ /**
+ * Set the content of Eddystone URL frames, but accepts a ManagedString as a url.
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower = MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER, bool connectable = true, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
+#endif
- int pairingStatus;
- ManagedString passKey;
- ManagedString deviceName;
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
+ /**
+ * Set the content of Eddystone UID frames
+ *
+ * @param uid_namespace: the uid namespace. Must 10 bytes long.
+ *
+ * @param uid_instance: the uid instance value. Must 6 bytes long.
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int advertiseEddystoneUid(const char* uid_namespace, const char* uid_instance, int8_t calibratedPower = MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER, bool connectable = true, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
+#endif
+ private:
+ /**
+ * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
+ *
+ * @param display The display instance used for displaying the histogram.
+ */
+ void showNameHistogram(MicroBitDisplay &display);
+
+ #define MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY 500
+ unsigned long pairing_completed_at_time;
+
+ int pairingStatus;
+ ManagedString passKey;
+ ManagedString deviceName;
};
-#endif
\ No newline at end of file
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitEddystone.h Wed Feb 08 07:49:17 2017 +0000
@@ -0,0 +1,104 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_EDDYSTONE_H
+#define MICROBIT_EDDYSTONE_H
+
+#include "MicroBitConfig.h"
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+#include "MicroBitBLEManager.h"
+
+/**
+ * Class definition for the MicroBitEddystone.
+ *
+ */
+class MicroBitEddystone
+{
+ public:
+
+ static MicroBitEddystone* getInstance();
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
+
+ /**
+ * Set the content of Eddystone URL frames
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int setURL(BLEDevice* ble, const char *url, int8_t calibratedPower = 0xF0);
+
+ /**
+ * Set the content of Eddystone URL frames, but accepts a ManagedString as a url.
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int setURL(BLEDevice* ble, ManagedString url, int8_t calibratedPower = 0xF0);
+
+#endif
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
+ /**
+ * Set the content of Eddystone UID frames
+ *
+ * @param uid_namespace the uid namespace. Must 10 bytes long.
+ *
+ * @param uid_instance the uid instance value. Must 6 bytes long.
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+ int setUID(BLEDevice* ble, const char* uid_namespace, const char* uid_instance, int8_t calibratedPower = 0xF0);
+#endif
+
+ private:
+
+ /**
+ * Private constructor.
+ */
+ MicroBitEddystone();
+
+ static MicroBitEddystone *_instance;
+};
+
+#endif
--- a/inc/core/MicroBitConfig.h Wed Jul 13 14:32:54 2016 +0000 +++ b/inc/core/MicroBitConfig.h Wed Feb 08 07:49:17 2017 +0000 @@ -72,6 +72,35 @@ #define MICROBIT_HEAP_END (CORTEX_M0_STACK_BASE - MICROBIT_STACK_SIZE) #endif +// Defines the size of a physical FLASH page in RAM. +#ifndef PAGE_SIZE +#define PAGE_SIZE 1024 +#endif + +// Defines where in memory persistent data is stored. +#ifndef KEY_VALUE_STORE_PAGE +#define KEY_VALUE_STORE_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 17)) +#endif + +#ifndef BLE_BOND_DATA_PAGE +#define BLE_BOND_DATA_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 18)) +#endif + +#ifndef DEFAULT_SCRATCH_PAGE +#define DEFAULT_SCRATCH_PAGE (PAGE_SIZE * (NRF_FICR->CODESIZE - 19)) +#endif + +// Address of the end of the current program in FLASH memory. +// This is recorded by the C/C++ linker, but the symbol name varies depending on which compiler is used. +#if defined(__arm) +extern uint32_t Image$$ER_IROM1$$RO$$Limit; +#define FLASH_PROGRAM_END (uint32_t) (&Image$$ER_IROM1$$RO$$Limit) +#else +extern uint32_t __etext; +#define FLASH_PROGRAM_END (uint32_t) (&__etext) +#endif + + // Enables or disables the MicroBitHeapllocator. Note that if disabled, no reuse of the SRAM normally // reserved for SoftDevice is possible, and out of memory condition will no longer be trapped... // i.e. panic() will no longer be triggered on memory full conditions. @@ -237,6 +266,18 @@ #define MICROBIT_BLE_DFU_SERVICE 1 #endif +// Enable/Disable availability of Eddystone URL APIs +// Set '1' to enable. +#ifndef MICROBIT_BLE_EDDYSTONE_URL +#define MICROBIT_BLE_EDDYSTONE_URL 1 +#endif + +// Enable/Disable availability of Eddystone UID APIs +// Set '1' to enable. +#ifndef MICROBIT_BLE_EDDYSTONE_UID +#define MICROBIT_BLE_EDDYSTONE_UID 0 +#endif + // Enable/Disable BLE Service: MicroBitEventService // This allows routing of events from the micro:bit message bus over BLE. // Set '1' to enable. @@ -318,6 +359,26 @@ #define MICROBIT_DEFAULT_SERIAL_MODE SYNC_SLEEP #endif +// +// File System configuration defaults +// + +// +// Defines the logical block size for the file system. +// Must be a factor of the physical PAGE_SIZE (ideally a power of two less). +// +#ifndef MBFS_BLOCK_SIZE +#define MBFS_BLOCK_SIZE 256 +#endif + +// +// FileSystem writeback cache size, in bytes. Defines how many bytes will be stored +// in RAM before being written back to FLASH. Set to zero to disable this feature. +// Should be <= MBFS_BLOCK_SIZE. +// +#ifndef MBFS_CACHE_SIZE +#define MBFS_CACHE_SIZE 16 +#endif // // I/O Options @@ -384,4 +445,4 @@ extern RawSerial* SERIAL_DEBUG; #endif -#endif \ No newline at end of file +#endif
--- a/source/bluetooth/MicroBitBLEManager.cpp Wed Jul 13 14:32:54 2016 +0000
+++ b/source/bluetooth/MicroBitBLEManager.cpp Wed Feb 08 07:49:17 2017 +0000
@@ -25,9 +25,10 @@
#include "MicroBitConfig.h"
#include "MicroBitBLEManager.h"
+#include "MicroBitEddystone.h"
#include "MicroBitStorage.h"
#include "MicroBitFiber.h"
-
+#include "MicroBitSystemTimer.h"
/* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
* If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
@@ -42,8 +43,7 @@
#include "ble.h"
-extern "C"
-{
+extern "C" {
#include "device_manager.h"
uint32_t btle_set_gatt_table_size(uint32_t size);
}
@@ -55,13 +55,28 @@
#pragma GCC diagnostic pop
#endif
-#define MICROBIT_PAIRING_FADE_SPEED 4
+#define MICROBIT_PAIRING_FADE_SPEED 4
-const char* MICROBIT_BLE_MANUFACTURER = NULL;
-const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
-const char* MICROBIT_BLE_HARDWARE_VERSION = NULL;
-const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
-const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
+//
+// Local enumeration of valid security modes. Used only to optimise pre‐processor comparisons.
+//
+#define __SECURITY_MODE_ENCRYPTION_OPEN_LINK 0
+#define __SECURITY_MODE_ENCRYPTION_NO_MITM 1
+#define __SECURITY_MODE_ENCRYPTION_WITH_MITM 2
+//
+// Some Black Magic to compare the definition of our security mode in MicroBitConfig with a given parameter.
+// Required as the MicroBitConfig option is actually an mbed enum, that is not normally comparable at compile time.
+//
+
+#define __CAT(a, ...) a##__VA_ARGS__
+#define SECURITY_MODE(x) __CAT(__, x)
+#define SECURITY_MODE_IS(x) (SECURITY_MODE(MICROBIT_BLE_SECURITY_LEVEL) == SECURITY_MODE(x))
+
+const char *MICROBIT_BLE_MANUFACTURER = NULL;
+const char *MICROBIT_BLE_MODEL = "BBC micro:bit";
+const char *MICROBIT_BLE_HARDWARE_VERSION = NULL;
+const char *MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
+const char *MICROBIT_BLE_SOFTWARE_VERSION = NULL;
const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
/*
@@ -69,17 +84,18 @@
* So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit
* whilst keeping the code modular.
*/
-static MicroBitBLEManager *manager = NULL; // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-(
-static uint8_t deviceID = 255; // Unique ID for the peer that has connected to us.
-static Gap::Handle_t pairingHandle = 0; // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly.
+MicroBitBLEManager *MicroBitBLEManager::manager = NULL; // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-(
+
+static uint8_t deviceID = 255; // Unique ID for the peer that has connected to us.
+static Gap::Handle_t pairingHandle = 0; // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly.
static void storeSystemAttributes(Gap::Handle_t handle)
{
- if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
+ if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
{
ManagedString key("bleSysAttrs");
- KeyValuePair* bleSysAttrs = manager->storage->get(key);
+ KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
BLESysAttribute attrib;
BLESysAttributeStore attribStore;
@@ -89,17 +105,17 @@
sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
//copy our stored sysAttrs
- if(bleSysAttrs != NULL)
+ if (bleSysAttrs != NULL)
{
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
delete bleSysAttrs;
}
//check if we need to update
- if(memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0)
+ if (memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0)
{
attribStore.sys_attrs[deviceID] = attrib;
- manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore));
+ MicroBitBLEManager::manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore));
}
}
}
@@ -109,20 +125,20 @@
*/
static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
{
- MicroBitEvent(MICROBIT_ID_BLE,MICROBIT_BLE_EVT_DISCONNECTED);
+ MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED);
storeSystemAttributes(reason->handle);
- if (manager)
- manager->advertise();
+ if (MicroBitBLEManager::manager)
+ MicroBitBLEManager::manager->advertise();
}
/**
* Callback when a BLE connection is established.
*/
-static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t*)
+static void bleConnectionCallback(const Gap::ConnectionCallbackParams_t *)
{
- MicroBitEvent(MICROBIT_ID_BLE,MICROBIT_BLE_EVT_CONNECTED);
+ MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED);
}
/**
@@ -133,23 +149,23 @@
int complete = 0;
deviceID = 255;
- dm_handle_t dm_handle = {0,0,0,0};
+ dm_handle_t dm_handle = {0, 0, 0, 0};
int ret = dm_handle_get(params->connHandle, &dm_handle);
if (ret == 0)
deviceID = dm_handle.device_id;
- if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
+ if (MicroBitBLEManager::manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
{
ManagedString key("bleSysAttrs");
- KeyValuePair* bleSysAttrs = manager->storage->get(key);
+ KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
BLESysAttributeStore attribStore;
BLESysAttribute attrib;
- if(bleSysAttrs != NULL)
+ if (bleSysAttrs != NULL)
{
//restore our sysAttrStore
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
@@ -161,40 +177,39 @@
complete = 1;
- if(ret == 0)
+ if (ret == 0)
ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
}
}
if (!complete)
sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
-
}
static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
{
- (void) handle; /* -Wunused-param */
+ (void)handle; /* -Wunused-param */
- ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
+ ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
- if (manager)
- manager->pairingRequested(passKey);
+ if (MicroBitBLEManager::manager)
+ MicroBitBLEManager::manager->pairingRequested(passKey);
}
static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
{
- (void) handle; /* -Wunused-param */
+ (void)handle; /* -Wunused-param */
- dm_handle_t dm_handle = {0,0,0,0};
+ dm_handle_t dm_handle = {0, 0, 0, 0};
int ret = dm_handle_get(handle, &dm_handle);
if (ret == 0)
deviceID = dm_handle.device_id;
- if (manager)
+ if (MicroBitBLEManager::manager)
{
pairingHandle = handle;
- manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
+ MicroBitBLEManager::manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
}
}
@@ -208,12 +223,11 @@
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
* Hence, the init() member function should be used to initialise the BLE stack.
*/
-MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage& _storage) :
- storage(&_storage)
+MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage &_storage) : storage(&_storage)
{
manager = this;
- this->ble = NULL;
- this->pairingStatus = 0;
+ this->ble = NULL;
+ this->pairingStatus = 0;
}
/**
@@ -224,12 +238,24 @@
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
* Hence, the init() member function should be used to initialise the BLE stack.
*/
-MicroBitBLEManager::MicroBitBLEManager() :
- storage(NULL)
+MicroBitBLEManager::MicroBitBLEManager() : storage(NULL)
{
manager = this;
- this->ble = NULL;
- this->pairingStatus = 0;
+ this->ble = NULL;
+ this->pairingStatus = 0;
+}
+
+/**
+ * When called, the micro:bit will begin advertising for a predefined period,
+ * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
+ */
+MicroBitBLEManager *MicroBitBLEManager::getInstance()
+{
+ if (manager == 0)
+ {
+ manager = new MicroBitBLEManager;
+ }
+ return manager;
}
/**
@@ -238,7 +264,7 @@
*/
void MicroBitBLEManager::advertise()
{
- if(ble)
+ if (ble)
ble->gap().startAdvertising();
}
@@ -255,18 +281,18 @@
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
* @endcode
*/
-void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding)
+void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel &messageBus, bool enableBonding)
{
- ManagedString BLEName("BBC micro:bit");
- this->deviceName = deviceName;
+ ManagedString BLEName("BBC micro:bit");
+ this->deviceName = deviceName;
#if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
- ManagedString namePrefix(" [");
- ManagedString namePostfix("]");
- BLEName = BLEName + namePrefix + deviceName + namePostfix;
+ ManagedString namePrefix(" [");
+ ManagedString namePostfix("]");
+ BLEName = BLEName + namePrefix + deviceName + namePostfix;
#endif
- // Start the BLE stack.
+// Start the BLE stack.
#if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
#endif
@@ -289,14 +315,26 @@
sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
#if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
- // Configure for private addresses, so kids' behaviour can't be easily tracked.
- ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
+ // Configure for private addresses, so kids' behaviour can't be easily tracked.
+ ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
#endif
// Setup our security requirements.
ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
- ble->securityManager().init(enableBonding, (SecurityManager::MICROBIT_BLE_SECURITY_LEVEL == SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM), SecurityManager::IO_CAPS_DISPLAY_ONLY);
+// @bluetooth_mdw: select either passkey pairing (more secure), "just works" pairing (less secure but nice and simple for the user)
+// or no security
+// Default to passkey pairing with MITM protection
+#if (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_NO_MITM))
+ // Just Works
+ ble->securityManager().init(enableBonding, false, SecurityManager::IO_CAPS_NONE);
+#elif (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_OPEN_LINK))
+ // no security
+ ble->securityManager().init(false, false, SecurityManager::IO_CAPS_DISPLAY_ONLY);
+#else
+ // passkey
+ ble->securityManager().init(enableBonding, true, SecurityManager::IO_CAPS_DISPLAY_ONLY);
+#endif
if (enableBonding)
{
@@ -329,13 +367,13 @@
// Configure the radio at our default power level
setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
- // Bring up core BLE services.
+// Bring up core BLE services.
#if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
new MicroBitDFUService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
- 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);
+ 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);
#else
(void)serialNumber;
#endif
@@ -346,16 +384,15 @@
(void)messageBus;
#endif
-
// Configure for high speed mode where possible.
Gap::ConnectionParams_t fast;
ble->getPreferredConnectionParams(&fast);
- fast.minConnectionInterval = 8; // 10 ms
+ fast.minConnectionInterval = 8; // 10 ms
fast.maxConnectionInterval = 16; // 20 ms
fast.slaveLatency = 0;
ble->setPreferredConnectionParams(&fast);
- // Setup advertising.
+// Setup advertising.
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
#else
@@ -370,9 +407,9 @@
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
#endif
- // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
- // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable.
- // If whiltelisting is disabled, then we always advertise.
+// If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
+// This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable.
+// If whiltelisting is disabled, then we always advertise.
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
if (whitelist.size > 0)
#endif
@@ -427,8 +464,8 @@
void MicroBitBLEManager::pairingRequested(ManagedString passKey)
{
// Update our mode to display the passkey.
- this->passKey = passKey;
- this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
+ this->passKey = passKey;
+ this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
}
/**
@@ -439,11 +476,13 @@
*/
void MicroBitBLEManager::pairingComplete(bool success)
{
- this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
+ this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
+
+ pairing_completed_at_time = system_timer_current_time();
- if(success)
+ if (success)
{
- this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
+ this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
fiber_add_idle_component(this);
}
}
@@ -454,11 +493,115 @@
*/
void MicroBitBLEManager::idleTick()
{
- if (ble)
- ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
+ if((system_timer_current_time() - pairing_completed_at_time) >= MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY) {
+ if (ble)
+ ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
+ fiber_remove_idle_component(this);
+ }
+
+}
+
+
+/**
+* Stops any currently running BLE advertisements
+*/
+void MicroBitBLEManager::stopAdvertising()
+{
+ ble->gap().stopAdvertising();
+}
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
+/**
+ * Set the content of Eddystone URL frames
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitBLEManager::advertiseEddystoneUrl(const char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
+{
+ ble->gap().stopAdvertising();
+ ble->clearAdvertisingPayload();
+
+ ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
+ ble->setAdvertisingInterval(interval);
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+
+ int ret = MicroBitEddystone::getInstance()->setURL(ble, url, calibratedPower);
+
+#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
+ ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
+#endif
+ ble->gap().startAdvertising();
+
+ return ret;
+}
- fiber_remove_idle_component(this);
+/**
+ * Set the content of Eddystone URL frames, but accepts a ManagedString as a url.
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitBLEManager::advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval)
+{
+ return advertiseEddystoneUrl((char *)url.toCharArray(), calibratedPower, connectable, interval);
}
+#endif
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
+/**
+ * Set the content of Eddystone UID frames
+ *
+ * @param uid_namespace: the uid namespace. Must 10 bytes long.
+ *
+ * @param uid_instance: the uid instance value. Must 6 bytes long.
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @param connectable true to keep bluetooth connectable for other services, false otherwise. (Defaults to true)
+ *
+ * @param interval the rate at which the micro:bit will advertise url frames. (Defaults to MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL)
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitBLEManager::advertiseEddystoneUid(const char* uid_namespace, const char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
+{
+ ble->gap().stopAdvertising();
+ ble->clearAdvertisingPayload();
+
+ ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
+ ble->setAdvertisingInterval(interval);
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+
+ int ret = MicroBitEddystone::getInstance()->setUID(ble, uid_namespace, uid_instance, calibratedPower);
+
+#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
+ ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
+#endif
+ ble->gap().startAdvertising();
+
+ return ret;
+}
+#endif
/**
* Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
@@ -472,21 +615,21 @@
* bleManager.pairingMode(uBit.display, uBit.buttonA);
* @endcode
*/
-void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& authorisationButton)
+void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &authorisationButton)
{
- ManagedString namePrefix("BBC micro:bit [");
- ManagedString namePostfix("]");
- ManagedString BLEName = namePrefix + deviceName + namePostfix;
+ ManagedString namePrefix("BBC micro:bit [");
+ ManagedString namePostfix("]");
+ ManagedString BLEName = namePrefix + deviceName + namePostfix;
- ManagedString msg("PAIRING MODE!");
+ ManagedString msg("PAIRING MODE!");
- int timeInPairingMode = 0;
- int brightness = 255;
- int fadeDirection = 0;
+ int timeInPairingMode = 0;
+ int brightness = 255;
+ int fadeDirection = 0;
ble->gap().stopAdvertising();
- // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices.
+// Clear the whitelist (if we have one), so that we're discoverable by all BLE devices.
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
Gap::Whitelist_t whitelist;
@@ -497,7 +640,7 @@
ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
#endif
- // Update the advertised name of this micro:bit to include the device name
+ // Update the advertised name of this micro:bit to include the device name
ble->clearAdvertisingPayload();
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
@@ -508,68 +651,68 @@
ble->gap().setAdvertisingTimeout(0);
ble->gap().startAdvertising();
- // Stop any running animations on the display
- display.stopAnimation();
- display.scroll(msg);
+ // Stop any running animations on the display
+ display.stopAnimation();
+ display.scroll(msg);
- // Display our name, visualised as a histogram in the display to aid identification.
- showNameHistogram(display);
+ // Display our name, visualised as a histogram in the display to aid identification.
+ showNameHistogram(display);
- while(1)
- {
- if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
- {
- timeInPairingMode = 0;
- 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");
- display.print(arrow,0,0,0);
+ while (1)
+ {
+ if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
+ {
+ timeInPairingMode = 0;
+ 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");
+ display.print(arrow, 0, 0, 0);
- if (fadeDirection == 0)
- brightness -= MICROBIT_PAIRING_FADE_SPEED;
- else
- brightness += MICROBIT_PAIRING_FADE_SPEED;
+ if (fadeDirection == 0)
+ brightness -= MICROBIT_PAIRING_FADE_SPEED;
+ else
+ brightness += MICROBIT_PAIRING_FADE_SPEED;
- if (brightness <= 40)
- display.clear();
+ if (brightness <= 40)
+ display.clear();
- if (brightness <= 0)
- fadeDirection = 1;
+ if (brightness <= 0)
+ fadeDirection = 1;
- if (brightness >= 255)
- fadeDirection = 0;
+ if (brightness >= 255)
+ fadeDirection = 0;
- if (authorisationButton.isPressed())
- {
- pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
- pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
- }
- }
+ if (authorisationButton.isPressed())
+ {
+ pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
+ pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
+ }
+ }
- if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
- {
- timeInPairingMode = 0;
- display.setBrightness(255);
- for (int i=0; i<passKey.length(); i++)
- {
- display.image.print(passKey.charAt(i),0,0);
- fiber_sleep(800);
- display.clear();
- fiber_sleep(200);
+ if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
+ {
+ timeInPairingMode = 0;
+ display.setBrightness(255);
+ for (int i = 0; i < passKey.length(); i++)
+ {
+ display.image.print(passKey.charAt(i), 0, 0);
+ fiber_sleep(800);
+ display.clear();
+ fiber_sleep(200);
- if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
- break;
- }
+ if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
+ break;
+ }
- fiber_sleep(1000);
- }
+ fiber_sleep(1000);
+ }
- if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
- {
- if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
- {
- 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");
- display.print(tick,0,0,0);
+ if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
+ {
+ if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
+ {
+ 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");
+ display.print(tick, 0, 0, 0);
fiber_sleep(15000);
- timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30;
+ timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30;
/*
* Disabled, as the API to return the number of active bonds is not reliable at present...
@@ -584,20 +727,20 @@
*
*
*/
- }
- else
- {
- 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");
- display.print(cross,0,0,0);
- }
- }
+ }
+ else
+ {
+ 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");
+ display.print(cross, 0, 0, 0);
+ }
+ }
- fiber_sleep(100);
- timeInPairingMode++;
+ fiber_sleep(100);
+ timeInPairingMode++;
- if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
- microbit_reset();
- }
+ if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
+ microbit_reset();
+ }
}
/**
@@ -613,7 +756,7 @@
int h;
display.clear();
- for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
+ for (int i = 0; i < MICROBIT_DFU_HISTOGRAM_WIDTH; i++)
{
h = (n % d) / ld;
@@ -621,7 +764,7 @@
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
- for (int j=0; j<h+1; j++)
- display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
+ for (int j = 0; j < h + 1; j++)
+ display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1, MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1, 255);
}
-}
\ No newline at end of file
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitEddystone.cpp Wed Feb 08 07:49:17 2017 +0000
@@ -0,0 +1,215 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitEddystone.h"
+
+MicroBitEddystone *MicroBitEddystone::_instance = NULL;
+
+/* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+const uint8_t EDDYSTONE_UUID[] = {0xAA, 0xFE};
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
+const char *EDDYSTONE_URL_PREFIXES[] = {"http://www.", "https://www.", "http://", "https://"};
+const size_t EDDYSTONE_URL_PREFIXES_LENGTH = sizeof(EDDYSTONE_URL_PREFIXES) / sizeof(char *);
+const char *EDDYSTONE_URL_SUFFIXES[] = {".com/", ".org/", ".edu/", ".net/", ".info/", ".biz/", ".gov/", ".com", ".org", ".edu", ".net", ".info", ".biz", ".gov"};
+const size_t EDDYSTONE_URL_SUFFIXES_LENGTH = sizeof(EDDYSTONE_URL_SUFFIXES) / sizeof(char *);
+const int EDDYSTONE_URL_MAX_LENGTH = 18;
+const uint8_t EDDYSTONE_URL_FRAME_TYPE = 0x10;
+#endif
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
+const int EDDYSTONE_UID_NAMESPACE_MAX_LENGTH = 10;
+const int EDDYSTONE_UID_INSTANCE_MAX_LENGTH = 6;
+const uint8_t EDDYSTONE_UID_FRAME_TYPE = 0x00;
+#endif
+
+/**
+ * Constructor.
+ *
+ * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
+ *
+ * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS).
+ *
+ * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
+ * Hence, the init() member function should be used to initialise the BLE stack.
+ */
+MicroBitEddystone::MicroBitEddystone()
+{
+}
+
+MicroBitEddystone* MicroBitEddystone::getInstance()
+{
+ if (_instance == 0)
+ _instance = new MicroBitEddystone;
+
+ return _instance;
+}
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
+
+/**
+ * Set the content of Eddystone URL frames
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitEddystone::setURL(BLEDevice* ble, const char* url, int8_t calibratedPower)
+{
+ int urlDataLength = 0;
+ char urlData[EDDYSTONE_URL_MAX_LENGTH];
+ memset(urlData, 0, EDDYSTONE_URL_MAX_LENGTH);
+
+ if (url == NULL || strlen(url) == 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Prefix
+ for (size_t i = 0; i < EDDYSTONE_URL_PREFIXES_LENGTH; i++)
+ {
+ size_t prefixLen = strlen(EDDYSTONE_URL_PREFIXES[i]);
+ if (strncmp(url, EDDYSTONE_URL_PREFIXES[i], prefixLen) == 0)
+ {
+ urlData[urlDataLength++] = i;
+ url += prefixLen;
+ break;
+ }
+ }
+
+ // Suffix
+ while (*url && (urlDataLength < EDDYSTONE_URL_MAX_LENGTH))
+ {
+ size_t i;
+ for (i = 0; i < EDDYSTONE_URL_SUFFIXES_LENGTH; i++)
+ {
+ size_t suffixLen = strlen(EDDYSTONE_URL_SUFFIXES[i]);
+ if (strncmp(url, EDDYSTONE_URL_SUFFIXES[i], suffixLen) == 0)
+ {
+ urlData[urlDataLength++] = i;
+ url += suffixLen;
+ break;
+ }
+ }
+
+ // Catch the default case where the suffix doesn't match a preset ones
+ if (i == EDDYSTONE_URL_SUFFIXES_LENGTH)
+ {
+ urlData[urlDataLength++] = *url;
+ ++url;
+ }
+ }
+
+ uint8_t rawFrame[EDDYSTONE_URL_MAX_LENGTH + 4];
+ size_t index = 0;
+ rawFrame[index++] = EDDYSTONE_UUID[0];
+ rawFrame[index++] = EDDYSTONE_UUID[1];
+ rawFrame[index++] = EDDYSTONE_URL_FRAME_TYPE;
+ rawFrame[index++] = calibratedPower;
+ memcpy(rawFrame + index, urlData, urlDataLength);
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, index + urlDataLength);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Set the content of Eddystone URL frames, but accepts a ManagedString as a url.
+ *
+ * @param url The url to broadcast
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitEddystone::setURL(BLEDevice* ble, ManagedString url, int8_t calibratedPower)
+{
+ return setURL(ble, (char *)url.toCharArray(), calibratedPower);
+}
+#endif
+
+#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
+
+/**
+ * Set the content of Eddystone UID frames
+ *
+ * @param uid_namespace the uid namespace. Must 10 bytes long.
+ *
+ * @param uid_instance the uid instance value. Must 6 bytes long.
+ *
+ * @param calibratedPower the transmission range of the beacon (Defaults to: 0xF0 ~10m).
+ *
+ * @note The calibratedPower value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
+ * More information can be found at https://github.com/google/eddystone/tree/master/eddystone-uid#tx-power
+ */
+int MicroBitEddystone::setUID(BLEDevice* ble, const char* uid_namespace, const char* uid_instance, int8_t calibratedPower)
+{
+ if (uid_namespace == NULL || uid_instance == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ char uidData[EDDYSTONE_UID_NAMESPACE_MAX_LENGTH + EDDYSTONE_UID_INSTANCE_MAX_LENGTH];
+
+ // UID namespace
+ memcpy(uidData, uid_namespace, EDDYSTONE_UID_NAMESPACE_MAX_LENGTH);
+
+ // UID instance
+ memcpy(uidData + EDDYSTONE_UID_NAMESPACE_MAX_LENGTH, uid_instance, EDDYSTONE_UID_INSTANCE_MAX_LENGTH);
+
+ uint8_t rawFrame[EDDYSTONE_UID_NAMESPACE_MAX_LENGTH + EDDYSTONE_UID_INSTANCE_MAX_LENGTH + 4];
+ size_t index = 0;
+ rawFrame[index++] = EDDYSTONE_UUID[0];
+ rawFrame[index++] = EDDYSTONE_UUID[1];
+ rawFrame[index++] = EDDYSTONE_UID_FRAME_TYPE;
+ rawFrame[index++] = calibratedPower;
+ memcpy(rawFrame + index, uidData, EDDYSTONE_UID_NAMESPACE_MAX_LENGTH + EDDYSTONE_UID_INSTANCE_MAX_LENGTH);
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, index + EDDYSTONE_UID_NAMESPACE_MAX_LENGTH + EDDYSTONE_UID_INSTANCE_MAX_LENGTH);
+
+ return MICROBIT_OK;
+}
+
+#endif
