Merge branch 'master' into basic-gestures

This commit is contained in:
Joe Finney 2016-01-05 15:50:53 +00:00
commit 17632adafa
18 changed files with 651 additions and 360 deletions

View file

@ -56,9 +56,12 @@ Either (1) the client does not possess the flash code and therefore must somehow
Stage 1 - Client Does Not Possess flash code
------------------------------------------
MicroBit has a special mode of operation or state known currently as "Blue Zone". The device enters this state when rebooted by pressing the reset button whilst at the same time holding down both buttons A and B. The device indicates itself to be in the Blue Zone mode by scrolling "BLUEZONE" across the display.
MicroBit has a special mode of operation or state known currently as "Pairing
Mode". The device enters this state when rebooted by pressing the reset button
whilst at the same time holding down both buttons A and B. The device
indicates itself to be in the pairing mode by scrolling "PAIRING MODE!" across the display.
Once in the Blue Zone state, the MicroBit DFU Service also enables another command via the ControlPoint characteristic known as 'REQUEST_FLASHCODE' which is represented by a value of 0x02.
Once in the pairin mode state, the MicroBit DFU Service also enables another command via the ControlPoint characteristic known as 'REQUEST_FLASHCODE' which is represented by a value of 0x02.
To obtain the flash code the client should enable GATT notifications on the FlashCode characteristic and then write 0x02 to the Control characteristic. The micro:bit will respond by displaying "PAIR?" on the LED display.
@ -66,7 +69,12 @@ The user must now press Button A on the micro:bit. This is an "authorization to
Stage 2 - Client in Possession of flash code
-------------------------------------------
If a device already knows the flashcode, it can connect any time and initiate rebooting into "Nordic DFU mode" by writing the FlashCode characteristic with the previously cached value and then writing the 'ENTER NORDIC BOOTLOADER' command (0x01) to the Control characteristic. The device will reboot into the stock nordic bootloader and then the attached device can interact with that to reflash the device. The device does NOT need to be in Blue Zone mode.
If a device already knows the flashcode, it can connect any time and initiate
rebooting into "Nordic DFU mode" by writing the FlashCode characteristic with
the previously cached value and then writing the 'ENTER NORDIC BOOTLOADER'
command (0x01) to the Control characteristic. The device will reboot into the
stock nordic bootloader and then the attached device can interact with that to
reflash the device. The device does NOT need to be in pairing mode.
Issues for Client Application Developers
----------------------------------------
@ -80,14 +88,16 @@ micro:bit human identifiers
---------------------------
In addition to a secret key (flash code) used in the FOTA process as described, micro:bits have a human readable identifier (public name) which is included in BLE advertising packets and can be discovered and used by human users during any process, including FOTA, where there needs to be a confirmation that the device which is to be interacted with is the one the human intends to interact with. In the context of this document, "interact with" means update using the FOTA procedure.
The public name is generated by the run time from the other half of the Nordic serial number. Humans can discover the public name of a device by switching into BLUEZONE mode. The public name is displayed in a coded but simple graphical form on the LED display and can then be entered into (say) a mobile application screen to verify the device to be updated is the one we mean to update.
The public name is generated by the run time from the other half of the Nordic
serial number. Humans can discover the public name of a device by switching
into pairing mode mode. The public name is displayed in a coded but simple graphical form on the LED display and can then be entered into (say) a mobile application screen to verify the device to be updated is the one we mean to update.
Summary of the FOTA Process
---------------------------
Case 1 - Client does not know the flash code
--------------------------------------------
a) User switches micro:bit into BLUEZONE mode - must do this first since it involves a reboot and will therefore disconnect the client
a) User switches micro:bit into pairing mode - must do this first since it involves a reboot and will therefore disconnect the client
b) Client connects to micro:bit
c) Client discovers MicroBit DFU service
d) Client enables notifications on the MicroBit DFU Service::FlashCode characteristic

View file

@ -32,35 +32,7 @@
#include "MicroBitFiber.h"
#include "MicroBitMessageBus.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 push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include "ble/BLE.h"
/*
* Return to our predefined compiler settings.
*/
#if !defined (__arm)
#pragma GCC diagnostic pop
#endif
#include "ble/services/DeviceInformationService.h"
#include "MicroBitDFUService.h"
#include "MicroBitEventService.h"
#include "MicroBitLEDService.h"
#include "MicroBitAccelerometerService.h"
#include "MicroBitMagnetometerService.h"
#include "MicroBitButtonService.h"
#include "MicroBitIOPinService.h"
#include "MicroBitTemperatureService.h"
#include "ExternalEvents.h"
#include "MicroBitBLEManager.h"
// MicroBit::flags values
#define MICROBIT_FLAG_SCHEDULER_RUNNING 0x00000001
@ -128,8 +100,8 @@ class MicroBit
MicroBitIO io;
// Bluetooth related member variables.
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitBLEManager bleManager;
BLEDevice *ble;
/**
* Constructor.
@ -164,11 +136,6 @@ class MicroBit
*/
void init();
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
*/
void deriveName();
/**
* Return the friendly name for this device.
*
@ -298,12 +265,6 @@ class MicroBit
// code integration a little bit easier for third parties.
extern MicroBit uBit;
//
// BLE callback when an active GATT session with another device is terminated.
// Used to reset state and restart advertising ourselves.
//
void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason);
// Entry point for application programs. Called after the super-main function
// has initialized the device and runtime environment.
extern "C" void app_main();

116
inc/MicroBitBLEManager.h Normal file
View file

@ -0,0 +1,116 @@
#ifndef MICROBIT_BLE_MANAGER_H
#define MICROBIT_BLE_MANAGER_H
#include "mbed.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 push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include "ble/BLE.h"
/*
* Return to our predefined compiler settings.
*/
#if !defined (__arm)
#pragma GCC diagnostic pop
#endif
#include "ble/services/DeviceInformationService.h"
#include "MicroBitDFUService.h"
#include "MicroBitEventService.h"
#include "MicroBitLEDService.h"
#include "MicroBitAccelerometerService.h"
#include "MicroBitMagnetometerService.h"
#include "MicroBitButtonService.h"
#include "MicroBitIOPinService.h"
#include "MicroBitTemperatureService.h"
#include "ExternalEvents.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
/**
* Class definition for the MicroBitBLEManager.
*
*/
class MicroBitBLEManager
{
public:
// The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
BLEDevice *ble;
/**
* Constructor.
*
* Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
* Note that the BLE stack *cannot* be brought up in a static context.
* (the software simply hangs or corrupts itself).
* Hence, we bring it up in an explicit init() method, rather than in the constructor.
*/
MicroBitBLEManager();
/**
* Post constructor initialisation method.
* After *MUCH* pain, it's noted that the BLE stack can't be brought up in a
* static context, so we bring it up here rather than in the constructor.
* n.b. This method *must* be called in main() or later, not before.
*
* Example:
* @code
* uBit.init();
* @endcode
*/
void init(ManagedString deviceName, ManagedString serialNumber);
/**
* 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.
*
* @param display a MicroBitDisplay to use when displaying pairing information.
*/
void pairingMode(MicroBitDisplay &display);
/**
* Method that is called whenever a BLE device disconnects from us.
* The nordic stack stops dvertising whenever a device connects, so we use
* this callback to restart advertising.
*/
void onDisconnectionCallback();
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void showNameHistogram(MicroBitDisplay &display);
/**
* A request to pair has been received from a BLE device.
* If we're in pairing mode, display the passkey to the user.
*/
void pairingRequested(ManagedString passKey);
/**
* A pairing request has been sucesfully completed.
* If we're in pairing mode, display feedback to the user.
*/
void pairingComplete(bool success);
private:
int pairingStatus;
ManagedString passKey;
ManagedString deviceName;
};
#endif

View file

@ -123,7 +123,7 @@
// by enabling/disabling the options below.
//
// n.b. The minimum set of services to enable over the air programming of the device will
// still be brought up in 'BLUEZONE' mode regardless of the settings below.
// still be brought up in pairing mode regardless of the settings below.
//
// Enable/Disable BLE during normal operation.
@ -132,10 +132,16 @@
#define MICROBIT_BLE_ENABLED 1
#endif
// Enable/Disable BLUEZONE mode at power up.
// Enable/Disable BLE pairing mode mode at power up.
// Set '1' to enable.
#ifndef MICROBIT_BLE_BLUEZONE
#define MICROBIT_BLE_BLUEZONE 1
#ifndef MICROBIT_BLE_PAIRING_MODE
#define MICROBIT_BLE_PAIRING_MODE 1
#endif
// Enable/Disable the use of private resolvable addresses.
// Set '1' to enable.
#ifndef MICROBIT_BLE_PRIVATE_ADDRESSES
#define MICROBIT_BLE_PRIVATE_ADDRESSES 0
#endif
// Enable/Disable BLE Service: MicroBitDFU

View file

@ -1,6 +1,6 @@
{
"name": "microbit-dal",
"version": "1.3.9",
"version": "1.4.0",
"license": "Apache2",
"description": "The runtime library for the BBC micro:bit, developed by Lancaster University",
"keywords": [

View file

@ -30,6 +30,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
"MicroBitListener.cpp"
"RefCounted.cpp"
"MemberFunctionCallback.cpp"
"ble-services/MicroBitBLEManager.cpp"
"ble-services/MicroBitDFUService.cpp"
"ble-services/MicroBitEventService.cpp"
"ble-services/MicroBitLEDService.cpp"

View file

@ -1,15 +1,5 @@
#include "MicroBit.h"
char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]";
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED) && CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A";
const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0";
const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
#endif
/**
* custom function for panic for malloc & new due to scoping issue.
*/
@ -19,26 +9,44 @@ void panic(int statusCode)
}
/**
* Perform a hard reset of the micro:bit.
* Callback that performs a hard reset when a BLE GAP disconnect occurs.
* Only used when an explicit reset is invoked locally whilst a BLE connection is in progress.
* This allows for a clean diconnect of the BLE connection before resetting.
*/
void
microbit_reset()
void bleDisconnectionResetCallback(const Gap::DisconnectionCallbackParams_t *)
{
NVIC_SystemReset();
}
/**
* Callback when a BLE GATT disconnect occurs.
* Perform a hard reset of the micro:bit.
* If BLE connected, then try to signal a disconnect first
*/
void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
void
microbit_reset()
{
if(uBit.ble && uBit.ble->getGapState().connected) {
uBit.ble->onDisconnection(bleDisconnectionResetCallback);
uBit.ble->gap().disconnect(Gap::REMOTE_USER_TERMINATED_CONNECTION);
// We should be reset by the disconnection callback, so we wait to
// allow that to happen. If it doesn't happen, then we fall through to the
// hard rest here. (For example there is a race condition where
// the remote device disconnects between us testing the connection
// state and re-setting the disconnection callback).
uBit.sleep(1000);
}
NVIC_SystemReset();
}
void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
{
(void) handle; /* -Wunused-param */
(void) reason; /* -Wunused-param */
uBit.ble->startAdvertising();
}
/**
* Constructor.
* Create a representation of a MicroBit device as a global singleton.
@ -76,7 +84,8 @@ MicroBit::MicroBit() :
MICROBIT_ID_IO_P9,MICROBIT_ID_IO_P10,MICROBIT_ID_IO_P11,
MICROBIT_ID_IO_P12,MICROBIT_ID_IO_P13,MICROBIT_ID_IO_P14,
MICROBIT_ID_IO_P15,MICROBIT_ID_IO_P16,MICROBIT_ID_IO_P19,
MICROBIT_ID_IO_P20)
MICROBIT_ID_IO_P20),
bleManager()
{
}
@ -107,79 +116,25 @@ void MicroBit::init()
// Seed our random number generator
seedRandom();
// Generate the name for our device.
this->deriveName();
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED)
// Start the BLE stack.
ble = new BLEDevice();
ble->init();
ble->onDisconnection(bleDisconnectionCallback);
// Bring up any configured auxiliary services.
#if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
ble_firmware_update_service = new MicroBitDFUService(*ble);
bleManager.init(this->getName(), this->getSerial());
ble = bleManager.ble;
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
// Create a temporary, so that compiler doesn't delete the pointer before DeviceInformationService copies it
ManagedString tmp = getSerial();
DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, tmp.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE)
new MicroBitEventService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_LED_SERVICE)
new MicroBitLEDService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_ACCELEROMETER_SERVICE)
new MicroBitAccelerometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_MAGNETOMETER_SERVICE)
new MicroBitMagnetometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_BUTTON_SERVICE)
new MicroBitButtonService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_IO_PIN_SERVICE)
new MicroBitIOPinService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_TEMPERATURE_SERVICE)
new MicroBitTemperatureService(*ble);
#endif
// Configure for high speed mode where possible.
Gap::ConnectionParams_t fast;
ble->getPreferredConnectionParams(&fast);
fast.minConnectionInterval = 8; // 10 ms
fast.maxConnectionInterval = 16; // 20 ms
fast.slaveLatency = 0;
ble->setPreferredConnectionParams(&fast);
// Setup advertising.
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)MICROBIT_BLE_DEVICE_NAME, sizeof(MICROBIT_BLE_DEVICE_NAME));
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200));
ble->startAdvertising();
#endif
// Start refreshing the Matrix Display
systemTicker.attach(this, &MicroBit::systemTick, MICROBIT_DISPLAY_REFRESH_PERIOD);
}
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
void MicroBit::deriveName()
ManagedString MicroBit::getName()
{
char nameBuffer[MICROBIT_NAME_LENGTH];
const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
{
{'z', 'v', 'g', 'p', 't'},
@ -189,13 +144,12 @@ void MicroBit::deriveName()
{'z', 'v', 'g', 'p', 't'}
};
char *name = MICROBIT_BLE_DEVICE_NAME+15;
// We count right to left, so fast forward the pointer.
// We count right to left, so create a pointer to the end of the buffer.
char *name = nameBuffer;
name += MICROBIT_NAME_LENGTH;
// Derive our name from the nrf51822's unique ID.
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_NAME_CODE_LETTERS;
int h;
@ -208,16 +162,8 @@ void MicroBit::deriveName()
ld *= MICROBIT_NAME_CODE_LETTERS;
*--name = codebook[i][h];
}
}
/**
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
ManagedString MicroBit::getName()
{
return ManagedString(MICROBIT_BLE_DEVICE_NAME+15, MICROBIT_NAME_LENGTH);
return ManagedString(nameBuffer, MICROBIT_NAME_LENGTH);
}
/**

View file

@ -127,20 +127,23 @@ microbit_create_sd_heap(HeapDefinition &heap)
{
#if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
// OK, see how much of the RAM assigned to Soft Device we can reclaim.
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED)
// Reclaim RAM from unusused areas on the BLE stack GATT table.
heap.heap_start = (uint32_t *)MICROBIT_HEAP_BASE_BLE_ENABLED;
heap.heap_end = (uint32_t *)MICROBIT_HEAP_SD_LIMIT;
#else
// Reclaim all the RAM normally reserved for the Nordic SoftDevice.
heap.heap_start = (uint32_t *)MICROBIT_HEAP_BASE_BLE_DISABLED;
heap.heap_end = (uint32_t *)MICROBIT_HEAP_SD_LIMIT;
#endif
microbit_initialise_heap(heap);
return MICROBIT_OK;
#else
return MICROBIT_NOT_SUPPORTED;
heap.heap_start = 0;
heap.heap_end = 0;
#endif
return MICROBIT_OK;
}
int

View file

@ -10,7 +10,7 @@ int main()
// Bring up soft reset button.
resetButton.mode(PullUp);
resetButton.fall(microbit_reset);
#if CONFIG_ENABLED(MICROBIT_DBG)
// For diagnostics. Gives time to open the console window. :-)
@ -30,56 +30,40 @@ int main()
// Bring up fiber scheduler
scheduler_init();
// Bring up random number generator, BLE, display and system timers.
uBit.init();
// Provide time for all threaded initialisers to complete.
uBit.sleep(100);
#if CONFIG_ENABLED(MICROBIT_BLE_BLUEZONE)
#if CONFIG_ENABLED(MICROBIT_BLE_PAIRING_MODE)
// Test if we need to enter BLE pairing mode...
int i=0;
while (uBit.buttonA.isPressed() && uBit.buttonB.isPressed() && i<10)
{
uBit.sleep(100);
i++;
if (i == 10)
{
// OK - we need to enter BLUE ZONE mode.
// Test to see if BLE and the necessary services have been brought up already.
// If not, start them.
// Bring up the BLE stack if it isn't alredy done.
if (!uBit.ble)
{
uBit.ble = new BLEDevice();
uBit.ble->init();
uBit.ble->onDisconnection(bleDisconnectionCallback);
uBit.bleManager.init(uBit.getName(), uBit.getSerial());
// Ensure we're advertising.
uBit.ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
uBit.ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)MICROBIT_BLE_DEVICE_NAME, sizeof(MICROBIT_BLE_DEVICE_NAME));
uBit.ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
uBit.ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200));
uBit.ble->startAdvertising();
}
if (!uBit.ble_firmware_update_service)
uBit.ble_firmware_update_service = new MicroBitDFUService(*uBit.ble);
// enter BLUE ZONE mode.
uBit.ble_firmware_update_service->pair();
// Enter pairing mode, using the LED matrix for any necessary pairing operations
uBit.bleManager.pairingMode(uBit.display);
}
}
#endif
app_main();
// If app_main exits, there may still be other fibers running, registered event handlers etc.
// Simply release this fiber, which will mean we enter the scheduler. Worse case, we then
// sit in the idle task forever, in a power efficient sleep.
release_fiber();
// We should never get here, but just in case.
while(1);
}

View file

@ -29,7 +29,11 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble) :
accelerometerDataCharacteristicBuffer[1] = 0;
accelerometerDataCharacteristicBuffer[2] = 0;
accelerometerPeriodCharacteristicBuffer = uBit.accelerometer.getPeriod();
// Set default security requirements
accelerometerDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
accelerometerPeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&accelerometerDataCharacteristic, &accelerometerPeriodCharacteristic};
GattService service(MicroBitAccelerometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
@ -68,14 +72,15 @@ void MicroBitAccelerometerService::onDataWritten(const GattWriteCallbackParams *
void MicroBitAccelerometerService::accelerometerUpdate(MicroBitEvent e)
{
(void) e; /* -Wunused-parameter */
if (ble.getGapState().connected)
{
accelerometerDataCharacteristicBuffer[0] = uBit.accelerometer.getX();
accelerometerDataCharacteristicBuffer[1] = uBit.accelerometer.getY();
accelerometerDataCharacteristicBuffer[2] = uBit.accelerometer.getZ();
accelerometerDataCharacteristicBuffer[0] = uBit.accelerometer.getX();
accelerometerDataCharacteristicBuffer[1] = uBit.accelerometer.getY();
accelerometerDataCharacteristicBuffer[2] = uBit.accelerometer.getZ();
ble.gattServer().notify(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer));
}
ble.gattServer().notify(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer));
}
}
const uint8_t MicroBitAccelerometerServiceUUID[] = {

View file

@ -0,0 +1,341 @@
#include "MicroBit.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
#include "ble.h"
/*
* Return to our predefined compiler settings.
*/
#if !defined(__arm)
#pragma GCC diagnostic pop
#endif
#define MICROBIT_BLE_ENABLE_BONDING true
#define MICROBIT_BLE_REQUIRE_MITM true
#define MICROBIT_PAIRING_MODE_TIMEOUT 90
#define MICROBIT_PAIRING_FADE_SPEED 4
const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A";
const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0";
const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
/*
* 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
* whilst keeping the code modular.
*/
static MicroBitBLEManager *manager = NULL;
/**
* Callback when a BLE GATT disconnect occurs.
*/
static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
{
(void) reason; /* -Wunused-param */
if (manager)
manager->onDisconnectionCallback();
}
static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
{
(void) handle; /* -Wunused-param */
ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
if (manager)
manager->pairingRequested(passKey);
}
static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
{
(void) handle; /* -Wunused-param */
if (manager)
manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
}
/**
* Constructor.
*
* Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
* Note that the BLE stack *cannot* be brought up in a static context.
* (the software simply hangs or corrupts itself).
* Hence, we bring it up in an explicit init() method, rather than in the constructor.
*/
MicroBitBLEManager::MicroBitBLEManager()
{
manager = this;
this->ble = NULL;
this->pairingStatus = 0;
}
/**
* Method that is called whenever a BLE device disconnects from us.
* The nordic stack stops dvertising whenever a device connects, so we use
* this callback to restart advertising.
*/
void MicroBitBLEManager::onDisconnectionCallback()
{
if(ble)
ble->startAdvertising();
}
/**
* Post constructor initialisation method.
* After *MUCH* pain, it's noted that the BLE stack can't be brought up in a
* static context, so we bring it up here rather than in the constructor.
* n.b. This method *must* be called in main() or later, not before.
*
* Example:
* @code
* uBit.init();
* @endcode
*/
void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber)
{
ManagedString BLEName("BBC micro:bit");
this->deviceName = deviceName;
// Start the BLE stack.
ble = new BLEDevice();
ble->init();
// automatically restart advertising after a device disconnects.
ble->onDisconnection(bleDisconnectionCallback);
// configure the stack to hold on to CPU during critical timing events.
// mbed-classic performs __disabe_irq calls in its timers, which can cause MIC failures
// on secure BLE channels.
ble_common_opt_radio_cpu_mutex_t opt;
opt.enable = 1;
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->setAddress(Gap::ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE, NULL);
#endif
// Setup our security requirements.
ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
ble->securityManager().init(MICROBIT_BLE_ENABLE_BONDING, MICROBIT_BLE_REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY);
// Bring up any configured auxiliary 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);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE)
new MicroBitEventService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_LED_SERVICE)
new MicroBitLEDService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_ACCELEROMETER_SERVICE)
new MicroBitAccelerometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_MAGNETOMETER_SERVICE)
new MicroBitMagnetometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_BUTTON_SERVICE)
new MicroBitButtonService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_IO_PIN_SERVICE)
new MicroBitIOPinService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_TEMPERATURE_SERVICE)
new MicroBitTemperatureService(*ble);
#endif
// Configure for high speed mode where possible.
Gap::ConnectionParams_t fast;
ble->getPreferredConnectionParams(&fast);
fast.minConnectionInterval = 8; // 10 ms
fast.maxConnectionInterval = 16; // 20 ms
fast.slaveLatency = 0;
ble->setPreferredConnectionParams(&fast);
// Setup advertising.
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(200);
ble->startAdvertising();
}
/**
* A request to pair has been received from a BLE device.
* If we're in pairing mode, display the passkey to the user.
*/
void MicroBitBLEManager::pairingRequested(ManagedString passKey)
{
this->passKey = passKey;
this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
}
/**
* A pairing request has been sucesfully completed.
* If we're in pairing mode, display feedback to the user.
*/
void MicroBitBLEManager::pairingComplete(bool success)
{
this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
if(success)
this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
}
/**
* 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.
*/
void MicroBitBLEManager::pairingMode(MicroBitDisplay &display)
{
ManagedString namePrefix("BBC micro:bit [");
ManagedString namePostfix("]");
ManagedString BLEName = namePrefix + deviceName + namePostfix;
ManagedString msg("PAIRING MODE!");
int timeInPairingMode = 0;
int brightness = 255;
int fadeDirection = 0;
// 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);
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(200);
ble->startAdvertising();
// 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);
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 (brightness <= 40)
display.clear();
if (brightness <= 0)
fadeDirection = 1;
if (brightness >= 255)
fadeDirection = 0;
if (uBit.buttonA.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);
uBit.sleep(800);
display.clear();
uBit.sleep(200);
if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
break;
}
uBit.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);
}
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);
}
}
uBit.sleep(30);
timeInPairingMode++;
if (timeInPairingMode >= MICROBIT_PAIRING_MODE_TIMEOUT * 30)
microbit_reset();
}
}
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
{
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
int h;
display.clear();
for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
{
h = (n % d) / ld;
n -= h;
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);
}
}

