Merge branch 'ble-profile' of https://github.com/lancaster-university/microbit-dal into ble-profile

This commit is contained in:
bluetooth-mdw 2015-09-29 11:14:18 +01:00
commit 7459213d10
32 changed files with 1619 additions and 104 deletions

View file

@ -22,6 +22,7 @@
#include "MicroBitPin.h"
#include "MicroBitCompass.h"
#include "MicroBitAccelerometer.h"
#include "MicroBitThermometer.h"
#include "MicroBitMultiButton.h"
#include "MicroBitSerial.h"
@ -36,6 +37,11 @@
#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"
// MicroBit::flags values
@ -100,15 +106,21 @@ class MicroBit
MicroBitMultiButton buttonAB;
MicroBitAccelerometer accelerometer;
MicroBitCompass compass;
MicroBitThermometer thermometer;
//An object of available IO pins on the device
MicroBitIO io;
// Bluetooth related member variables.
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitEventService *ble_event_service;
MicroBitLEDService *ble_led_service;
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitEventService *ble_event_service;
MicroBitLEDService *ble_led_service;
MicroBitAccelerometerService *ble_accelerometer_service;
MicroBitMagnetometerService *ble_magnetometer_service;
MicroBitButtonService *ble_button_service;
MicroBitIOPinService *ble_io_pin_service;
MicroBitTemperatureService *ble_temperature_service;
/**
* Constructor.

View file

@ -36,6 +36,10 @@
#define MMA8653_SAMPLE_RANGES 3
#define MMA8653_SAMPLE_RATES 8
/*
* Accelerometer events
*/
#define MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE 1
struct MMA8653Sample
{

View file

@ -24,19 +24,25 @@ class MicroBitAccelerometerService
*/
MicroBitAccelerometerService(BLEDevice &_ble);
private:
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
void onDataWritten(const GattWriteCallbackParams *params);
private:
/**
* Accelerometer update callback
*/
void accelerometerUpdate(MicroBitEvent e);
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
uint16_t accelerometerDataCharacteristicBuffer[3];
uint8_t accelerometerPeriodCharacteristicBuffer;
uint16_t accelerometerPeriodCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t accelerometerDataCharacteristicHandle;

View file

@ -0,0 +1,54 @@
#ifndef MICROBIT_BUTTON_SERVICE_H
#define MICROBIT_BUTTON_SERVICE_H
#include "MicroBit.h"
// UUIDs for our service and characteristics
extern const uint8_t MicroBitButtonServiceUUID[];
extern const uint8_t MicroBitButtonAServiceDataUUID[];
extern const uint8_t MicroBitButtonBServiceDataUUID[];
/**
* Class definition for a MicroBit BLE Button Service.
* Provides access to live button data via BLE, and provides basic configuration options.
*/
class MicroBitButtonService
{
public:
/**
* Constructor.
* Create a representation of the ButtonService
* @param _ble The instance of a BLE device that we're running on.
*/
MicroBitButtonService(BLEDevice &_ble);
private:
/**
* Button A update callback
*/
void buttonAUpdate(MicroBitEvent e);
/**
* Button B update callback
*/
void buttonBUpdate(MicroBitEvent e);
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
uint8_t buttonADataCharacteristicBuffer;
uint8_t buttonBDataCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t buttonADataCharacteristicHandle;
GattAttribute::Handle_t buttonBDataCharacteristicHandle;
};
#endif

View file

@ -36,12 +36,27 @@
#define MAG_CTRL_REG1 0x10
#define MAG_CTRL_REG2 0x11
/**
* Configuration options
*/
struct MAG3110SampleRateConfig
{
uint32_t sample_period;
uint8_t ctrl_reg1;
};
extern const MAG3110SampleRateConfig MAG3110SampleRate[];
#define MAG3110_SAMPLE_RATES 11
/*
* Compass events
*/
#define MICROBIT_COMPASS_EVT_CAL_REQUIRED 1
#define MICROBIT_COMPASS_EVT_CAL_START 2
#define MICROBIT_COMPASS_EVT_CAL_END 3
#define MICROBIT_COMPASS_EVT_DATA_UPDATE 4
#define MICROBIT_COMPASS_EVT_CONFIG_NEEDED 5
/*
* Status Bits
@ -85,17 +100,17 @@ class MicroBitCompass : public MicroBitComponent
* Used to track asynchronous events in the event bus.
*/
uint16_t address; // I2C address of the magnetmometer.
uint16_t address; // I2C address of the magnetmometer.
uint16_t samplePeriod; // The time between samples, in millseconds.
unsigned long eventStartTime; // used to store the current system clock when async calibration has started
unsigned long eventStartTime; // used to store the current system clock when async calibration has started
public:
CompassSample minSample; // Calibration sample.
CompassSample maxSample; // Calibration sample.
CompassSample average; // Centre point of sample data.
CompassSample sample; // The latest sample data recorded.
DigitalIn int1; // Data ready interrupt.
public:
/**
* Constructor.
@ -116,7 +131,29 @@ class MicroBitCompass : public MicroBitComponent
* @endcode
*/
MicroBitCompass(uint16_t id, uint16_t address);
/**
* Configures the compass for the 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.
*/
void configure();
/**
* Attempts to set the sample rate of the compass 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.
*/
void setPeriod(int period);
/**
* Reads the currently configured sample rate of the compass.
* @return The time between samples, in milliseconds.
*/
int getPeriod();
/**
* Gets the current heading of the device, relative to magnetic north.
* @return the current heading, in degrees.
@ -172,6 +209,12 @@ class MicroBitCompass : public MicroBitComponent
*/
int getZ();
/**
* Reads the currently die temperature of the compass.
* @return The temperature, in degrees celsius.
*/
int readTemperature();
/**
* Perform the asynchronous calibration of the compass.
* This will fire MICROBIT_COMPASS_EVT_CAL_START and MICROBIT_COMPASS_EVT_CAL_END when finished.
@ -257,7 +300,7 @@ class MicroBitCompass : public MicroBitComponent
* @param reg The based address of the 16 bit register to access.
* @return The register value, interpreted as a 8 bi signed value.
*/
int16_t read8(uint8_t reg);
uint8_t read8(uint8_t reg);
};
#endif

View file

@ -16,6 +16,7 @@
#define MICROBIT_ID_ACCELEROMETER 4
#define MICROBIT_ID_COMPASS 5
#define MICROBIT_ID_DISPLAY 6
#define MICROBIT_ID_THERMOMETER 7
//EDGE connector events
#define MICROBIT_IO_PINS 20

View file

@ -157,9 +157,41 @@
// This enables the control and the LED matrix display via BLE.
// Set '1' to enable.
#ifndef MICROBIT_BLE_LED_SERVICE
#define MICROBIT_BLE_LED_SERVICE 1
#define MICROBIT_BLE_LED_SERVICE 0
#endif
// Enable/Disable BLE Service: MicroBitAccelerometerService
// This enables live access to the on board 3 axis accelerometer.
// Set '1' to enable.
#ifndef MICROBIT_BLE_ACCELEROMETER_SERVICE
#define MICROBIT_BLE_ACCELEROMETER_SERVICE 0
#endif
// Enable/Disable BLE Service: MicroBitMagnetometerService
// This enables live access to the on board 3 axis magnetometer.
// Set '1' to enable.
#ifndef MICROBIT_BLE_MAGNETOMETER_SERVICE
#define MICROBIT_BLE_MAGNETOMETER_SERVICE 0
#endif
// Enable/Disable BLE Service: MicroBitButtonService
// This enables live access to the two micro:bit buttons.
// Set '1' to enable.
#ifndef MICROBIT_BLE_BUTTON_SERVICE
#define MICROBIT_BLE_BUTTON_SERVICE 0
#endif
// This enables live access to the two micro:bit buttons.
// Set '1' to enable.
#ifndef MICROBIT_BLE_IO_PIN_SERVICE
#define MICROBIT_BLE_IO_PIN_SERVICE 0
#endif
// This enables live access to the die temperature sensors on the micro:bit.
// Set '1' to enable.
#ifndef MICROBIT_BLE_TEMPERATURE_SERVICE
#define MICROBIT_BLE_TEMPERATURE_SERVICE 0
#endif
// Defines the maximum length strong that can be written to the
// display over BLE.

View file

@ -68,9 +68,6 @@ class MicroBitDFUService
private:
// BLE pairing name of this device, encoded as an integer.
uint32_t flashCode;
// State of paiting process.
bool authenticated;
bool flashCodeRequested;
@ -78,9 +75,12 @@ class MicroBitDFUService
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
// memory for our 8 bit control characteristic.
uint8_t controlByte;
// BLE pairing name of this device, encoded as an integer.
uint32_t flashCode;
GattAttribute::Handle_t microBitDFUServiceControlCharacteristicHandle;
GattAttribute::Handle_t microBitDFUServiceFlashCodeCharacteristicHandle;

View file

@ -7,6 +7,8 @@
extern const uint8_t MicroBitEventServiceUUID[];
extern const uint8_t MicroBitEventServiceMicroBitEventCharacteristicUUID[];
extern const uint8_t MicroBitEventServiceClientEventCharacteristicUUID[];
extern const uint8_t MicroBitEventServiceMicroBitRequirementsCharacteristicUUID[];
extern const uint8_t MicroBitEventServiceClientRequirementsCharacteristicUUID[];
struct EventServiceEvent
{
@ -19,7 +21,7 @@ struct EventServiceEvent
* Class definition for a MicroBit BLE Event Service.
* Provides a _ble gateway onto the MicroBit Message Bus.
*/
class MicroBitEventService
class MicroBitEventService : public MicroBitComponent
{
public:
@ -29,7 +31,13 @@ class MicroBitEventService
* @param BLE The instance of a BLE device that we're running on.
*/
MicroBitEventService(BLEDevice &_ble);
/**
* Periodic callback from MicroBit scheduler.
* If we're no longer connected, remove any registered Message Bus listeners.
*/
virtual void idleTick();
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
@ -40,17 +48,32 @@ class MicroBitEventService
*/
void onMicroBitEvent(MicroBitEvent evt);
/**
* read callback on microBitRequirements characteristic.
* Used to iterate through the events that the code on this micro:bit is interested in.
*/
void onRequirementsRead(GattReadAuthCallbackParams *params);
private:
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
// memory for our event characteristics.
EventServiceEvent clientEventBuffer;
EventServiceEvent microBitEventBuffer;
EventServiceEvent microBitRequirementsBuffer;
EventServiceEvent clientRequirementsBuffer;
// handles on this service's characterisitics.
GattAttribute::Handle_t microBitEventCharacteristicHandle;
GattAttribute::Handle_t clientRequirementsCharacteristicHandle;
GattAttribute::Handle_t clientEventCharacteristicHandle;
GattCharacteristic *microBitRequirementsCharacteristic;
// Message bus offset last sent to the client...
uint16_t messageBusListenerOffset;
};

111
inc/MicroBitIOPinService.h Normal file
View file

@ -0,0 +1,111 @@
#ifndef MICROBIT_IO_PIN_SERVICE_H
#define MICROBIT_IO_PIN_SERVICE_H
#include "MicroBit.h"
#define MICROBIT_IO_PIN_SERVICE_PINCOUNT 20
#define MICROBIT_IO_PIN_SERVICE_DATA_SIZE 10
// UUIDs for our service and characteristics
extern const uint8_t MicroBitIOPinServiceUUID[];
extern const uint8_t MicroBitIOPinServiceADConfigurationUUID[];
extern const uint8_t MicroBitIOPinServiceIOConfigurationUUID[];
extern const uint8_t MicroBitIOPinServiceDataUUID[];
extern MicroBitPin * const MicroBitIOPins[];
/**
* Name value pair definition, as used to read abd write pin values over BLE.
*/
struct IOData
{
uint8_t pin;
uint8_t value;
};
/**
* Class definition for a MicroBit BLE IOPin Service.
* Provides access to live ioPin data via BLE, and provides basic configuration options.
*/
class MicroBitIOPinService : public MicroBitComponent
{
public:
/**
* Constructor.
* Create a representation of the IOPinService
* @param _ble The instance of a BLE device that we're running on.
*/
MicroBitIOPinService(BLEDevice &_ble);
/**
* periodic callback from MicroBit scheduler.
* Check if any of the pins we're watching need updating. Apply a BLE NOTIFY if so...
*/
virtual void idleTick();
private:
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
void onDataWritten(const GattWriteCallbackParams *params);
/**
* Callback. invoked when the BLE data characteristic is read.
* reads all the pins marked as inputs, and updates the data stored in the BLE stack.
*/
void onDataRead(GattReadAuthCallbackParams *params);
/**
* Determines if the given pin was configured as a digital pin by the BLE ADPinConfigurationCharacterisitic.
*
* @param pin the enumeration of the pin to test
* @return 1 if this pin is configured as a digital value, 0 otherwise
*/
int isDigital(int i);
/**
* Determines if the given pin was configured as an analog pin by the BLE ADPinConfigurationCharacterisitic.
*
* @param pin the enumeration of the pin to test
* @return 1 if this pin is configured as a analog value, 0 otherwise
*/
int isAnalog(int i);
/**
* Determines if the given pin was configured as an input by the BLE IOPinConfigurationCharacterisitic.
*
* @param pin the enumeration of the pin to test
* @return 1 if this pin is configured as an input, 0 otherwise
*/
int isInput(int i);
/**
* Determines if the given pin was configured as output by the BLE IOPinConfigurationCharacterisitic.
*
* @param pin the enumeration of the pin to test
* @return 1 if this pin is configured as an output, 0 otherwise
*/
int isOutput(int i);
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
uint32_t ioPinServiceADCharacteristicBuffer;
uint32_t ioPinServiceIOCharacteristicBuffer;
IOData ioPinServiceDataCharacteristicBuffer[MICROBIT_IO_PIN_SERVICE_DATA_SIZE];
// Historic information about our pin data data.
uint8_t ioPinServiceIOData[MICROBIT_IO_PIN_SERVICE_PINCOUNT];
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t ioPinServiceADCharacteristicHandle;
GattAttribute::Handle_t ioPinServiceIOCharacteristicHandle;
GattCharacteristic *ioPinServiceDataCharacteristic;
};
#endif

View file

@ -5,7 +5,7 @@
#include "MicroBitEvent.h"
#include "MemberFunctionCallback.h"
// MessageBusListener flags...
// MicroBitListener flags...
#define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001
#define MESSAGE_BUS_LISTENER_METHOD 0x0002
#define MESSAGE_BUS_LISTENER_BUSY 0x0004
@ -13,6 +13,7 @@
#define MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY 0x0010
#define MESSAGE_BUS_LISTENER_DROP_IF_BUSY 0x0020
#define MESSAGE_BUS_LISTENER_NONBLOCKING 0x0040
#define MESSAGE_BUS_LISTENER_URGENT 0x0080
struct MicroBitListener
{

View file

@ -0,0 +1,63 @@
#ifndef MICROBIT_MAGNETOMETER_SERVICE_H
#define MICROBIT_MAGNETOMETER_SERVICE_H
#include "MicroBit.h"
// UUIDs for our service and characteristics
extern const uint8_t MicroBitMagnetometerServiceUUID[];
extern const uint8_t MicroBitMagnetometerServiceDataUUID[];
extern const uint8_t MicroBitMagnetometerServiceBearingUUID[];
extern const uint8_t MicroBitMagnetometerServicePeriodUUID[];
/**
* Class definition for a MicroBit BLE Magnetometer Service.
* Provides access to live magnetometer data via BLE, and provides basic configuration options.
*/
class MicroBitMagnetometerService
{
public:
/**
* Constructor.
* Create a representation of the MagnetometerService
* @param _ble The instance of a BLE device that we're running on.
*/
MicroBitMagnetometerService(BLEDevice &_ble);
private:
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
void onDataWritten(const GattWriteCallbackParams *params);
/**
* Magnetometer update callback
*/
void magnetometerUpdate(MicroBitEvent e);
/**
* Sample Period Change Needed callback.
* Reconfiguring the magnetometer can to a REALLY long time (sometimes even seconds to complete)
* So we do this in the background when necessary, through this event handler.
*/
void samplePeriodUpdateNeeded(MicroBitEvent e);
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit control characteristics.
int16_t magnetometerDataCharacteristicBuffer[3];
uint16_t magnetometerBearingCharacteristicBuffer;
uint16_t magnetometerPeriodCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t magnetometerDataCharacteristicHandle;
GattAttribute::Handle_t magnetometerBearingCharacteristicHandle;
GattAttribute::Handle_t magnetometerPeriodCharacteristicHandle;
};
#endif

View file

@ -64,8 +64,10 @@ class MicroBitMessageBus : public MicroBitComponent
* or the constructors provided by MicroBitEvent.
*
* @param evt The event to send.
* @param mask The type of listeners to process (optional). Matches MicroBitListener flags. If not defined, all standard listeners will be processed.
* @return The 1 if all matching listeners were processed, 0 if further processing is required.
*/
void process(MicroBitEvent evt);
int process(MicroBitEvent &evt, uint32_t mask = MESSAGE_BUS_LISTENER_REENTRANT | MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY | MESSAGE_BUS_LISTENER_DROP_IF_BUSY | MESSAGE_BUS_LISTENER_NONBLOCKING);
/**
* Register a listener function.
@ -203,6 +205,13 @@ class MicroBitMessageBus : public MicroBitComponent
template <typename T>
void ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent));
/**
* Returns the microBitListener with the given position in our list.
* @param n The position in the list to return.
* @return the MicroBitListener at postion n in the list, or NULL if the position is invalid.
*/
MicroBitListener *elementAt(int n);
/**
* Returns a 'nonce' for use with the NONCE_ID channel of the message bus.
*/

View file

@ -131,6 +131,30 @@ class MicroBitPin : public MicroBitComponent
*/
int getAnalogValue();
/**
* Determines if this IO pin is currently configured as an input.
* @return 1 if pin is an analog or digital input, 0 otherwise.
*/
int isInput();
/**
* Determines if this IO pin is currently configured as an output.
* @return 1 if pin is an analog or digital output, 0 otherwise.
*/
int isOutput();
/**
* Determines if this IO pin is currently configured for digital use.
* @return 1 if pin is digital, 0 otherwise.
*/
int isDigital();
/**
* Determines if this IO pin is currently configured for analog use.
* @return 1 if pin is analog, 0 otherwise.
*/
int isAnalog();
/**
* Configures this IO pin as a makey makey style touch sensor (if necessary) and tests its current debounced state.
* @return 1 if pin is touched, 0 otherwise.

View file

@ -0,0 +1,45 @@
#ifndef MICROBIT_TEMPERATURE_SERVICE_H
#define MICROBIT_TEMPERATURE_SERVICE_H
#include "MicroBit.h"
// UUIDs for our service and characteristics
extern const uint8_t MicroBitTemperatureServiceUUID[];
extern const uint8_t MicroBitTemperatureServiceDataUUID[];
/**
* Class definition for a MicroBit BLE Temperture Service.
* Provides access to live temperature data via BLE.
*/
class MicroBitTemperatureService
{
public:
/**
* Constructor.
* Create a representation of the TempertureService
* @param _ble The instance of a BLE device that we're running on.
*/
MicroBitTemperatureService(BLEDevice &_ble);
/**
* Temperature update callback
*/
void temperatureUpdate(MicroBitEvent e);
private:
// Bluetooth stack we're running on.
BLEDevice &ble;
// memory for our 8 bit temperature characteristic.
int8_t temperatureDataCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t temperatureDataCharacteristicHandle;
};
#endif

102
inc/MicroBitThermometer.h Normal file
View file

@ -0,0 +1,102 @@
#ifndef MICROBIT_THERMOMETER_H
#define MICROBIT_THERMOMETER_H
#include "mbed.h"
#include "MicroBitComponent.h"
#define MICROBIT_THERMOMETER_PERIOD 1000
#define MAG3110_SAMPLE_RATES 11
/*
* Temperature events
*/
#define MICROBIT_THERMOMETER_EVT_UPDATE 1
/**
* Class definition for MicroBit Thermometer.
*
* Infers and stores the ambient temoperature based on the surface temperature
* of the various chips on the micro:bit.
*
*/
class MicroBitThermometer : public MicroBitComponent
{
unsigned long sampleTime;
uint32_t samplePeriod;
int16_t temperature;
public:
/**
* Constructor.
* Create new object that can sense temperature.
* @param id the ID of the new MicroBitThermometer object.
*
* Example:
* @code
* thermometer(MICROBIT_ID_THERMOMETER);
* @endcode
*
* Possible Events:
* @code
* MICROBIT_THERMOMETER_EVT_CHANGED
* @endcode
*/
MicroBitThermometer(uint16_t id);
/**
* Set the sample rate at which the temperatureis read (in ms).
* n.b. the temperature is alwasy read in the background, so wis only updated
* when the processor is idle, or when the temperature is explicitly read.
* The default sample period is 1 second.
* @param period the requested time between samples, in milliseconds.
*/
void setPeriod(int period);
/**
* Reads the currently configured sample rate of the thermometer.
* @return The time between samples, in milliseconds.
*/
int getPeriod();
/**
* Gets the current temperature of the microbit.
* @return the current temperature, in degrees celsius.
*
* Example:
* @code
* uBit.thermometer.getTemperature();
* @endcode
*/
int getTemperature();
/**
* Periodic callback from MicroBit idle thread.
* Check if any data is ready for reading by checking the interrupt.
*/
virtual void idleTick();
/**
* Indicates if we'd like some processor time to sense the temperature. 0 means we're not due to read the tmeperature yet.
* @returns 1 if we'd like some processor time, 0 otherwise.
*/
virtual int isIdleCallbackNeeded();
private:
/**
* Determines if we're due to take another temeoratur reading
* @return 1 if we're due to take a temperature reading, 0 otherwise.
*/
int isSampleNeeded();
/**
* Updates our recorded temeprature from the many sensors on the micro:bit!
*/
void updateTemperature();
};
#endif

View file

@ -18,6 +18,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
"MicroBitFiber.cpp"
"ManagedString.cpp"
"MicroBitAccelerometer.cpp"
"MicroBitThermometer.cpp"
"MicroBitIO.cpp"
"MicroBitCompat.cpp"
"MicroBitImage.cpp"
@ -32,6 +33,10 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
"ble-services/MicroBitEventService.cpp"
"ble-services/MicroBitLEDService.cpp"
"ble-services/MicroBitAccelerometerService.cpp"
"ble-services/MicroBitMagnetometerService.cpp"
"ble-services/MicroBitButtonService.cpp"
"ble-services/MicroBitIOPinService.cpp"
"ble-services/MicroBitTemperatureService.cpp"
)
if (YOTTA_CFG_MICROBIT_CONFIGFILE)

View file

@ -59,6 +59,7 @@ MicroBit::MicroBit() :
buttonAB(MICROBIT_ID_BUTTON_AB,MICROBIT_ID_BUTTON_A,MICROBIT_ID_BUTTON_B),
accelerometer(MICROBIT_ID_ACCELEROMETER, MMA8653_DEFAULT_ADDR),
compass(MICROBIT_ID_COMPASS, MAG3110_DEFAULT_ADDR),
thermometer(MICROBIT_ID_THERMOMETER),
io(MICROBIT_ID_IO_P0,MICROBIT_ID_IO_P1,MICROBIT_ID_IO_P2,
MICROBIT_ID_IO_P3,MICROBIT_ID_IO_P4,MICROBIT_ID_IO_P5,
MICROBIT_ID_IO_P6,MICROBIT_ID_IO_P7,MICROBIT_ID_IO_P8,
@ -121,6 +122,26 @@ void MicroBit::init()
ble_led_service = new MicroBitLEDService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_ACCELEROMETER_SERVICE)
ble_accelerometer_service = new MicroBitAccelerometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_MAGNETOMETER_SERVICE)
ble_magnetometer_service = new MicroBitMagnetometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_BUTTON_SERVICE)
ble_button_service = new MicroBitButtonService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_IO_PIN_SERVICE)
ble_io_pin_service = new MicroBitIOPinService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_TEMPERATURE_SERVICE)
ble_temperature_service = new MicroBitTemperatureService(*ble);
#endif
// 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));

