Merge pull request #231 from bluetooth-mdw/master
Refactored Eddystone support, added UID frame type
This commit is contained in:
commit
614083f5cf
|
@ -35,7 +35,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
* The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
|
* 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...
|
* as a compatability option, but does not support the options used...
|
||||||
*/
|
*/
|
||||||
#if !defined (__arm)
|
#if !defined(__arm)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
#endif
|
#endif
|
||||||
|
@ -44,7 +44,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
/*
|
/*
|
||||||
* Return to our predefined compiler settings.
|
* Return to our predefined compiler settings.
|
||||||
*/
|
*/
|
||||||
#if !defined (__arm)
|
#if !defined(__arm)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -61,23 +61,23 @@ DEALINGS IN THE SOFTWARE.
|
||||||
#include "MicroBitButton.h"
|
#include "MicroBitButton.h"
|
||||||
#include "MicroBitStorage.h"
|
#include "MicroBitStorage.h"
|
||||||
|
|
||||||
#define MICROBIT_BLE_PAIR_REQUEST 0x01
|
#define MICROBIT_BLE_PAIR_REQUEST 0x01
|
||||||
#define MICROBIT_BLE_PAIR_COMPLETE 0x02
|
#define MICROBIT_BLE_PAIR_COMPLETE 0x02
|
||||||
#define MICROBIT_BLE_PAIR_PASSCODE 0x04
|
#define MICROBIT_BLE_PAIR_PASSCODE 0x04
|
||||||
#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08
|
#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08
|
||||||
|
|
||||||
#define MICROBIT_BLE_PAIRING_TIMEOUT 90
|
#define MICROBIT_BLE_PAIRING_TIMEOUT 90
|
||||||
#define MICROBIT_BLE_POWER_LEVELS 8
|
#define MICROBIT_BLE_POWER_LEVELS 8
|
||||||
#define MICROBIT_BLE_MAXIMUM_BONDS 4
|
#define MICROBIT_BLE_MAXIMUM_BONDS 4
|
||||||
#define MICROBIT_BLE_ENABLE_BONDING true
|
#define MICROBIT_BLE_ENABLE_BONDING true
|
||||||
|
|
||||||
#define MICROBIT_BLE_EDDYSTONE_URL_ADV_INTERVAL 400
|
#define MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL 400
|
||||||
|
|
||||||
extern const int8_t MICROBIT_BLE_POWER_LEVEL[];
|
extern const int8_t MICROBIT_BLE_POWER_LEVEL[];
|
||||||
|
|
||||||
struct BLESysAttribute
|
struct BLESysAttribute
|
||||||
{
|
{
|
||||||
uint8_t sys_attr[8];
|
uint8_t sys_attr[8];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BLESysAttributeStore
|
struct BLESysAttributeStore
|
||||||
|
@ -91,13 +91,14 @@ struct BLESysAttributeStore
|
||||||
*/
|
*/
|
||||||
class MicroBitBLEManager : MicroBitComponent
|
class MicroBitBLEManager : MicroBitComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static MicroBitBLEManager *manager;
|
||||||
|
|
||||||
// The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
|
// The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
|
||||||
BLEDevice *ble;
|
BLEDevice *ble;
|
||||||
|
|
||||||
//an instance of MicroBitStorage used to store sysAttrs from softdevice
|
//an instance of MicroBitStorage used to store sysAttrs from softdevice
|
||||||
MicroBitStorage* storage;
|
MicroBitStorage *storage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -109,7 +110,7 @@ class MicroBitBLEManager : MicroBitComponent
|
||||||
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
|
* @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.
|
* Hence, the init() member function should be used to initialise the BLE stack.
|
||||||
*/
|
*/
|
||||||
MicroBitBLEManager(MicroBitStorage& _storage);
|
MicroBitBLEManager(MicroBitStorage &_storage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -121,6 +122,15 @@ class MicroBitBLEManager : MicroBitComponent
|
||||||
*/
|
*/
|
||||||
MicroBitBLEManager();
|
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
|
* Post constructor initialisation method as the BLE stack cannot be brought
|
||||||
* up in a static context.
|
* up in a static context.
|
||||||
|
@ -134,7 +144,7 @@ class MicroBitBLEManager : MicroBitComponent
|
||||||
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
|
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
|
||||||
* @endcode
|
* @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.
|
* Change the output power level of the transmitter to the given value.
|
||||||
|
@ -176,65 +186,85 @@ class MicroBitBLEManager : MicroBitComponent
|
||||||
*/
|
*/
|
||||||
int getBondCount();
|
int getBondCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to pair has been received from a BLE device.
|
* A request to pair has been received from a BLE device.
|
||||||
* If we're in pairing mode, display the passkey to the user.
|
* If we're in pairing mode, display the passkey to the user.
|
||||||
* Also, purge the bonding table if it has reached capacity.
|
* Also, purge the bonding table if it has reached capacity.
|
||||||
*
|
*
|
||||||
* @note for internal use only.
|
* @note for internal use only.
|
||||||
*/
|
*/
|
||||||
void pairingRequested(ManagedString passKey);
|
void pairingRequested(ManagedString passKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pairing request has been sucessfully completed.
|
* A pairing request has been sucessfully completed.
|
||||||
* If we're in pairing mode, display a success or failure message.
|
* If we're in pairing mode, display a success or failure message.
|
||||||
*
|
*
|
||||||
* @note for internal use only.
|
* @note for internal use only.
|
||||||
*/
|
*/
|
||||||
void pairingComplete(bool success);
|
void pairingComplete(bool success);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Periodic callback in thread context.
|
* Periodic callback in thread context.
|
||||||
* We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
|
* 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
|
* Stops any currently running BLE advertisements
|
||||||
*/
|
*/
|
||||||
void stopAdvertising();
|
void stopAdvertising();
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
|
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
|
||||||
/**
|
/**
|
||||||
* Transmits an Eddystone url
|
* Starts Bluetooth advertising of Eddystone URL frames
|
||||||
* @param url: the url to transmit. Must be no longer than the supported eddystone url length
|
* @param url: the url to transmit. Must be no longer than the supported eddystone url length
|
||||||
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
* The value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
|
* The 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-url#tx-power-level
|
* More information can be found at https://github.com/google/eddystone/tree/master/eddystone-url#tx-power-level
|
||||||
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
* @param interval: the advertising interval of the beacon
|
* @param interval: the advertising interval of the beacon
|
||||||
*/
|
*/
|
||||||
void advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_URL_ADV_INTERVAL);
|
void advertiseEddystoneUrl(char *url, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmits a eddystone url, but accepts a ManagedString as a url. For more info see
|
* Starts Bluetooth advertising of Eddystone URL frames, but accepts a ManagedString as a url. For more info see
|
||||||
* advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
* advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
*/
|
*/
|
||||||
void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_URL_ADV_INTERVAL);
|
void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
|
||||||
|
/**
|
||||||
|
* Starts Bluetooth advertising of Eddystone UID frames
|
||||||
|
* (from the Eddystone spec) the namespace portion of the ID may be used to group a particular set of beacons, while the instance ID
|
||||||
|
* identifies individual devices in the group.
|
||||||
|
* @param uid_namespace: the uid namespace. Must 10 bytes long.
|
||||||
|
* @param uid_instance: the uid instance value. Must 6 bytes long.
|
||||||
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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
|
||||||
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
|
* @param interval: the advertising interval of the beacon
|
||||||
|
*/
|
||||||
|
void advertiseEddystoneUid(char *uid_namespace, char *uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
|
* Starts Bluetooth advertising of Eddystone URL frames, but accepts ManagedStrings as the uid parts. For more info see
|
||||||
*
|
* advertiseEddystoneUid(char* uid_namespace, char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
* @param display The display instance used for displaying the histogram.
|
*/
|
||||||
*/
|
void advertiseEddystoneUid(ManagedString uid_namespace, ManagedString uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval = MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL);
|
||||||
void showNameHistogram(MicroBitDisplay &display);
|
#endif
|
||||||
|
|
||||||
int pairingStatus;
|
private:
|
||||||
ManagedString passKey;
|
/**
|
||||||
ManagedString deviceName;
|
* 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);
|
||||||
|
|
||||||
|
int pairingStatus;
|
||||||
|
ManagedString passKey;
|
||||||
|
ManagedString deviceName;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
#define MICROBIT_BLE_EDDYSTONE_URL_ADV_INTERVAL 400
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 transmit. Must be no longer than the supported eddystone url length
|
||||||
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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-url#tx-power-level
|
||||||
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
|
* @param interval: the advertising interval of the beacon
|
||||||
|
*/
|
||||||
|
void setEddystoneUrl(BLEDevice *ble, char *url, int8_t calibratedPower);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of Eddystone URL frames, but accepts a ManagedString as a url. For more info see
|
||||||
|
* setEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
*/
|
||||||
|
void setEddystoneUrl(BLEDevice *ble, ManagedString url, int8_t 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 calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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
|
||||||
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
|
* @param interval: the advertising interval of the beacon
|
||||||
|
*/
|
||||||
|
void setEddystoneUid(BLEDevice *ble, char *uid_namespace, char *uid_instance, int8_t calibratedPower);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of Eddystone URL frames, but accepts a ManagedString as a url. For more info see
|
||||||
|
* setEddystoneUid(char* uid_namespace, char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
*/
|
||||||
|
void setEddystoneUid(BLEDevice *ble, ManagedString uid_namespace, ManagedString uid_instance, int8_t calibratedPower);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
static MicroBitEddystone *_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -266,13 +266,18 @@ extern uint32_t __etext;
|
||||||
#define MICROBIT_BLE_DFU_SERVICE 1
|
#define MICROBIT_BLE_DFU_SERVICE 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Enable/Disable BLE Service: PhysicalWeb
|
// Enable/Disable availability of Eddystone URL APIs
|
||||||
// This enables the physical web apis
|
|
||||||
// Set '1' to enable.
|
// Set '1' to enable.
|
||||||
#ifndef MICROBIT_BLE_EDDYSTONE_URL
|
#ifndef MICROBIT_BLE_EDDYSTONE_URL
|
||||||
#define MICROBIT_BLE_EDDYSTONE_URL 0
|
#define MICROBIT_BLE_EDDYSTONE_URL 0
|
||||||
#endif
|
#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
|
// Enable/Disable BLE Service: MicroBitEventService
|
||||||
// This allows routing of events from the micro:bit message bus over BLE.
|
// This allows routing of events from the micro:bit message bus over BLE.
|
||||||
// Set '1' to enable.
|
// Set '1' to enable.
|
||||||
|
|
|
@ -144,6 +144,10 @@
|
||||||
#define MICROBIT_BLE_EDDYSTONE_URL YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EDDYSTONE_URL
|
#define MICROBIT_BLE_EDDYSTONE_URL YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EDDYSTONE_URL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EDDYSTONE_UID
|
||||||
|
#define MICROBIT_BLE_EDDYSTONE_UID YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EDDYSTONE_UID
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EVENT_SERVICE
|
#ifdef YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EVENT_SERVICE
|
||||||
#define MICROBIT_BLE_EVENT_SERVICE YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EVENT_SERVICE
|
#define MICROBIT_BLE_EVENT_SERVICE YOTTA_CFG_MICROBIT_DAL_BLUETOOTH_EVENT_SERVICE
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,10 +25,10 @@ DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "MicroBitConfig.h"
|
#include "MicroBitConfig.h"
|
||||||
#include "MicroBitBLEManager.h"
|
#include "MicroBitBLEManager.h"
|
||||||
|
#include "MicroBitEddystone.h"
|
||||||
#include "MicroBitStorage.h"
|
#include "MicroBitStorage.h"
|
||||||
#include "MicroBitFiber.h"
|
#include "MicroBitFiber.h"
|
||||||
|
|
||||||
|
|
||||||
/* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
|
/* 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)
|
* 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
|
* The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
|
||||||
|
@ -42,8 +42,7 @@ DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "ble.h"
|
#include "ble.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#include "device_manager.h"
|
#include "device_manager.h"
|
||||||
uint32_t btle_set_gatt_table_size(uint32_t size);
|
uint32_t btle_set_gatt_table_size(uint32_t size);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +54,7 @@ uint32_t btle_set_gatt_table_size(uint32_t size);
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MICROBIT_PAIRING_FADE_SPEED 4
|
#define MICROBIT_PAIRING_FADE_SPEED 4
|
||||||
|
|
||||||
//
|
//
|
||||||
// Local enumeration of valid security modes. Used only to optimise pre‐processor comparisons.
|
// Local enumeration of valid security modes. Used only to optimise pre‐processor comparisons.
|
||||||
|
@ -68,43 +67,34 @@ uint32_t btle_set_gatt_table_size(uint32_t size);
|
||||||
// Required as the MicroBitConfig option is actually an mbed enum, that is not normally comparable at compile time.
|
// 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 __CAT(a, ...) a##__VA_ARGS__
|
||||||
#define SECURITY_MODE(x) __CAT(__,x)
|
#define SECURITY_MODE(x) __CAT(__, x)
|
||||||
#define SECURITY_MODE_IS(x) (SECURITY_MODE(MICROBIT_BLE_SECURITY_LEVEL) == SECURITY_MODE(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_MANUFACTURER = NULL;
|
||||||
const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
|
const char *MICROBIT_BLE_MODEL = "BBC micro:bit";
|
||||||
const char* MICROBIT_BLE_HARDWARE_VERSION = NULL;
|
const char *MICROBIT_BLE_HARDWARE_VERSION = NULL;
|
||||||
const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
|
const char *MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
|
||||||
const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
|
const char *MICROBIT_BLE_SOFTWARE_VERSION = NULL;
|
||||||
const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
|
const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
|
||||||
|
|
||||||
#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_UUID[] = {0xAA, 0xFE};
|
|
||||||
const uint8_t EDDYSTONE_URL_FRAME_TYPE = 0x10;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods.
|
* Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods.
|
||||||
* So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit
|
* 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.
|
* 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. :-(
|
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 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)
|
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");
|
ManagedString key("bleSysAttrs");
|
||||||
|
|
||||||
KeyValuePair* bleSysAttrs = manager->storage->get(key);
|
KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
|
||||||
|
|
||||||
BLESysAttribute attrib;
|
BLESysAttribute attrib;
|
||||||
BLESysAttributeStore attribStore;
|
BLESysAttributeStore attribStore;
|
||||||
|
@ -114,17 +104,17 @@ static void storeSystemAttributes(Gap::Handle_t handle)
|
||||||
sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
|
sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
|
||||||
|
|
||||||
//copy our stored sysAttrs
|
//copy our stored sysAttrs
|
||||||
if(bleSysAttrs != NULL)
|
if (bleSysAttrs != NULL)
|
||||||
{
|
{
|
||||||
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
|
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
|
||||||
delete bleSysAttrs;
|
delete bleSysAttrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if we need to update
|
//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;
|
attribStore.sys_attrs[deviceID] = attrib;
|
||||||
manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore));
|
MicroBitBLEManager::manager->storage->put(key, (uint8_t *)&attribStore, sizeof(attribStore));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,20 +124,20 @@ static void storeSystemAttributes(Gap::Handle_t handle)
|
||||||
*/
|
*/
|
||||||
static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
|
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);
|
storeSystemAttributes(reason->handle);
|
||||||
|
|
||||||
if (manager)
|
if (MicroBitBLEManager::manager)
|
||||||
manager->advertise();
|
MicroBitBLEManager::manager->advertise();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback when a BLE connection is established.
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,23 +148,23 @@ static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *pa
|
||||||
int complete = 0;
|
int complete = 0;
|
||||||
deviceID = 255;
|
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);
|
int ret = dm_handle_get(params->connHandle, &dm_handle);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
deviceID = dm_handle.device_id;
|
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");
|
ManagedString key("bleSysAttrs");
|
||||||
|
|
||||||
KeyValuePair* bleSysAttrs = manager->storage->get(key);
|
KeyValuePair *bleSysAttrs = MicroBitBLEManager::manager->storage->get(key);
|
||||||
|
|
||||||
BLESysAttributeStore attribStore;
|
BLESysAttributeStore attribStore;
|
||||||
BLESysAttribute attrib;
|
BLESysAttribute attrib;
|
||||||
|
|
||||||
if(bleSysAttrs != NULL)
|
if (bleSysAttrs != NULL)
|
||||||
{
|
{
|
||||||
//restore our sysAttrStore
|
//restore our sysAttrStore
|
||||||
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
|
memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
|
||||||
|
@ -186,40 +176,39 @@ static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *pa
|
||||||
|
|
||||||
complete = 1;
|
complete = 1;
|
||||||
|
|
||||||
if(ret == 0)
|
if (ret == 0)
|
||||||
ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
|
ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!complete)
|
if (!complete)
|
||||||
sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
|
sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
|
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)
|
if (MicroBitBLEManager::manager)
|
||||||
manager->pairingRequested(passKey);
|
MicroBitBLEManager::manager->pairingRequested(passKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
|
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);
|
int ret = dm_handle_get(handle, &dm_handle);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
deviceID = dm_handle.device_id;
|
deviceID = dm_handle.device_id;
|
||||||
|
|
||||||
if (manager)
|
if (MicroBitBLEManager::manager)
|
||||||
{
|
{
|
||||||
pairingHandle = handle;
|
pairingHandle = handle;
|
||||||
manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
|
MicroBitBLEManager::manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,12 +222,11 @@ static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager
|
||||||
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
|
* @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.
|
* Hence, the init() member function should be used to initialise the BLE stack.
|
||||||
*/
|
*/
|
||||||
MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage& _storage) :
|
MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage &_storage) : storage(&_storage)
|
||||||
storage(&_storage)
|
|
||||||
{
|
{
|
||||||
manager = this;
|
manager = this;
|
||||||
this->ble = NULL;
|
this->ble = NULL;
|
||||||
this->pairingStatus = 0;
|
this->pairingStatus = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,12 +237,24 @@ MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage& _storage) :
|
||||||
* @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
|
* @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.
|
* Hence, the init() member function should be used to initialise the BLE stack.
|
||||||
*/
|
*/
|
||||||
MicroBitBLEManager::MicroBitBLEManager() :
|
MicroBitBLEManager::MicroBitBLEManager() : storage(NULL)
|
||||||
storage(NULL)
|
|
||||||
{
|
{
|
||||||
manager = this;
|
manager = this;
|
||||||
this->ble = NULL;
|
this->ble = NULL;
|
||||||
this->pairingStatus = 0;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -263,7 +263,7 @@ MicroBitBLEManager::MicroBitBLEManager() :
|
||||||
*/
|
*/
|
||||||
void MicroBitBLEManager::advertise()
|
void MicroBitBLEManager::advertise()
|
||||||
{
|
{
|
||||||
if(ble)
|
if (ble)
|
||||||
ble->gap().startAdvertising();
|
ble->gap().startAdvertising();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,18 +280,18 @@ void MicroBitBLEManager::advertise()
|
||||||
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
|
* bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
|
||||||
* @endcode
|
* @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");
|
ManagedString BLEName("BBC micro:bit");
|
||||||
this->deviceName = deviceName;
|
this->deviceName = deviceName;
|
||||||
|
|
||||||
#if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
|
#if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
|
||||||
ManagedString namePrefix(" [");
|
ManagedString namePrefix(" [");
|
||||||
ManagedString namePostfix("]");
|
ManagedString namePostfix("]");
|
||||||
BLEName = BLEName + namePrefix + deviceName + namePostfix;
|
BLEName = BLEName + namePrefix + deviceName + namePostfix;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start the BLE stack.
|
// Start the BLE stack.
|
||||||
#if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
|
#if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
|
||||||
btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
|
btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
|
||||||
#endif
|
#endif
|
||||||
|
@ -314,16 +314,16 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
|
||||||
sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
|
sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
|
||||||
|
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
|
#if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
|
||||||
// Configure for private addresses, so kids' behaviour can't be easily tracked.
|
// Configure for private addresses, so kids' behaviour can't be easily tracked.
|
||||||
ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
|
ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Setup our security requirements.
|
// Setup our security requirements.
|
||||||
ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
|
ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
|
||||||
ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
|
ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
|
||||||
// @bluetooth_mdw: select either passkey pairing (more secure), "just works" pairing (less secure but nice and simple for the user)
|
// @bluetooth_mdw: select either passkey pairing (more secure), "just works" pairing (less secure but nice and simple for the user)
|
||||||
// or no security
|
// or no security
|
||||||
// Default to passkey pairing with MITM protection
|
// Default to passkey pairing with MITM protection
|
||||||
#if (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_NO_MITM))
|
#if (SECURITY_MODE_IS(SECURITY_MODE_ENCRYPTION_NO_MITM))
|
||||||
// Just Works
|
// Just Works
|
||||||
ble->securityManager().init(enableBonding, false, SecurityManager::IO_CAPS_NONE);
|
ble->securityManager().init(enableBonding, false, SecurityManager::IO_CAPS_NONE);
|
||||||
|
@ -366,13 +366,13 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
|
||||||
// Configure the radio at our default power level
|
// Configure the radio at our default power level
|
||||||
setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
|
setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
|
||||||
|
|
||||||
// Bring up core BLE services.
|
// Bring up core BLE services.
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
|
#if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
|
||||||
new MicroBitDFUService(*ble);
|
new MicroBitDFUService(*ble);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
|
#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
|
#else
|
||||||
(void)serialNumber;
|
(void)serialNumber;
|
||||||
#endif
|
#endif
|
||||||
|
@ -383,16 +383,15 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
|
||||||
(void)messageBus;
|
(void)messageBus;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Configure for high speed mode where possible.
|
// Configure for high speed mode where possible.
|
||||||
Gap::ConnectionParams_t fast;
|
Gap::ConnectionParams_t fast;
|
||||||
ble->getPreferredConnectionParams(&fast);
|
ble->getPreferredConnectionParams(&fast);
|
||||||
fast.minConnectionInterval = 8; // 10 ms
|
fast.minConnectionInterval = 8; // 10 ms
|
||||||
fast.maxConnectionInterval = 16; // 20 ms
|
fast.maxConnectionInterval = 16; // 20 ms
|
||||||
fast.slaveLatency = 0;
|
fast.slaveLatency = 0;
|
||||||
ble->setPreferredConnectionParams(&fast);
|
ble->setPreferredConnectionParams(&fast);
|
||||||
|
|
||||||
// Setup advertising.
|
// Setup advertising.
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
|
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
|
||||||
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
|
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
|
||||||
#else
|
#else
|
||||||
|
@ -407,9 +406,9 @@ void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumb
|
||||||
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
|
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
|
// 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.
|
// 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 whiltelisting is disabled, then we always advertise.
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
|
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
|
||||||
if (whitelist.size > 0)
|
if (whitelist.size > 0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -464,8 +463,8 @@ int MicroBitBLEManager::getBondCount()
|
||||||
void MicroBitBLEManager::pairingRequested(ManagedString passKey)
|
void MicroBitBLEManager::pairingRequested(ManagedString passKey)
|
||||||
{
|
{
|
||||||
// Update our mode to display the passkey.
|
// Update our mode to display the passkey.
|
||||||
this->passKey = passKey;
|
this->passKey = passKey;
|
||||||
this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
|
this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -476,11 +475,11 @@ void MicroBitBLEManager::pairingRequested(ManagedString passKey)
|
||||||
*/
|
*/
|
||||||
void MicroBitBLEManager::pairingComplete(bool success)
|
void MicroBitBLEManager::pairingComplete(bool success)
|
||||||
{
|
{
|
||||||
this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
|
this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
|
||||||
|
|
||||||
if(success)
|
if (success)
|
||||||
{
|
{
|
||||||
this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
|
this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
|
||||||
fiber_add_idle_component(this);
|
fiber_add_idle_component(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -507,7 +506,7 @@ void MicroBitBLEManager::stopAdvertising()
|
||||||
|
|
||||||
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
|
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
|
||||||
/**
|
/**
|
||||||
* Transmits an Eddystone url
|
* Starts Bluetooth advertising of Eddystone URL frames
|
||||||
* @param url: the url to transmit. Must be no longer than the supported eddystone url length
|
* @param url: the url to transmit. Must be no longer than the supported eddystone url length
|
||||||
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
* The value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
|
* The value ranges from -100 to +20 to a resolution of 1. The calibrated power should be binary encoded.
|
||||||
|
@ -515,59 +514,18 @@ void MicroBitBLEManager::stopAdvertising()
|
||||||
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
* @param interval: the advertising interval of the beacon
|
* @param interval: the advertising interval of the beacon
|
||||||
*/
|
*/
|
||||||
void MicroBitBLEManager::advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
void MicroBitBLEManager::advertiseEddystoneUrl(char *url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
{
|
{
|
||||||
int urlDataLength = 0;
|
|
||||||
char urlData[EDDYSTONE_URL_MAX_LENGTH];
|
|
||||||
memset(urlData, 0, EDDYSTONE_URL_MAX_LENGTH);
|
|
||||||
|
|
||||||
if ((url == NULL) || (strlen(url) == 0)) { return; }
|
|
||||||
|
|
||||||
// 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->gap().stopAdvertising();
|
ble->gap().stopAdvertising();
|
||||||
ble->clearAdvertisingPayload();
|
ble->clearAdvertisingPayload();
|
||||||
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
|
|
||||||
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
|
|
||||||
ble->accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, index+urlDataLength);
|
|
||||||
ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
|
ble->setAdvertisingType(connectable ? GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED : GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
|
||||||
ble->setAdvertisingInterval(interval);
|
ble->setAdvertisingInterval(interval);
|
||||||
|
|
||||||
|
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
|
||||||
|
|
||||||
|
MicroBitEddystone::getInstance()->setEddystoneUrl(ble, url, calibratedPower);
|
||||||
|
|
||||||
#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
|
#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
|
||||||
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
|
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
|
||||||
#endif
|
#endif
|
||||||
|
@ -575,7 +533,7 @@ void MicroBitBLEManager::advertiseEddystoneUrl(char* url, int8_t calibratedPower
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transmits a eddystone url, but accepts a ManagedString as a url. For more info see
|
* Starts Bluetooth advertising of Eddystone URL frames, but accepts a ManagedString as a url. For more info see
|
||||||
* advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
* advertiseEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
*/
|
*/
|
||||||
void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
@ -584,6 +542,48 @@ void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool conne
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID)
|
||||||
|
/**
|
||||||
|
* Starts Bluetooth advertising of Eddystone UID frames
|
||||||
|
* (from the Eddystone spec) the namespace portion of the ID may be used to group a particular set of beacons, while the instance ID
|
||||||
|
* identifies individual devices in the group.
|
||||||
|
* @param uid_namespace: the uid namespace. Must 10 bytes long.
|
||||||
|
* @param uid_instance: the uid instance value. Must 6 bytes long.
|
||||||
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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
|
||||||
|
* @param connectable: true to keep bluetooth connectable for other services, false otherwise
|
||||||
|
* @param interval: the advertising interval of the beacon
|
||||||
|
*/
|
||||||
|
void MicroBitBLEManager::advertiseEddystoneUid(char *uid_namespace, 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);
|
||||||
|
|
||||||
|
MicroBitEddystone::getInstance()->setEddystoneUid(ble, uid_namespace, uid_instance, calibratedPower);
|
||||||
|
|
||||||
|
#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
|
||||||
|
ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
|
||||||
|
#endif
|
||||||
|
ble->gap().startAdvertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts Bluetooth advertising of Eddystone UID frames, but accepts a ManagedStrings as a uid args. For more info see
|
||||||
|
* advertiseEddystoneUid(char* uid_namespace, char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
* @return 0 for success or MICROBIT_INVALID_PARAMETER if parameters are not valid
|
||||||
|
*/
|
||||||
|
void advertiseEddystoneUid(ManagedString uid_namespace, ManagedString uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
{
|
||||||
|
advertiseEddystoneUid((char *)uid_namespace.toCharArray(), (char *)uid_instance.toCharArray(), calibratedPower, connectable, interval);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
|
* Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
|
||||||
* of the micro:bit in cases where BLE is disabled during normal operation.
|
* of the micro:bit in cases where BLE is disabled during normal operation.
|
||||||
|
@ -596,21 +596,21 @@ void advertiseEddystoneUrl(ManagedString url, int8_t calibratedPower, bool conne
|
||||||
* bleManager.pairingMode(uBit.display, uBit.buttonA);
|
* bleManager.pairingMode(uBit.display, uBit.buttonA);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& authorisationButton)
|
void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &authorisationButton)
|
||||||
{
|
{
|
||||||
ManagedString namePrefix("BBC micro:bit [");
|
ManagedString namePrefix("BBC micro:bit [");
|
||||||
ManagedString namePostfix("]");
|
ManagedString namePostfix("]");
|
||||||
ManagedString BLEName = namePrefix + deviceName + namePostfix;
|
ManagedString BLEName = namePrefix + deviceName + namePostfix;
|
||||||
|
|
||||||
ManagedString msg("PAIRING MODE!");
|
ManagedString msg("PAIRING MODE!");
|
||||||
|
|
||||||
int timeInPairingMode = 0;
|
int timeInPairingMode = 0;
|
||||||
int brightness = 255;
|
int brightness = 255;
|
||||||
int fadeDirection = 0;
|
int fadeDirection = 0;
|
||||||
|
|
||||||
ble->gap().stopAdvertising();
|
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)
|
#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
|
||||||
BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
|
BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
|
||||||
Gap::Whitelist_t whitelist;
|
Gap::Whitelist_t whitelist;
|
||||||
|
@ -621,7 +621,7 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& a
|
||||||
ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
|
ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
|
||||||
#endif
|
#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->clearAdvertisingPayload();
|
||||||
|
|
||||||
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
|
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
|
||||||
|
@ -632,68 +632,68 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& a
|
||||||
ble->gap().setAdvertisingTimeout(0);
|
ble->gap().setAdvertisingTimeout(0);
|
||||||
ble->gap().startAdvertising();
|
ble->gap().startAdvertising();
|
||||||
|
|
||||||
// Stop any running animations on the display
|
// Stop any running animations on the display
|
||||||
display.stopAnimation();
|
display.stopAnimation();
|
||||||
display.scroll(msg);
|
display.scroll(msg);
|
||||||
|
|
||||||
// Display our name, visualised as a histogram in the display to aid identification.
|
// Display our name, visualised as a histogram in the display to aid identification.
|
||||||
showNameHistogram(display);
|
showNameHistogram(display);
|
||||||
|
|
||||||
while(1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
|
if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
|
||||||
{
|
{
|
||||||
timeInPairingMode = 0;
|
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");
|
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);
|
display.print(arrow, 0, 0, 0);
|
||||||
|
|
||||||
if (fadeDirection == 0)
|
if (fadeDirection == 0)
|
||||||
brightness -= MICROBIT_PAIRING_FADE_SPEED;
|
brightness -= MICROBIT_PAIRING_FADE_SPEED;
|
||||||
else
|
else
|
||||||
brightness += MICROBIT_PAIRING_FADE_SPEED;
|
brightness += MICROBIT_PAIRING_FADE_SPEED;
|
||||||
|
|
||||||
if (brightness <= 40)
|
if (brightness <= 40)
|
||||||
display.clear();
|
display.clear();
|
||||||
|
|
||||||
if (brightness <= 0)
|
if (brightness <= 0)
|
||||||
fadeDirection = 1;
|
fadeDirection = 1;
|
||||||
|
|
||||||
if (brightness >= 255)
|
if (brightness >= 255)
|
||||||
fadeDirection = 0;
|
fadeDirection = 0;
|
||||||
|
|
||||||
if (authorisationButton.isPressed())
|
if (authorisationButton.isPressed())
|
||||||
{
|
{
|
||||||
pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
|
pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
|
||||||
pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
|
pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
|
if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
|
||||||
{
|
{
|
||||||
timeInPairingMode = 0;
|
timeInPairingMode = 0;
|
||||||
display.setBrightness(255);
|
display.setBrightness(255);
|
||||||
for (int i=0; i<passKey.length(); i++)
|
for (int i = 0; i < passKey.length(); i++)
|
||||||
{
|
{
|
||||||
display.image.print(passKey.charAt(i),0,0);
|
display.image.print(passKey.charAt(i), 0, 0);
|
||||||
fiber_sleep(800);
|
fiber_sleep(800);
|
||||||
display.clear();
|
display.clear();
|
||||||
fiber_sleep(200);
|
fiber_sleep(200);
|
||||||
|
|
||||||
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
|
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber_sleep(1000);
|
fiber_sleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
|
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
|
||||||
{
|
{
|
||||||
if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
|
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");
|
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);
|
display.print(tick, 0, 0, 0);
|
||||||
fiber_sleep(15000);
|
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...
|
* Disabled, as the API to return the number of active bonds is not reliable at present...
|
||||||
|
@ -708,20 +708,20 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& a
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
else
|
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");
|
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);
|
display.print(cross, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fiber_sleep(100);
|
fiber_sleep(100);
|
||||||
timeInPairingMode++;
|
timeInPairingMode++;
|
||||||
|
|
||||||
if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
|
if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
|
||||||
microbit_reset();
|
microbit_reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -737,7 +737,7 @@ void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
|
||||||
int h;
|
int h;
|
||||||
|
|
||||||
display.clear();
|
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;
|
h = (n % d) / ld;
|
||||||
|
|
||||||
|
@ -745,7 +745,7 @@ void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
|
||||||
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
|
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
|
||||||
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
|
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
|
||||||
|
|
||||||
for (int j=0; j<h+1; j++)
|
for (int j = 0; j < h + 1; j++)
|
||||||
display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
|
display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH - i - 1, MICROBIT_DFU_HISTOGRAM_HEIGHT - j - 1, 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
MicroBitEddystone *MicroBitEddystone::_instance;
|
||||||
|
|
||||||
|
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 transmit. Must be no longer than the supported eddystone url length
|
||||||
|
* @param calibratedPower: the calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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-url#tx-power-level
|
||||||
|
*/
|
||||||
|
void MicroBitEddystone::setEddystoneUrl(BLEDevice *ble, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of Eddystone URL frames, but accepts a ManagedString as a url. For more info see
|
||||||
|
* setEddystoneUrl(char* url, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
*/
|
||||||
|
void setEddystoneUrl(BLEDevice *ble, ManagedString url, int8_t calibratedPower)
|
||||||
|
{
|
||||||
|
setEddystoneUrl(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 calibrated to transmit at. This is the received power at 0 meters in dBm.
|
||||||
|
* The 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
|
||||||
|
*/
|
||||||
|
void MicroBitEddystone::setEddystoneUid(BLEDevice *ble, char *uid_namespace, char *uid_instance, int8_t calibratedPower)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of Eddystone UID frames, but accepts ManagedStrings as a uid args. For more info see
|
||||||
|
* setEddystoneUid(char* uid_namespace, char* uid_instance, int8_t calibratedPower, bool connectable, uint16_t interval)
|
||||||
|
*/
|
||||||
|
void MicroBitEddystone::setEddystoneUid(BLEDevice *ble, ManagedString uid_namespace, ManagedString uid_instance, int8_t calibratedPower)
|
||||||
|
{
|
||||||
|
setEddystoneUid(ble, (char *)uid_namespace.toCharArray(), (char *)uid_instance.toCharArray(), calibratedPower);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue