From eb5ed28f74c188222e749799c1c707411327a7cc Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 22 Sep 2015 16:13:08 +0100 Subject: [PATCH] microbit: First cut at MicroBitButtonService and microBitIOPinService Strictly still development - untested code. --- inc/MicroBit.h | 4 + inc/MicroBitButtonService.h | 54 ++++ inc/MicroBitConfig.h | 15 +- inc/MicroBitIOPinService.h | 111 +++++++ inc/MicroBitMagnetometerService.h | 2 +- inc/MicroBitPin.h | 24 ++ source/CMakeLists.txt | 2 + source/MicroBit.cpp | 8 + source/MicroBitCompass.cpp | 5 +- source/MicroBitMessageBus.cpp | 2 - source/MicroBitPin.cpp | 36 +++ source/ble-services/MicroBitButtonService.cpp | 106 +++++++ source/ble-services/MicroBitIOPinService.cpp | 284 ++++++++++++++++++ .../MicroBitMagnetometerService.cpp | 1 - 14 files changed, 647 insertions(+), 7 deletions(-) create mode 100644 inc/MicroBitButtonService.h create mode 100644 inc/MicroBitIOPinService.h create mode 100644 source/ble-services/MicroBitButtonService.cpp create mode 100644 source/ble-services/MicroBitIOPinService.cpp diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 64e8488..d8af98b 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -38,6 +38,8 @@ #include "MicroBitLEDService.h" #include "MicroBitAccelerometerService.h" #include "MicroBitMagnetometerService.h" +#include "MicroBitButtonService.h" +#include "MicroBitIOPinService.h" #include "ExternalEvents.h" // MicroBit::flags values @@ -113,6 +115,8 @@ class MicroBit MicroBitLEDService *ble_led_service; MicroBitAccelerometerService *ble_accelerometer_service; MicroBitMagnetometerService *ble_magnetometer_service; + MicroBitButtonService *ble_button_service; + MicroBitIOPinService *ble_io_pin_service; /** * Constructor. 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/MicroBitConfig.h b/inc/MicroBitConfig.h index 99d37c3..686f3d2 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -171,7 +171,20 @@ // 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 1 +#define MICROBIT_BLE_MAGNETOMETER_SERVICE 1 +#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 1 +#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 1 #endif // Defines the maximum length strong that can be written to the 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/MicroBitMagnetometerService.h b/inc/MicroBitMagnetometerService.h index c01220c..a7ec9c5 100644 --- a/inc/MicroBitMagnetometerService.h +++ b/inc/MicroBitMagnetometerService.h @@ -42,7 +42,7 @@ class MicroBitMagnetometerService BLEDevice &ble; // memory for our 8 bit control characteristics. - uint16_t magnetometerDataCharacteristicBuffer[3]; + int16_t magnetometerDataCharacteristicBuffer[3]; uint16_t magnetometerBearingCharacteristicBuffer; uint16_t magnetometerPeriodCharacteristicBuffer; 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/source/CMakeLists.txt b/source/CMakeLists.txt index abaaf36..40c3713 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -33,6 +33,8 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "ble-services/MicroBitLEDService.cpp" "ble-services/MicroBitAccelerometerService.cpp" "ble-services/MicroBitMagnetometerService.cpp" + "ble-services/MicroBitButtonService.cpp" + "ble-services/MicroBitIOPinService.cpp" ) if (YOTTA_CFG_MICROBIT_CONFIGFILE) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index b35dacd..9971a35 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -129,6 +129,14 @@ void MicroBit::init() 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 + // 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/MicroBitCompass.cpp b/source/MicroBitCompass.cpp index 245e27e..2d05ae8 100644 --- a/source/MicroBitCompass.cpp +++ b/source/MicroBitCompass.cpp @@ -40,7 +40,7 @@ MicroBitCompass::MicroBitCompass(uint16_t id, uint16_t address) : average(), sam 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; @@ -132,6 +132,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); @@ -337,7 +338,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)); diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index 442085b..6d69d02 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -109,8 +109,6 @@ void MicroBitMessageBus::queueEvent(MicroBitEvent &evt) // 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); - pc.printf("QueueEvent: Queueing: %d\n", !processingComplete); - if (!processingComplete) { // We need to queue this event for later processing... 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/ble-services/MicroBitButtonService.cpp b/source/ble-services/MicroBitButtonService.cpp new file mode 100644 index 0000000..a8e26aa --- /dev/null +++ b/source/ble-services/MicroBitButtonService.cpp @@ -0,0 +1,106 @@ +/** + * 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.updateCharacteristicValue(buttonADataCharacteristicHandle, (const uint8_t *)&buttonADataCharacteristicBuffer, sizeof(buttonADataCharacteristicBuffer)); + ble.updateCharacteristicValue(buttonBDataCharacteristicHandle, (const 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 (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 (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/MicroBitIOPinService.cpp b/source/ble-services/MicroBitIOPinService.cpp new file mode 100644 index 0000000..a882204 --- /dev/null +++ b/source/ble-services/MicroBitIOPinService.cpp @@ -0,0 +1,284 @@ +/** + * 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.updateCharacteristicValue(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer)); + ble.updateCharacteristicValue(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer)); + + ble.onDataWritten(this, &MicroBitIOPinService::onDataWritten); +} + +/** + * 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.updateCharacteristicValue(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.updateCharacteristicValue(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.updateCharacteristicValue(ioPinServiceDataCharacteristic->getValueHandle(), (uint8_t *)&ioPinServiceDataCharacteristic, 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 *)&ioPinServiceDataCharacteristic, 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,0x58,0x99,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitIOPinServiceADConfigurationUUID[] = { + 0xe9,0x5d,0x8d,0x00,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8 +}; + +const uint8_t MicroBitIOPinServiceDataUUID[] = { + 0xe9,0x5d,0xc5,0x8c,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/MicroBitMagnetometerService.cpp b/source/ble-services/MicroBitMagnetometerService.cpp index ca3d7f0..268e37c 100644 --- a/source/ble-services/MicroBitMagnetometerService.cpp +++ b/source/ble-services/MicroBitMagnetometerService.cpp @@ -85,7 +85,6 @@ 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 };