diff --git a/inc/MicroBit.h b/inc/MicroBit.h index cdd23d7..9f8a56e 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -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. diff --git a/inc/MicroBitAccelerometer.h b/inc/MicroBitAccelerometer.h index b9cbc4f..7456177 100644 --- a/inc/MicroBitAccelerometer.h +++ b/inc/MicroBitAccelerometer.h @@ -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 { diff --git a/inc/MicroBitAccelerometerService.h b/inc/MicroBitAccelerometerService.h index 65dc5d6..00976af 100644 --- a/inc/MicroBitAccelerometerService.h +++ b/inc/MicroBitAccelerometerService.h @@ -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; diff --git a/inc/MicroBitButtonService.h b/inc/MicroBitButtonService.h new file mode 100644 index 0000000..6e908f0 --- /dev/null +++ b/inc/MicroBitButtonService.h @@ -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 + diff --git a/inc/MicroBitCompass.h b/inc/MicroBitCompass.h index 3863f71..00dc547 100644 --- a/inc/MicroBitCompass.h +++ b/inc/MicroBitCompass.h @@ -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 diff --git a/inc/MicroBitComponent.h b/inc/MicroBitComponent.h index 7779069..f836a36 100644 --- a/inc/MicroBitComponent.h +++ b/inc/MicroBitComponent.h @@ -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 diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 03f4e1f..dd06ebf 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -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. diff --git a/inc/MicroBitDFUService.h b/inc/MicroBitDFUService.h index eaa5850..1d94f32 100644 --- a/inc/MicroBitDFUService.h +++ b/inc/MicroBitDFUService.h @@ -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; diff --git a/inc/MicroBitEventService.h b/inc/MicroBitEventService.h index 1c751ca..3ce1aa6 100644 --- a/inc/MicroBitEventService.h +++ b/inc/MicroBitEventService.h @@ -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; + }; diff --git a/inc/MicroBitIOPinService.h b/inc/MicroBitIOPinService.h new file mode 100644 index 0000000..f4bf4af --- /dev/null +++ b/inc/MicroBitIOPinService.h @@ -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 + diff --git a/inc/MicroBitListener.h b/inc/MicroBitListener.h index 176dd0c..46f2939 100644 --- a/inc/MicroBitListener.h +++ b/inc/MicroBitListener.h @@ -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 { diff --git a/inc/MicroBitMagnetometerService.h b/inc/MicroBitMagnetometerService.h new file mode 100644 index 0000000..ee9595a --- /dev/null +++ b/inc/MicroBitMagnetometerService.h @@ -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 + diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index fdace6a..6c2a747 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -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 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. */ diff --git a/inc/MicroBitPin.h b/inc/MicroBitPin.h index e4ba2fd..83190f1 100644 --- a/inc/MicroBitPin.h +++ b/inc/MicroBitPin.h @@ -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. diff --git a/inc/MicroBitTemperatureService.h b/inc/MicroBitTemperatureService.h new file mode 100644 index 0000000..0720d3c --- /dev/null +++ b/inc/MicroBitTemperatureService.h @@ -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 + diff --git a/inc/MicroBitThermometer.h b/inc/MicroBitThermometer.h new file mode 100644 index 0000000..7048978 --- /dev/null +++ b/inc/MicroBitThermometer.h @@ -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 diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 6f3cc60..2be1c77 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -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) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index 2faec40..e99aa96 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -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)); diff --git a/source/MicroBitAccelerometer.cpp b/source/MicroBitAccelerometer.cpp index c87e117..a849c31 100644 --- a/source/MicroBitAccelerometer.cpp +++ b/source/MicroBitAccelerometer.cpp @@ -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. diff --git a/source/MicroBitCompass.cpp b/source/MicroBitCompass.cpp index 42bb755..b6a192c 100644 --- a/source/MicroBitCompass.cpp +++ b/source/MicroBitCompass.cpp @@ -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 +}; diff --git a/source/MicroBitFiber.cpp b/source/MicroBitFiber.cpp index 636f485..67b3351 100644 --- a/source/MicroBitFiber.cpp +++ b/source/MicroBitFiber.cpp @@ -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(); } diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index 3d7b999..6478945 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -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; +} + diff --git a/source/MicroBitPin.cpp b/source/MicroBitPin.cpp index 9d03e8c..ab463b8 100644 --- a/source/MicroBitPin.cpp +++ b/source/MicroBitPin.cpp @@ -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. diff --git a/source/MicroBitThermometer.cpp b/source/MicroBitThermometer.cpp new file mode 100644 index 0000000..8e1df04 --- /dev/null +++ b/source/MicroBitThermometer.cpp @@ -0,0 +1,136 @@ +#include "MicroBit.h" +#include "nrf_soc.h" + +/** + * 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::MicroBitThermometer(uint16_t id) +{ + this->id = id; + this->samplePeriod = MICROBIT_THERMOMETER_PERIOD; + this->sampleTime = 0; + + uBit.addIdleComponent(this); +} + +/** + * Gets the current temperature of the microbit. + * @return the current temperature, in degrees celsius. + * + * Example: + * @code + * uBit.thermometer.getTemperature(); + * @endcode + */ +int MicroBitThermometer::getTemperature() +{ + if (isSampleNeeded()) + updateTemperature(); + + return temperature; +} + +/** + * 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. + */ +int MicroBitThermometer::isIdleCallbackNeeded() +{ + return isSampleNeeded(); +} +/** + * periodic callback. + * Check once every second or so for a new temperature reading. + */ +void MicroBitThermometer::idleTick() +{ + if (isSampleNeeded()) + updateTemperature(); +} + +/** + * Determines if we're due to take another temeoratur reading + * @return 1 if we're due to take a temperature reading, 0 otherwise. + */ +int MicroBitThermometer::isSampleNeeded() +{ + return ticks >= sampleTime; +} + +/** + * 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 MicroBitThermometer::setPeriod(int period) +{ + samplePeriod = period; +} + +/** + * Reads the currently configured sample rate of the thermometer. + * @return The time between samples, in milliseconds. + */ +int MicroBitThermometer::getPeriod() +{ + return samplePeriod; +} + +/** + * Updates our recorded temperature from the many sensors on the micro:bit! + */ +void MicroBitThermometer::updateTemperature() +{ + int32_t processorTemperature; + + // For now, we just rely on the nrf senesor to be the most accurate. + // The compass module also has a temperature sensor, and has the lowest power consumption, so will run the cooler... + // ...however it isn't trimmed for accuracy during manufacture, so requires calibration. + + if (uBit.ble) + { + // If Bluetooth is enabled, we need to go through the Nordic software to safely do this + sd_temp_get(&processorTemperature); + } + else + { + // Othwerwise, we access the information directly... + uint32_t *TEMP = (uint32_t *)0x4000C508; + + NRF_TEMP->TASKS_START = 1; + + while (NRF_TEMP->EVENTS_DATARDY == 0); + + NRF_TEMP->EVENTS_DATARDY = 0; + + processorTemperature = *TEMP; + + NRF_TEMP->TASKS_STOP = 1; + } + + + // Record our reading... + temperature = processorTemperature / 4; + + // Schedule our next sample. + sampleTime = ticks + samplePeriod; + + // Send an event to indicate that we'e updated our temperature. + MicroBitEvent e(id, MICROBIT_THERMOMETER_EVT_UPDATE); +} + + diff --git a/source/ble-services/MicroBitAccelerometerService.cpp b/source/ble-services/MicroBitAccelerometerService.cpp index 874a3a6..3a2cb08 100644 --- a/source/ble-services/MicroBitAccelerometerService.cpp +++ b/source/ble-services/MicroBitAccelerometerService.cpp @@ -20,7 +20,7 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble) : GattCharacteristic accelerometerDataCharacteristic(MicroBitAccelerometerServiceDataUUID, (uint8_t *)accelerometerDataCharacteristicBuffer, 0, sizeof(accelerometerDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); - GattCharacteristic accelerometerPeriodCharacteristic(MicroBitAccelerometerServicePeriodUUID, (uint8_t *)accelerometerPeriodCharacteristicBuffer, 0, + GattCharacteristic accelerometerPeriodCharacteristic(MicroBitAccelerometerServicePeriodUUID, (uint8_t *)&accelerometerPeriodCharacteristicBuffer, 0, sizeof(accelerometerPeriodCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); @@ -28,7 +28,7 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble) : accelerometerDataCharacteristicBuffer[0] = 0; accelerometerDataCharacteristicBuffer[1] = 0; accelerometerDataCharacteristicBuffer[2] = 0; - accelerometerPeriodCharacteristicBuffer = 0; + accelerometerPeriodCharacteristicBuffer = uBit.accelerometer.getPeriod(); GattCharacteristic *characteristics[] = {&accelerometerDataCharacteristic, &accelerometerPeriodCharacteristic}; GattService service(MicroBitAccelerometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); @@ -38,13 +38,13 @@ MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble) : accelerometerDataCharacteristicHandle = accelerometerDataCharacteristic.getValueHandle(); accelerometerPeriodCharacteristicHandle = accelerometerPeriodCharacteristic.getValueHandle(); - ble.updateCharacteristicValue(accelerometerDataCharacteristicHandle, (const uint8_t *)&accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer)); - ble.updateCharacteristicValue(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer)); + ble.gattServer().write(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer)); + ble.gattServer().write(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer)); ble.onDataWritten(this, &MicroBitAccelerometerService::onDataWritten); + uBit.MessageBus.listen(MICROBIT_ID_ACCELEROMETER, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE, this, &MicroBitAccelerometerService::accelerometerUpdate, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); } - /** * Callback. Invoked when any of our attributes are written via BLE. */ @@ -52,11 +52,30 @@ void MicroBitAccelerometerService::onDataWritten(const GattWriteCallbackParams * { if (params->handle == accelerometerPeriodCharacteristicHandle && params->len >= sizeof(accelerometerPeriodCharacteristicBuffer)) { - accelerometerPeriodCharacteristicBuffer = *((uint8_t *)params->data); + accelerometerPeriodCharacteristicBuffer = *((uint16_t *)params->data); uBit.accelerometer.setPeriod(accelerometerPeriodCharacteristicBuffer); + + // The accelerometer will choose the nearest period to that requested that it can support + // Read back the ACTUAL period it is using, and report this back. + accelerometerPeriodCharacteristicBuffer = uBit.accelerometer.getPeriod(); + ble.gattServer().write(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer)); } } +/** + * Accelerometer update callback + */ +void MicroBitAccelerometerService::accelerometerUpdate(MicroBitEvent e) +{ + if (ble.getGapState().connected) + { + accelerometerDataCharacteristicBuffer[0] = uBit.accelerometer.getX(); + accelerometerDataCharacteristicBuffer[1] = uBit.accelerometer.getY(); + accelerometerDataCharacteristicBuffer[2] = uBit.accelerometer.getZ(); + + ble.gattServer().notify(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer)); + } +} const uint8_t MicroBitAccelerometerServiceUUID[] = { 0xe9,0x5d,0x07,0x53,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 diff --git a/source/ble-services/MicroBitButtonService.cpp b/source/ble-services/MicroBitButtonService.cpp new file mode 100644 index 0000000..459b7ac --- /dev/null +++ b/source/ble-services/MicroBitButtonService.cpp @@ -0,0 +1,112 @@ +/** + * Class definition for the custom MicroBit Button Service. + * Provides a BLE service to remotely read the state of each button, and configure its behaviour. + */ + +#include "MicroBit.h" +#include "ble/UUID.h" + +#include "MicroBitButtonService.h" + +/** + * Constructor. + * Create a representation of the ButtonService + * @param _ble The instance of a BLE device that we're running on. + */ +MicroBitButtonService::MicroBitButtonService(BLEDevice &_ble) : + ble(_ble) +{ + // Create the data structures that represent each of our characteristics in Soft Device. + GattCharacteristic buttonADataCharacteristic(MicroBitButtonAServiceDataUUID, (uint8_t *)&buttonADataCharacteristicBuffer, 0, + sizeof(buttonADataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + GattCharacteristic buttonBDataCharacteristic(MicroBitButtonBServiceDataUUID, (uint8_t *)&buttonADataCharacteristicBuffer, 0, + sizeof(buttonADataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + + // Initialise our characteristic values. + buttonADataCharacteristicBuffer = 0; + buttonBDataCharacteristicBuffer = 0; + + GattCharacteristic *characteristics[] = {&buttonADataCharacteristic, &buttonBDataCharacteristic}; + GattService service(MicroBitButtonServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + + buttonADataCharacteristicHandle = buttonADataCharacteristic.getValueHandle(); + buttonBDataCharacteristicHandle = buttonBDataCharacteristic.getValueHandle(); + + ble.gattServer().write(buttonADataCharacteristicHandle,(uint8_t *)&buttonADataCharacteristicBuffer, sizeof(buttonADataCharacteristicBuffer)); + ble.gattServer().write(buttonBDataCharacteristicHandle,(uint8_t *)&buttonBDataCharacteristicBuffer, sizeof(buttonBDataCharacteristicBuffer)); + + uBit.MessageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, this, &MicroBitButtonService::buttonAUpdate, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); + uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, this, &MicroBitButtonService::buttonBUpdate, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); +} + + +/** + * Button B update callback + */ +void MicroBitButtonService::buttonAUpdate(MicroBitEvent e) +{ + if (ble.getGapState().connected) + { + if (e.value == MICROBIT_BUTTON_EVT_UP) + { + buttonADataCharacteristicBuffer = 0; + ble.gattServer().notify(buttonADataCharacteristicHandle,(uint8_t *)&buttonADataCharacteristicBuffer, sizeof(buttonADataCharacteristicBuffer)); + } + + if (e.value == MICROBIT_BUTTON_EVT_DOWN) + { + buttonADataCharacteristicBuffer = 1; + ble.gattServer().notify(buttonADataCharacteristicHandle,(uint8_t *)&buttonADataCharacteristicBuffer, sizeof(buttonADataCharacteristicBuffer)); + } + + if (e.value == MICROBIT_BUTTON_EVT_HOLD) + { + buttonADataCharacteristicBuffer = 2; + ble.gattServer().notify(buttonADataCharacteristicHandle,(uint8_t *)&buttonADataCharacteristicBuffer, sizeof(buttonADataCharacteristicBuffer)); + } + } +} + +/** + * Button A update callback + */ +void MicroBitButtonService::buttonBUpdate(MicroBitEvent e) +{ + if (ble.getGapState().connected) + { + if (e.value == MICROBIT_BUTTON_EVT_UP) + { + buttonBDataCharacteristicBuffer = 0; + ble.gattServer().notify(buttonBDataCharacteristicHandle,(uint8_t *)&buttonBDataCharacteristicBuffer, sizeof(buttonBDataCharacteristicBuffer)); + } + + if (e.value == MICROBIT_BUTTON_EVT_DOWN) + { + buttonBDataCharacteristicBuffer = 1; + ble.gattServer().notify(buttonBDataCharacteristicHandle,(uint8_t *)&buttonBDataCharacteristicBuffer, sizeof(buttonBDataCharacteristicBuffer)); + } + + if (e.value == MICROBIT_BUTTON_EVT_HOLD) + { + buttonBDataCharacteristicBuffer = 2; + ble.gattServer().notify(buttonBDataCharacteristicHandle,(uint8_t *)&buttonBDataCharacteristicBuffer, sizeof(buttonBDataCharacteristicBuffer)); + } + } +} + +const uint8_t MicroBitButtonServiceUUID[] = { + 0xe9,0x5d,0x98,0x82,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitButtonAServiceDataUUID[] = { + 0xe9,0x5d,0xda,0x90,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +} +; +const uint8_t MicroBitButtonBServiceDataUUID[] = { + 0xe9,0x5d,0xda,0x91,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + diff --git a/source/ble-services/MicroBitDFUService.cpp b/source/ble-services/MicroBitDFUService.cpp index 702249f..defd65b 100644 --- a/source/ble-services/MicroBitDFUService.cpp +++ b/source/ble-services/MicroBitDFUService.cpp @@ -242,13 +242,13 @@ void MicroBitDFUService::releaseFlashCode() */ const uint8_t MicroBitDFUServiceUUID[] = { - 0xd8,0xaf,0x99,0x1c,0x71,0x44,0x43,0xd7,0x95,0x4b,0x99,0x51,0x2f,0x95,0xf9,0x9c + 0xe9,0x5d,0x93,0xb0,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; const uint8_t MicroBitDFUServiceControlCharacteristicUUID[] = { - 0x97,0x10,0x95,0x47,0xe6,0x3a,0x44,0x2a,0xbf,0x89,0x9d,0x73,0x04,0x13,0xdc,0x2f + 0xe9,0x5d,0x93,0xb1,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; const uint8_t MicroBitDFUServiceFlashCodeCharacteristicUUID[] = { - 0x94,0x7b,0x69,0x34,0x64,0xd1,0x4f,0xad,0x9b,0xd0,0xcc,0x9d,0x6e,0x9f,0x3e,0xa3 + 0xe9,0x5d,0x93,0xb2,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; diff --git a/source/ble-services/MicroBitEventService.cpp b/source/ble-services/MicroBitEventService.cpp index 0963168..6719d98 100644 --- a/source/ble-services/MicroBitEventService.cpp +++ b/source/ble-services/MicroBitEventService.cpp @@ -21,21 +21,31 @@ MicroBitEventService::MicroBitEventService(BLEDevice &_ble) : GattCharacteristic clientEventCharacteristic(MicroBitEventServiceClientEventCharacteristicUUID, (uint8_t *)&clientEventBuffer, 0, sizeof(EventServiceEvent), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); + GattCharacteristic clientRequirementsCharacteristic(MicroBitEventServiceClientRequirementsCharacteristicUUID, (uint8_t *)&clientRequirementsBuffer, 0, sizeof(EventServiceEvent), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); + + microBitRequirementsCharacteristic = new GattCharacteristic(MicroBitEventServiceMicroBitRequirementsCharacteristicUUID, (uint8_t *)µBitRequirementsBuffer, 0, sizeof(EventServiceEvent), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + microBitRequirementsCharacteristic->setReadAuthorizationCallback(this, &MicroBitEventService::onRequirementsRead); + clientEventBuffer.type = 0x00; clientEventBuffer.reason = 0x00; - microBitEventBuffer.type = 0x00; - microBitEventBuffer.reason = 0x00; + microBitEventBuffer = microBitRequirementsBuffer = clientRequirementsBuffer = clientEventBuffer; + + messageBusListenerOffset = 0; - GattCharacteristic *characteristics[] = {µBitEventCharacteristic, &clientEventCharacteristic}; + GattCharacteristic *characteristics[] = {µBitEventCharacteristic, &clientEventCharacteristic, &clientRequirementsCharacteristic, microBitRequirementsCharacteristic}; GattService service(MicroBitEventServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); ble.addService(service); microBitEventCharacteristicHandle = microBitEventCharacteristic.getValueHandle(); clientEventCharacteristicHandle = clientEventCharacteristic.getValueHandle(); + clientRequirementsCharacteristicHandle = clientRequirementsCharacteristic.getValueHandle(); ble.onDataWritten(this, &MicroBitEventService::onDataWritten); + + uBit.addIdleComponent(this); } @@ -56,6 +66,19 @@ void MicroBitEventService::onDataWritten(const GattWriteCallbackParams *params) len-=4; e++; } + return; + } + + if (params->handle == clientRequirementsCharacteristicHandle) { + // Read and register for all the events given... + while (len >= 4) + { + uBit.MessageBus.listen(e->type, e->reason, this, &MicroBitEventService::onMicroBitEvent, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); + + len-=4; + e++; + } + return; } } @@ -70,19 +93,62 @@ void MicroBitEventService::onMicroBitEvent(MicroBitEvent evt) e->type = evt.source; e->reason = evt.value; - ble.updateCharacteristicValue(microBitEventCharacteristicHandle, (const uint8_t *)e, sizeof(EventServiceEvent)); + ble.gattServer().notify(microBitEventCharacteristicHandle, (const uint8_t *)e, sizeof(EventServiceEvent)); + } +} + +/** + * Periodic callback from MicroBit scheduler. + * If we're no longer connected, remove any registered Message Bus listeners. + */ +void MicroBitEventService::idleTick() +{ + if (!ble.getGapState().connected && messageBusListenerOffset >0) { + messageBusListenerOffset = 0; + uBit.MessageBus.ignore(MICROBIT_ID_ANY, MICROBIT_EVT_ANY, this, &MicroBitEventService::onMicroBitEvent); } } -const uint8_t MicroBitEventServiceUUID[] = { +/** + * read callback on data characteristic. + * reads all the pins marked as inputs, and updates the data stored in the BLE stack. + */ +void MicroBitEventService::onRequirementsRead(GattReadAuthCallbackParams *params) +{ + if (params->handle == microBitRequirementsCharacteristic->getValueHandle()) + { + // Walk through the lsit of message bus listeners. + // We send one at a time, and our client can keep reading from this characterisitic until we return an emtpy value. + MicroBitListener *l = uBit.MessageBus.elementAt(messageBusListenerOffset++); + + if (l != NULL) + { + microBitRequirementsBuffer.type = l->id; + microBitRequirementsBuffer.reason = l->value; + ble.gattServer().write(microBitRequirementsCharacteristic->getValueHandle(), (uint8_t *)µBitRequirementsBuffer, sizeof(EventServiceEvent)); + } else { + ble.gattServer().write(microBitRequirementsCharacteristic->getValueHandle(), (uint8_t *)µBitRequirementsBuffer, 0); + } + } +} + +const uint8_t MicroBitEventServiceUUID[] = { 0xe9,0x5d,0x93,0xaf,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; -const uint8_t MicroBitEventServiceMicroBitEventCharacteristicUUID[] = { +const uint8_t MicroBitEventServiceMicroBitEventCharacteristicUUID[] = { 0xe9,0x5d,0x97,0x75,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; -const uint8_t MicroBitEventServiceClientEventCharacteristicUUID[] = { +const uint8_t MicroBitEventServiceClientEventCharacteristicUUID[] = { 0xe9,0x5d,0x54,0x04,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 }; +const uint8_t MicroBitEventServiceMicroBitRequirementsCharacteristicUUID[] = { + 0xe9,0x5d,0xb8,0x4c,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitEventServiceClientRequirementsCharacteristicUUID[] = { + 0xe9,0x5d,0x23,0xc4,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + diff --git a/source/ble-services/MicroBitIOPinService.cpp b/source/ble-services/MicroBitIOPinService.cpp new file mode 100644 index 0000000..38319c7 --- /dev/null +++ b/source/ble-services/MicroBitIOPinService.cpp @@ -0,0 +1,285 @@ +/** + * Class definition for the custom MicroBit IOPin Service. + * Provides a BLE service to remotely read the state of the ioPin, and configure its behaviour. + */ + +#include "MicroBit.h" +#include "ble/UUID.h" + +#include "MicroBitIOPinService.h" + +/** + * Constructor. + * Create a representation of the IOPinService + * @param _ble The instance of a BLE device that we're running on. + */ +MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble) : + ble(_ble) +{ + // Create the AD characteristic, that defines whether each pin is treated as analogue or digital + GattCharacteristic ioPinServiceADCharacteristic(MicroBitIOPinServiceADConfigurationUUID, (uint8_t *)&ioPinServiceADCharacteristicBuffer, 0, sizeof(ioPinServiceADCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); + + // Create the IO characteristic, that defines whether each pin is treated as input or output + GattCharacteristic ioPinServiceIOCharacteristic(MicroBitIOPinServiceIOConfigurationUUID, (uint8_t *)&ioPinServiceIOCharacteristicBuffer, 0, sizeof(ioPinServiceIOCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE); + + // Create the Data characteristic, that allows the actual read and write operations. + ioPinServiceDataCharacteristic = new GattCharacteristic(MicroBitIOPinServiceDataUUID, (uint8_t *)ioPinServiceDataCharacteristicBuffer, 0, sizeof(ioPinServiceDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + ioPinServiceDataCharacteristic->setReadAuthorizationCallback(this, &MicroBitIOPinService::onDataRead); + + ioPinServiceADCharacteristicBuffer = 0; + ioPinServiceIOCharacteristicBuffer = 0; + memset(ioPinServiceIOData, 0, sizeof(ioPinServiceIOData)); + + GattCharacteristic *characteristics[] = {&ioPinServiceADCharacteristic, &ioPinServiceIOCharacteristic, ioPinServiceDataCharacteristic}; + GattService service(MicroBitIOPinServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + + ioPinServiceADCharacteristicHandle = ioPinServiceADCharacteristic.getValueHandle(); + ioPinServiceIOCharacteristicHandle = ioPinServiceIOCharacteristic.getValueHandle(); + + ble.gattServer().write(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer)); + ble.gattServer().write(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer)); + + ble.onDataWritten(this, &MicroBitIOPinService::onDataWritten); + uBit.addIdleComponent(this); +} + +/** + * Determines if the given pin was configured as a digital pin by the BLE IOPinConfigurationCharacterisitic. + * + * @param pin the enumeration of the pin to test + * @return 1 if this pin is configured as a digital value, 0 otherwise + */ +int MicroBitIOPinService::isDigital(int i) +{ + return ((ioPinServiceADCharacteristicBuffer & (1 << i)) == 0); +} + +/** + * Determines if the given pin was configured as an analog pin by the BLE IOPinConfigurationCharacterisitic. + * + * @param pin the enumeration of the pin to test + * @return 1 if this pin is configured as a analog value, 0 otherwise + */ +int MicroBitIOPinService::isAnalog(int i) +{ + return ((ioPinServiceADCharacteristicBuffer & (1 << i)) != 0); +} + +/** + * 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 MicroBitIOPinService::isInput(int i) +{ + return ((ioPinServiceIOCharacteristicBuffer & (1 << i)) != 0); +} + +/** + * 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 MicroBitIOPinService::isOutput(int i) +{ + return ((ioPinServiceIOCharacteristicBuffer & (1 << i)) == 0); +} + +/** + * Callback. Invoked when any of our attributes are written via BLE. + */ +void MicroBitIOPinService::onDataWritten(const GattWriteCallbackParams *params) +{ + // Check for writes to the IO configuration characteristic + if (params->handle == ioPinServiceIOCharacteristicHandle && params->len >= sizeof(ioPinServiceIOCharacteristicBuffer)) + { + uint32_t *value = (uint32_t *)params->data; + + // Our IO configuration may be changing... read the new value, and push it back into the BLE stack. + ioPinServiceIOCharacteristicBuffer = *value; + ble.gattServer().write(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer)); + + // Also, drop any selected pins into input mode, so we can pick up changes later + for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++) + { + if(isDigital(i) && isInput(i)) + MicroBitIOPins[i]->getDigitalValue(); + + if(isAnalog(i) && isInput(i)) + MicroBitIOPins[i]->getAnalogValue(); + } + } + + // Check for writes to the IO configuration characteristic + if (params->handle == ioPinServiceADCharacteristicHandle && params->len >= sizeof(ioPinServiceADCharacteristicBuffer)) + { + uint32_t *value = (uint32_t *)params->data; + + // Our IO configuration may be changing... read the new value, and push it back into the BLE stack. + ioPinServiceADCharacteristicBuffer = *value; + ble.gattServer().write(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer)); + + // Also, drop any selected pins into input mode, so we can pick up changes later + for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++) + { + if(isDigital(i) && isInput(i)) + MicroBitIOPins[i]->getDigitalValue(); + + if(isAnalog(i) && isInput(i)) + MicroBitIOPins[i]->getAnalogValue(); + } + } + + if (params->handle == ioPinServiceDataCharacteristic->getValueHandle()) + { + // We have some pin data to change... + int len = params->len; + IOData *data = (IOData *)params->data; + + // There may be multiple write operaitons... take each in turn and update the pin values + while (len >= sizeof(IOData)) + { + if (isOutput(data->pin)) + { + if (isDigital(data->pin)) + MicroBitIOPins[data->pin]->setDigitalValue(data->value); + else + MicroBitIOPins[data->pin]->setAnalogValue(data->value*4); + } + + data++; + len -= sizeof(IOData); + } + } +} + +/** + * read callback on data characteristic. + * reads all the pins marked as inputs, and updates the data stored in the BLE stack. + */ +void MicroBitIOPinService::onDataRead(GattReadAuthCallbackParams *params) +{ + if (params->handle == ioPinServiceDataCharacteristic->getValueHandle()) + { + + // Scan through all pins that our BLE client may be listening for. If any have changed value, update the BLE characterisitc, and NOTIFY our client. + int pairs = 0; + + for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++) + { + if (isInput(i)) + { + uint8_t value; + + if (isDigital(i)) + value = MicroBitIOPins[i]->getDigitalValue(); + else + value = MicroBitIOPins[i]->getAnalogValue(); + + ioPinServiceIOData[i] = value; + ioPinServiceDataCharacteristicBuffer[pairs].pin = i; + ioPinServiceDataCharacteristicBuffer[pairs].value = value; + + pairs++; + + if (pairs >= MICROBIT_IO_PIN_SERVICE_DATA_SIZE) + break; + } + } + + // If there's any data, issue a BLE notification. + if (pairs > 0) + ble.gattServer().notify(ioPinServiceDataCharacteristic->getValueHandle(), (uint8_t *)ioPinServiceDataCharacteristicBuffer, pairs * sizeof(IOData)); + } +} + + +/** + * Periodic callback from MicroBit scheduler. + * Check if any of the pins we're watching need updating. Apply a BLE NOTIFY if so... + */ +void MicroBitIOPinService::idleTick() +{ + // If we're not we're connected, then there's nothing to do... + if (!ble.getGapState().connected) + return; + + // Scan through all pins that our BLE client may be listening for. If any have changed value, update the BLE characterisitc, and NOTIFY our client. + int pairs = 0; + + for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++) + { + if (isInput(i)) + { + uint8_t value; + + if (isDigital(i)) + value = MicroBitIOPins[i]->getDigitalValue(); + else + value = MicroBitIOPins[i]->getAnalogValue(); + + // If the data has changed, send an update. + if (value != ioPinServiceIOData[i]) + { + ioPinServiceIOData[i] = value; + + ioPinServiceDataCharacteristicBuffer[pairs].pin = i; + ioPinServiceDataCharacteristicBuffer[pairs].value = value; + + pairs++; + + if (pairs >= MICROBIT_IO_PIN_SERVICE_DATA_SIZE) + break; + } + } + } + + // If there were any changes, issue a BLE notification. + if (pairs > 0) + ble.gattServer().notify(ioPinServiceDataCharacteristic->getValueHandle(), (uint8_t *)ioPinServiceDataCharacteristicBuffer, pairs * sizeof(IOData)); +} + +const uint8_t MicroBitIOPinServiceUUID[] = { + 0xe9,0x5d,0x12,0x7b,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitIOPinServiceIOConfigurationUUID[] = { + 0xe9,0x5d,0xb9,0xfe,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitIOPinServiceADConfigurationUUID[] = { + 0xe9,0x5d,0x58,0x99,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitIOPinServiceDataUUID[] = { + 0xe9,0x5d,0x8d,0x00,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +MicroBitPin * const MicroBitIOPins[] = { + &uBit.io.P0, + &uBit.io.P1, + &uBit.io.P2, + &uBit.io.P3, + &uBit.io.P4, + &uBit.io.P5, + &uBit.io.P6, + &uBit.io.P7, + &uBit.io.P8, + &uBit.io.P9, + &uBit.io.P10, + &uBit.io.P11, + &uBit.io.P12, + &uBit.io.P13, + &uBit.io.P14, + &uBit.io.P15, + &uBit.io.P16, + &uBit.io.P19, + &uBit.io.P20 +}; + + diff --git a/source/ble-services/MicroBitLEDService.cpp b/source/ble-services/MicroBitLEDService.cpp index ae8c301..99538ae 100644 --- a/source/ble-services/MicroBitLEDService.cpp +++ b/source/ble-services/MicroBitLEDService.cpp @@ -32,7 +32,6 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) : matrixCharacteristic.setReadAuthorizationCallback(this, &MicroBitLEDService::onDataRead); - GattCharacteristic *characteristics[] = {&matrixCharacteristic, &textCharacteristic, &scrollingSpeedCharacteristic}; GattService service(MicroBitLEDServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); @@ -42,12 +41,10 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) : textCharacteristicHandle = textCharacteristic.getValueHandle(); scrollingSpeedCharacteristicHandle = scrollingSpeedCharacteristic.getValueHandle(); - ble.updateCharacteristicValue(scrollingSpeedCharacteristicHandle, (const uint8_t *)&scrollingSpeedCharacteristicBuffer, sizeof(scrollingSpeedCharacteristicBuffer)); - ble.updateCharacteristicValue(matrixCharacteristicHandle, (const uint8_t *)&matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer)); + ble.gattServer().write(scrollingSpeedCharacteristicHandle, (const uint8_t *)&scrollingSpeedCharacteristicBuffer, sizeof(scrollingSpeedCharacteristicBuffer)); + ble.gattServer().write(matrixCharacteristicHandle, (const uint8_t *)&matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer)); ble.onDataWritten(this, &MicroBitLEDService::onDataWritten); - - } @@ -111,7 +108,7 @@ void MicroBitLEDService::onDataRead(GattReadAuthCallbackParams *params) } } - ble.updateCharacteristicValue(matrixCharacteristicHandle, (const uint8_t *)&matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer)); + ble.gattServer().write(matrixCharacteristicHandle, (const uint8_t *)&matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer)); } } diff --git a/source/ble-services/MicroBitMagnetometerService.cpp b/source/ble-services/MicroBitMagnetometerService.cpp new file mode 100644 index 0000000..b4d4b47 --- /dev/null +++ b/source/ble-services/MicroBitMagnetometerService.cpp @@ -0,0 +1,122 @@ +/** + * Class definition for the custom MicroBit Magnetometer Service. + * Provides a BLE service to remotely read the state of the magnetometer, and configure its behaviour. + */ + +#include "MicroBit.h" +#include "ble/UUID.h" + +#include "MicroBitMagnetometerService.h" + +/** + * Constructor. + * Create a representation of the MagnetometerService + * @param _ble The instance of a BLE device that we're running on. + */ +MicroBitMagnetometerService::MicroBitMagnetometerService(BLEDevice &_ble) : + ble(_ble) +{ + // Create the data structures that represent each of our characteristics in Soft Device. + GattCharacteristic magnetometerDataCharacteristic(MicroBitMagnetometerServiceDataUUID, (uint8_t *)magnetometerDataCharacteristicBuffer, 0, + sizeof(magnetometerDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + GattCharacteristic magnetometerBearingCharacteristic(MicroBitMagnetometerServiceBearingUUID, (uint8_t *)&magnetometerBearingCharacteristicBuffer, 0, + sizeof(magnetometerBearingCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + GattCharacteristic magnetometerPeriodCharacteristic(MicroBitMagnetometerServicePeriodUUID, (uint8_t *)&magnetometerPeriodCharacteristicBuffer, 0, + sizeof(magnetometerPeriodCharacteristicBuffer), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); + + // Initialise our characteristic values. + magnetometerDataCharacteristicBuffer[0] = 0; + magnetometerDataCharacteristicBuffer[1] = 0; + magnetometerDataCharacteristicBuffer[2] = 0; + magnetometerBearingCharacteristicBuffer = 0; + magnetometerPeriodCharacteristicBuffer = uBit.compass.getPeriod(); + + GattCharacteristic *characteristics[] = {&magnetometerDataCharacteristic, &magnetometerBearingCharacteristic, &magnetometerPeriodCharacteristic}; + GattService service(MicroBitMagnetometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + + magnetometerDataCharacteristicHandle = magnetometerDataCharacteristic.getValueHandle(); + magnetometerBearingCharacteristicHandle = magnetometerBearingCharacteristic.getValueHandle(); + magnetometerPeriodCharacteristicHandle = magnetometerPeriodCharacteristic.getValueHandle(); + + ble.gattServer().notify(magnetometerDataCharacteristicHandle,(uint8_t *)magnetometerDataCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer)); + ble.gattServer().notify(magnetometerBearingCharacteristicHandle,(uint8_t *)&magnetometerBearingCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer)); + ble.gattServer().write(magnetometerPeriodCharacteristicHandle, (const uint8_t *)&magnetometerPeriodCharacteristicBuffer, sizeof(magnetometerPeriodCharacteristicBuffer)); + + ble.onDataWritten(this, &MicroBitMagnetometerService::onDataWritten); + uBit.MessageBus.listen(MICROBIT_ID_COMPASS, MICROBIT_COMPASS_EVT_DATA_UPDATE, this, &MicroBitMagnetometerService::magnetometerUpdate, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); + + uBit.MessageBus.listen(MICROBIT_ID_COMPASS, MICROBIT_COMPASS_EVT_CONFIG_NEEDED, this, &MicroBitMagnetometerService::samplePeriodUpdateNeeded); +} + +/** + * Callback. Invoked when any of our attributes are written via BLE. + */ +void MicroBitMagnetometerService::onDataWritten(const GattWriteCallbackParams *params) +{ + if (params->handle == magnetometerPeriodCharacteristicHandle && params->len >= sizeof(magnetometerPeriodCharacteristicBuffer)) + { + magnetometerPeriodCharacteristicBuffer = *((uint16_t *)params->data); + MicroBitEvent evt(MICROBIT_ID_COMPASS, MICROBIT_COMPASS_EVT_CONFIG_NEEDED); + } +} + +/** + * Magnetometer update callback + */ +void MicroBitMagnetometerService::magnetometerUpdate(MicroBitEvent e) +{ + if (ble.getGapState().connected) + { + magnetometerDataCharacteristicBuffer[0] = uBit.compass.getX(); + magnetometerDataCharacteristicBuffer[1] = uBit.compass.getY(); + magnetometerDataCharacteristicBuffer[2] = uBit.compass.getZ(); + magnetometerBearingCharacteristicBuffer = (uint16_t) uBit.compass.heading(); + magnetometerPeriodCharacteristicBuffer = uBit.compass.getPeriod(); + + ble.gattServer().write(magnetometerPeriodCharacteristicHandle, (const uint8_t *)&magnetometerPeriodCharacteristicBuffer, sizeof(magnetometerPeriodCharacteristicBuffer)); + ble.gattServer().notify(magnetometerDataCharacteristicHandle,(uint8_t *)magnetometerDataCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer)); + ble.gattServer().notify(magnetometerBearingCharacteristicHandle,(uint8_t *)&magnetometerBearingCharacteristicBuffer, sizeof(magnetometerBearingCharacteristicBuffer)); + } +} + +/** + * 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 MicroBitMagnetometerService::samplePeriodUpdateNeeded(MicroBitEvent e) +{ + // Reconfigure the compass. This might take a while... + uBit.compass.setPeriod(magnetometerPeriodCharacteristicBuffer); + + // The compass will choose the nearest sample period to that we've specified. + // Read the ACTUAL sample period back. + magnetometerPeriodCharacteristicBuffer = uBit.compass.getPeriod(); + + // Ensure this is reflected in our BLE connection. + ble.gattServer().write(magnetometerPeriodCharacteristicHandle, (const uint8_t *)&magnetometerPeriodCharacteristicBuffer, sizeof(magnetometerPeriodCharacteristicBuffer)); + +} + +const uint8_t MicroBitMagnetometerServiceUUID[] = { + 0xe9,0x5d,0xf2,0xd8,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitMagnetometerServiceDataUUID[] = { + 0xe9,0x5d,0xfb,0x11,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitMagnetometerServicePeriodUUID[] = { + 0xe9,0x5d,0x38,0x6c,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitMagnetometerServiceBearingUUID[] = { + 0xe9,0x5d,0x97,0x15,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + + diff --git a/source/ble-services/MicroBitTemperatureService.cpp b/source/ble-services/MicroBitTemperatureService.cpp new file mode 100644 index 0000000..014737f --- /dev/null +++ b/source/ble-services/MicroBitTemperatureService.cpp @@ -0,0 +1,57 @@ +/** + * Class definition for the custom MicroBit Temperature Service. + * Provides a BLE service to remotely read the state of the temperature, and configure its behaviour. + */ + +#include "MicroBit.h" +#include "ble/UUID.h" + +#include "MicroBitTemperatureService.h" + +/** + * Constructor. + * Create a representation of the TemperatureService + * @param _ble The instance of a BLE device that we're running on. + */ +MicroBitTemperatureService::MicroBitTemperatureService(BLEDevice &_ble) : + ble(_ble) +{ + // Create the data structures that represent each of our characteristics in Soft Device. + GattCharacteristic temperatureDataCharacteristic(MicroBitTemperatureServiceDataUUID, (uint8_t *)&temperatureDataCharacteristicBuffer, 0, + sizeof(temperatureDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); + + // Initialise our characteristic values. + temperatureDataCharacteristicBuffer = 0; + + GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic}; + GattService service(MicroBitTemperatureServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); + + ble.addService(service); + + temperatureDataCharacteristicHandle = temperatureDataCharacteristic.getValueHandle(); + ble.gattServer().write(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer)); + + uBit.MessageBus.listen(MICROBIT_ID_THERMOMETER, MICROBIT_THERMOMETER_EVT_UPDATE, this, &MicroBitTemperatureService::temperatureUpdate, MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT); +} + +/** + * Temperature update callback + */ +void MicroBitTemperatureService::temperatureUpdate(MicroBitEvent e) +{ + if (ble.getGapState().connected) + { + temperatureDataCharacteristicBuffer = uBit.thermometer.getTemperature(); + ble.gattServer().notify(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer)); + } +} + +const uint8_t MicroBitTemperatureServiceUUID[] = { + 0xe9,0x5d,0x61,0x00,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitTemperatureServiceDataUUID[] = { + 0xe9,0x5d,0x8a,0x38,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +