Merge branch 'master' into compass-autocalibration

This commit is contained in:
Joe Finney 2016-01-09 19:10:04 +00:00
commit 0da7903e24
21 changed files with 1014 additions and 508 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

@ -33,35 +33,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
@ -131,8 +103,8 @@ class MicroBit
MicroBitIO io;
// Bluetooth related member variables.
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitBLEManager bleManager;
BLEDevice *ble;
/**
* Constructor.
@ -167,11 +139,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.
*
@ -301,12 +268,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(const Gap::DisconnectionCallbackParams_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();

View File

@ -6,8 +6,8 @@
#include "MicroBitCoordinateSystem.h"
/**
* Relevant pin assignments
*/
* Relevant pin assignments
*/
#define MICROBIT_PIN_ACCEL_DATA_READY P0_28
/**
@ -35,8 +35,8 @@
/**
* MMA8653 constants
*/
* MMA8653 constants
*/
#define MMA8653_WHOAMI_VAL 0x5A
#define MMA8653_SAMPLE_RANGES 3
@ -45,7 +45,42 @@
/*
* Accelerometer events
*/
#define MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE 1
#define MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE 1
/*
* Gesture events
*/
#define MICROBIT_ACCELEROMETER_EVT_TILT_UP 1
#define MICROBIT_ACCELEROMETER_EVT_TILT_DOWN 2
#define MICROBIT_ACCELEROMETER_EVT_TILT_LEFT 3
#define MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT 4
#define MICROBIT_ACCELEROMETER_EVT_FACE_UP 5
#define MICROBIT_ACCELEROMETER_EVT_FACE_DOWN 6
#define MICROBIT_ACCELEROMETER_EVT_FREEFALL 7
#define MICROBIT_ACCELEROMETER_EVT_3G 8
#define MICROBIT_ACCELEROMETER_EVT_6G 9
#define MICROBIT_ACCELEROMETER_EVT_8G 10
#define MICROBIT_ACCELEROMETER_EVT_SHAKE 11
/*
* Gesture recogniser constants
*/
#define MICROBIT_ACCELEROMETER_REST_TOLERANCE 200
#define MICROBIT_ACCELEROMETER_TILT_TOLERANCE 200
#define MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE 400
#define MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE 1000
#define MICROBIT_ACCELEROMETER_3G_TOLERANCE 3072
#define MICROBIT_ACCELEROMETER_6G_TOLERANCE 6144
#define MICROBIT_ACCELEROMETER_8G_TOLERANCE 8192
#define MICROBIT_ACCELEROMETER_GESTURE_DAMPING 10
#define MICROBIT_ACCELEROMETER_SHAKE_DAMPING 10
#define MICROBIT_ACCELEROMETER_REST_THRESHOLD (MICROBIT_ACCELEROMETER_REST_TOLERANCE * MICROBIT_ACCELEROMETER_REST_TOLERANCE)
#define MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD (MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE * MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE)
#define MICROBIT_ACCELEROMETER_3G_THRESHOLD (MICROBIT_ACCELEROMETER_3G_TOLERANCE * MICROBIT_ACCELEROMETER_3G_TOLERANCE)
#define MICROBIT_ACCELEROMETER_6G_THRESHOLD (MICROBIT_ACCELEROMETER_6G_TOLERANCE * MICROBIT_ACCELEROMETER_6G_TOLERANCE)
#define MICROBIT_ACCELEROMETER_8G_THRESHOLD (MICROBIT_ACCELEROMETER_8G_TOLERANCE * MICROBIT_ACCELEROMETER_8G_TOLERANCE)
#define MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD 4
struct MMA8653Sample
{
@ -70,12 +105,38 @@ struct MMA8653SampleRangeConfig
extern const MMA8653SampleRangeConfig MMA8653SampleRange[];
extern const MMA8653SampleRateConfig MMA8653SampleRate[];
enum BasicGesture
{
GESTURE_NONE,
GESTURE_UP,
GESTURE_DOWN,
GESTURE_LEFT,
GESTURE_RIGHT,
GESTURE_FACE_UP,
GESTURE_FACE_DOWN,
GESTURE_FREEFALL,
GESTURE_3G,
GESTURE_6G,
GESTURE_8G,
GESTURE_SHAKE
};
struct ShakeHistory
{
uint16_t shaken:1,
x:1,
y:1,
z:1,
count:4,
timer:8;
};
/**
* Class definition for MicroBit Accelerometer.
*
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
* Also includes basic data caching and on demand activation.
*/
* Class definition for MicroBit Accelerometer.
*
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
* Also includes basic data caching and on demand activation.
*/
class MicroBitAccelerometer : public MicroBitComponent
{
/**
@ -83,29 +144,33 @@ class MicroBitAccelerometer : public MicroBitComponent
* Used to track asynchronous events in the event bus.
*/
uint16_t address; // I2C address of this accelerometer.
uint16_t samplePeriod; // The time between samples, in milliseconds.
uint8_t sampleRange; // The sample range of the accelerometer in g.
MMA8653Sample sample; // The last sample read.
DigitalIn int1; // Data ready interrupt.
float pitch; // Pitch of the device, in radians.
float roll; // Roll of the device, in radians.
uint16_t address; // I2C address of this accelerometer.
uint16_t samplePeriod; // The time between samples, in milliseconds.
uint8_t sampleRange; // The sample range of the accelerometer in g.
MMA8653Sample sample; // The last sample read.
DigitalIn int1; // Data ready interrupt.
float pitch; // Pitch of the device, in radians.
float roll; // Roll of the device, in radians.
uint8_t sigma; // the number of ticks that the instantaneous gesture has been stable.
BasicGesture lastGesture; // the last, stable gesture recorded.
BasicGesture currentGesture; // the instantaneous, unfiltered gesture detected.
ShakeHistory shake; // State information needed to detect shake events.
public:
/**
* Constructor.
* Create an accelerometer representation with the given ID.
* @param id the ID of the new object.
* @param address the default base address of the accelerometer.
*
* Example:
* @code
* accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR)
* @endcode
*/
* Constructor.
* Create an accelerometer representation with the given ID.
* @param id the ID of the new object.
* @param address the default base address of the accelerometer.
*
* Example:
* @code
* accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR)
* @endcode
*/
MicroBitAccelerometer(uint16_t id, uint16_t address);
/**
* Configures the accelerometer for G range and sample rate defined
* in this object. The nearest values are chosen to those defined
@ -117,26 +182,26 @@ class MicroBitAccelerometer : public MicroBitComponent
int configure();
/**
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
* This is called by the tick() member function, if the interrupt is set.
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the read request fails.
*/
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
* This is called by the tick() member function, if the interrupt is set.
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the read request fails.
*/
int update();
/**
* Attempts to set the sample rate of the accelerometer to the specified value (in ms).
* n.b. the requested rate may not be possible on the hardware. In this case, the
* nearest lower rate is chosen.
* @param period the requested time between samples, in milliseconds.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
*/
* Attempts to set the sample rate of the accelerometer to the specified value (in ms).
* n.b. the requested rate may not be possible on the hardware. In this case, the
* nearest lower rate is chosen.
* @param period the requested time between samples, in milliseconds.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
*/
int setPeriod(int period);
/**
* Reads the currently configured sample rate of the accelerometer.
* @return The time between samples, in milliseconds.
*/
* Reads the currently configured sample rate of the accelerometer.
* @return The time between samples, in milliseconds.
*/
int getPeriod();
/**
@ -155,14 +220,14 @@ class MicroBitAccelerometer : public MicroBitComponent
int getRange();
/**
* Attempts to determine the 8 bit ID from the accelerometer.
* @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
*
* Example:
* @code
* uBit.accelerometer.whoAmI();
* @endcode
*/
* Attempts to determine the 8 bit ID from the accelerometer.
* @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
*
* Example:
* @code
* uBit.accelerometer.whoAmI();
* @endcode
*/
int whoAmI();
/**
@ -224,44 +289,77 @@ class MicroBitAccelerometer : public MicroBitComponent
float getRollRadians();
/**
* periodic callback from MicroBit idle thread.
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
*/
virtual void idleTick();
* Reads the last recorded gesture detected.
* @return The last gesture detected.
*
* Example:
* @code
* if (uBit.accelerometer.getGesture() == SHAKE)
* @endcode
*/
BasicGesture getGesture();
/**
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
*/
* periodic callback from MicroBit idle thread.
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
*/
virtual void idleTick();
/**
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
*/
virtual int isIdleCallbackNeeded();
private:
/**
* Issues a standard, 2 byte I2C command write to the accelerometer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to write to.
* @param value The value to write.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
*/
* Issues a standard, 2 byte I2C command write to the accelerometer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to write to.
* @param value The value to write.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
*/
int writeCommand(uint8_t reg, uint8_t value);
/**
* Issues a read command into the specified buffer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to access.
* @param buffer Memory area to read the data into.
* @param length The number of bytes to read.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
*/
* Issues a read command into the specified buffer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to access.
* @param buffer Memory area to read the data into.
* @param length The number of bytes to read.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
*/
int readCommand(uint8_t reg, uint8_t* buffer, int length);
/**
* Recalculate roll and pitch values for the current sample.
* We only do this at most once per sample, as the necessary trigonemteric functions are rather
* heavyweight for a CPU without a floating point unit...
*/
* Recalculate roll and pitch values for the current sample.
* We only do this at most once per sample, as the necessary trigonemteric functions are rather
* heavyweight for a CPU without a floating point unit...
*/
void recalculatePitchRoll();
/**
*
* Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote
* stability.
*/
void updateGesture();
/**
* Service function. Calculates the current scalar acceleration of the device (x^2 + y^2 + z^2).
* It does not, however, square root the result, as this is a relatively high cost operation.
* This is left to application code should it be needed.
* @return the sum of the square of the acceleration of the device across all axes.
*/
int instantaneousAccelerationSquared();
/**
* Service function. Determines the best guess posture of the device based on instantaneous data.
* This makes no use of historic data, and forms this input to th filter implemented in updateGesture().
* @return A best guess of the curret posture of the device, based on instanataneous data.
*/
BasicGesture instantaneousPosture();
};
#endif

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