View file

@ -27,6 +27,10 @@ MicroBitButtonService::MicroBitButtonService(BLEDevice &_ble) :
// Initialise our characteristic values.
buttonADataCharacteristicBuffer = 0;
buttonBDataCharacteristicBuffer = 0;
// Set default security requirements
buttonADataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
buttonBDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&buttonADataCharacteristic, &buttonBDataCharacteristic};
GattService service(MicroBitButtonServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -1,214 +1,106 @@
/**
* Class definition for a MicroBit Device Firmware Update loader.
*
* This is actually just a frontend to a memory resident nordic DFU loader.
* Here we deal with the MicroBit 'pairing' functionality with BLE devices, and
* very basic authentication and authorization.
*
* This implementation is not intended to be fully secure, but rather intends to:
*
* 1. Provide a simple mechanism to identify an individual MicroBit amongst a classroom of others
* 2. Allow BLE devices to discover and cache a passcode that can be used to flash the device over BLE.
* 3. Provide a BLE escape route for programs that 'brick' the MicroBit.
*
* Represents the device as a whole, and includes member variables to that reflect the components of the system.
*/
* Class definition for a MicroBit Device Firmware Update loader.
*
* This is actually just a frontend to a memory resident nordic DFU loader.
*
* We rely on the BLE standard pairing processes to provide encryption and authentication.
* We assume any device that is paied with the micro:bit is authorized to reprogram the device.
*
*/
#include "MicroBit.h"
#include "ble/UUID.h"
#if !defined(__arm)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
/*
* 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...
*/
extern "C" {
#include "dfu_app_handler.h"
}
/*
* Return to our predefined compiler settings.
*/
#if !defined(__arm)
#pragma GCC diagnostic pop
#endif
/**
* Constructor.
* Create a representation of a MicroBit device.
* @param messageBus callback function to receive MicroBitMessageBus events.
*/
* Constructor.
* Create a representation of a MicroBit device.
* @param messageBus callback function to receive MicroBitMessageBus events.
*/
MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
ble(_ble)
ble(_ble)
{
// Opcodes can be issued here to control the MicroBitDFU Service, as defined above.
WriteOnlyGattCharacteristic<uint8_t> microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte);
// Read/Write characteristic to enable unlocking and discovery of the MicroBit's flashcode.
GattCharacteristic microBitDFUServiceFlashCodeCharacteristic(MicroBitDFUServiceFlashCodeCharacteristicUUID, (uint8_t *)&flashCode, 0, sizeof(uint32_t),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
authenticated = false;
flashCodeRequested = false;
GattCharacteristic microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte, 0, sizeof(uint8_t),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
controlByte = 0x00;
flashCode = 0x00;
GattCharacteristic *characteristics[] = {&microBitDFUServiceControlCharacteristic, &microBitDFUServiceFlashCodeCharacteristic};
// Set default security requirements
microBitDFUServiceControlCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&microBitDFUServiceControlCharacteristic};
GattService service(MicroBitDFUServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
ble.addService(service);
microBitDFUServiceControlCharacteristicHandle = microBitDFUServiceControlCharacteristic.getValueHandle();
microBitDFUServiceFlashCodeCharacteristicHandle = microBitDFUServiceFlashCodeCharacteristic.getValueHandle();
ble.gattServer().write(microBitDFUServiceControlCharacteristicHandle, &controlByte, sizeof(uint8_t));
ble.gattServer().onDataWritten(this, &MicroBitDFUService::onDataWritten);
}
void MicroBitDFUService::onButtonA(MicroBitEvent e)
{
(void) e; /* -Wunused-parameter */
if (flashCodeRequested)
{
releaseFlashCode();
uBit.display.scroll("");
showTick();
flashCodeRequested = false;
authenticated = true;
}
}
void MicroBitDFUService::onButtonB(MicroBitEvent e)
{
(void) e; /* -Wunused-parameter */
uBit.display.scroll("VERSION: TODO");
showNameHistogram();
}
/**
* Begin the pairing process. Typically called when device is powered up with buttons held down.
* Scroll a description on the display, then displays the device ID code as a histogram on the matrix display.
*/
void MicroBitDFUService::pair()
{
uBit.MessageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, this, &MicroBitDFUService::onButtonA);
uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, this, &MicroBitDFUService::onButtonB);
uBit.display.scroll("BLUE ZONE...");
showNameHistogram();
while(1)
{
if (flashCodeRequested)
uBit.display.scroll("PAIR?");
// If our peer disconnects, drop all state.
if ((authenticated || flashCodeRequested) && !ble.getGapState().connected)
{
authenticated = false;
flashCodeRequested = false;
flashCode = 0x00;
}
uBit.sleep(500);
}
}
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
* Callback. Invoked when any of our attributes are written via BLE.
*/
void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
{
if (params->handle == microBitDFUServiceControlCharacteristicHandle)
{
if (params->len < 1)
return;
switch(params->data[0])
if(params->len > 0 && params->data[0] == MICROBIT_DFU_OPCODE_START_DFU)
{
case MICROBIT_DFU_OPCODE_START_DFU:
if (authenticated)
{
uBit.display.scroll("");
uBit.display.clear();
uBit.display.stopAnimation();
uBit.display.clear();
#if CONFIG_ENABLED(MICROBIT_DBG)
uBit.serial.printf(" ACTIVATING BOOTLOADER.\n");
uBit.serial.printf(" ACTIVATING BOOTLOADER.\n");
#endif
bootloader_start();
}
break;
case MICROBIT_DFU_OPCODE_START_PAIR:
#if CONFIG_ENABLED(MICROBIT_DBG)
uBit.serial.printf(" PAIRING REQUESTED.\n");
#endif
flashCodeRequested = true;
break;
// Perform an explicit disconnection to assist our peer to reconnect to the DFU service
ble.disconnect(Gap::LOCAL_HOST_TERMINATED_CONNECTION);
// Call bootloader_start implicitly trough a event handler call
// it is a work around for bootloader_start not being public in sdk 8.1
ble_dfu_t p_dfu;
ble_dfu_evt_t p_evt;
p_dfu.conn_handle = params->connHandle;
p_evt.ble_dfu_evt_type = BLE_DFU_START;
dfu_app_on_dfu_evt(&p_dfu, &p_evt);
}
}
if (params->handle == microBitDFUServiceFlashCodeCharacteristicHandle)
{
if (params->len >= 4)
{
uint32_t lockCode=0;
memcpy(&lockCode, params->data, 4);
if (lockCode == NRF_FICR->DEVICEID[0])
{
#if CONFIG_ENABLED(MICROBIT_DBG)
uBit.serial.printf("MicroBitDFU: FLASHCODE AUTHENTICATED\n");
#endif
authenticated = true;
}else{
authenticated = false;
}
}
}
}
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void MicroBitDFUService::showTick()
{
uBit.display.stopAnimation();
uBit.display.image.setPixelValue(0,3, 255);
uBit.display.image.setPixelValue(1,4, 255);
uBit.display.image.setPixelValue(2,3, 255);
uBit.display.image.setPixelValue(3,2, 255);
uBit.display.image.setPixelValue(4,1, 255);
}
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void MicroBitDFUService::showNameHistogram()
{
uBit.display.stopAnimation();
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
int h;
uBit.display.clear();
for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
{
h = (n % d) / ld;
n -= h;
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
for (int j=0; j<h+1; j++)
uBit.display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
}
}
/**
* Displays the device's ID code as a histogram on the LED matrix display.
*/
void MicroBitDFUService::releaseFlashCode()
{
flashCode = NRF_FICR->DEVICEID[0];
ble.gattServer().notify(microBitDFUServiceFlashCodeCharacteristicHandle,(uint8_t *)&flashCode, sizeof(uint32_t));
}
/**
* UUID definitions for BLE Services and Characteristics.
*/
* UUID definitions for BLE Services and Characteristics.
*/
const uint8_t MicroBitDFUServiceUUID[] = {
0xe9,0x5d,0x93,0xb0,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
@ -218,6 +110,3 @@ const uint8_t MicroBitDFUServiceControlCharacteristicUUID[] = {
0xe9,0x5d,0x93,0xb1,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
};
const uint8_t MicroBitDFUServiceFlashCodeCharacteristicUUID[] = {
0xe9,0x5d,0x93,0xb2,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
};

View file

@ -34,6 +34,12 @@ MicroBitEventService::MicroBitEventService(BLEDevice &_ble) :
messageBusListenerOffset = 0;
// Set default security requirements
microBitEventCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
clientEventCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
clientRequirementsCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
microBitRequirementsCharacteristic->requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&microBitEventCharacteristic, &clientEventCharacteristic, &clientRequirementsCharacteristic, microBitRequirementsCharacteristic};
GattService service(MicroBitEventServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -30,6 +30,11 @@ MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble) :
ioPinServiceADCharacteristicBuffer = 0;
ioPinServiceIOCharacteristicBuffer = 0;
memset(ioPinServiceIOData, 0, sizeof(ioPinServiceIOData));
// Set default security requirements
ioPinServiceADCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
ioPinServiceIOCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
ioPinServiceDataCharacteristic->requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&ioPinServiceADCharacteristic, &ioPinServiceIOCharacteristic, ioPinServiceDataCharacteristic};
GattService service(MicroBitIOPinServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -32,6 +32,11 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) :
matrixCharacteristic.setReadAuthorizationCallback(this, &MicroBitLEDService::onDataRead);
// Set default security requirements
matrixCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
textCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
scrollingSpeedCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&matrixCharacteristic, &textCharacteristic, &scrollingSpeedCharacteristic};
GattService service(MicroBitLEDServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -33,7 +33,12 @@ MicroBitMagnetometerService::MicroBitMagnetometerService(BLEDevice &_ble) :
magnetometerDataCharacteristicBuffer[2] = 0;
magnetometerBearingCharacteristicBuffer = 0;
magnetometerPeriodCharacteristicBuffer = uBit.compass.getPeriod();
// Set default security requirements
magnetometerDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
magnetometerBearingCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
magnetometerPeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&magnetometerDataCharacteristic, &magnetometerBearingCharacteristic, &magnetometerPeriodCharacteristic};
GattService service(MicroBitMagnetometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));

View file

@ -26,7 +26,11 @@ MicroBitTemperatureService::MicroBitTemperatureService(BLEDevice &_ble) :
// Initialise our characteristic values.
temperatureDataCharacteristicBuffer = 0;
temperaturePeriodCharacteristicBuffer = uBit.thermometer.getPeriod();
// Set default security requirements
temperatureDataCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
temperaturePeriodCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic, &temperaturePeriodCharacteristic};
GattService service(MicroBitTemperatureServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));