View file

@ -144,7 +144,6 @@ int MicroBitAccelerometer::whoAmI()
void MicroBitAccelerometer::update()
{
int8_t data[6];
static int count=0;
readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6);
@ -174,7 +173,8 @@ void MicroBitAccelerometer::update()
sample.y *= this->sampleRange;
sample.z *= this->sampleRange;
//TODO: Issue an event.
// Indicate that a new sample is available
MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE);
};
/**
@ -263,7 +263,7 @@ int MicroBitAccelerometer::getZ()
{
return sample.z;
}
/**
* periodic callback from MicroBit clock.

View file

@ -23,23 +23,21 @@ MicroBitCompass::MicroBitCompass(uint16_t id, uint16_t address) : average(), sam
this->id = id;
this->address = address;
//we presume it's calibrated until the average values are read.
//we presume the device calibrated until the average values are read.
this->status = 0x01;
//initialise eventStartTime to 0
this->eventStartTime = 0;
// Enable automatic reset after each sample;
writeCommand(MAG_CTRL_REG2, 0xA0);
// Select 10Hz update rate, with oversampling. Also enables the device.
writeCommand(MAG_CTRL_REG1, 0x61);
// Select 10Hz update rate, with oversampling, and enable the device.
this->samplePeriod = 100;
this->configure();
//fetch our previous average values
average.x = read16(MAG_OFF_X_MSB);
average.y = read16(MAG_OFF_Y_MSB);
average.z = read16(MAG_OFF_Z_MSB);
if(average.x == 0 && average.y == 0 && average.z == 0)
status &= ~MICROBIT_COMPASS_STATUS_CALIBRATED;
@ -106,9 +104,9 @@ int16_t MicroBitCompass::read16(uint8_t reg)
* @param reg The based address of the 16 bit register to access.
* @return The register value, interpreted as a 8 bi signed value.
*/
int16_t MicroBitCompass::read8(uint8_t reg)
uint8_t MicroBitCompass::read8(uint8_t reg)
{
int8_t data;
uint8_t data;
data = 0;
readCommand(reg, (uint8_t*) &data, 1);
@ -131,6 +129,7 @@ int MicroBitCompass::heading()
{
if(status & MICROBIT_COMPASS_STATUS_CALIBRATING)
return MICROBIT_COMPASS_IS_CALIBRATING;
else if(!(status & MICROBIT_COMPASS_STATUS_CALIBRATED))
{
MicroBitEvent(id, MICROBIT_COMPASS_EVT_CAL_REQUIRED);
@ -174,8 +173,14 @@ void MicroBitCompass::idleTick()
eventStartTime = 0;
calibrateEnd();
}
}
else
{
// Indicate that a new sample is available
MicroBitEvent e(id, MICROBIT_COMPASS_EVT_DATA_UPDATE);
}
}
}
/**
@ -220,6 +225,65 @@ int MicroBitCompass::getZ()
return sample.z;
}
/**
* Configures the compass for the 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.
*/
void MicroBitCompass::configure()
{
const MAG3110SampleRateConfig *actualSampleRate;
// First, take the device offline, so it can be configured.
writeCommand(MAG_CTRL_REG1, 0x00);
// Wait for the part to enter standby mode...
while(this->read8(MAG_SYSMOD) & 0x03)
uBit.sleep(100);
// Find the nearest sample rate to that specified.
actualSampleRate = &MAG3110SampleRate[MAG3110_SAMPLE_RATES-1];
for (int i=MAG3110_SAMPLE_RATES-1; i>=0; i--)
{
if(MAG3110SampleRate[i].sample_period < this->samplePeriod * 1000)
break;
actualSampleRate = &MAG3110SampleRate[i];
}
// OK, we have the correct data. Update our local state.
this->samplePeriod = actualSampleRate->sample_period / 1000;
// Enable automatic reset after each sample;
writeCommand(MAG_CTRL_REG2, 0xA0);
// Bring the device online, with the requested sample frequency.
writeCommand(MAG_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01);
}
/**
* Attempts to set the sample rate of the compass 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.
*/
void MicroBitCompass::setPeriod(int period)
{
this->samplePeriod = period;
this->configure();
}
/**
* Reads the currently configured sample rate of the compass.
* @return The time between samples, in milliseconds.
*/
int MicroBitCompass::getPeriod()
{
return (int)samplePeriod;
}
/**
* Attempts to determine the 8 bit ID from the magnetometer.
* @return the id of the compass (magnetometer)
@ -237,6 +301,18 @@ int MicroBitCompass::whoAmI()
return (int)data;
}
/**
* Reads the currently die temperature of the compass.
* @return The temperature, in degrees celsius.
*/
int MicroBitCompass::readTemperature()
{
int8_t temperature;
readCommand(MAG_DIE_TEMP, (uint8_t *)&temperature, 1);
return temperature;
}
/**
* Perform a calibration of the compass.
* This will fire MICROBIT_COMPASS_EVT_CAL_START.
@ -281,7 +357,7 @@ void MicroBitCompass::calibrateEnd()
status &= ~MICROBIT_COMPASS_STATUS_CALIBRATING;
status |= MICROBIT_COMPASS_STATUS_CALIBRATED;
//Store x, y and z values in persistent storage on the MAG3110
writeCommand(MAG_OFF_X_LSB, (uint8_t)average.x);
writeCommand(MAG_OFF_X_MSB, (uint8_t)(average.x >> 8));
@ -333,6 +409,21 @@ void MicroBitCompass::clearCalibration()
*/
int MicroBitCompass::isIdleCallbackNeeded()
{
//Active HI
// The MAG3110 raises an interrupt line when data is ready, which we sample here.
// The interrupt line is active HI, so simply return the state of the pin.
return int1;
}
const MAG3110SampleRateConfig MAG3110SampleRate[MAG3110_SAMPLE_RATES] = {
{12500, 0x00}, // 80 Hz
{25000, 0x20}, // 40 Hz
{50000, 0x40}, // 20 Hz
{100000, 0x60}, // 10 hz
{200000, 0x80}, // 5 hz
{400000, 0x88}, // 2.5 hz
{800000, 0x90}, // 1.25 hz
{1600000, 0xb0}, // 0.63 hz
{3200000, 0xd0}, // 0.31 hz
{6400000, 0xf0}, // 0.16 hz
{12800000, 0xf8} // 0.08 hz
};