@ -42,6 +42,7 @@
#define MICROBIT_ID_IO_P20 25 //SDA
#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton
#define MICROBIT_ID_GESTURE 27 // Gesture events
#define MICROBIT_ID_NOTIFY 1023 // Notfication channel, for general purpose synchronisation
#define MICROBIT_ID_NOTIFY_ONE 1022 // Notfication channel, for general purpose synchronisation

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.10",
"version": "1.4.2",
"license": "Apache2",
"description": "The runtime library for the BBC micro:bit, developed by Lancaster University",
"keywords": [
@ -15,8 +15,8 @@
"homepage": "https://developer.mbed.org/teams/Lancaster-University/code/microbit/",
"dependencies": {
"mbed-classic": "~0.0.4",
"ble": "lancaster-university/BLE_API#master",
"ble-nrf51822": "lancaster-university/nrf51822#master"
"ble": "lancaster-university/BLE_API#v2.1.11",
"ble-nrf51822": "lancaster-university/nrf51822#v2.2.5"
},
"extraIncludes": [
"inc"

View File

@ -31,6 +31,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,18 +9,36 @@ 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
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) reason; /* -Wunused-param */
@ -38,6 +46,7 @@ void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
uBit.ble->startAdvertising();
}
/**
* Constructor.
* Create a representation of a MicroBit device as a global singleton.
@ -75,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()
{
}
@ -106,70 +116,13 @@ 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(200);
ble->startAdvertising();
#endif
// Start refreshing the Matrix Display
systemTicker.attach(this, &MicroBit::systemTick, MICROBIT_DISPLAY_REFRESH_PERIOD);
@ -310,10 +263,13 @@ void MicroBit::compassCalibrator(MicroBitEvent)
}
/**
* 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'},
@ -323,13 +279,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;
@ -342,16 +297,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

@ -1,20 +1,20 @@
/**
* Class definition for MicroBit Accelerometer.
*
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
* Also includes basic data caching and on demand activation.
*/
* Class definition for MicroBit Accelerometer.
*
* Represents an implementation of the Freescale MMA8653 3 axis accelerometer
* Also includes basic data caching and on demand activation.
*/
#include "MicroBit.h"
/**
* Configures the accelerometer for G range and sample rate defined
* in this object. The nearest values are chosen to those defined
* that are supported by the hardware. The instance variables are then
* updated to reflect reality.
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured.
*/
* Configures the accelerometer for G range and sample rate defined
* in this object. The nearest values are chosen to those defined
* that are supported by the hardware. The instance variables are then
* updated to reflect reality.
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured.
*/
int MicroBitAccelerometer::configure()
{
const MMA8653SampleRangeConfig *actualSampleRange;
@ -50,7 +50,7 @@ int MicroBitAccelerometer::configure()
result = writeCommand(MMA8653_CTRL_REG1, 0x00);
if (result != 0)
return MICROBIT_I2C_ERROR;
// Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA!
result = writeCommand(MMA8653_CTRL_REG2, 0x10);
if (result != 0)
@ -70,7 +70,7 @@ int MicroBitAccelerometer::configure()
result = writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg);
if (result != 0)
return MICROBIT_I2C_ERROR;
// Bring the device back online, with 10bit wide samples at the requested frequency.
result = writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01);
if (result != 0)
@ -80,31 +80,31 @@ int MicroBitAccelerometer::configure()
}
/**
* Issues a standard, 2 byte I2C command write to the accelerometer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to write to.
* @param value The value to write.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
*/
* Issues a standard, 2 byte I2C command write to the accelerometer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to write to.
* @param value The value to write.
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
*/
int MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value)
{
uint8_t command[2];
command[0] = reg;
command[1] = value;
return uBit.i2c.write(address, (const char *)command, 2);
}
/**
* Issues a read command into the specified buffer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to access.
* @param buffer Memory area to read the data into.
* @param length The number of bytes to read.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
*/
* Issues a read command into the specified buffer.
* Blocks the calling thread until complete.
*
* @param reg The address of the register to access.
* @param buffer Memory area to read the data into.
* @param length The number of bytes to read.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
*/
int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length)
{
int result;
@ -124,16 +124,16 @@ int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length)
}
/**
* Constructor.
* Create an accelerometer representation with the given ID.
* @param id the ID of the new object.
* @param address the default base address of the accelerometer.
*
* Example:
* @code
* accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR)
* @endcode
*/
* Constructor.
* Create an accelerometer representation with the given ID.
* @param id the ID of the new object.
* @param address the default base address of the accelerometer.
*
* Example:
* @code
* accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR)
* @endcode
*/
MicroBitAccelerometer::MicroBitAccelerometer(uint16_t id, uint16_t address) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY)
{
// Store our identifiers.
@ -145,20 +145,30 @@ MicroBitAccelerometer::MicroBitAccelerometer(uint16_t id, uint16_t address) : sa
this->samplePeriod = 20;
this->sampleRange = 2;
// Initialise gesture history
this->sigma = 0;
this->lastGesture = GESTURE_NONE;
this->currentGesture = GESTURE_NONE;
this->shake.x = 0;
this->shake.y = 0;
this->shake.z = 0;
this->shake.count = 0;
this->shake.timer = 0;
// Configure and enable the accelerometer.
if (this->configure() == MICROBIT_OK)
uBit.flags |= MICROBIT_FLAG_ACCELEROMETER_RUNNING;
}
/**
* Attempts to determine the 8 bit ID from the accelerometer.
* @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
*
* Example:
* @code
* uBit.accelerometer.whoAmI();
* @endcode
*/
* Attempts to determine the 8 bit ID from the accelerometer.
* @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
*
* Example:
* @code
* uBit.accelerometer.whoAmI();
* @endcode
*/
int MicroBitAccelerometer::whoAmI()
{
uint8_t data;
@ -172,11 +182,11 @@ int MicroBitAccelerometer::whoAmI()
}
/**
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
* This is called by the tick() member function, if the interrupt is set!
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the read request fails.
*/
* Reads the acceleration data from the accelerometer, and stores it in our buffer.
* This is called by the tick() member function, if the interrupt is set!
*
* @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the read request fails.
*/
int MicroBitAccelerometer::update()
{
int8_t data[6];
@ -190,7 +200,7 @@ int MicroBitAccelerometer::update()
sample.x = data[0];
sample.y = data[2];
sample.z = data[4];
// Normalize the data in the 0..1024 range.
sample.x *= 8;
sample.y *= 8;
@ -208,15 +218,141 @@ int MicroBitAccelerometer::update()
sample.y *= this->sampleRange;
sample.z *= this->sampleRange;
// Indicat that pitch and roll data is now stale, and needs to be recalculated if needed.
// Indicate that pitch and roll data is now stale, and needs to be recalculated if needed.
status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID;
// Update gesture tracking
updateGesture();
// Indicate that a new sample is available
MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE);
return MICROBIT_OK;
};
/**
* Service function. Calculates the current scalar acceleration of the device (x^2 + y^2 + z^2).
* It does not, however, square root the result, as this is a relatively high cost operation.
* This is left to application code should it be needed.
*
* @return the sum of the square of the acceleration of the device across all axes.
*/
int MicroBitAccelerometer::instantaneousAccelerationSquared()
{
// Use pythagoras theorem to determine the combined force acting on the device.
return (int)sample.x*(int)sample.x + (int)sample.y*(int)sample.y + (int)sample.z*(int)sample.z;
}
/**
* Service function. Determines the best guess posture of the device based on instantaneous data.
* This makes no use of historic data (except for shake), and forms this input to the filter implemented in updateGesture().
*
* @return A best guess of the current posture of the device, based on instantaneous data.
*/
BasicGesture MicroBitAccelerometer::instantaneousPosture()
{
int force = instantaneousAccelerationSquared();
bool shakeDetected = false;
// Test for shake events.
// We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by
// a string acceleration to the right, then we can infer a shake. Similarly, we can do this for each acxis (left/right, up/down, in/out).
//
// If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device
// has been shaken.
if ((sample.x < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (sample.x > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x))
{
shakeDetected = true;
shake.x = !shake.x;
}
if ((sample.y < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (sample.y > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y))
{
shakeDetected = true;
shake.y = !shake.y;
}
if ((sample.z < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (sample.z > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z))
{
shakeDetected = true;
shake.z = !shake.z;
}
if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD && ++shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD)
shake.shaken = 1;
if (++shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING)
{
shake.timer = 0;
if (shake.count > 0)
{
if(--shake.count == 0)
shake.shaken = 0;
}
}
if (shake.shaken)
return GESTURE_SHAKE;
if (force < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD)
return GESTURE_FREEFALL;
if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD)
return GESTURE_3G;
if (force > MICROBIT_ACCELEROMETER_6G_THRESHOLD)
return GESTURE_6G;
if (force > MICROBIT_ACCELEROMETER_8G_THRESHOLD)
return GESTURE_8G;
// Determine our posture.
if (sample.x < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_LEFT;
if (sample.x > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_RIGHT;
if (sample.y < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_DOWN;
if (sample.y > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_UP;
if (sample.z < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_FACE_UP;
if (sample.z > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
return GESTURE_FACE_DOWN;
return GESTURE_NONE;
}
void MicroBitAccelerometer::updateGesture()
{
// Determine what it looks like we're doing based on the latest sample...
BasicGesture g = instantaneousPosture();
// Perform some low pass filtering to reduce jitter from any detected effects
if (g == currentGesture)
{
if (sigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
sigma++;
}
else
{
currentGesture = g;
sigma = 0;
}
// If we've reached threshold, update our record and raise the relevant event...
if (currentGesture != lastGesture && sigma >= MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
{
lastGesture = currentGesture;
MicroBitEvent e(MICROBIT_ID_GESTURE, lastGesture);
}
}
/**
* Attempts to set the sample rate of the accelerometer to the specified value (in ms).
* n.b. the requested rate may not be possible on the hardware. In this case, the
@ -401,9 +537,23 @@ void MicroBitAccelerometer::recalculatePitchRoll()
}
/**
* periodic callback from MicroBit clock.
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
*/
* Reads the last recorded gesture detected.
* @return The last gesture detected.
*
* Example:
* @code
* if (uBit.accelerometer.getGesture() == SHAKE)
* @endcode
*/
BasicGesture MicroBitAccelerometer::getGesture()
{
return lastGesture;
}
/**
* periodic callback from MicroBit clock.
* Check if any data is ready for reading by checking the interrupt flag on the accelerometer
*/
void MicroBitAccelerometer::idleTick()
{
// Poll interrupt line from accelerometer.
@ -414,8 +564,8 @@ void MicroBitAccelerometer::idleTick()
}
/**
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
*/
* Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
*/
int MicroBitAccelerometer::isIdleCallbackNeeded()
{
return !int1;

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(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,19 +1,13 @@
/**
* 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"
@ -41,209 +35,72 @@ extern "C" {
#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
// 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;
// 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;
p_dfu.conn_handle = params->connHandle;
p_evt.ble_dfu_evt_type = BLE_DFU_START;
dfu_app_on_dfu_evt(&p_dfu, &p_evt);
}
break;
case MICROBIT_DFU_OPCODE_START_PAIR:
#if CONFIG_ENABLED(MICROBIT_DBG)
uBit.serial.printf(" PAIRING REQUESTED.\n");
#endif
flashCodeRequested = true;
break;
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
@ -253,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 *));