View file

@ -162,7 +162,7 @@ void scheduler_init()
idle = getFiberContext();
idle->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
idle->tcb.LR = (uint32_t) &idle_task;
// Flag that we now have a scheduler running
uBit.flags |= MICROBIT_FLAG_SCHEDULER_RUNNING;
}
@ -224,6 +224,9 @@ void scheduler_event(MicroBitEvent evt)
f = t;
}
// Unregister this event, as we've woken up all the fibers with this match.
uBit.MessageBus.ignore(evt.source, evt.value, scheduler_event);
}
@ -306,6 +309,9 @@ void fiber_wait_for_event(uint16_t id, uint16_t value)
// Add ourselves to the sleep queue. We maintain strict ordering here to reduce lookup times.
queue_fiber(f, &waitQueue);
// Register to receive this event, so we can wake up the fiber when it happens.
uBit.MessageBus.listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_NONBLOCKING);
// Finally, enter the scheduler.
schedule();
}

View file

@ -104,16 +104,25 @@ void async_callback(void *param)
*/
void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
{
MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
int processingComplete;
__disable_irq();
if (evt_queue_tail == NULL)
evt_queue_head = evt_queue_tail = item;
else
evt_queue_tail->next = item;
// Firstly, process all handler regsitered as URGENT. These pre-empt the queue, and are useful for fast, high priority services.
processingComplete = this->process(evt, MESSAGE_BUS_LISTENER_URGENT);
__enable_irq();
if (!processingComplete)
{
// We need to queue this event for later processing...
MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
__disable_irq();
if (evt_queue_tail == NULL)
evt_queue_head = evt_queue_tail = item;
else
evt_queue_tail->next = item;
__enable_irq();
}
}
/**
@ -154,7 +163,7 @@ void MicroBitMessageBus::idleTick()
// Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
while (item)
{
// send the event.
// send the event to all standard event listeners.
this->process(item->evt);
// Free the queue item.
@ -212,43 +221,43 @@ void MicroBitMessageBus::send(MicroBitEvent evt)
* This will attempt to call the event handler directly, but spawn a fiber should that
* event handler attempt a blocking operation.
* @param evt The event to be delivered.
* @param mask The type of listeners to process (optional). Matches MicroBitListener flags. If not defined, all standard listeners will be processed.
* @return The 1 if all matching listeners were processed, 0 if further processing is required.
*/
void MicroBitMessageBus::process(MicroBitEvent evt)
int MicroBitMessageBus::process(MicroBitEvent &evt, uint32_t mask)
{
MicroBitListener *l;
int complete = 1;
l = listeners;
while (l != NULL)
{
if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
{
l->evt = evt;
if(l->flags & mask)
{
l->evt = evt;
// OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
// This is normally only done for trusted system components.
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
// should the event handler attempt a blocking operation, but doesn't have the overhead
// of creating a fiber needlessly. (cool huh?)
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING)
async_callback(l);
// OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
// This is normally only done for trusted system components.
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
// should the event handler attempt a blocking operation, but doesn't have the overhead
// of creating a fiber needlessly. (cool huh?)
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING)
async_callback(l);
else
invoke(async_callback, l);
}
else
invoke(async_callback, l);
{
complete = 0;
}
}
l = l->next;
}
// Finally, forward the event to any other internal subsystems that may be interested.
// We *could* do this through the message bus of course, but this saves additional RAM,
// and procssor time (as we know these are non-blocking calls).
// Wake up any fibers that are blocked on this event
if (uBit.flags & MICROBIT_FLAG_SCHEDULER_RUNNING)
scheduler_event(evt);
// See if this event needs to be propogated through our BLE interface
if (uBit.ble_event_service)
uBit.ble_event_service->onMicroBitEvent(evt);
return complete;
}
/**
@ -365,7 +374,6 @@ int MicroBitMessageBus::add(MicroBitListener *newListener)
if (newListener == NULL)
return 0;
l = listeners;
// Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
@ -450,25 +458,28 @@ int MicroBitMessageBus::remove(MicroBitListener *listener)
// Walk this list of event handlers. Delete any that match the given listener.
while (l != NULL)
{
if (l->id == listener->id && l->value == listener->value && ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD)))
if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))
{
if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
{
// Found a match. Remove from the list.
if (p == NULL)
listeners = l->next;
else
p->next = l->next;
if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
{
// Found a match. Remove from the list.
if (p == NULL)
listeners = l->next;
else
p->next = l->next;
// delete the listener.
MicroBitListener *t = l;
l = l->next;
// delete the listener.
MicroBitListener *t = l;
l = l->next;
delete t;
removed++;
delete t;
removed++;
continue;
continue;
}
}
}
@ -479,3 +490,24 @@ int MicroBitMessageBus::remove(MicroBitListener *listener)
return removed;
}
/**
* Returns the microBitListener with the given position in our list.
* @param n The position in the list to return.
* @return the MicroBitListener at postion n in the list, or NULL if the position is invalid.
*/
MicroBitListener* MicroBitMessageBus::elementAt(int n)
{
MicroBitListener *l = listeners;
while (n > 0)
{
if (l == NULL)
return NULL;
n--;
l = l->next;
}
return l;
}

View file

@ -171,6 +171,42 @@ int MicroBitPin::getAnalogValue()
return ((AnalogIn *)pin)->read_u16();
}
/**
* Determines if this IO pin is currently configured as an input.
* @return 1 if pin is an analog or digital input, 0 otherwise.
*/
int MicroBitPin::isInput()
{
return (status & (IO_STATUS_DIGITAL_IN | IO_STATUS_ANALOG_IN)) == 0 ? 0 : 1;
}
/**
* Determines if this IO pin is currently configured as an output.
* @return 1 if pin is an analog or digital output, 0 otherwise.
*/
int MicroBitPin::isOutput()
{
return (status & (IO_STATUS_DIGITAL_OUT | IO_STATUS_ANALOG_OUT)) == 0 ? 0 : 1;
}
/**
* Determines if this IO pin is currently configured for digital use.
* @return 1 if pin is digital, 0 otherwise.
*/
int MicroBitPin::isDigital()
{
return (status & (IO_STATUS_DIGITAL_IN | IO_STATUS_DIGITAL_OUT)) == 0 ? 0 : 1;
}
/**
* Determines if this IO pin is currently configured for analog use.
* @return 1 if pin is analog, 0 otherwise.
*/
int MicroBitPin::isAnalog()
{
return (status & (IO_STATUS_ANALOG_IN | IO_STATUS_ANALOG_OUT)) == 0 ? 0 : 1;
}
/**
* Configures this IO pin as a makey makey style touch sensor (if necessary) and tests its current debounced state.
* @return 1 if pin is touched, 0 otherwise.

View file

@ -0,0 +1,136 @@
#include "MicroBit.h"
#include "nrf_soc.h"
/**
* Constructor.