Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
Revision 1:8aa5cdb4ab67, committed 2016-04-07
- Comitter:
- Jonathan Austin
- Date:
- Thu Apr 07 01:33:22 2016 +0100
- Parent:
- 0:fb15f7887843
- Child:
- 2:557d9ac9e959
- Commit message:
- Synchronized with git rev 55cb9199
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AUTHORS Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,9 @@ +Joe Finney (@finneyj) +James Devine (@jamesadevine) +Martin Woolley (@bluetooth-mdw) +Michał Moskal (@mmoskal) +Robert May (@remay) +Jonathan Protzenko (@msprotz) +James Sheppard (@jamessheppard) +Damien George (@dpgeorge) +Mathew Else (@matthewelse)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,39 @@
+# microbit-dal
+
+The core set of drivers, mechanisms and types that make up the micro:bit runtime.
+
+## Overview
+
+The micro:bit runtime provides an easy to use environment for programming the BBC micro:bit in the C/C++ language, written by Lancaster University. It contains device drivers for all the hardware capabilities of the micro:bit, and also a suite of runtime mechanisms to make programming the micro:bit easier and more flexible. These range from control of the LED matrix display to peer-to-peer radio communication and secure Bluetooth Low Energy services. The micro:bit runtime is proudly built on the ARM mbed and Nordic nrf51 platforms.
+
+In addition to supporting development in C/C++, the runtime is also designed specifically to support higher level languages provided by our partners that target the micro:bit. It is currently used as a support library for all the languages on the BBC www.microbit.co.uk website, including Microsoft Block, Microsoft TouchDevelop, Code Kingdoms JavaScript and Micropython languages.
+
+## Links
+
+[micro:bit runtime docs](http://lancaster-university.github.io/microbit-docs/) | [uBit](https://github.com/lancaster-university/microbit) | [samples](https://github.com/lancaster-university/microbit-samples)
+
+## Build Environments
+
+| Build Environment | Documentation |
+| ------------- |-------------|
+| ARM mbed online | http://lancaster-university.github.io/microbit-docs/online-toolchains/#mbed |
+| yotta | http://lancaster-university.github.io/microbit-docs/offline-toolchains/#yotta |
+
+
+
+## Hello World!
+
+```cpp
+#include "MicroBitDisplay.h"
+
+MicroBitDisplay display;
+
+int main()
+{
+ display.scroll("Hello world!");
+}
+```
+
+## BBC Community Guidelines
+
+[BBC Community Guidelines](https://www.microbit.co.uk/help#sect_cg)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/inc/bluetooth/ExternalEvents.h Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,38 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef EXTERNAL_EVENTS_H +#define EXTERNAL_EVENTS_H + +/** + * This header file contains IDs and event codes use for Bluetooth communication. + */ + +#define MICROBIT_ID_BLE 1000 +#define MICROBIT_ID_BLE_UART 1001 + +#include "MESEvents.h" + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/inc/bluetooth/MESEvents.h Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,117 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef MES_EVENTS_H +#define MES_EVENTS_H + +// +// MicroBit Event Service Event ID's and values +// + +// +// Events that master devices respond to: +// +#define MES_REMOTE_CONTROL_ID 1001 +#define MES_REMOTE_CONTROL_EVT_PLAY 1 +#define MES_REMOTE_CONTROL_EVT_PAUSE 2 +#define MES_REMOTE_CONTROL_EVT_STOP 3 +#define MES_REMOTE_CONTROL_EVT_NEXTTRACK 4 +#define MES_REMOTE_CONTROL_EVT_PREVTRACK 5 +#define MES_REMOTE_CONTROL_EVT_FORWARD 6 +#define MES_REMOTE_CONTROL_EVT_REWIND 7 +#define MES_REMOTE_CONTROL_EVT_VOLUMEUP 8 +#define MES_REMOTE_CONTROL_EVT_VOLUMEDOWN 9 + + +#define MES_CAMERA_ID 1002 +#define MES_CAMERA_EVT_LAUNCH_PHOTO_MODE 1 +#define MES_CAMERA_EVT_LAUNCH_VIDEO_MODE 2 +#define MES_CAMERA_EVT_TAKE_PHOTO 3 +#define MES_CAMERA_EVT_START_VIDEO_CAPTURE 4 +#define MES_CAMERA_EVT_STOP_VIDEO_CAPTURE 5 +#define MES_CAMERA_EVT_STOP_PHOTO_MODE 6 +#define MES_CAMERA_EVT_STOP_VIDEO_MODE 7 +#define MES_CAMERA_EVT_TOGGLE_FRONT_REAR 8 + + +#define MES_ALERTS_ID 1004 +#define MES_ALERT_EVT_DISPLAY_TOAST 1 +#define MES_ALERT_EVT_VIBRATE 2 +#define MES_ALERT_EVT_PLAY_SOUND 3 +#define MES_ALERT_EVT_PLAY_RINGTONE 4 +#define MES_ALERT_EVT_FIND_MY_PHONE 5 +#define MES_ALERT_EVT_ALARM1 6 +#define MES_ALERT_EVT_ALARM2 7 +#define MES_ALERT_EVT_ALARM3 8 +#define MES_ALERT_EVT_ALARM4 9 +#define MES_ALERT_EVT_ALARM5 10 +#define MES_ALERT_EVT_ALARM6 11 + +// +// Events that master devices generate: +// +#define MES_SIGNAL_STRENGTH_ID 1101 +#define MES_SIGNAL_STRENGTH_EVT_NO_BAR 1 +#define MES_SIGNAL_STRENGTH_EVT_ONE_BAR 2 +#define MES_SIGNAL_STRENGTH_EVT_TWO_BAR 3 +#define MES_SIGNAL_STRENGTH_EVT_THREE_BAR 4 +#define MES_SIGNAL_STRENGTH_EVT_FOUR_BAR 5 + + +#define MES_DEVICE_INFO_ID 1103 +#define MES_DEVICE_ORIENTATION_LANDSCAPE 1 +#define MES_DEVICE_ORIENTATION_PORTRAIT 2 +#define MES_DEVICE_GESTURE_NONE 3 +#define MES_DEVICE_GESTURE_DEVICE_SHAKEN 4 +#define MES_DEVICE_DISPLAY_OFF 5 +#define MES_DEVICE_DISPLAY_ON 6 +#define MES_DEVICE_INCOMING_CALL 7 +#define MES_DEVICE_INCOMING_MESSAGE 8 + + +#define MES_DPAD_CONTROLLER_ID 1104 +#define MES_DPAD_BUTTON_A_DOWN 1 +#define MES_DPAD_BUTTON_A_UP 2 +#define MES_DPAD_BUTTON_B_DOWN 3 +#define MES_DPAD_BUTTON_B_UP 4 +#define MES_DPAD_BUTTON_C_DOWN 5 +#define MES_DPAD_BUTTON_C_UP 6 +#define MES_DPAD_BUTTON_D_DOWN 7 +#define MES_DPAD_BUTTON_D_UP 8 +#define MES_DPAD_BUTTON_1_DOWN 9 +#define MES_DPAD_BUTTON_1_UP 10 +#define MES_DPAD_BUTTON_2_DOWN 11 +#define MES_DPAD_BUTTON_2_UP 12 +#define MES_DPAD_BUTTON_3_DOWN 13 +#define MES_DPAD_BUTTON_3_UP 14 +#define MES_DPAD_BUTTON_4_DOWN 15 +#define MES_DPAD_BUTTON_4_UP 16 + +// +// Events that typically use radio broadcast: +// +#define MES_BROADCAST_GENERAL_ID 2000 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitAccelerometerService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,83 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_ACCELEROMETER_SERVICE_H
+#define MICROBIT_ACCELEROMETER_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitAccelerometer.h"
+#include "EventModel.h"
+
+// UUIDs for our service and characteristics
+extern const uint8_t MicroBitAccelerometerServiceUUID[];
+extern const uint8_t MicroBitAccelerometerServiceDataUUID[];
+extern const uint8_t MicroBitAccelerometerServicePeriodUUID[];
+
+
+/**
+ * Class definition for a MicroBit BLE Accelerometer Service.
+ * Provides access to live accelerometer data via Bluetooth, and provides basic configuration options.
+ */
+class MicroBitAccelerometerService
+{
+ public:
+
+ /**
+ * Constructor.
+ * Create a representation of the AccelerometerService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _accelerometer An instance of MicroBitAccelerometer.
+ */
+ MicroBitAccelerometerService(BLEDevice &_ble, MicroBitAccelerometer &_acclerometer);
+
+
+ private:
+
+ /**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+ void onDataWritten(const GattWriteCallbackParams *params);
+
+ /**
+ * Accelerometer update callback
+ */
+ void accelerometerUpdate(MicroBitEvent e);
+
+ // Bluetooth stack we're running on.
+ BLEDevice &ble;
+ MicroBitAccelerometer &accelerometer;
+
+ // memory for our 8 bit control characteristics.
+ uint16_t accelerometerDataCharacteristicBuffer[3];
+ uint16_t accelerometerPeriodCharacteristicBuffer;
+
+ // Handles to access each characteristic when they are held by Soft Device.
+ GattAttribute::Handle_t accelerometerDataCharacteristicHandle;
+ GattAttribute::Handle_t accelerometerPeriodCharacteristicHandle;
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitBLEManager.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,215 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_BLE_MANAGER_H
+#define MICROBIT_BLE_MANAGER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+/*
+ * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined (__arm)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#include "ble/BLE.h"
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined (__arm)
+#pragma GCC diagnostic pop
+#endif
+
+#include "ble/services/DeviceInformationService.h"
+#include "MicroBitDFUService.h"
+#include "MicroBitEventService.h"
+#include "MicroBitLEDService.h"
+#include "MicroBitAccelerometerService.h"
+#include "MicroBitMagnetometerService.h"
+#include "MicroBitButtonService.h"
+#include "MicroBitIOPinService.h"
+#include "MicroBitTemperatureService.h"
+#include "ExternalEvents.h"
+#include "MicroBitButton.h"
+#include "MicroBitStorage.h"
+
+#define MICROBIT_BLE_PAIR_REQUEST 0x01
+#define MICROBIT_BLE_PAIR_COMPLETE 0x02
+#define MICROBIT_BLE_PAIR_PASSCODE 0x04
+#define MICROBIT_BLE_PAIR_SUCCESSFUL 0x08
+
+#define MICROBIT_BLE_PAIRING_TIMEOUT 90
+#define MICROBIT_BLE_POWER_LEVELS 8
+#define MICROBIT_BLE_MAXIMUM_BONDS 4
+#define MICROBIT_BLE_ENABLE_BONDING true
+
+extern const int8_t MICROBIT_BLE_POWER_LEVEL[];
+
+struct BLESysAttribute
+{
+ uint8_t sys_attr[8] = { 0 };
+};
+
+struct BLESysAttributeStore
+{
+ BLESysAttribute sys_attrs[MICROBIT_BLE_MAXIMUM_BONDS];
+};
+
+/**
+ * Class definition for the MicroBitBLEManager.
+ *
+ */
+class MicroBitBLEManager : MicroBitComponent
+{
+ public:
+
+ // The mbed abstraction of the BlueTooth Low Energy (BLE) hardware
+ BLEDevice *ble;
+
+ //an instance of MicroBitStorage used to store sysAttrs from softdevice
+ MicroBitStorage* storage;
+
+ /**
+ * Constructor.
+ *
+ * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
+ *
+ * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS).
+ *
+ * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
+ * Hence, the init() member function should be used to initialise the BLE stack.
+ */
+ MicroBitBLEManager(MicroBitStorage& _storage);
+
+ /**
+ * Constructor.
+ *
+ * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
+ *
+ * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
+ * Hence, the init() member function should be used to initialise the BLE stack.
+ */
+ MicroBitBLEManager();
+
+ /**
+ * Post constructor initialisation method as the BLE stack cannot be brought
+ * up in a static context.
+ *
+ * @param deviceName The name used when advertising
+ * @param serialNumber The serial number exposed by the device information service
+ * @param messageBus An instance of an EventModel, used during pairing.
+ * @param enableBonding If true, the security manager enabled bonding.
+ *
+ * @code
+ * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
+ * @endcode
+ */
+ void init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding);
+
+ /**
+ * Change the output power level of the transmitter to the given value.
+ *
+ * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
+ *
+ * @code
+ * // maximum transmission power.
+ * bleManager.setTransmitPower(7);
+ * @endcode
+ */
+ int setTransmitPower(int power);
+
+ /**
+ * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
+ * of the micro:bit in cases where BLE is disabled during normal operation.
+ *
+ * @param display An instance of MicroBitDisplay used when displaying pairing information.
+ * @param authorizationButton The button to use to authorise a pairing request.
+ *
+ * @code
+ * // initiate pairing mode
+ * bleManager.pairingMode(uBit.display, uBit.buttonA);
+ * @endcode
+ */
+ void pairingMode(MicroBitDisplay &display, MicroBitButton &authorisationButton);
+
+ /**
+ * When called, the micro:bit will begin advertising for a predefined period,
+ * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
+ */
+ void advertise();
+
+ /**
+ * Determines the number of devices currently bonded with this micro:bit.
+ * @return The number of active bonds.
+ */
+ int getBondCount();
+
+ /**
+ * A request to pair has been received from a BLE device.
+ * If we're in pairing mode, display the passkey to the user.
+ * Also, purge the bonding table if it has reached capacity.
+ *
+ * @note for internal use only.
+ */
+ void pairingRequested(ManagedString passKey);
+
+ /**
+ * A pairing request has been sucessfully completed.
+ * If we're in pairing mode, display a success or failure message.
+ *
+ * @note for internal use only.
+ */
+ void pairingComplete(bool success);
+
+ /**
+ * Periodic callback in thread context.
+ * We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
+ */
+ void idleTick();
+
+ private:
+
+ /**
+ * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
+ *
+ * @param display The display instance used for displaying the histogram.
+ */
+ void showNameHistogram(MicroBitDisplay &display);
+
+ int pairingStatus;
+ ManagedString passKey;
+ ManagedString deviceName;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitButtonService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,80 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_BUTTON_SERVICE_H
+#define MICROBIT_BUTTON_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "EventModel.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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitDFUService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,103 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_DFU_SERVICE_H
+#define MICROBIT_DFU_SERVICE_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitEvent.h"
+
+// MicroBit ControlPoint OpCodes
+// Requests transfer to the Nordic DFU bootloader.
+#define MICROBIT_DFU_OPCODE_START_DFU 1
+
+// visual ID code constants
+#define MICROBIT_DFU_HISTOGRAM_WIDTH 5
+#define MICROBIT_DFU_HISTOGRAM_HEIGHT 5
+
+// UUIDs for our service and characteristics
+extern const uint8_t MicroBitDFUServiceUUID[];
+extern const uint8_t MicroBitDFUServiceControlCharacteristicUUID[];
+extern const uint8_t MicroBitDFUServiceFlashCodeCharacteristicUUID[];
+
+// Handle on the memory resident Nordic bootloader.
+extern "C" void bootloader_start(void);
+
+/**
+ * Class definition for a MicroBit Device Firmware Update loader.
+ * This service allows hexes to be flashed remotely from another Bluetooth
+ * device.
+ */
+class MicroBitDFUService
+{
+ public:
+
+ /**
+ * Constructor.
+ * Initialise the Device Firmware Update service.
+ * @param _ble The instance of a BLE device that we're running on.
+ */
+ MicroBitDFUService(BLEDevice &_ble);
+
+ /**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+ virtual void onDataWritten(const GattWriteCallbackParams *params);
+
+ private:
+
+ // State of paiting process.
+ bool authenticated;
+ bool flashCodeRequested;
+
+ // Bluetooth stack we're running on.
+ BLEDevice &ble;
+
+ // 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;
+
+ // Displays the device's ID code as a histogram on the LED matrix display.
+ void showNameHistogram();
+
+ // Displays an acknowledgement on the LED matrix display.
+ void showTick();
+
+ // Update BLE characteristic to release our flash code.
+ void releaseFlashCode();
+
+ // Event handlers for button clicks.
+ void onButtonA(MicroBitEvent e);
+ void onButtonB(MicroBitEvent e);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitEventService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,111 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_EVENT_SERVICE_H
+#define MICROBIT_EVENT_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitEvent.h"
+#include "EventModel.h"
+
+// UUIDs for our service and characteristics
+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
+{
+ uint16_t type;
+ uint16_t reason;
+};
+
+
+/**
+ * Class definition for a MicroBit BLE Event Service.
+ * Provides a BLE gateway onto an Event Model.
+ */
+class MicroBitEventService : public MicroBitComponent
+{
+ public:
+
+ /**
+ * Constructor.
+ * Create a representation of the EventService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _messageBus An instance of an EventModel which events will be mirrored from.
+ */
+ MicroBitEventService(BLEDevice &_ble, EventModel &_messageBus);
+
+ /**
+ * 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.
+ */
+ void onDataWritten(const GattWriteCallbackParams *params);
+
+ /**
+ * Callback. Invoked when any events are sent on the microBit message bus.
+ */
+ 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;
+ EventModel &messageBus;
+
+ // 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;
+
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitIOPinService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,143 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_IO_PIN_SERVICE_H
+#define MICROBIT_IO_PIN_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitIO.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 and write pin values over BLE.
+ */
+struct IOData
+{
+ uint8_t pin;
+ uint8_t value;
+};
+
+/**
+ * Class definition for the custom MicroBit IOPin Service.
+ * Provides a BLE service to remotely read the state of the I/O Pin, and configure its behaviour.
+ */
+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.
+ * @param _io An instance of MicroBitIO that this service will use to perform
+ * I/O operations.
+ */
+ MicroBitIOPinService(BLEDevice &_ble, MicroBitIO &_io);
+
+ /**
+ * Periodic callback from MicroBit scheduler.
+ *
+ * Check if any of the pins we're watching need updating. Notify any connected
+ * device with any changes.
+ */
+ 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 characteristic.
+ */
+ void onDataRead(GattReadAuthCallbackParams *params);
+
+ /**
+ * Determines if the given pin was configured as a digital pin by the BLE ADPinConfigurationCharacterisitic.
+ *
+ * @param i the enumeration of the pin to test
+ * @return 1 if this pin is configured as digital, 0 otherwise
+ */
+ int isDigital(int i);
+
+ /**
+ * Determines if the given pin was configured as an analog pin by the BLE ADPinConfigurationCharacterisitic.
+ *
+ * @param i the enumeration of the pin to test
+ * @return 1 if this pin is configured as analog, 0 otherwise
+ */
+ int isAnalog(int i);
+
+ /**
+ * Determines if the given pin was configured as an input by the BLE IOPinConfigurationCharacterisitic.
+ *
+ * @param i 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 i 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;
+ MicroBitIO &io;
+
+ // 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitLEDService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,91 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_LED_SERVICE_H
+#define MICROBIT_LED_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitDisplay.h"
+
+// Defines the buffer size for scrolling text over BLE, hence also defines
+// the maximum string length that can be scrolled via the BLE service.
+#define MICROBIT_BLE_MAXIMUM_SCROLLTEXT 20
+
+// UUIDs for our service and characteristics
+extern const uint8_t MicroBitLEDServiceUUID[];
+extern const uint8_t MicroBitLEDServiceMatrixUUID[];
+extern const uint8_t MicroBitLEDServiceTextUUID[];
+extern const uint8_t MicroBitLEDServiceScrollingSpeedUUID[];
+
+
+/**
+ * Class definition for the custom MicroBit LED Service.
+ * Provides a BLE service to remotely read and write the state of the LED display.
+ */
+class MicroBitLEDService
+{
+ public:
+
+ /**
+ * Constructor.
+ * Create a representation of the LEDService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _display An instance of MicroBitDisplay to interface with.
+ */
+ MicroBitLEDService(BLEDevice &_ble, MicroBitDisplay &_display);
+
+ /**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+ void onDataWritten(const GattWriteCallbackParams *params);
+
+ /**
+ * Callback. Invoked when any of our attributes are read via BLE.
+ */
+ void onDataRead(GattReadAuthCallbackParams *params);
+
+ private:
+
+ // Bluetooth stack we're running on.
+ BLEDevice &ble;
+ MicroBitDisplay &display;
+
+ // memory for our 8 bit control characteristics.
+ uint8_t matrixCharacteristicBuffer[5];
+ uint16_t scrollingSpeedCharacteristicBuffer;
+ uint8_t textCharacteristicBuffer[MICROBIT_BLE_MAXIMUM_SCROLLTEXT];
+
+ // Handles to access each characteristic when they are held by Soft Device.
+ GattAttribute::Handle_t matrixCharacteristicHandle;
+ GattAttribute::Handle_t textCharacteristicHandle;
+ GattAttribute::Handle_t scrollingSpeedCharacteristicHandle;
+
+ // We hold a copy of the GattCharacteristic, as mbed's BLE API requires this to provide read callbacks (pity!).
+ GattCharacteristic matrixCharacteristic;
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitMagnetometerService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,91 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_MAGNETOMETER_SERVICE_H
+#define MICROBIT_MAGNETOMETER_SERVICE_H
+
+#include "ble/BLE.h"
+#include "MicroBitConfig.h"
+#include "MicroBitCompass.h"
+#include "EventModel.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 the 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.
+ * @param _compass An instance of MicroBitCompass to use as our Magnetometer source.
+ */
+ MicroBitMagnetometerService(BLEDevice &_ble, MicroBitCompass &_compass);
+
+ 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;
+ MicroBitCompass &compass;
+
+ // 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitTemperatureService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,82 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_TEMPERATURE_SERVICE_H
+#define MICROBIT_TEMPERATURE_SERVICE_H
+
+#include "MicroBitConfig.h"
+#include "ble/BLE.h"
+#include "MicroBitThermometer.h"
+#include "EventModel.h"
+
+// UUIDs for our service and characteristics
+extern const uint8_t MicroBitTemperatureServiceUUID[];
+extern const uint8_t MicroBitTemperatureServiceDataUUID[];
+extern const uint8_t MicroBitTemperatureServicePeriodUUID[];
+
+
+/**
+ * Class definition for the custom MicroBit Temperature Service.
+ * Provides a BLE service to remotely read the silicon temperature of the nRF51822.
+ */
+class MicroBitTemperatureService
+{
+ public:
+
+ /**
+ * Constructor.
+ * Create a representation of the TemperatureService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _thermometer An instance of MicroBitThermometer to use as our temperature source.
+ */
+ MicroBitTemperatureService(BLEDevice &_ble, MicroBitThermometer &_thermometer);
+
+ /**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+ void onDataWritten(const GattWriteCallbackParams *params);
+
+ /**
+ * Temperature update callback
+ */
+ void temperatureUpdate(MicroBitEvent e);
+
+ private:
+
+ // Bluetooth stack we're running on.
+ BLEDevice &ble;
+ MicroBitThermometer &thermometer;
+
+ // memory for our 8 bit temperature characteristic.
+ int8_t temperatureDataCharacteristicBuffer;
+ uint16_t temperaturePeriodCharacteristicBuffer;
+
+ // Handles to access each characteristic when they are held by Soft Device.
+ GattAttribute::Handle_t temperatureDataCharacteristicHandle;
+ GattAttribute::Handle_t temperaturePeriodCharacteristicHandle;
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/bluetooth/MicroBitUARTService.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,277 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_UART_SERVICE_H
+#define MICROBIT_UART_SERVICE_H
+
+#include "mbed.h"
+#include "ble/UUID.h"
+#include "ble/BLE.h"
+#include "MicroBitConfig.h"
+#include "MicroBitSerial.h"
+
+#define MICROBIT_UART_S_DEFAULT_BUF_SIZE 20
+
+#define MICROBIT_UART_S_EVT_DELIM_MATCH 1
+#define MICROBIT_UART_S_EVT_HEAD_MATCH 2
+#define MICROBIT_UART_S_EVT_RX_FULL 3
+
+/**
+ * Class definition for the custom MicroBit UART Service.
+ * Provides a BLE service that acts as a UART port, enabling the reception and transmission
+ * of an arbitrary number of bytes.
+ */
+class MicroBitUARTService
+{
+ uint8_t* rxBuffer;
+
+ uint8_t* txBuffer;
+
+ uint8_t rxBufferHead;
+ uint8_t rxBufferTail;
+ uint8_t rxBufferSize;
+
+ uint8_t txBufferSize;
+
+ uint32_t txCharacteristicHandle;
+
+ // Bluetooth stack we're running on.
+ BLEDevice &ble;
+
+ //delimeters used for matching on receive.
+ ManagedString delimeters;
+
+ //a variable used when a user calls the eventAfter() method.
+ int rxBuffHeadMatch;
+
+ /**
+ * A callback function for whenever a Bluetooth device writes to our TX characteristic.
+ */
+ void onDataWritten(const GattWriteCallbackParams *params);
+
+ /**
+ * An internal method that copies values from a circular buffer to a linear buffer.
+ *
+ * @param circularBuff a pointer to the source circular buffer
+ * @param circularBuffSize the size of the circular buffer
+ * @param linearBuff a pointer to the destination linear buffer
+ * @param tailPosition the tail position in the circular buffer you want to copy from
+ * @param headPosition the head position in the circular buffer you want to copy to
+ *
+ * @note this method assumes that the linear buffer has the appropriate amount of
+ * memory to contain the copy operation
+ */
+ void circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition);
+
+ public:
+
+ /**
+ * Constructor for the UARTService.
+ * @param _ble an instance of BLEDevice
+ * @param rxBufferSize the size of the rxBuffer
+ * @param txBufferSize the size of the txBuffer
+ *
+ * @note The default size is MICROBIT_UART_S_DEFAULT_BUF_SIZE (20 bytes).
+ */
+ MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE, uint8_t txBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE);
+
+ /**
+ * Retreives a single character from our RxBuffer.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read a single character, and return immediately
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
+ */
+ int getc(MicroBitSerialMode mode = SYNC_SLEEP);
+
+ /**
+ * places a single character into our transmission buffer,
+ *
+ * @param c the character to transmit
+ *
+ * @return the number of characters written (0, or 1).
+ */
+ int putc(char c);
+
+ /**
+ * Copies characters into the buffer used for Transmitting to the central device.
+ *
+ * @param buf a buffer containing length number of bytes.
+ * @param length the size of the buffer.
+ *
+ * @return the number of characters copied into the buffer
+ *
+ * @note no modes for sending are available at the moment, due to interrupt overhead.
+ */
+ int send(const uint8_t *buf, int length);
+
+ /**
+ * Copies characters into the buffer used for Transmitting to the central device.
+ *
+ * @param s the string to transmit
+ *
+ * @return the number of characters copied into the buffer
+ *
+ * @note no modes for sending are available at the moment, due to interrupt overhead.
+ */
+ int send(ManagedString s);
+
+ /**
+ * Reads a number of characters from the rxBuffer and fills user given buffer.
+ *
+ * @param buf a pointer to a buffer of len bytes.
+ * @param len the size of the user allocated buffer
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read all available characters, and return immediately
+ * until the buffer limit is reached
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all determine whether the given number of characters
+ * are available in our buffer, if not, it will set an event and sleep
+ * until the number of characters are avaialable.
+ *
+ * @return the number of characters digested
+ */
+ int read(uint8_t *buf, int len, MicroBitSerialMode mode = SYNC_SLEEP);
+
+ /**
+ * Reads a number of characters from the rxBuffer and returns them as a ManagedString
+ *
+ * @param len the number of characters to read.
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read all available characters, and return immediately
+ * until the buffer limit is reached
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all determine whether the given number of characters
+ * are available in our buffer, if not, it will set an event and sleep
+ * until the number of characters are avaialable.
+ *
+ * @return an empty ManagedString on error, or a ManagedString containing characters
+ */
+ ManagedString read(int len, MicroBitSerialMode mode = SYNC_SLEEP);
+
+ /**
+ * Reads characters until a character matches one of the given delimeters
+ *
+ * @param delimeters the number of characters to match against
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt read the immediate buffer, and look for a match.
+ * If there isn't, an empty ManagedString will be returned.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
+ * if a match is not found, it will block on an event, fired when a
+ * character is matched.
+ *
+ * @return an empty ManagedString on error, or a ManagedString containing characters
+ */
+ ManagedString readUntil(ManagedString delimeters, MicroBitSerialMode mode = SYNC_SLEEP);
+
+ /**
+ * Configures an event to be fired on a match with one of the delimeters.
+ *
+ * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+ int eventOn(ManagedString delimeters, MicroBitSerialMode mode = ASYNC);
+
+ /**
+ * Configures an event to be fired after "len" characters.
+ *
+ * @param len the number of characters to wait before triggering the event
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ */
+ int eventAfter(int len, MicroBitSerialMode mode = ASYNC);
+
+ /**
+ * Determines if we have space in our rxBuff.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ */
+ int isReadable();
+
+ /**
+ * @return The currently buffered number of bytes in our rxBuff.
+ */
+ int rxBufferedSize();
+
+ /**
+ * @return The currently buffered number of bytes in our txBuff.
+ */
+ int txBufferedSize();
+};
+
+extern const uint8_t UARTServiceBaseUUID[UUID::LENGTH_OF_LONG_UUID];
+extern const uint16_t UARTServiceShortUUID;
+extern const uint16_t UARTServiceTXCharacteristicShortUUID;
+extern const uint16_t UARTServiceRXCharacteristicShortUUID;
+
+extern const uint8_t UARTServiceUUID[UUID::LENGTH_OF_LONG_UUID];
+extern const uint8_t UARTServiceUUID_reversed[UUID::LENGTH_OF_LONG_UUID];
+
+extern const uint8_t UARTServiceTXCharacteristicUUID[UUID::LENGTH_OF_LONG_UUID];
+extern const uint8_t UARTServiceRXCharacteristicUUID[UUID::LENGTH_OF_LONG_UUID];
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/ErrorNo.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,86 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef ERROR_NO_H
+#define ERROR_NO_H
+
+#include "MicroBitConfig.h"
+
+/**
+ * Error codes used in the micro:bit runtime.
+ * These may be returned from functions implemented in the micro:bit runtime.
+ */
+enum ErrorCode{
+
+ // No error occurred.
+ MICROBIT_OK = 0,
+
+ // Invalid parameter given.
+ MICROBIT_INVALID_PARAMETER = -1001,
+
+ // Requested operation is unsupported.
+ MICROBIT_NOT_SUPPORTED = -1002,
+
+ // Device calibration errors
+ MICROBIT_CALIBRATION_IN_PROGRESS = -1003,
+ MICROBIT_CALIBRATION_REQUIRED = -1004,
+
+ // The requested operation could not be performed as the device has run out of some essential resource (e.g. allocated memory)
+ MICROBIT_NO_RESOURCES = -1005,
+
+ // The requested operation could not be performed as some essential resource is busy (e.g. the display)
+ MICROBIT_BUSY = -1006,
+
+ // The requested operation was cancelled before it completed.
+ MICROBIT_CANCELLED = -1007,
+
+ // I2C Communication error occured (typically I2C module on processor has locked up.)
+ MICROBIT_I2C_ERROR = -1010,
+
+ // The serial bus is currently in use by another fiber.
+ MICROBIT_SERIAL_IN_USE = -1011,
+
+ // The requested operation had no data to return.
+ MICROBIT_NO_DATA = -1012
+};
+
+/**
+ * Error codes used in the micro:bit runtime.
+ */
+enum PanicCode{
+ // PANIC Codes. These are not return codes, but are terminal conditions.
+ // These induce a panic operation, where all code stops executing, and a panic state is
+ // entered where the panic code is diplayed.
+
+ // Out out memory error. Heap storage was requested, but is not available.
+ MICROBIT_OOM = 20,
+
+ // Corruption detected in the micro:bit heap space
+ MICROBIT_HEAP_ERROR = 30,
+
+ // Dereference of a NULL pointer through the ManagedType class,
+ MICROBIT_NULL_DEREFERENCE = 40,
+};
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/EventModel.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,430 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef EVENT_MODEL_H
+#define EVENT_MODEL_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitEvent.h"
+#include "MicroBitListener.h"
+#include "ErrorNo.h"
+
+/**
+ * Class definition for the micro:bit EventModel.
+ *
+ * It is common to need to send events from one part of a program (or system) to another.
+ * The way that these events are stored and delivered is known as an Event Model...
+ *
+ * The micro:bit can be programmed in a number of languages, and it not be good to
+ * constrain those languages to any particular event model (e.g. they may have their own already).
+ *
+ * This class defines the functionality an event model needs to have to be able to interact
+ * with events generated and/or used by the micro:bit runtime. Programmer may choose to implement
+ * such funcitonality to integrate their own event models.
+ *
+ * This is an example of a key principle in computing - ABSTRACTION. This is now part of the
+ * UK's Computing curriculum in schools... so ask your teacher about it. :-)
+ *
+ * An EventModel implementation is provided in the MicroBitMessageBus class.
+ */
+class EventModel
+{
+ public:
+
+ static EventModel *defaultEventBus;
+
+ /**
+ * Queues the given event to be sent to all registered recipients.
+ * The method of delivery will vary depending on the underlying implementation.
+ *
+ * @param The event to send.
+ *
+ * @return This default implementation simply returns MICROBIT_NOT_SUPPORTED.
+ */
+ virtual int send(MicroBitEvent evt)
+ {
+ (void) evt;
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ /**
+ * Add the given MicroBitListener to the list of event handlers, unconditionally.
+ *
+ * @param listener The MicroBitListener to validate.
+ *
+ * @return This default implementation simply returns MICROBIT_NOT_SUPPORTED.
+ */
+ virtual int add(MicroBitListener *listener)
+ {
+ (void) listener;
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ /**
+ * Remove the given MicroBitListener from the list of event handlers.
+ *
+ * @param listener The MicroBitListener to remove.
+ *
+ * @return This default implementation simply returns MICROBIT_NOT_SUPPORTED.
+ */
+ virtual int remove(MicroBitListener *listener)
+ {
+ (void) listener;
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ /**
+ * Returns the MicroBitListener at the given position in the list.
+ *
+ * @param n The index of the desired MicroBitListener.
+ *
+ * @return This default implementation simply returns NULL.
+ */
+ MicroBitListener *elementAt(int n)
+ {
+ (void) n;
+ return NULL;
+ }
+
+ /**
+ * Define the default EventModel to use for events raised and consumed by the microbit-dal runtime.
+ * The default EventModel may be changed at any time.
+ *
+ * @param model A new instance of an EventModel to use as the default.
+ *
+ * @return MICROBIT_OK on success.
+ *
+ * Example:
+ * @code
+ * MicroBitMessageBus b();
+ * EventModel:setDefaultEventModel(b);
+ * @endcode
+ */
+ static int setDefaultEventModel(EventModel &model)
+ {
+ EventModel::defaultEventBus = &model;
+ return MICROBIT_OK;
+ }
+
+ /**
+ * Register a listener function.
+ *
+ * An EventModel implementing this interface may optionally choose to override this method,
+ * if that EventModel supports asynchronous callbacks to user code, but there is no
+ * requirement to do so.
+ *
+ * @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
+ * Use MICROBIT_ID_ANY to receive events from all components.
+ *
+ * @param value The value of messages to listen for. Events with any other values will be filtered.
+ * Use MICROBIT_EVT_ANY to receive events of any value.
+ *
+ * @param handler The function to call when an event is received.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ *
+ * @return MICROBIT_OK on success, or any valid error code defined in "ErrNo.h". The default implementation
+ * simply returns MICROBIT_NOT_SUPPORTED.
+ *
+ * @code
+ * void onButtonBClicked(MicroBitEvent)
+ * {
+ * //do something
+ * }
+ *
+ * // call onButtonBClicked when ever a click event from buttonB is detected.
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ * @endcode
+ */
+ int listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS)
+ {
+ if (handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener *newListener = new MicroBitListener(id, value, handler, flags);
+
+ if(add(newListener) == MICROBIT_OK)
+ return MICROBIT_OK;
+
+ delete newListener;
+
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ /**
+ * Register a listener function.
+ *
+ * An EventModel implementing this interface may optionally choose to override this method,
+ * if that EventModel supports asynchronous callbacks to user code, but there is no
+ * requirement to do so.
+ *
+ * @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
+ * Use MICROBIT_ID_ANY to receive events from all components.
+ *
+ * @param value The value of messages to listen for. Events with any other values will be filtered.
+ * Use MICROBIT_EVT_ANY to receive events of any value.
+ *
+ * @param handler The function to call when an event is received.
+ *
+ * @param arg Provide the callback with in an additional argument.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ *
+ * @return MICROBIT_OK on success, or any valid error code defined in "ErrNo.h". The default implementation
+ * simply returns MICROBIT_NOT_SUPPORTED.
+ *
+ * @code
+ * void onButtonBClicked(MicroBitEvent, void* data)
+ * {
+ * //do something
+ * }
+ *
+ * // call onButtonBClicked when ever a click event from buttonB is detected.
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ * @endcode
+ */
+ int listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS)
+ {
+ if (handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg, flags);
+
+ if(add(newListener) == MICROBIT_OK)
+ return MICROBIT_OK;
+
+ delete newListener;
+
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ /**
+ * Register a listener function.
+ *
+ * @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
+ * Use MICROBIT_ID_ANY to receive events from all components.
+ *
+ * @param value The value of messages to listen for. Events with any other values will be filtered.
+ * Use MICROBIT_EVT_ANY to receive events of any value.
+ *
+ * @param hander The function to call when an event is received.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler or object
+ * pointers are NULL.
+ *
+ * @code
+ * void SomeClass::onButtonBClicked(MicroBitEvent)
+ * {
+ * //do something
+ * }
+ *
+ * SomeClass s = new SomeClass();
+ *
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick);
+ * @endcode
+ */
+ template <typename T>
+ int listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS);
+
+
+ /**
+ * Unregister a listener function.
+ * Listeners are identified by the Event ID, Event value and handler registered using listen().
+ *
+ * @param id The Event ID used to register the listener.
+ * @param value The Event value used to register the listener.
+ * @param handler The function used to register the listener.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler
+ * given is NULL.
+ *
+ * Example:
+ * @code
+ * void onButtonBClick(MicroBitEvent)
+ * {
+ * //do something
+ * }
+ *
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ *
+ * // the previously created listener is now ignored.
+ * uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ * @endcode
+ */
+ int ignore(int id, int value, void (*handler)(MicroBitEvent))
+ {
+ if (handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener listener(id, value, handler);
+ remove(&listener);
+
+ return MICROBIT_OK;
+ }
+
+ /**
+ * Unregister a listener function.
+ * Listeners are identified by the Event ID, Event value and handler registered using listen().
+ *
+ * @param id The Event ID used to register the listener.
+ * @param value The Event value used to register the listener.
+ * @param handler The function used to register the listener.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler
+ * given is NULL.
+ *
+ * Example:
+ * @code
+ * void onButtonBClick(MicroBitEvent, void* data)
+ * {
+ * //do something
+ * }
+ *
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ *
+ * // the previously created listener is now ignored.
+ * uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
+ * @endcode
+ */
+ int ignore(int id, int value, void (*handler)(MicroBitEvent, void*))
+ {
+ if (handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener listener(id, value, handler, NULL);
+ remove(&listener);
+
+ return MICROBIT_OK;
+ }
+
+ /**
+ * Unregister a listener function.
+ * Listners are identified by the Event ID, Event value and handler registered using listen().
+ *
+ * @param id The Event ID used to register the listener.
+ * @param value The Event value used to register the listener.
+ * @param handler The function used to register the listener.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler or object
+ * pointers are NULL.
+ *
+ * Example:
+ * @code
+ *
+ * void SomeClass::onButtonBClick()
+ * {
+ * //do something
+ * }
+ *
+ * SomeClass s = new SomeClass();
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick);
+ *
+ * // the previously created listener is now ignored.
+ * uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick);
+ * @endcode
+ */
+ template <typename T>
+ int ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent));
+
+};
+
+/**
+ * A registration function to allow C++ member functions (methods) to be registered as an event
+ * listener.
+ *
+ * @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
+ * Use MICROBIT_ID_ANY to receive events from all components.
+ *
+ * @param value The value of messages to listen for. Events with any other values will be filtered.
+ * Use MICROBIT_EVT_ANY to receive events of any value.
+ *
+ * @param object The object on which the method should be invoked.
+ *
+ * @param handler The method to call when an event is received.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler or object
+ * pointers are NULL.
+ */
+template <typename T>
+int EventModel::listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags)
+{
+ if (object == NULL || handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener *newListener = new MicroBitListener(id, value, object, handler, flags);
+
+ if(add(newListener) == MICROBIT_OK)
+ return MICROBIT_OK;
+
+ delete newListener;
+ return MICROBIT_NOT_SUPPORTED;
+}
+
+/**
+ * Unregister a listener function.
+ * Listners are identified by the Event ID, Event value and handler registered using listen().
+ *
+ * @param id The Event ID used to register the listener.
+ * @param value The Event value used to register the listener.
+ * @param handler The function used to register the listener.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER if the handler or object
+ * pointers are NULL.
+ *
+ * Example:
+ * @code
+ *
+ * void SomeClass::onButtonBClick()
+ * {
+ * //do something
+ * }
+ *
+ * SomeClass s = new SomeClass();
+ * uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick);
+ *
+ * // the previously created listener is now ignored.
+ * uBit.messageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick);
+ * @endcode
+ */
+template <typename T>
+int EventModel::ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent))
+{
+ if (handler == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ MicroBitListener listener(id, value, object, handler);
+ remove(&listener);
+
+ return MICROBIT_OK;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MemberFunctionCallback.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,114 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MEMBER_FUNCTION_CALLBACK_H
+#define MEMBER_FUNCTION_CALLBACK_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitEvent.h"
+#include "MicroBitCompat.h"
+
+/**
+ * Class definition for a MemberFunctionCallback.
+ *
+ * C++ member functions (also known as methods) have a more complex
+ * representation than normal C functions. This class allows a reference to
+ * a C++ member function to be stored then called at a later date.
+ *
+ * This class is used extensively by the MicroBitMessageBus to deliver
+ * events to C++ methods.
+ */
+class MemberFunctionCallback
+{
+ private:
+ void* object;
+ uint32_t method[4];
+ void (*invoke)(void *object, uint32_t *method, MicroBitEvent e);
+ template <typename T> static void methodCall(void* object, uint32_t*method, MicroBitEvent e);
+
+ public:
+
+ /**
+ * Constructor. Creates a MemberFunctionCallback based on a pointer to given method.
+ *
+ * @param object The object the callback method should be invooked on.
+ *
+ * @param method The method to invoke.
+ */
+ template <typename T> MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e));
+
+ /**
+ * A comparison of two MemberFunctionCallback objects.
+ *
+ * @return true if the given MemberFunctionCallback is equivalent to this one, false otherwise.
+ */
+ bool operator==(const MemberFunctionCallback &mfc);
+
+ /**
+ * Calls the method reference held by this MemberFunctionCallback.
+ *
+ * @param e The event to deliver to the method
+ */
+ void fire(MicroBitEvent e);
+};
+
+/**
+ * Constructor. Creates a MemberFunctionCallback based on a pointer to given method.
+ *
+ * @param object The object the callback method should be invooked on.
+ *
+ * @param method The method to invoke.
+ */
+template <typename T>
+MemberFunctionCallback::MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e))
+{
+ this->object = object;
+ memclr(this->method, sizeof(this->method));
+ memcpy(this->method, &method, sizeof(method));
+ invoke = &MemberFunctionCallback::methodCall<T>;
+}
+
+/**
+ * A template used to create a static method capable of invoking a C++ member function (method)
+ * based on the given parameters.
+ *
+ * @param object The object the callback method should be invooked on.
+ *
+ * @param method The method to invoke.
+ *
+ * @param method The MicroBitEvent to supply to the given member function.
+ */
+template <typename T>
+void MemberFunctionCallback::methodCall(void *object, uint32_t *method, MicroBitEvent e)
+{
+ T* o = (T*)object;
+ void (T::*m)(MicroBitEvent);
+ memcpy(&m, method, sizeof(m));
+
+ (o->*m)(e);
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitCompat.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,111 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * This file contains functions used to maintain compatability and portability.
+ * It also contains constants that are used elsewhere in the DAL.
+ */
+
+#ifndef MICROBIT_COMPAT_H
+#define MICROBIT_COMPAT_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+#define PI 3.14159265359
+
+/**
+ * Determines the smallest of the two numbers
+ *
+ * @param a the first number
+ *
+ * @param b the second number
+ *
+ * @return The smallest of the two given values.
+ */
+inline int min(int a, int b)
+{
+ return (a < b ? a : b);
+}
+
+/**
+ * Determines the largest of the two numbers
+ *
+ * @param a the first number
+ *
+ * @param b the second number
+ *
+ * @return The larger of the two given values.
+ */
+inline int max(int a, int b)
+{
+ return (a > b ? a : b);
+}
+
+/**
+ * Sets a given area of memory to zero.
+ *
+ * @param a the pointer to the beginning of the memory to clear
+ *
+ * @param b the number of bytes to clear.
+ */
+inline void *memclr(void *a, size_t b)
+{
+ return memset(a,0,b);
+}
+
+/**
+ * Determines if the given character is a printable ASCII/UTF8 decimal digit (0..9).
+ *
+ * @param c the character to check
+ *
+ * @return true if the character is a digit, false otherwise.
+ */
+inline bool isdigit(char c)
+{
+ return (c > 47 && c < 58);
+}
+
+/**
+ * Performs an in buffer reverse of a given char array.
+ *
+ * @param s the string to reverse.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int string_reverse(char *s);
+
+/**
+ * Converts a given integer into a string representation.
+ *
+ * @param n The number to convert.
+ *
+ * @param s A pointer to the buffer where the resulting string will be stored.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int itoa(int n, char *s);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitComponent.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,152 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_COMPONENT_H
+#define MICROBIT_COMPONENT_H
+
+#include "MicroBitConfig.h"
+
+// Enumeration of core components.
+#define MICROBIT_ID_BUTTON_A 1
+#define MICROBIT_ID_BUTTON_B 2
+#define MICROBIT_ID_BUTTON_RESET 3
+#define MICROBIT_ID_ACCELEROMETER 4
+#define MICROBIT_ID_COMPASS 5
+#define MICROBIT_ID_DISPLAY 6
+
+//EDGE connector events
+#define MICROBIT_IO_PINS 20
+
+#define MICROBIT_ID_IO_P0 7 //P0 is the left most pad (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P1 8 //P1 is the middle pad (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P2 9 //P2 is the right most pad (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P3 10 //COL1 (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P4 11 //BTN_A
+#define MICROBIT_ID_IO_P5 12 //COL2 (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P6 13 //ROW2
+#define MICROBIT_ID_IO_P7 14 //ROW1
+#define MICROBIT_ID_IO_P8 15 //PIN 18
+#define MICROBIT_ID_IO_P9 16 //ROW3
+#define MICROBIT_ID_IO_P10 17 //COL3 (ANALOG/DIGITAL)
+#define MICROBIT_ID_IO_P11 18 //BTN_B
+#define MICROBIT_ID_IO_P12 19 //PIN 20
+#define MICROBIT_ID_IO_P13 20 //SCK
+#define MICROBIT_ID_IO_P14 21 //MISO
+#define MICROBIT_ID_IO_P15 22 //MOSI
+#define MICROBIT_ID_IO_P16 23 //PIN 16
+#define MICROBIT_ID_IO_P19 24 //SCL
+#define MICROBIT_ID_IO_P20 25 //SDA
+
+#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton
+#define MICROBIT_ID_GESTURE 27 // Gesture events
+
+#define MICROBIT_ID_THERMOMETER 28
+#define MICROBIT_ID_RADIO 29
+#define MICROBIT_ID_RADIO_DATA_READY 30
+#define MICROBIT_ID_MULTIBUTTON_ATTACH 31
+#define MICROBIT_ID_SERIAL 32
+
+#define MICROBIT_ID_MESSAGE_BUS_LISTENER 1021 // Message bus indication that a handler for a given ID has been registered.
+#define MICROBIT_ID_NOTIFY_ONE 1022 // Notfication channel, for general purpose synchronisation
+#define MICROBIT_ID_NOTIFY 1023 // Notfication channel, for general purpose synchronisation
+
+// Universal flags used as part of the status field
+#define MICROBIT_COMPONENT_RUNNING 0x01
+
+
+/**
+ * Class definition for MicroBitComponent.
+ *
+ * All components should inherit from this class.
+ *
+ * If a component requires regular updates, then you should add the component
+ * to the systemTick and idleTick queues.
+ *
+ * The system timer will call systemTick() once the component has been added to
+ * the array of system components using system_timer_add_component. This callback
+ * will be in interrupt context.
+ *
+ * The idle thread will call idleTick() once the component has been added to the array
+ * of idle components using fiber_add_idle_component. Updates are determined by
+ * the isIdleCallbackNeeded() member function.
+ */
+class MicroBitComponent
+{
+ protected:
+
+ uint16_t id; // Event Bus ID
+ uint8_t status; // keeps track of various component state, and also indicates if data is ready.
+
+ public:
+
+ /**
+ * The default constructor of a MicroBitComponent
+ */
+ MicroBitComponent()
+ {
+ this->id = 0;
+ this->status = 0;
+ }
+
+ /**
+ * The system timer will call this member function once the component has been added to
+ * the array of system components using system_timer_add_component. This callback
+ * will be in interrupt context.
+ */
+ virtual void systemTick(){
+
+ }
+
+ /**
+ * The idle thread will call this member function once the component has been added to the array
+ * of idle components using fiber_add_idle_component. Updates are determined by
+ * the isIdleCallbackNeeded() member function.
+ */
+ virtual void idleTick()
+ {
+
+ }
+
+ /**
+ * When added to the idleThreadComponents array, this function will be called to determine
+ * if and when data is ready.
+ *
+ * @note override this if you want to request to be scheduled as soon as possible.
+ */
+ virtual int isIdleCallbackNeeded()
+ {
+ return 0;
+ }
+
+ /**
+ * If you have added your component to the idle or system tick component arrays,
+ * you must remember to remove your component from them if your component is destructed.
+ */
+ virtual ~MicroBitComponent()
+ {
+ }
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/inc/core/MicroBitConfig.h Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,373 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +/** + * Compile time configuration options for the micro:bit runtime. + */ + +#ifndef MICROBIT_CONFIG_H +#define MICROBIT_CONFIG_H + +#include "mbed.h" + +// +// Memory configuration +// + +// The start address of usable RAM memory. +#ifndef MICROBIT_SRAM_BASE +#define MICROBIT_SRAM_BASE 0x20000008 +#endif + +// Physical address of the top of SRAM. +#ifndef MICROBIT_SRAM_END +#define MICROBIT_SRAM_END 0x20004000 +#endif + +// The end address of memory normally reserved for Soft Device. +#ifndef MICROBIT_SD_LIMIT +#define MICROBIT_SD_LIMIT 0x20002000 +#endif + +// The physical address in memory of the Soft Device GATT table. +#ifndef MICROBIT_SD_GATT_TABLE_START +#define MICROBIT_SD_GATT_TABLE_START 0x20001900 +#endif + +// Physical address of the top of the system stack (on mbed-classic this is the top of SRAM) +#ifndef CORTEX_M0_STACK_BASE +#define CORTEX_M0_STACK_BASE MICROBIT_SRAM_END +#endif + +// Amount of memory reserved for the stack at the end of memory (bytes). +#ifndef MICROBIT_STACK_SIZE +#define MICROBIT_STACK_SIZE 2048 +#endif + +// Physical address of the end of mbed heap space. +#ifndef MICROBIT_HEAP_END +#define MICROBIT_HEAP_END (CORTEX_M0_STACK_BASE - MICROBIT_STACK_SIZE) +#endif + +// Enables or disables the MicroBitHeapllocator. Note that if disabled, no reuse of the SRAM normally +// reserved for SoftDevice is possible, and out of memory condition will no longer be trapped... +// i.e. panic() will no longer be triggered on memory full conditions. +#ifndef MICROBIT_HEAP_ALLOCATOR +#define MICROBIT_HEAP_ALLOCATOR 1 +#endif + +// Block size used by the allocator in bytes. +// n.b. Currently only 32 bits (4 bytes) is supported. +#ifndef MICROBIT_HEAP_BLOCK_SIZE +#define MICROBIT_HEAP_BLOCK_SIZE 4 +#endif + +// The proportion of SRAM available on the mbed heap to reserve for the micro:bit heap. +#ifndef MICROBIT_NESTED_HEAP_SIZE +#define MICROBIT_NESTED_HEAP_SIZE 0.75 +#endif + +// If defined, reuse any unused SRAM normally reserved for SoftDevice (Nordic's memory resident BLE stack) as heap memory. +// The amount of memory reused depends upon whether or not BLE is enabled using MICROBIT_BLE_ENABLED. +// Set '1' to enable. +#ifndef MICROBIT_HEAP_REUSE_SD +#define MICROBIT_HEAP_REUSE_SD 1 +#endif + +// The amount of memory allocated to Soft Device to hold its BLE GATT table. +// For standard S110 builds, this should be word aligned and in the range 0x300 - 0x700. +// Any unused memory will be automatically reclaimed as HEAP memory if both MICROBIT_HEAP_REUSE_SD and MICROBIT_HEAP_ALLOCATOR are enabled. +#ifndef MICROBIT_SD_GATT_TABLE_SIZE +#define MICROBIT_SD_GATT_TABLE_SIZE 0x300 +#endif + +// +// Fiber scheduler configuration +// + +// Scheduling quantum (milliseconds) +// Also used to drive the micro:bit runtime system ticker. +#ifndef SYSTEM_TICK_PERIOD_MS +#define SYSTEM_TICK_PERIOD_MS 6 +#endif + +// +// Message Bus: +// Default behaviour for event handlers, if not specified in the listen() call +// +// Permissable values are: +// MESSAGE_BUS_LISTENER_REENTRANT +// MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY +// MESSAGE_BUS_LISTENER_DROP_IF_BUSY +// MESSAGE_BUS_LISTENER_IMMEDIATE + +#ifndef EVENT_LISTENER_DEFAULT_FLAGS +#define EVENT_LISTENER_DEFAULT_FLAGS MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY +#endif + +// +// Maximum event queue depth. If a queue exceeds this depth, further events will be dropped. +// Used to prevent message queues growing uncontrollably due to badly behaved user code and causing panic conditions. +// +#ifndef MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH +#define MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH 10 +#endif + +// +// Core micro:bit services +// + +// To reduce memory cost and complexity, the micro:bit allows components to register for +// periodic callback events during interrupt context, which occur every scheduling quantum (FIBER_TICK_PERIOD_MS) +// This defines the maximum size of interrupt callback list. +#ifndef MICROBIT_SYSTEM_COMPONENTS +#define MICROBIT_SYSTEM_COMPONENTS 10 +#endif + +// To reduce memory cost and complexity, the micro:bit allows components to register for +// periodic callback events when the processor is idle. +// This defines the maximum size of the idle callback list. +#ifndef MICROBIT_IDLE_COMPONENTS +#define MICROBIT_IDLE_COMPONENTS 6 +#endif + +// +// BLE options +// +// The BLE stack is very memory hungry. Each service can therefore be compiled in or out +// by enabling/disabling the options below. +// +// n.b. The minimum set of services to enable over the air programming of the device will +// still be brought up in pairing mode regardless of the settings below. +// + +// Enable/Disable BLE during normal operation. +// Set '1' to enable. +#ifndef MICROBIT_BLE_ENABLED +#define MICROBIT_BLE_ENABLED 1 +#endif + +// Enable/Disable BLE pairing mode mode at power up. +// Set '1' to enable. +#ifndef MICROBIT_BLE_PAIRING_MODE +#define MICROBIT_BLE_PAIRING_MODE 1 +#endif + +// Enable/Disable the use of private resolvable addresses. +// Set '1' to enable. +// n.b. This is known to be a feature that suffers compatibility issues with many BLE central devices. +#ifndef MICROBIT_BLE_PRIVATE_ADDRESSES +#define MICROBIT_BLE_PRIVATE_ADDRESSES 0 +#endif + +// Convenience option to enable / disable BLE security entirely +// Open BLE links are not secure, but commonly used during the development of BLE services +// Set '1' to disable all secuity +#ifndef MICROBIT_BLE_OPEN +#define MICROBIT_BLE_OPEN 0 +#endif + +// Configure for open BLE operation if so configured +#if (MICROBIT_BLE_OPEN == 1) +#define MICROBIT_BLE_SECURITY_LEVEL SECURITY_MODE_ENCRYPTION_OPEN_LINK +#define MICROBIT_BLE_WHITELIST 0 +#define MICROBIT_BLE_ADVERTISING_TIMEOUT 0 +#define MICROBIT_BLE_DEFAULT_TX_POWER 6 +#endif + + +// Define the default, global BLE security requirements for MicroBit BLE services +// May be one of the following options (see mbed's SecurityManager class implementaiton detail) +// SECURITY_MODE_ENCRYPTION_OPEN_LINK: No bonding, encryption, or whitelisting required. +// SECURITY_MODE_ENCRYPTION_NO_MITM: Bonding, encyption and whitelisting but no passkey. +// SECURITY_MODE_ENCRYPTION_WITH_MITM: Bonding, encrytion and whitelisting with passkey authentication. +// +#ifndef MICROBIT_BLE_SECURITY_LEVEL +#define MICROBIT_BLE_SECURITY_LEVEL SECURITY_MODE_ENCRYPTION_WITH_MITM +#endif + +// Enable/Disable the use of BLE whitelisting. +// If enabled, the micro:bit will only respond to connection requests from +// known, bonded devices. +#ifndef MICROBIT_BLE_WHITELIST +#define MICROBIT_BLE_WHITELIST 1 +#endif + +// Define the period of time for which the BLE stack will advertise (seconds) +// Afer this period, advertising will cease. Set to '0' for no timeout (always advertise). +#ifndef MICROBIT_BLE_ADVERTISING_TIMEOUT +#define MICROBIT_BLE_ADVERTISING_TIMEOUT 0 +#endif + +// Defines default power level of the BLE radio transmitter. +// Valid values are in the range 0..7 inclusive, with 0 being the lowest power and 7 the highest power. +// Based on trials undertaken by the BBC, the radio is normally set to its lowest power level +// to best protect children's privacy. +#ifndef MICROBIT_BLE_DEFAULT_TX_POWER +#define MICROBIT_BLE_DEFAULT_TX_POWER 0 +#endif + +// Enable/Disable BLE Service: MicroBitDFU +// This allows over the air programming during normal operation. +// Set '1' to enable. +#ifndef MICROBIT_BLE_DFU_SERVICE +#define MICROBIT_BLE_DFU_SERVICE 1 +#endif + +// Enable/Disable BLE Service: MicroBitEventService +// This allows routing of events from the micro:bit message bus over BLE. +// Set '1' to enable. +#ifndef MICROBIT_BLE_EVENT_SERVICE +#define MICROBIT_BLE_EVENT_SERVICE 1 +#endif + +// Enable/Disable BLE Service: MicroBitDeviceInformationService +// This enables the standard BLE device information service. +// Set '1' to enable. +#ifndef MICROBIT_BLE_DEVICE_INFORMATION_SERVICE +#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE 1 +#endif + +// +// Accelerometer options +// + +// Enable this to read 10 bits of data from the acclerometer. +// Otherwise, 8 bits are used. +// Set '1' to enable. +#ifndef USE_ACCEL_LSB +#define USE_ACCEL_LSB 0 +#endif + +// +// Display options +// + +// Selects the matrix configuration for the display driver. +// Known, acceptable options are: +// +#define MICROBUG_REFERENCE_DEVICE 1 +#define MICROBIT_3X9 2 +#define MICROBIT_SB1 3 +#define MICROBIT_SB2 4 + +#ifndef MICROBIT_DISPLAY_TYPE +#define MICROBIT_DISPLAY_TYPE MICROBIT_SB2 +#endif + +// Selects the minimum permissable brightness level for the device +// in the region of 0 (off) to 255 (full brightness) +#ifndef MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS +#define MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS 1 +#endif + +// Selects the maximum permissable brightness level for the device +// in the region of 0 (off) to 255 (full brightness) +#ifndef MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS +#define MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS 255 +#endif + +// Selects the default brightness for the display +// in the region of zero (off) to 255 (full brightness) +#ifndef MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS +#define MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS +#endif + +// Selects the default scroll speed for the display. +// The time taken to move a single pixel (ms). +#ifndef MICROBIT_DEFAULT_SCROLL_SPEED +#define MICROBIT_DEFAULT_SCROLL_SPEED 120 +#endif + +// Selects the number of pixels a scroll will move in each quantum. +#ifndef MICROBIT_DEFAULT_SCROLL_STRIDE +#define MICROBIT_DEFAULT_SCROLL_STRIDE -1 +#endif + +// Selects the time each character will be shown on the display during print operations. +// The time each character is shown on the screen (ms). +#ifndef MICROBIT_DEFAULT_PRINT_SPEED +#define MICROBIT_DEFAULT_PRINT_SPEED 400 +#endif + +//Configures the default serial mode used by serial read and send calls. +#ifndef MICROBIT_DEFAULT_SERIAL_MODE +#define MICROBIT_DEFAULT_SERIAL_MODE SYNC_SLEEP +#endif + + +// +// Panic options +// + +// Enable this to invoke a panic on out of memory conditions. +// Set '1' to enable. +#ifndef MICROBIT_PANIC_HEAP_FULL +#define MICROBIT_PANIC_HEAP_FULL 1 +#endif + +// +// Debug options +// + +// Enable this to route debug messages through the USB serial interface. +// n.b. This also disables the user serial port 'uBit.serial'. +// Set '1' to enable. +#ifndef MICROBIT_DBG +#define MICROBIT_DBG 0 +#endif + +// Enable this to receive diagnostic messages from the heap allocator via the USB serial interface. +// n.b. This requires MICROBIT_DBG to be defined. +// Set '1' to enable. +#ifndef MICROBIT_HEAP_DBG +#define MICROBIT_HEAP_DBG 0 +#endif + +// Versioning options. +// We use semantic versioning (http://semver.org/) to identify differnet versions of the micro:bit runtime. +// Where possible we use yotta (an ARM mbed build tool) to help us track versions. +// if this isn't available, it can be defined manually as a configuration option. +// +#ifndef MICROBIT_DAL_VERSION +#define MICROBIT_DAL_VERSION "unknown" +#endif + + +// +// Helper macro used by the micro:bit runtime to determine if a boolean configuration option is set. +// +#define CONFIG_ENABLED(X) (X == 1) +#define CONFIG_DISABLED(X) (X != 1) + +#if CONFIG_ENABLED(MICROBIT_HEAP_ALLOCATOR) +#include "MicroBitHeapAllocator.h" +#endif + +#if CONFIG_ENABLED(MICROBIT_DBG) +extern RawSerial* SERIAL_DEBUG; +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitDevice.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,136 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Device specific funcitons for the nrf51822 device.
+ *
+ * Provides a degree of platform independence for microbit-dal functionality.
+ *
+ * TODO: Determine if any of this belongs in an mbed target definition.
+ * TODO: Review microbit-dal to place all such functions here.
+ */
+#ifndef MICROBIT_DEVICE_H
+#define MICROBIT_DEVICE_H
+
+#define MICROBIT_NAME_LENGTH 5
+#define MICROBIT_NAME_CODE_LETTERS 5
+#define MICROBIT_PANIC_ERROR_CHARS 4
+
+#include "MicroBitConfig.h"
+#include "MicroBitMatrixMaps.h"
+
+/**
+ * Determines if a BLE stack is currently running.
+ *
+ * @return true is a bluetooth stack is operational, false otherwise.
+ */
+bool ble_running();
+
+/**
+ * Derive a unique, consistent serial number of this device from internal data.
+ *
+ * @return the serial number of this device.
+ */
+uint32_t microbit_serial_number();
+
+/**
+ * Derive the friendly name for this device, based on its serial number.
+ *
+ * @return the serial number of this device.
+ */
+char* microbit_friendly_name();
+
+/**
+ * Perform a hard reset of the micro:bit.
+ */
+void microbit_reset();
+
+/**
+ * Determine the version of microbit-dal currently running.
+ * @return a pointer to a character buffer containing a representation of the semantic version number.
+ */
+const char * microbit_dal_version();
+
+/**
+ * Disables all interrupts and user processing.
+ * Displays "=(" and an accompanying status code on the default display.
+ * @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
+ *
+ * @code
+ * microbit_panic(20);
+ * @endcode
+ */
+void microbit_panic(int statusCode);
+
+/**
+ * Defines the length of time that the device will remain in a error state before resetting.
+ *
+ * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
+ *
+ * @code
+ * microbit_panic_timeout(4);
+ * @endcode
+ */
+void microbit_panic_timeout(int iterations);
+
+/**
+ * Generate a random number in the given range.
+ * We use a simple Galois LFSR random number generator here,
+ * as a Galois LFSR is sufficient for our applications, and much more lightweight
+ * than the hardware random number generator built int the processor, which takes
+ * a long time and uses a lot of energy.
+ *
+ * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
+ * have a think why not. :-)
+ *
+ * @param max the upper range to generate a number for. This number cannot be negative.
+ *
+ * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
+ *
+ * @code
+ * microbit_random(200); //a number between 0 and 199
+ * @endcode
+ */
+int microbit_random(int max);
+
+/**
+ * Seed the random number generator (RNG).
+ *
+ * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
+ * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
+ * with a less than optimal application interface. A Galois LFSR is sufficient for our
+ * applications, and much more lightweight.
+ */
+void microbit_seed_random();
+
+/**
+ * Seed the pseudo random number generator (RNG) using the given 32-bit value.
+ * This function does not use the NRF51822's in built cryptographic random number generator.
+ *
+ * @param seed The value to use as a seed.
+ */
+void microbit_seed_random(uint32_t seed);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitFiber.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,401 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Functionality definitions for the MicroBit Fiber scheduler.
+ *
+ * This lightweight, non-preemptive scheduler provides a simple threading mechanism for two main purposes:
+ *
+ * 1) To provide a clean abstraction for application languages to use when building async behaviour (callbacks).
+ * 2) To provide ISR decoupling for EventModel events generated in an ISR context.
+ *
+ * TODO: Consider a split mode scheduler, that monitors used stack size, and maintains a dedicated, persistent
+ * stack for any long lived fibers with large stack
+ */
+#ifndef MICROBIT_FIBER_H
+#define MICROBIT_FIBER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitEvent.h"
+#include "EventModel.h"
+
+// Fiber Scheduler Flags
+#define MICROBIT_SCHEDULER_RUNNING 0x01
+
+// Fiber Flags
+#define MICROBIT_FIBER_FLAG_FOB 0x01
+#define MICROBIT_FIBER_FLAG_PARENT 0x02
+#define MICROBIT_FIBER_FLAG_CHILD 0x04
+#define MICROBIT_FIBER_FLAG_DO_NOT_PAGE 0x08
+
+/**
+ * Thread Context for an ARM Cortex M0 core.
+ *
+ * This is probably overkill, but the ARMCC compiler uses a lot register optimisation
+ * in its calling conventions, so better safe than sorry!
+ */
+struct Cortex_M0_TCB
+{
+ uint32_t R0;
+ uint32_t R1;
+ uint32_t R2;
+ uint32_t R3;
+ uint32_t R4;
+ uint32_t R5;
+ uint32_t R6;
+ uint32_t R7;
+ uint32_t R8;
+ uint32_t R9;
+ uint32_t R10;
+ uint32_t R11;
+ uint32_t R12;
+ uint32_t SP;
+ uint32_t LR;
+ uint32_t stack_base;
+};
+
+/**
+ * Representation of a single Fiber
+ */
+struct Fiber
+{
+ Cortex_M0_TCB tcb; // Thread context when last scheduled out.
+ uint32_t stack_bottom; // The start address of this Fiber's stack. The stack is heap allocated, and full descending.
+ uint32_t stack_top; // The end address of this Fiber's stack.
+ uint32_t context; // Context specific information.
+ uint32_t flags; // Information about this fiber.
+ Fiber **queue; // The queue this fiber is stored on.
+ Fiber *next, *prev; // Position of this Fiber on the run queue.
+};
+
+extern Fiber *currentFiber;
+
+
+/**
+ * Initialises the Fiber scheduler.
+ * Creates a Fiber context around the calling thread, and adds it to the run queue as the current thread.
+ *
+ * This function must be called once only from the main thread, and before any other Fiber operation.
+ *
+ * @param _messageBus An event model, used to direct the priorities of the scheduler.
+ */
+void scheduler_init(EventModel &_messageBus);
+
+/**
+ * Determines if the fiber scheduler is operational.
+ *
+ * @return 1 if the fber scheduler is running, 0 otherwise.
+ */
+int fiber_scheduler_running();
+
+/**
+ * Exit point for all fibers.
+ *
+ * Any fiber reaching the end of its entry function will return here for recycling.
+ */
+void release_fiber(void);
+void release_fiber(void *param);
+
+/**
+ * Launches a fiber.
+ *
+ * @param ep the entry point for the fiber.
+ *
+ * @param cp the completion routine after ep has finished execution
+ */
+void launch_new_fiber(void (*ep)(void), void (*cp)(void))
+#ifdef __GCC__
+ __attribute__((naked))
+#endif
+;
+
+/**
+ * Launches a fiber with a parameter
+ *
+ * @param ep the entry point for the fiber.
+ *
+ * @param cp the completion routine after ep has finished execution
+ *
+ * @param pm the parameter to provide to ep and cp.
+ */
+void launch_new_fiber_param(void (*ep)(void *), void (*cp)(void *), void *pm)
+#ifdef __GCC__
+ __attribute__((naked))
+#endif
+;
+
+/**
+ * Creates a new Fiber, and launches it.
+ *
+ * @param entry_fn The function the new Fiber will begin execution in.
+ *
+ * @param completion_fn The function called when the thread completes execution of entry_fn.
+ * Defaults to release_fiber.
+ *
+ * @return The new Fiber, or NULL if the operation could not be completed.
+ */
+Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void) = release_fiber);
+
+
+/**
+ * Creates a new parameterised Fiber, and launches it.
+ *
+ * @param entry_fn The function the new Fiber will begin execution in.
+ *
+ * @param param an untyped parameter passed into the entry_fn and completion_fn.
+ *
+ * @param completion_fn The function called when the thread completes execution of entry_fn.
+ * Defaults to release_fiber.
+ *
+ * @return The new Fiber, or NULL if the operation could not be completed.
+ */
+Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *) = release_fiber);
+
+
+/**
+ * Calls the Fiber scheduler.
+ * The calling Fiber will likely be blocked, and control given to another waiting fiber.
+ * Call this function to yield control of the processor when you have nothing more to do.
+ */
+void schedule();
+
+/**
+ * Blocks the calling thread for the given period of time.
+ * The calling thread will be immediateley descheduled, and placed onto a
+ * wait queue until the requested amount of time has elapsed.
+ *
+ * @param t The period of time to sleep, in milliseconds.
+ *
+ * @note the fiber will not be be made runnable until after the elapsed time, but there
+ * are no guarantees precisely when the fiber will next be scheduled.
+ */
+void fiber_sleep(unsigned long t);
+
+/**
+ * The timer callback, called from interrupt context once every SYSTEM_TICK_PERIOD_MS milliseconds.
+ * This function checks to determine if any fibers blocked on the sleep queue need to be woken up
+ * and made runnable.
+ */
+void scheduler_tick();
+
+/**
+ * Blocks the calling thread until the specified event is raised.
+ * The calling thread will be immediateley descheduled, and placed onto a
+ * wait queue until the requested event is received.
+ *
+ * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
+ *
+ * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
+ *
+ * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
+ *
+ * @code
+ * fiber_wait_for_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ * @endcode
+ *
+ * @note the fiber will not be be made runnable until after the event is raised, but there
+ * are no guarantees precisely when the fiber will next be scheduled.
+ */
+int fiber_wait_for_event(uint16_t id, uint16_t value);
+
+/**
+ * Configures the fiber context for the current fiber to block on an event ID
+ * and value, but does not deschedule the fiber.
+ *
+ * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
+ *
+ * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
+ *
+ * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
+ *
+ * @code
+ * fiber_wake_on_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * //perform some time critical operation.
+ *
+ * //deschedule the current fiber manually, waiting for the previously configured event.
+ * schedule();
+ * @endcode
+ */
+int fiber_wake_on_event(uint16_t id, uint16_t value);
+
+/**
+ * Executes the given function asynchronously if necessary.
+ *
+ * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
+ * that complete very quickly, bringing unecessary RAM overhead.
+ *
+ * This function takes a snapshot of the current processor context, then attempts to optimistically call the given function directly.
+ * We only create an additional fiber if that function performs a block operation.
+ *
+ * @param entry_fn The function to execute.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int invoke(void (*entry_fn)(void));
+
+/**
+ * Executes the given function asynchronously if necessary, and offers the ability to provide a parameter.
+ *
+ * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
+ * that complete very quickly, bringing unecessary RAM. overhead
+ *
+ * This function takes a snapshot of the current fiber context, then attempt to optimistically call the given function directly.
+ * We only create an additional fiber if that function performs a block operation.
+ *
+ * @param entry_fn The function to execute.
+ *
+ * @param param an untyped parameter passed into the entry_fn and completion_fn.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int invoke(void (*entry_fn)(void *), void *param);
+
+/**
+ * Resizes the stack allocation of the current fiber if necessary to hold the system stack.
+ *
+ * If the stack allocation is large enough to hold the current system stack, then this function does nothing.
+ * Otherwise, the the current allocation of the fiber is freed, and a larger block is allocated.
+ *
+ * @param f The fiber context to verify.
+ *
+ * @return The stack depth of the given fiber.
+ */
+inline void verify_stack_size(Fiber *f);
+
+/**
+ * Event callback. Called from an instance of MicroBitMessageBus whenever an event is raised.
+ *
+ * This function checks to determine if any fibers blocked on the wait queue need to be woken up
+ * and made runnable due to the event.
+ *
+ * @param evt the event that has just been raised on an instance of MicroBitMessageBus.
+ */
+void scheduler_event(MicroBitEvent evt);
+
+/**
+ * Determines if any fibers are waiting to be scheduled.
+ *
+ * @return The number of fibers currently on the run queue
+ */
+int scheduler_runqueue_empty();
+
+/**
+ * Utility function to add the currenty running fiber to the given queue.
+ *
+ * Perform a simple add at the head, to avoid complexity,
+ *
+ * Queues are normally very short, so maintaining a doubly linked, sorted list typically outweighs the cost of
+ * brute force searching.
+ *
+ * @param f The fiber to add to the queue
+ *
+ * @param queue The run queue to add the fiber to.
+ */
+void queue_fiber(Fiber *f, Fiber **queue);
+
+/**
+ * Utility function to the given fiber from whichever queue it is currently stored on.
+ *
+ * @param f the fiber to remove.
+ */
+void dequeue_fiber(Fiber *f);
+
+/**
+ * Set of tasks to perform when idle.
+ * Service any background tasks that are required, and attempt a power efficient sleep.
+ */
+void idle();
+
+/**
+ * The idle task, which is called when the runtime has no fibers that require execution.
+ *
+ * This function typically calls idle().
+ */
+void idle_task();
+
+/**
+ * Adds a component to the array of idle thread components, which are processed
+ * when the run queue is empty.
+ *
+ * The system timer will poll isIdleCallbackNeeded on each component to determine
+ * if the scheduler should schedule the idle_task imminently.
+ *
+ * @param component The component to add to the array.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
+ *
+ * fiber_add_idle_component(accelerometer);
+ * @endcode
+ */
+int fiber_add_idle_component(MicroBitComponent *component);
+
+/**
+ * Remove a component from the array of idle thread components
+ *
+ * @param component The component to remove from the idle component array.
+ *
+ * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
+ *
+ * fiber_add_idle_component(accelerometer);
+ *
+ * fiber_remove_idle_component(accelerometer);
+ * @endcode
+ */
+int fiber_remove_idle_component(MicroBitComponent *component);
+
+/**
+ * Determines if the processor is executing in interrupt context.
+ *
+ * @return true if any the processor is currently executing any interrupt service routine. False otherwise.
+ */
+inline int inInterruptContext()
+{
+ return (((int)__get_IPSR()) & 0x003F) > 0;
+}
+
+/**
+ * Assembler Context switch routing.
+ * Defined in CortexContextSwitch.s.
+ */
+extern "C" void swap_context(Cortex_M0_TCB *from, Cortex_M0_TCB *to, uint32_t from_stack, uint32_t to_stack);
+extern "C" void save_context(Cortex_M0_TCB *tcb, uint32_t stack);
+extern "C" void save_register_context(Cortex_M0_TCB *tcb);
+extern "C" void restore_register_context(Cortex_M0_TCB *tcb);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitFont.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,100 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_FONT_H
+#define MICROBIT_FONT_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+#define MICROBIT_FONT_WIDTH 5
+#define MICROBIT_FONT_HEIGHT 5
+#define MICROBIT_FONT_ASCII_START 32
+#define MICROBIT_FONT_ASCII_END 126
+
+/**
+ * Class definition for a MicrobitFont
+ * This class represents a font that can be used by the display to render text.
+ *
+ * A MicroBitFont is 5x5.
+ * Each Row is represented by a byte in the array.
+ *
+ * Row Format:
+ * ================================================================
+ * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+ * ================================================================
+ * | N/A | N/A | N/A | Col 1 | Col 2 | Col 3 | Col 4 | Col 5 |
+ * | 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01 |
+ *
+ * Example: { 0x08, 0x08, 0x08, 0x0, 0x08 }
+ *
+ * The above will produce an exclaimation mark on the second column in form the left.
+ *
+ * We could compress further, but the complexity of decode would likely outweigh the gains.
+ */
+class MicroBitFont
+{
+ public:
+
+ static const unsigned char* defaultFont;
+ static MicroBitFont systemFont;
+
+ const unsigned char* characters;
+
+ int asciiEnd;
+
+ /**
+ * Constructor.
+ *
+ * Sets the font represented by this font object.
+ *
+ * @param font A pointer to the beginning of the new font.
+ *
+ * @param asciiEnd the char value at which this font finishes.
+ */
+ MicroBitFont(const unsigned char* font, int asciiEnd = MICROBIT_FONT_ASCII_END);
+
+ /**
+ * Default Constructor.
+ *
+ * Configures the default font for the display to use.
+ */
+ MicroBitFont();
+
+ /**
+ * Modifies the current system font to the given instance of MicroBitFont.
+ *
+ * @param font the new font that will be used to render characters on the display.
+ */
+ static void setSystemFont(MicroBitFont font);
+
+ /**
+ * Retreives the font object used for rendering characters on the display.
+ */
+ static MicroBitFont getSystemFont();
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitHeapAllocator.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,167 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * A simple 32 bit block based memory allocator. This allows one or more memory segments to
+ * be designated as heap storage, and is designed to run in a static memory area or inside the standard C
+ * heap for use by the micro:bit runtime. This is required for several reasons:
+ *
+ * 1) It reduces memory fragmentation due to the high churn sometime placed on the heap
+ * by ManagedTypes, fibers and user code. Underlying heap implentations are often have very simplistic
+ * allocation pilicies and suffer from fragmentation in prolonged use - which can cause programs to
+ * stop working after a period of time. The algorithm implemented here is simple, but highly tolerant to
+ * large amounts of churn.
+ *
+ * 2) It allows us to reuse the 8K of SRAM set aside for SoftDevice as additional heap storage
+ * when BLE is not in use.
+ *
+ * 3) It gives a simple example of how memory allocation works! :-)
+ *
+ * P.S. This is a very simple allocator, therefore not without its weaknesses. Why don't you consider
+ * what these are, and consider the tradeoffs against simplicity...
+ *
+ * @note The need for this should be reviewed in the future, if a different memory allocator is
+ * made availiable in the mbed platform.
+ *
+ * TODO: Consider caching recently freed blocks to improve allocation time.
+ */
+
+#ifndef MICROBIT_HEAP_ALLOCTOR_H
+#define MICROBIT_HEAP_ALLOCTOR_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include <new>
+
+// The maximum number of heap segments that can be created.
+#define MICROBIT_MAXIMUM_HEAPS 2
+
+// Flag to indicate that a given block is FREE/USED
+#define MICROBIT_HEAP_BLOCK_FREE 0x80000000
+
+/**
+ * Create and initialise a given memory region as for heap storage.
+ * After this is called, any future calls to malloc, new, free or delete may use the new heap.
+ * The heap allocator will attempt to allocate memory from heaps in the order that they are created.
+ * i.e. memory will be allocated from first heap created until it is full, then the second heap, and so on.
+ *
+ * @param start The start address of memory to use as a heap region.
+ *
+ * @param end The end address of memory to use as a heap region.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated.
+ *
+ * @note Only code that #includes MicroBitHeapAllocator.h will use this heap. This includes all micro:bit runtime
+ * code, and user code targetting the runtime. External code can choose to include this file, or
+ * simply use the standard heap.
+ */
+int microbit_create_heap(uint32_t start, uint32_t end);
+
+/**
+ * Create and initialise a heap region within the current the heap region specified
+ * by the linker script.
+ *
+ * If the requested amount is not available, then the amount requested will be reduced
+ * automatically to fit the space available.
+ *
+ * @param ratio The proportion of the underlying heap to allocate.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated.
+ */
+int microbit_create_nested_heap(float ratio);
+
+/**
+ * Attempt to allocate a given amount of memory from any of our configured heap areas.
+ *
+ * @param size The amount of memory, in bytes, to allocate.
+ *
+ * @return A pointer to the allocated memory, or NULL if insufficient memory is available.
+ */
+void *microbit_malloc(size_t size);
+
+
+/**
+ * Release a given area of memory from the heap.
+ *
+ * @param mem The memory area to release.
+ */
+void microbit_free(void *mem);
+
+/*
+ * Wrapper function to ensure we have an explicit handle on the heap allocator provided
+ * by our underlying platform.
+ *
+ * @param size The amount of memory, in bytes, to allocate.
+ *
+ * @return A pointer to the memory allocated. NULL if no memory is available.
+ */
+inline void *native_malloc(size_t size)
+{
+ return malloc(size);
+}
+
+/*
+ * Wrapper function to ensure we have an explicit handle on the heap allocator provided
+ * by our underlying platform.
+ *
+ * @param p Pointer to the memory to be freed.
+ */
+inline void native_free(void *p)
+{
+ free(p);
+}
+
+/**
+ * Overrides the 'new' operator globally, and redirects calls to the micro:bit heap allocator.
+ */
+inline void* operator new(size_t size) throw(std::bad_alloc)
+{
+ return microbit_malloc(size);
+}
+
+/**
+ * Overrides the 'new' operator globally, and redirects calls to the micro:bit theap allocator.
+ */
+inline void* operator new[](size_t size) throw(std::bad_alloc)
+{
+ return microbit_malloc(size);
+}
+
+/**
+ * Overrides the 'delete' operator globally, and redirects calls to the micro:bit theap allocator.
+ */
+inline void operator delete(void *ptr) throw()
+{
+ microbit_free(ptr);
+}
+
+
+// Macros to override overrides the 'malloc' and 'delete' functions globally, and redirects calls
+// to the micro:bit theap allocator.
+
+#define malloc(X) microbit_malloc( X )
+#define free(X) microbit_free( X )
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitListener.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,168 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_LISTENER_H
+#define MICROBIT_LISTENER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitEvent.h"
+#include "MemberFunctionCallback.h"
+#include "MicroBitConfig.h"
+
+// MicroBitListener flags...
+#define MESSAGE_BUS_LISTENER_PARAMETERISED 0x0001
+#define MESSAGE_BUS_LISTENER_METHOD 0x0002
+#define MESSAGE_BUS_LISTENER_BUSY 0x0004
+#define MESSAGE_BUS_LISTENER_REENTRANT 0x0008
+#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
+#define MESSAGE_BUS_LISTENER_DELETING 0x8000
+
+#define MESSAGE_BUS_LISTENER_IMMEDIATE (MESSAGE_BUS_LISTENER_NONBLOCKING | MESSAGE_BUS_LISTENER_URGENT)
+
+/**
+ * This structure defines a MicroBitListener used to invoke functions, or member
+ * functions if an instance of EventModel receives an event whose id and value
+ * match this MicroBitListener's id and value.
+ */
+struct MicroBitListener
+{
+ uint16_t id; // The ID of the component that this listener is interested in.
+ uint16_t value; // Value this listener is interested in receiving.
+ uint16_t flags; // Status and configuration options codes for this listener.
+
+ union
+ {
+ void (*cb)(MicroBitEvent);
+ void (*cb_param)(MicroBitEvent, void *);
+ MemberFunctionCallback *cb_method;
+ };
+
+ void* cb_arg; // Optional argument to be passed to the caller.
+
+ MicroBitEvent evt;
+ MicroBitEventQueueItem *evt_queue;
+
+ MicroBitListener *next;
+
+ /**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param handler A function pointer to call when the event is detected.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+ MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent), uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS);
+
+ /**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener, this constructor accepts an additional
+ * parameter "arg", which is passed to the handler.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param handler A function pointer to call when the event is detected.
+ *
+ * @param arg A pointer to some data that will be given to the handler.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+ MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg, uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS);
+
+
+ /**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener, with a callback to a C++ member function.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param object The C++ object on which to call the event handler.
+ *
+ * @param method The method within the C++ object to call.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+ template <typename T>
+ MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent), uint16_t flags = EVENT_LISTENER_DEFAULT_FLAGS);
+
+ /**
+ * Destructor. Ensures all resources used by this listener are freed.
+ */
+ ~MicroBitListener();
+
+ /**
+ * Queues and event up to be processed.
+ *
+ * @param e The event to queue
+ */
+ void queue(MicroBitEvent e);
+};
+
+/**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener, with a callback to a C++ member function.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param object The C++ object on which to call the event handler.
+ *
+ * @param method The method within the C++ object to call.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+template <typename T>
+MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent), uint16_t flags)
+{
+ this->id = id;
+ this->value = value;
+ this->cb_method = new MemberFunctionCallback(object, method);
+ this->cb_arg = NULL;
+ this->flags = flags | MESSAGE_BUS_LISTENER_METHOD;
+ this->next = NULL;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/core/MicroBitSystemTimer.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,149 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Definitions for the MicroBit system timer.
+ *
+ * This module provides:
+ *
+ * 1) a concept of global system time since power up
+ * 2) a simple periodic multiplexing API for the underlying mbed implementation.
+ *
+ * The latter is useful to avoid costs associated with multiple mbed Ticker instances
+ * in microbit-dal components, as each incurs a significant additional RAM overhead (circa 80 bytes).
+ */
+
+#ifndef MICROBIT_SYSTEM_TIMER_H
+#define MICROBIT_SYSTEM_TIMER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+
+/**
+ * Initialises the system wide timer.
+ *
+ * This must be called before any components register to receive periodic periodic callbacks.
+ *
+ * @param timer_period The initial period between interrupts, in millseconds.
+ *
+ * @return MICROBIT_OK on success.
+ */
+int system_timer_init(int period);
+
+/**
+ * Reconfigures the system wide timer to the given period in milliseconds.
+ *
+ * @param period the new period of the timer in milliseconds
+ *
+ * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if period < 1
+ */
+int system_timer_set_period(int period);
+
+/**
+ * Accessor to obtain the current tick period in milliseconds
+ *
+ * @return the current tick period in milliseconds
+ */
+int system_timer_get_period();
+
+/**
+ * Determines the time since the device was powered on.
+ *
+ * @return the current time since power on in milliseconds
+ */
+unsigned long system_timer_current_time();
+
+/**
+ * Timer callback. Called from interrupt context, once per period.
+ *
+ * Simply checks to determine if any fibers blocked on the sleep queue need to be woken up
+ * and made runnable.
+ */
+void system_timer_tick();
+
+/**
+ * Add a component to the array of system components. This component will then receive
+ * periodic callbacks, once every tick period in interrupt context.
+ *
+ * @param component The component to add.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the component array is full.
+ *
+ * @code
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitDisplay* display = new MicroBitDisplay();
+ *
+ * system_timer_add_component(display);
+ * @endcode
+ */
+int system_timer_add_component(MicroBitComponent *component);
+
+/**
+ * Remove a component from the array of system components. This component will no longer receive
+ * periodic callbacks.
+ *
+ * @param component The component to remove.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
+ *
+ * @code
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitDisplay* display = new MicroBitDisplay();
+ *
+ * system_timer_add_component(display);
+ *
+ * system_timer_remove_component(display);
+ * @endcode
+ */
+int system_timer_remove_component(MicroBitComponent *component);
+
+/**
+ * A simple C/C++ wrapper to allow periodic callbacks to standard C functions transparently.
+ */
+class MicroBitSystemTimerCallback : MicroBitComponent
+{
+ void (*fn)(void);
+
+ /**
+ * Creates an object that receives periodic callbacks from the system timer,
+ * and, in turn, calls a plain C function as provided as a parameter.
+ *
+ * @param function the function to invoke upon a systemTick.
+ */
+ public:
+ MicroBitSystemTimerCallback(void (*function)(void))
+ {
+ fn = function;
+ system_timer_add_component(this);
+ }
+
+ void systemTick()
+ {
+ fn();
+ }
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/inc/core/NotifyEvents.h Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,37 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 British Broadcasting Corporation. +This software is provided by Lancaster University by arrangement with the BBC. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + +#ifndef NOTIFY_EVENTS_H +#define NOTIFY_EVENTS_H + +/** + * This file contains events used on the general purpose Eventing channel + * MICROBIT_ID_NOTIFY, new events should be added here, to prevent duplication. + */ +#define MICROBIT_DISPLAY_EVT_FREE 1 +#define MICROBIT_SERIAL_EVT_TX_EMPTY 2 +#define MICROBIT_UART_S_EVT_TX_EMPTY 3 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/DynamicPwm.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,216 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+#ifndef MICROBIT_DYNAMIC_PWM_H
+#define MICROBIT_DYNAMIC_PWM_H
+
+#define NO_PWMS 3
+#define MICROBIT_DEFAULT_PWM_PERIOD 20000
+
+enum PwmPersistence
+{
+ PWM_PERSISTENCE_TRANSIENT = 1,
+ PWM_PERSISTENCE_PERSISTENT = 2,
+};
+
+/**
+ * Class definition for DynamicPwm.
+ *
+ * This class addresses a few issues found in the underlying libraries.
+ * This provides the ability for a neat, clean swap between PWM channels.
+ */
+class DynamicPwm : public PwmOut
+{
+ private:
+ static DynamicPwm* pwms[NO_PWMS];
+ static uint8_t lastUsed;
+ static uint16_t sharedPeriod;
+ uint8_t flags;
+ float lastValue;
+
+
+
+ /**
+ * An internal constructor used when allocating a new DynamicPwm instance.
+ *
+ * @param pin the name of the pin for the pwm to target
+ *
+ * @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
+ * or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
+ */
+ DynamicPwm(PinName pin, PwmPersistence persistence = PWM_PERSISTENCE_TRANSIENT);
+
+ public:
+
+ /**
+ * Redirects the pwm channel to point at a different pin.
+ *
+ * @param pin the desired pin to output a PWM wave.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->redirect(p0); // pwm is now produced on p0
+ * @endcode
+ */
+ void redirect(PinName pin);
+
+
+ /**
+ * Creates a new DynamicPwm instance, or reuses an existing instance that
+ * has a persistence level of PWM_PERSISTENCE_TRANSIENT.
+ *
+ * @param pin the name of the pin for the pwm to target
+ *
+ * @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
+ * or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
+ *
+ * @return a pointer to the first available free pwm channel - or the first one that can be reallocated. If
+ * no channels are available, NULL is returned.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * @endcode
+ */
+ static DynamicPwm* allocate(PinName pin, PwmPersistence persistence = PWM_PERSISTENCE_TRANSIENT);
+
+ /**
+ * Frees this DynamicPwm instance for reuse.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate();
+ * pwm->release();
+ * @endcode
+ */
+ void release();
+
+ /**
+ * A lightweight wrapper around the super class' write in order to capture the value
+ *
+ * @param value the duty cycle percentage in floating point format.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate();
+ * pwm->write(0.5);
+ * @endcode
+ */
+ int write(float value);
+
+ /**
+ * Retreives the PinName associated with this DynamicPwm instance.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // returns the PinName n.
+ * pwm->getPinName();
+ * @endcode
+ *
+ * @note This should be used to check that the DynamicPwm instance has not
+ * been reallocated for use in another part of a program.
+ */
+ PinName getPinName();
+
+ /**
+ * Retreives the last value that has been written to this DynamicPwm instance.
+ * in the range 0 - 1023 inclusive.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->write(0.5);
+ *
+ * // will return 512.
+ * pwm->getValue();
+ * @endcode
+ */
+ int getValue();
+
+ /**
+ * Retreives the current period in use by the entire PWM module in microseconds.
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->getPeriod();
+ * @endcode
+ */
+ int getPeriodUs();
+
+ /**
+ * Retreives the current period in use by the entire PWM module in milliseconds.
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->setPeriodUs(20000);
+ *
+ * // will return 20000
+ * pwm->getPeriod();
+ * @endcode
+ */
+ int getPeriod();
+
+ /**
+ * Sets the period used by the WHOLE PWM module.
+ *
+ * @param period the desired period in microseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if period is out of range
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // period now is 20ms
+ * pwm->setPeriodUs(20000);
+ * @endcode
+ *
+ * @note Any changes to the period will AFFECT ALL CHANNELS.
+ */
+ int setPeriodUs(int period);
+
+ /**
+ * Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
+ *
+ * @param period the desired period in milliseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if period is out of range
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // period now is 20ms
+ * pwm->setPeriod(20);
+ * @endcode
+ */
+ int setPeriod(int period);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitAccelerometer.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,467 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_ACCELEROMETER_H
+#define MICROBIT_ACCELEROMETER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitCoordinateSystem.h"
+#include "MicroBitI2C.h"
+
+/**
+ * Relevant pin assignments
+ */
+#define MICROBIT_PIN_ACCEL_DATA_READY P0_28
+
+/**
+ * Status flags
+ */
+#define MICROBIT_ACCEL_PITCH_ROLL_VALID 0x02
+#define MICROBIT_ACCEL_ADDED_TO_IDLE 0x04
+
+/**
+ * I2C constants
+ */
+#define MMA8653_DEFAULT_ADDR 0x3A
+
+/**
+ * MMA8653 Register map (partial)
+ */
+#define MMA8653_STATUS 0x00
+#define MMA8653_OUT_X_MSB 0x01
+#define MMA8653_WHOAMI 0x0D
+#define MMA8653_XYZ_DATA_CFG 0x0E
+#define MMA8653_CTRL_REG1 0x2A
+#define MMA8653_CTRL_REG2 0x2B
+#define MMA8653_CTRL_REG3 0x2C
+#define MMA8653_CTRL_REG4 0x2D
+#define MMA8653_CTRL_REG5 0x2E
+
+
+/**
+ * MMA8653 constants
+ */
+#define MMA8653_WHOAMI_VAL 0x5A
+
+#define MMA8653_SAMPLE_RANGES 3
+#define MMA8653_SAMPLE_RATES 8
+
+/**
+ * Accelerometer events
+ */
+#define MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE 1
+
+/**
+ * Gesture events
+ */
+#define MICROBIT_ACCELEROMETER_EVT_TILT_UP 1
+#define MICROBIT_ACCELEROMETER_EVT_TILT_DOWN 2
+#define MICROBIT_ACCELEROMETER_EVT_TILT_LEFT 3
+#define MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT 4
+#define MICROBIT_ACCELEROMETER_EVT_FACE_UP 5
+#define MICROBIT_ACCELEROMETER_EVT_FACE_DOWN 6
+#define MICROBIT_ACCELEROMETER_EVT_FREEFALL 7
+#define MICROBIT_ACCELEROMETER_EVT_3G 8
+#define MICROBIT_ACCELEROMETER_EVT_6G 9
+#define MICROBIT_ACCELEROMETER_EVT_8G 10
+#define MICROBIT_ACCELEROMETER_EVT_SHAKE 11
+
+/**
+ * Gesture recogniser constants
+ */
+#define MICROBIT_ACCELEROMETER_REST_TOLERANCE 200
+#define MICROBIT_ACCELEROMETER_TILT_TOLERANCE 200
+#define MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE 400
+#define MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE 1000
+#define MICROBIT_ACCELEROMETER_3G_TOLERANCE 3072
+#define MICROBIT_ACCELEROMETER_6G_TOLERANCE 6144
+#define MICROBIT_ACCELEROMETER_8G_TOLERANCE 8192
+#define MICROBIT_ACCELEROMETER_GESTURE_DAMPING 10
+#define MICROBIT_ACCELEROMETER_SHAKE_DAMPING 10
+
+#define MICROBIT_ACCELEROMETER_REST_THRESHOLD (MICROBIT_ACCELEROMETER_REST_TOLERANCE * MICROBIT_ACCELEROMETER_REST_TOLERANCE)
+#define MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD (MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE * MICROBIT_ACCELEROMETER_FREEFALL_TOLERANCE)
+#define MICROBIT_ACCELEROMETER_3G_THRESHOLD (MICROBIT_ACCELEROMETER_3G_TOLERANCE * MICROBIT_ACCELEROMETER_3G_TOLERANCE)
+#define MICROBIT_ACCELEROMETER_6G_THRESHOLD (MICROBIT_ACCELEROMETER_6G_TOLERANCE * MICROBIT_ACCELEROMETER_6G_TOLERANCE)
+#define MICROBIT_ACCELEROMETER_8G_THRESHOLD (MICROBIT_ACCELEROMETER_8G_TOLERANCE * MICROBIT_ACCELEROMETER_8G_TOLERANCE)
+#define MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD 4
+
+struct MMA8653Sample
+{
+ int16_t x;
+ int16_t y;
+ int16_t z;
+};
+
+struct MMA8653SampleRateConfig
+{
+ uint32_t sample_period;
+ uint8_t ctrl_reg1;
+};
+
+struct MMA8653SampleRangeConfig
+{
+ uint8_t sample_range;
+ uint8_t xyz_data_cfg;
+};
+
+
+extern const MMA8653SampleRangeConfig MMA8653SampleRange[];
+extern const MMA8653SampleRateConfig MMA8653SampleRate[];
+
+enum BasicGesture
+{
+ GESTURE_NONE,
+ GESTURE_UP,
+ GESTURE_DOWN,
+ GESTURE_LEFT,
+ GESTURE_RIGHT,
+ GESTURE_FACE_UP,
+ GESTURE_FACE_DOWN,
+ GESTURE_FREEFALL,
+ GESTURE_3G,
+ GESTURE_6G,
+ GESTURE_8G,
+ GESTURE_SHAKE
+};
+
+struct ShakeHistory
+{
+ uint16_t shaken:1,
+ x:1,
+ y:1,
+ z:1,
+ count:4,
+ timer:8;
+};
+
+/**
+ * Class definition for MicroBit Accelerometer.
+ *
+ * Represents an implementation of the Freescale MMA8653 3 axis accelerometer
+ * Also includes basic data caching and on demand activation.
+ */
+class MicroBitAccelerometer : public MicroBitComponent
+{
+ uint16_t address; // I2C address of this accelerometer.
+ uint16_t samplePeriod; // The time between samples, in milliseconds.
+ uint8_t sampleRange; // The sample range of the accelerometer in g.
+ MMA8653Sample sample; // The last sample read.
+ DigitalIn int1; // Data ready interrupt.
+ float pitch; // Pitch of the device, in radians.
+ MicroBitI2C& i2c; // The I2C interface to use.
+ float roll; // Roll of the device, in radians.
+ uint8_t sigma; // the number of ticks that the instantaneous gesture has been stable.
+ BasicGesture lastGesture; // the last, stable gesture recorded.
+ BasicGesture currentGesture; // the instantaneous, unfiltered gesture detected.
+ ShakeHistory shake; // State information needed to detect shake events.
+
+ public:
+
+ /**
+ * Constructor.
+ * Create a software abstraction of an accelerometer.
+ *
+ * @param _i2c an instance of MicroBitI2C used to communicate with the onboard accelerometer.
+ *
+ * @param address the default I2C address of the accelerometer. Defaults to: MMA8653_DEFAULT_ADDR.
+ *
+ * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER
+ *
+ * @code
+ * MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c);
+ * @endcode
+ */
+ MicroBitAccelerometer(MicroBitI2C &_i2c, uint16_t address = MMA8653_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_ACCELEROMETER);
+
+ /**
+ * Configures the accelerometer for G range and sample rate defined
+ * in this object. The nearest values are chosen to those defined
+ * that are supported by the hardware. The instance variables are then
+ * updated to reflect reality.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured.
+ */
+ int configure();
+
+ /**
+ * Reads the acceleration data from the accelerometer, and stores it in our buffer.
+ * This only happens if the accelerometer indicates that it has new data via int1.
+ *
+ * On first use, this member function will attempt to add this component to the
+ * list of fiber components in order to constantly update the values stored
+ * by this object.
+ *
+ * This technique is called lazy instantiation, and it means that we do not
+ * obtain the overhead from non-chalantly adding this component to fiber components.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails.
+ */
+ int updateSample();
+
+ /**
+ * Attempts to set the sample rate of the accelerometer to the specified value (in ms).
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
+ *
+ * @code
+ * // sample rate is now 20 ms.
+ * accelerometer.setPeriod(20);
+ * @endcode
+ *
+ * @note The requested rate may not be possible on the hardware. In this case, the
+ * nearest lower rate is chosen.
+ */
+ int setPeriod(int period);
+
+ /**
+ * Reads the currently configured sample rate of the accelerometer.
+ *
+ * @return The time between samples, in milliseconds.
+ */
+ int getPeriod();
+
+ /**
+ * Attempts to set the sample range of the accelerometer to the specified value (in g).
+ *
+ * @param range The requested sample range of samples, in g.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
+ *
+ * @code
+ * // the sample range of the accelerometer is now 8G.
+ * accelerometer.setRange(8);
+ * @endcode
+ *
+ * @note The requested range may not be possible on the hardware. In this case, the
+ * nearest lower range is chosen.
+ */
+ int setRange(int range);
+
+ /**
+ * Reads the currently configured sample range of the accelerometer.
+ *
+ * @return The sample range, in g.
+ */
+ int getRange();
+
+ /**
+ * Attempts to read the 8 bit ID from the accelerometer, this can be used for
+ * validation purposes.
+ *
+ * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
+ *
+ * @code
+ * accelerometer.whoAmI();
+ * @endcode
+ */
+ int whoAmI();
+
+ /**
+ * Reads the value of the X axis from the latest update retrieved from the accelerometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The force measured in the X axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getX();
+ * @endcode
+ */
+ int getX(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Reads the value of the Y axis from the latest update retrieved from the accelerometer.
+ *
+ * @return The force measured in the Y axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getY();
+ * @endcode
+ */
+ int getY(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Reads the value of the Z axis from the latest update retrieved from the accelerometer.
+ *
+ * @return The force measured in the Z axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getZ();
+ * @endcode
+ */
+ int getZ(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The pitch of the device, in degrees.
+ *
+ * @code
+ * accelerometer.getPitch();
+ * @endcode
+ */
+ int getPitch();
+
+ /**
+ * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The pitch of the device, in radians.
+ *
+ * @code
+ * accelerometer.getPitchRadians();
+ * @endcode
+ */
+ float getPitchRadians();
+
+ /**
+ * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The roll of the device, in degrees.
+ *
+ * @code
+ * accelerometer.getRoll();
+ * @endcode
+ */
+ int getRoll();
+
+ /**
+ * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The roll of the device, in radians.
+ *
+ * @code
+ * accelerometer.getRollRadians();
+ * @endcode
+ */
+ float getRollRadians();
+
+ /**
+ * Retrieves the last recorded gesture.
+ *
+ * @return The last gesture that was detected.
+ *
+ * Example:
+ * @code
+ * MicroBitDisplay display;
+ *
+ * if (accelerometer.getGesture() == SHAKE)
+ * display.scroll("SHAKE!");
+ * @endcode
+ */
+ BasicGesture getGesture();
+
+ /**
+ * A periodic callback invoked by the fiber scheduler idle thread.
+ *
+ * Internally calls updateSample().
+ */
+ virtual void idleTick();
+
+ /**
+ * Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
+ *
+ * We check if any data is ready for reading by checking the interrupt flag on the accelerometer.
+ */
+ virtual int isIdleCallbackNeeded();
+
+ /**
+ * Destructor for MicroBitButton, where we deregister this instance from the array of fiber components.
+ */
+ ~MicroBitAccelerometer();
+
+ private:
+
+ /**
+ * Issues a standard, 2 byte I2C command write to the accelerometer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to write to.
+ *
+ * @param value The value to write.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
+ */
+ int writeCommand(uint8_t reg, uint8_t value);
+
+ /**
+ * Issues a read command, copying data into the specified buffer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to access.
+ *
+ * @param buffer Memory area to read the data into.
+ *
+ * @param length The number of bytes to read.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
+ */
+ int readCommand(uint8_t reg, uint8_t* buffer, int length);
+
+ /**
+ * Recalculate roll and pitch values for the current sample.
+ *
+ * @note We only do this at most once per sample, as the necessary trigonemteric functions are rather
+ * heavyweight for a CPU without a floating point unit.
+ */
+ void recalculatePitchRoll();
+
+ /**
+ * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote
+ * stability.
+ */
+ void updateGesture();
+
+ /**
+ * A service function.
+ * It calculates the current scalar acceleration of the device (x^2 + y^2 + z^2).
+ * It does not, however, square root the result, as this is a relatively high cost operation.
+ *
+ * This is left to application code should it be needed.
+ *
+ * @return the sum of the square of the acceleration of the device across all axes.
+ */
+ int instantaneousAccelerationSquared();
+
+ /**
+ * Service function.
+ * Determines a 'best guess' posture of the device based on instantaneous data.
+ *
+ * This makes no use of historic data, and forms this input to the filter implemented in updateGesture().
+ *
+ * @return A 'best guess' of the current posture of the device, based on instanataneous data.
+ */
+ BasicGesture instantaneousPosture();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitButton.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,145 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_BUTTON_H
+#define MICROBIT_BUTTON_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitEvent.h"
+
+#define MICROBIT_PIN_BUTTON_A P0_17
+#define MICROBIT_PIN_BUTTON_B P0_26
+#define MICROBIT_PIN_BUTTON_RESET P0_19
+
+#define MICROBIT_BUTTON_EVT_DOWN 1
+#define MICROBIT_BUTTON_EVT_UP 2
+#define MICROBIT_BUTTON_EVT_CLICK 3
+#define MICROBIT_BUTTON_EVT_LONG_CLICK 4
+#define MICROBIT_BUTTON_EVT_HOLD 5
+#define MICROBIT_BUTTON_EVT_DOUBLE_CLICK 6
+
+#define MICROBIT_BUTTON_LONG_CLICK_TIME 1000
+#define MICROBIT_BUTTON_HOLD_TIME 1500
+
+#define MICROBIT_BUTTON_STATE 1
+#define MICROBIT_BUTTON_STATE_HOLD_TRIGGERED 2
+#define MICROBIT_BUTTON_STATE_CLICK 4
+#define MICROBIT_BUTTON_STATE_LONG_CLICK 8
+
+#define MICROBIT_BUTTON_SIGMA_MIN 0
+#define MICROBIT_BUTTON_SIGMA_MAX 12
+#define MICROBIT_BUTTON_SIGMA_THRESH_HI 8
+#define MICROBIT_BUTTON_SIGMA_THRESH_LO 2
+#define MICROBIT_BUTTON_DOUBLE_CLICK_THRESH 50
+
+enum MicroBitButtonEventConfiguration
+{
+ MICROBIT_BUTTON_SIMPLE_EVENTS,
+ MICROBIT_BUTTON_ALL_EVENTS
+};
+
+
+/**
+ * Class definition for MicroBit Button.
+ *
+ * Represents a single, generic button on the device.
+ */
+class MicroBitButton : public MicroBitComponent
+{
+ PinName name; // mbed pin name for this button.
+ DigitalIn pin; // The mbed object looking after this pin at any point in time (may change!).
+
+ unsigned long downStartTime; // used to store the current system clock when a button down event occurs
+ uint8_t sigma; // integration of samples over time. We use this for debouncing, and noise tolerance for touch sensing
+ MicroBitButtonEventConfiguration eventConfiguration; // Do we want to generate high level event (clicks), or defer this to another service.
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Create a software representation of a button.
+ *
+ * @param name the physical pin on the processor that should be used as input.
+ *
+ * @param id the ID of the new MicroBitButton object.
+ *
+ * @param eventConfiguration Configures the events that will be generated by this MicroBitButton instance.
+ * Defaults to MICROBIT_BUTTON_ALL_EVENTS.
+ *
+ * @param mode the configuration of internal pullups/pulldowns, as defined in the mbed PinMode class. PullNone by default.
+ *
+ * @code
+ * buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
+ * @endcode
+ */
+ MicroBitButton(PinName name, uint16_t id, MicroBitButtonEventConfiguration eventConfiguration = MICROBIT_BUTTON_ALL_EVENTS, PinMode mode = PullNone);
+
+ /**
+ * Tests if this Button is currently pressed.
+ *
+ * @code
+ * if(buttonA.isPressed())
+ * display.scroll("Pressed!");
+ * @endcode
+ *
+ * @return 1 if this button is pressed, 0 otherwise.
+ */
+ int isPressed();
+
+ /**
+ * Changes the event configuration used by this button to the given MicroBitButtonEventConfiguration.
+ *
+ * All subsequent events generated by this button will then be informed by this configuraiton.
+ *
+ * @param config The new configuration for this button. Legal values are MICROBIT_BUTTON_ALL_EVENTS or MICROBIT_BUTTON_SIMPLE_EVENTS.
+ *
+ * Example:
+ * @code
+ * // Configure a button to generate all possible events.
+ * buttonA.setEventConfiguration(MICROBIT_BUTTON_ALL_EVENTS);
+ *
+ * // Configure a button to suppress MICROBIT_BUTTON_EVT_CLICK and MICROBIT_BUTTON_EVT_LONG_CLICK events.
+ * buttonA.setEventConfiguration(MICROBIT_BUTTON_SIMPLE_EVENTS);
+ * @endcode
+ */
+ void setEventConfiguration(MicroBitButtonEventConfiguration config);
+
+ /**
+ * periodic callback from MicroBit system timer.
+ *
+ * Check for state change for this button, and fires various events on a state change.
+ */
+ virtual void systemTick();
+
+ /**
+ * Destructor for MicroBitButton, where we deregister this instance from the array of fiber components.
+ */
+ ~MicroBitButton();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitCompass.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,518 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_COMPASS_H
+#define MICROBIT_COMPASS_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitCoordinateSystem.h"
+#include "MicroBitAccelerometer.h"
+#include "MicroBitStorage.h"
+
+/**
+ * Relevant pin assignments
+ */
+#define MICROBIT_PIN_COMPASS_DATA_READY P0_29
+
+/**
+ * I2C constants
+ */
+#define MAG3110_DEFAULT_ADDR 0x1D
+
+/**
+ * MAG3110 Register map
+ */
+#define MAG_DR_STATUS 0x00
+#define MAG_OUT_X_MSB 0x01
+#define MAG_OUT_X_LSB 0x02
+#define MAG_OUT_Y_MSB 0x03
+#define MAG_OUT_Y_LSB 0x04
+#define MAG_OUT_Z_MSB 0x05
+#define MAG_OUT_Z_LSB 0x06
+#define MAG_WHOAMI 0x07
+#define MAG_SYSMOD 0x08
+#define MAG_OFF_X_MSB 0x09
+#define MAG_OFF_X_LSB 0x0A
+#define MAG_OFF_Y_MSB 0x0B
+#define MAG_OFF_Y_LSB 0x0C
+#define MAG_OFF_Z_MSB 0x0D
+#define MAG_OFF_Z_LSB 0x0E
+#define MAG_DIE_TEMP 0x0F
+#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 // DEPRECATED
+#define MICROBIT_COMPASS_EVT_CAL_START 2 // DEPRECATED
+#define MICROBIT_COMPASS_EVT_CAL_END 3 // DEPRECATED
+
+#define MICROBIT_COMPASS_EVT_DATA_UPDATE 4
+#define MICROBIT_COMPASS_EVT_CONFIG_NEEDED 5
+#define MICROBIT_COMPASS_EVT_CALIBRATE 6
+
+/**
+ * Status Bits
+ */
+#define MICROBIT_COMPASS_STATUS_CALIBRATED 2
+#define MICROBIT_COMPASS_STATUS_CALIBRATING 4
+#define MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE 8
+
+/**
+ * Term to convert sample data into SI units
+ */
+#define MAG3110_NORMALIZE_SAMPLE(x) (100*x)
+
+/**
+ * MAG3110 MAGIC ID value
+ * Returned from the MAG_WHO_AM_I register for ID purposes.
+ */
+#define MAG3110_WHOAMI_VAL 0xC4
+
+struct CompassSample
+{
+ int x;
+ int y;
+ int z;
+
+ CompassSample()
+ {
+ this->x = 0;
+ this->y = 0;
+ this->z = 0;
+ }
+
+ CompassSample(int x, int y, int z)
+ {
+ this->x = x;
+ this->y = y;
+ this->z = z;
+ }
+
+ bool operator==(const CompassSample& other) const
+ {
+ return x == other.x && y == other.y && z == other.z;
+ }
+
+ bool operator!=(const CompassSample& other) const
+ {
+ return !(x == other.x && y == other.y && z == other.z);
+ }
+};
+
+/**
+ * Class definition for MicroBit Compass.
+ *
+ * Represents an implementation of the Freescale MAG3110 I2C Magnetmometer.
+ * Also includes basic caching, calibration and on demand activation.
+ */
+class MicroBitCompass : public MicroBitComponent
+{
+ uint16_t address; // I2C address of the magnetmometer.
+ uint16_t samplePeriod; // The time between samples, in millseconds.
+
+ CompassSample average; // Centre point of sample data.
+ CompassSample sample; // The latest sample data recorded.
+ DigitalIn int1; // Data ready interrupt.
+ MicroBitI2C& i2c; // The I2C interface the sensor is connected to.
+ MicroBitAccelerometer* accelerometer; // The accelerometer to use for tilt compensation.
+ MicroBitStorage* storage; // An instance of MicroBitStorage used for persistence.
+
+ public:
+
+ /**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _accelerometer an instance of the accelerometer, used for tilt compensation.
+ *
+ * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer(i2c);
+ *
+ * MicroBitStorage storage;
+ *
+ * MicroBitCompass compass(i2c, accelerometer, storage);
+ * @endcode
+ */
+ MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, MicroBitStorage& _storage, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS);
+
+ /**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _accelerometer an instance of the accelerometer, used for tilt compensation.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer(i2c);
+ *
+ * MicroBitCompass compass(i2c, accelerometer, storage);
+ * @endcode
+ */
+ MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS);
+
+ /**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitStorage storage;
+ *
+ * MicroBitCompass compass(i2c, storage);
+ * @endcode
+ */
+ MicroBitCompass(MicroBitI2C& _i2c, MicroBitStorage& _storage, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS);
+
+ /**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitCompass compass(i2c);
+ * @endcode
+ */
+ MicroBitCompass(MicroBitI2C& _i2c, uint16_t address = MAG3110_DEFAULT_ADDR, uint16_t id = MICROBIT_ID_COMPASS);
+
+ /**
+ * 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.
+ *
+ * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be configured.
+ */
+ int configure();
+
+ /**
+ * Attempts to set the sample rate of the compass to the specified value (in ms).
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be updated.
+ *
+ * @code
+ * // sample rate is now 20 ms.
+ * compass.setPeriod(20);
+ * @endcode
+ *
+ * @note The requested rate may not be possible on the hardware. In this case, the
+ * nearest lower rate is chosen.
+ */
+ int 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.
+ *
+ * If the compass is not calibrated, it will raise the MICROBIT_COMPASS_EVT_CALIBRATE event.
+ *
+ * Users wishing to implement their own calibration algorithms should listen for this event,
+ * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before
+ * the user program continues.
+ *
+ * @return the current heading, in degrees. Or MICROBIT_CALIBRATION_IN_PROGRESS if the compass is calibrating.
+ *
+ * @code
+ * compass.heading();
+ * @endcode
+ */
+ int heading();
+
+ /**
+ * Attempts to read the 8 bit ID from the magnetometer, this can be used for
+ * validation purposes.
+ *
+ * @return the 8 bit ID returned by the magnetometer, or MICROBIT_I2C_ERROR if the request fails.
+ *
+ * @code
+ * compass.whoAmI();
+ * @endcode
+ */
+ int whoAmI();
+
+ /**
+ * Reads the value of the X axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the X axis, in nano teslas.
+ *
+ * @code
+ * compass.getX();
+ * @endcode
+ */
+ int getX(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Reads the value of the Y axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the Y axis, in nano teslas.
+ *
+ * @code
+ * compass.getY();
+ * @endcode
+ */
+ int getY(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Reads the value of the Z axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the Z axis, in nano teslas.
+ *
+ * @code
+ * compass.getZ();
+ * @endcode
+ */
+ int getZ(MicroBitCoordinateSystem system = SIMPLE_CARTESIAN);
+
+ /**
+ * Determines the overall magnetic field strength based on the latest update from the magnetometer.
+ *
+ * @return The magnetic force measured across all axis, in nano teslas.
+ *
+ * @code
+ * compass.getFieldStrength();
+ * @endcode
+ */
+ int getFieldStrength();
+
+ /**
+ * Reads the current die temperature of the compass.
+ *
+ * @return the temperature in degrees celsius, or MICROBIT_I2C_ERROR if the temperature reading could not be retreived
+ * from the accelerometer.
+ */
+ int readTemperature();
+
+ /**
+ * Perform a calibration of the compass.
+ *
+ * This method will be called automatically if a user attempts to read a compass value when
+ * the compass is uncalibrated. It can also be called at any time by the user.
+ *
+ * The method will only return once the compass has been calibrated.
+ *
+ * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed,
+ * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully.
+ *
+ * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS
+ */
+ int calibrate();
+
+ /**
+ * Configure the compass to use the calibration data that is supplied to this call.
+ *
+ * Calibration data is comprised of the perceived zero offset of each axis of the compass.
+ *
+ * After calibration this should now take into account trimming errors in the magnetometer,
+ * and any "hard iron" offsets on the device.
+ *
+ * @param calibration A CompassSample containing the offsets for the x, y and z axis.
+ */
+ void setCalibration(CompassSample calibration);
+
+ /**
+ * Provides the calibration data currently in use by the compass.
+ *
+ * More specifically, the x, y and z zero offsets of the compass.
+ *
+ * @return calibration A CompassSample containing the offsets for the x, y and z axis.
+ */
+ CompassSample getCalibration();
+
+ /**
+ * Updates the local sample, only if the compass indicates that
+ * data is stale.
+ *
+ * @note Can be used to trigger manual updates, if the device is running without a scheduler.
+ * Also called internally by all get[X,Y,Z]() member functions.
+ */
+ int updateSample();
+
+ /**
+ * Periodic callback from MicroBit idle thread.
+ *
+ * Calls updateSample().
+ */
+ virtual void idleTick();
+
+ /**
+ * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration.
+ */
+ int isCalibrated();
+
+ /**
+ * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating.
+ */
+ int isCalibrating();
+
+ /**
+ * Clears the calibration held in persistent storage, and sets the calibrated flag to zero.
+ */
+ void clearCalibration();
+
+ /**
+ * Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
+ */
+ virtual int isIdleCallbackNeeded();
+
+ /**
+ * Destructor for MicroBitCompass, where we deregister this instance from the array of fiber components.
+ */
+ ~MicroBitCompass();
+
+ private:
+
+ /**
+ * Issues a standard, 2 byte I2C command write to the accelerometer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to write to.
+ *
+ * @param value The value to write.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
+ */
+ int writeCommand(uint8_t reg, uint8_t value);
+
+ /**
+ * Issues a read command, copying data into the specified buffer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to access.
+ *
+ * @param buffer Memory area to read the data into.
+ *
+ * @param length The number of bytes to read.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
+ */
+ int readCommand(uint8_t reg, uint8_t* buffer, int length);
+
+ /**
+ * Issues a read of a given address, and returns the value.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the 16 bit register to access.
+ *
+ * @return The register value, interpreted as a 16 but signed value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed.
+ */
+ int read16(uint8_t reg);
+
+ /**
+ * Issues a read of a given address, and returns the value.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the 16 bit register to access.
+ *
+ * @return The register value, interpreted as a 8 bit unsigned value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed.
+ */
+ int read8(uint8_t reg);
+
+ /**
+ * Calculates a tilt compensated bearing of the device, using the accelerometer.
+ */
+ int tiltCompensatedBearing();
+
+ /**
+ * Calculates a non-tilt compensated bearing of the device.
+ */
+ int basicBearing();
+
+ /**
+ * An initialisation member function used by the many constructors of MicroBitCompass.
+ *
+ * @param id the unique identifier for this compass instance.
+ *
+ * @param address the base address of the magnetometer on the i2c bus.
+ */
+ void init(uint16_t id, uint16_t address);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitCompassCalibrator.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,84 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_COMPASS_CALIBRATOR_H
+#define MICROBIT_COMPASS_CALIBRATOR_H
+
+#include "MicroBitConfig.h"
+#include "MicroBitCompass.h"
+#include "MicroBitAccelerometer.h"
+#include "MicroBitDisplay.h"
+
+
+/**
+ * Class definition for an interactive compass calibration algorithm.
+ *
+ * The algorithm uses an accelerometer to ensure that a broad range of sample data has been gathered
+ * from the compass module, then performs a least mean squares optimisation of the
+ * results to determine the calibration data for the compass.
+ *
+ * The LED matrix display is used to provide feedback to the user on the gestures required.
+ *
+ * This class listens for calibration requests from the compass (on the default event model),
+ * and automatically initiates a calibration sequence as necessary.
+ */
+class MicroBitCompassCalibrator
+{
+ MicroBitCompass& compass;
+ MicroBitAccelerometer& accelerometer;
+ MicroBitDisplay& display;
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Create an object capable of calibrating the compass.
+ *
+ * The algorithm uses an accelerometer to ensure that a broad range of sample data has been gathered
+ * from the compass module, then performs a least mean squares optimisation of the
+ * results to determine the calibration data for the compass.
+ *
+ * The LED matrix display is used to provide feedback to the user on the gestures required.
+ *
+ * @param compass The compass instance to calibrate.
+ *
+ * @param accelerometer The accelerometer to gather contextual data from.
+ *
+ * @param display The LED matrix to display user feedback on.
+ */
+ MicroBitCompassCalibrator(MicroBitCompass& _compass, MicroBitAccelerometer& _accelerometer, MicroBitDisplay& _display);
+
+ /**
+ * Performs a simple game that in parallel, calibrates the compass.
+ *
+ * This function is executed automatically when the user requests a compass bearing, and compass calibration is required.
+ *
+ * This function is, by design, synchronous and only returns once calibration is complete.
+ */
+ void calibrate(MicroBitEvent);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitDisplay.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,639 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_DISPLAY_H
+#define MICROBIT_DISPLAY_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "ManagedString.h"
+#include "MicroBitComponent.h"
+#include "MicroBitImage.h"
+#include "MicroBitFont.h"
+#include "MicroBitMatrixMaps.h"
+#include "MicroBitLightSensor.h"
+
+/**
+ * Event codes raised by MicroBitDisplay
+ */
+#define MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE 1
+#define MICROBIT_DISPLAY_EVT_LIGHT_SENSE 2
+
+//
+// Internal constants
+//
+
+#define MICROBIT_DISPLAY_SPACING 1
+#define MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH 8
+#define MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS -255
+
+enum AnimationMode {
+ ANIMATION_MODE_NONE,
+ ANIMATION_MODE_STOPPED,
+ ANIMATION_MODE_SCROLL_TEXT,
+ ANIMATION_MODE_PRINT_TEXT,
+ ANIMATION_MODE_SCROLL_IMAGE,
+ ANIMATION_MODE_ANIMATE_IMAGE,
+ ANIMATION_MODE_PRINT_CHARACTER
+};
+
+enum DisplayMode {
+ DISPLAY_MODE_BLACK_AND_WHITE,
+ DISPLAY_MODE_GREYSCALE,
+ DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
+};
+
+enum DisplayRotation {
+ MICROBIT_DISPLAY_ROTATION_0,
+ MICROBIT_DISPLAY_ROTATION_90,
+ MICROBIT_DISPLAY_ROTATION_180,
+ MICROBIT_DISPLAY_ROTATION_270
+};
+
+/**
+ * Class definition for MicroBitDisplay.
+ *
+ * A MicroBitDisplay represents the LED matrix array on the micro:bit.
+ */
+class MicroBitDisplay : public MicroBitComponent
+{
+ uint8_t width;
+ uint8_t height;
+ uint8_t brightness;
+ uint8_t strobeRow;
+ uint8_t rotation;
+ uint8_t mode;
+ uint8_t greyscaleBitMsk;
+ uint8_t timingCount;
+ uint32_t col_mask;
+
+ Timeout renderTimer;
+ PortOut *LEDMatrix;
+
+ //
+ // State used by all animation routines.
+ //
+
+ // The animation mode that's currently running (if any)
+ volatile AnimationMode animationMode;
+
+ // The time in milliseconds between each frame update.
+ uint16_t animationDelay;
+
+ // The time in milliseconds since the frame update.
+ uint16_t animationTick;
+
+ // Stop playback of any animations
+ void stopAnimation(int delay);
+
+ //
+ // State for scrollString() method.
+ // This is a surprisingly intricate method.
+ //
+ // The text being displayed.
+ ManagedString scrollingText;
+
+ // The index of the character currently being displayed.
+ uint16_t scrollingChar;
+
+ // The number of pixels the current character has been shifted on the display.
+ uint8_t scrollingPosition;
+
+ //
+ // State for printString() method.
+ //
+ // The text being displayed. NULL if no message is scheduled for playback.
+ // We *could* get some reuse in here with the scroll* variables above,
+ // but best to keep it clean in case kids try concurrent operation (they will!),
+ // given the small RAM overhead needed to maintain orthogonality.
+ ManagedString printingText;
+
+ // The index of the character currently being displayed.
+ uint16_t printingChar;
+
+ //
+ // State for scrollImage() method.
+ //
+ // The image being displayed.
+ MicroBitImage scrollingImage;
+
+ // The number of pixels the image has been shifted on the display.
+ int16_t scrollingImagePosition;
+
+ // The number of pixels the image is shifted on the display in each quantum.
+ int8_t scrollingImageStride;
+
+ // A pointer to an instance of light sensor, if in use
+ MicroBitLightSensor* lightSensor;
+
+ // Flag to indicate if image has been rendered to screen yet (or not)
+ bool scrollingImageRendered;
+
+ const MatrixMap &matrixMap;
+
+ // Internal methods to handle animation.
+
+ /**
+ * Periodic callback, that we use to perform any animations we have running.
+ */
+ void animationUpdate();
+
+ /**
+ * Called by the display in an interval determined by the brightness of the display, to give an impression
+ * of brightness.
+ */
+ void renderFinish();
+
+ /**
+ * Translates a bit mask to a bit mask suitable for the nrf PORT0 and PORT1.
+ * Brightness has two levels on, or off.
+ */
+ void render();
+
+ /**
+ * Renders the current image, and drops the fourth frame to allow for
+ * sensors that require the display to operate.
+ */
+ void renderWithLightSense();
+
+ /**
+ * Translates a bit mask into a timer interrupt that gives the appearence of greyscale.
+ */
+ void renderGreyscale();
+
+ /**
+ * Internal scrollText update method.
+ * Shift the screen image by one pixel to the left. If necessary, paste in the next char.
+ */
+ void updateScrollText();
+
+ /**
+ * Internal printText update method.
+ * Paste the next character in the string.
+ */
+ void updatePrintText();
+
+ /**
+ * Internal scrollImage update method.
+ * Paste the stored bitmap at the appropriate point.
+ */
+ void updateScrollImage();
+
+ /**
+ * Internal animateImage update method.
+ * Paste the stored bitmap at the appropriate point and stop on the last frame.
+ */
+ void updateAnimateImage();
+
+ /**
+ * Broadcasts an event onto the defult EventModel indicating that the
+ * current animation has completed.
+ */
+ void sendAnimationCompleteEvent();
+
+ /**
+ * Blocks the current fiber until the display is available (i.e. does not effect is being displayed).
+ * Animations are queued until their time to display.
+ */
+ void waitForFreeDisplay();
+
+ /**
+ * Blocks the current fiber until the current animation has finished.
+ * If the scheduler is not running, this call will essentially perform a spinning wait.
+ */
+ void fiberWait();
+
+ /**
+ * Enables or disables the display entirely, and releases the pins for other uses.
+ *
+ * @param enableDisplay true to enabled the display, or false to disable it.
+ */
+ void setEnable(bool enableDisplay);
+
+public:
+ // The mutable bitmap buffer being rendered to the LED matrix.
+ MicroBitImage image;
+
+ /**
+ * Constructor.
+ *
+ * Create a software representation the micro:bit's 5x5 LED matrix.
+ * The display is initially blank.
+ *
+ * @param id The id the display should use when sending events on the MessageBus. Defaults to MICROBIT_ID_DISPLAY.
+ *
+ * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates.
+ * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * @endcode
+ */
+ MicroBitDisplay(uint16_t id = MICROBIT_ID_DISPLAY, const MatrixMap &map = microbitMatrixMap);
+
+ /**
+ * Stops any currently running animation, and any that are waiting to be displayed.
+ */
+ void stopAnimation();
+
+ /**
+ * Frame update method, invoked periodically to strobe the display.
+ */
+ virtual void systemTick();
+
+ /**
+ * Prints the given character to the display, if it is not in use.
+ *
+ * @param c The character to display.
+ *
+ * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
+ * or until the Displays next use.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY is the screen is in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync('p');
+ * display.printAsync('p',100);
+ * @endcode
+ */
+ int printCharAsync(char c, int delay = 0);
+
+ /**
+ * Prints the given ManagedString to the display, one character at a time.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Must be > 0.
+ * Defaults to: MICROBIT_DEFAULT_PRINT_SPEED.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync("abc123",400);
+ * @endcode
+ */
+ int printAsync(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED);
+
+ /**
+ * Prints the given image to the display, if the display is not in use.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param i The image to display.
+ *
+ * @param x The horizontal position on the screen to display the image. Defaults to 0.
+ *
+ * @param y The vertical position on the screen to display the image. Defaults to 0.
+ *
+ * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults to 0.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.print(i,400);
+ * @endcode
+ */
+ int printAsync(MicroBitImage i, int x = 0, int y = 0, int alpha = 0, int delay = 0);
+
+ /**
+ * Prints the given character to the display.
+ *
+ * @param c The character to display.
+ *
+ * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
+ * or until the Displays next use.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync('p');
+ * display.printAsync('p',100);
+ * @endcode
+ */
+ int printChar(char c, int delay = 0);
+
+ /**
+ * Prints the given string to the display, one character at a time.
+ *
+ * Blocks the calling thread until all the text has been displayed.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_PRINT_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.print("abc123",400);
+ * @endcode
+ */
+ int print(ManagedString s, int delay = MICROBIT_DEFAULT_PRINT_SPEED);
+
+ /**
+ * Prints the given image to the display.
+ * Blocks the calling thread until all the image has been displayed.
+ *
+ * @param i The image to display.
+ *
+ * @param x The horizontal position on the screen to display the image. Defaults to 0.
+ *
+ * @param y The vertical position on the screen to display the image. Defaults to 0.
+ *
+ * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
+ *
+ * @param delay The time to display the image for, or zero to show the image forever. Defaults to 0.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.print(i,400);
+ * @endcode
+ */
+ int print(MicroBitImage i, int x = 0, int y = 0, int alpha = 0, int delay = 0);
+
+ /**
+ * Scrolls the given string to the display, from right to left.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.scrollAsync("abc123",100);
+ * @endcode
+ */
+ int scrollAsync(ManagedString s, int delay = MICROBIT_DEFAULT_SCROLL_SPEED);
+
+ /**
+ * Scrolls the given image across the display, from right to left.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time between updates, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.scrollAsync(i,100,1);
+ * @endcode
+ */
+ int scrollAsync(MicroBitImage image, int delay = MICROBIT_DEFAULT_SCROLL_SPEED, int stride = MICROBIT_DEFAULT_SCROLL_STRIDE);
+
+ /**
+ * Scrolls the given string across the display, from right to left.
+ * Blocks the calling thread until all text has been displayed.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.scroll("abc123",100);
+ * @endcode
+ */
+ int scroll(ManagedString s, int delay = MICROBIT_DEFAULT_SCROLL_SPEED);
+
+ /**
+ * Scrolls the given image across the display, from right to left.
+ * Blocks the calling thread until all the text has been displayed.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time between updates, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.scroll(i,100,1);
+ * @endcode
+ */
+ int scroll(MicroBitImage image, int delay = MICROBIT_DEFAULT_SCROLL_SPEED, int stride = MICROBIT_DEFAULT_SCROLL_STRIDE);
+
+ /**
+ * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
+ * Returns immediately.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time to delay between each update of the display, in milliseconds.
+ *
+ * @param stride The number of pixels to shift by in each update.
+ *
+ * @param startingPosition the starting position on the display for the animation
+ * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const int heart_w = 10;
+ * const int heart_h = 5;
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, };
+ *
+ * MicroBitImage i(heart_w,heart_h,heart);
+ * display.animateAsync(i,100,5);
+ * @endcode
+ */
+ int animateAsync(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS);
+
+ /**
+ * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
+ * Blocks the calling thread until the animation is complete.
+ *
+ *
+ * @param delay The time to delay between each update of the display, in milliseconds.
+ *
+ * @param stride The number of pixels to shift by in each update.
+ *
+ * @param startingPosition the starting position on the display for the animation
+ * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const int heart_w = 10;
+ * const int heart_h = 5;
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, };
+ *
+ * MicroBitImage i(heart_w,heart_h,heart);
+ * display.animate(i,100,5);
+ * @endcode
+ */
+ int animate(MicroBitImage image, int delay, int stride, int startingPosition = MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS);
+
+ /**
+ * Configures the brightness of the display.
+ *
+ * @param b The brightness to set the brightness to, in the range 0 - 255.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER
+ *
+ * @code
+ * display.setBrightness(255); //max brightness
+ * @endcode
+ */
+ int setBrightness(int b);
+
+ /**
+ * Configures the mode of the display.
+ *
+ * @param mode The mode to swap the display into. One of: DISPLAY_MODE_GREYSCALE,
+ * DISPLAY_MODE_BLACK_AND_WHITE, DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
+ *
+ * @code
+ * display.setDisplayMode(DISPLAY_MODE_GREYSCALE); //per pixel brightness
+ * @endcode
+ */
+ void setDisplayMode(DisplayMode mode);
+
+ /**
+ * Retrieves the mode of the display.
+ *
+ * @return the current mode of the display
+ */
+ int getDisplayMode();
+
+ /**
+ * Fetches the current brightness of this display.
+ *
+ * @return the brightness of this display, in the range 0..255.
+ *
+ * @code
+ * display.getBrightness(); //the current brightness
+ * @endcode
+ */
+ int getBrightness();
+
+ /**
+ * Rotates the display to the given position.
+ *
+ * Axis aligned values only.
+ *
+ * @code
+ * display.rotateTo(MICROBIT_DISPLAY_ROTATION_180); //rotates 180 degrees from original orientation
+ * @endcode
+ */
+ void rotateTo(DisplayRotation position);
+
+ /**
+ * Enables the display, should only be called if the display is disabled.
+ *
+ * @code
+ * display.enable(); //Enables the display mechanics
+ * @endcode
+ *
+ * @note Only enables the display if the display is currently disabled.
+ */
+ void enable();
+
+ /**
+ * Disables the display, which releases control of the GPIO pins used by the display,
+ * which are exposed on the edge connector.
+ *
+ * @code
+ * display.disable(); //disables the display
+ * @endcode
+ *
+ * @note Only disables the display if the display is currently enabled.
+ */
+ void disable();
+
+ /**
+ * Clears the display of any remaining pixels.
+ *
+ * `display.image.clear()` can also be used!
+ *
+ * @code
+ * display.clear(); //clears the display
+ * @endcode
+ */
+ void clear();
+
+ /**
+ * Updates the font that will be used for display operations.
+ *
+ * @param font the new font that will be used to render characters.
+ *
+ * @note DEPRECATED! Please use MicroBitFont::setSystemFont() instead.
+ */
+ void setFont(MicroBitFont font);
+
+ /**
+ * Retrieves the font object used for rendering characters on the display.
+ *
+ * @note DEPRECATED! Please use MicroBitFont::getSystemFont() instead.
+ */
+ MicroBitFont getFont();
+
+ /**
+ * Captures the bitmap currently being rendered on the display.
+ *
+ * @return a MicroBitImage containing the captured data.
+ */
+ MicroBitImage screenShot();
+
+ /**
+ * Gives a representative figure of the light level in the current environment
+ * where are micro:bit is situated.
+ *
+ * Internally, it constructs an instance of a MicroBitLightSensor if not already configured
+ * and sets the display mode to DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE.
+ *
+ * This also changes the tickPeriod to MICROBIT_LIGHT_SENSOR_TICK_SPEED so
+ * that the display does not suffer from artifacts.
+ *
+ * @return an indicative light level in the range 0 - 255.
+ *
+ * @note this will return 0 on the first call to this method, a light reading
+ * will be available after the display has activated the light sensor for the
+ * first time.
+ */
+ int readLightLevel();
+
+ /**
+ * Destructor for MicroBitDisplay, where we deregister this instance from the array of system components.
+ */
+ ~MicroBitDisplay();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitI2C.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,107 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_I2C_H
+#define MICROBIT_I2C_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+#define MICROBIT_I2C_MAX_RETRIES 9
+
+/**
+ * Class definition for MicroBitI2C.
+ *
+ * Presents a wrapped mbed call to capture failed I2C operations caused by a known silicon bug in the nrf51822.
+ * Attempts to automatically reset and restart the I2C hardware if this case is detected.
+ *
+ * For reference see PAN56 in:
+ *
+ * https://www.nordicsemi.com/eng/nordic/Products/nRF51822/PAN-nRF51822/24634
+ *
+ * v2.0 through to v2.4
+ */
+class MicroBitI2C : public I2C
+{
+ uint8_t retries;
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Create an instance of MicroBitI2C for I2C communication.
+ *
+ * @param sda the Pin to be used for SDA
+ *
+ * @param scl the Pin to be used for SCL
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ * @endcode
+ *
+ * @note This class presents a wrapped mbed call to capture failed I2C operations caused by a known silicon bug in the nrf51822.
+ * Attempts to automatically reset and restart the I2C hardware if this case is detected.
+ *
+ * For reference see PAN56 in:
+ *
+ * https://www.nordicsemi.com/eng/nordic/Products/nRF51822/PAN-nRF51822/24634
+ *
+ * v2.0 through to v2.4
+ */
+ MicroBitI2C(PinName sda, PinName scl);
+
+ /**
+ * Performs a complete read transaction. The bottom bit of the address is forced to 1 to indicate a read.
+ *
+ * @param address 8-bit I2C slave address [ addr | 1 ]
+ *
+ * @param data A pointer to a byte buffer used for storing retrieved data.
+ *
+ * @param length Number of bytes to read.
+ *
+ * @param repeated if true, stop is not sent at the end. Defaults to false.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if an unresolved read failure is detected.
+ */
+ int read(int address, char *data, int length, bool repeated = false);
+
+ /**
+ * Performs a complete write transaction. The bottom bit of the address is forced to 0 to indicate a write.
+ *
+ * @param address 8-bit I2C slave address [ addr | 0 ]
+ *
+ * @param data A pointer to a byte buffer containing the data to write.
+ *
+ * @param length Number of bytes to write
+ *
+ * @param repeated if true, stop is not sent at the end. Defaults to false.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if an unresolved write failure is detected.
+ */
+ int write(int address, const char *data, int length, bool repeated = false);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitIO.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,81 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_IO_H
+#define MICROBIT_IO_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitPin.h"
+
+/**
+ * Class definition for MicroBit IO.
+ *
+ * Represents a collection of all I/O pins on the edge connector.
+ */
+class MicroBitIO
+{
+ public:
+
+ MicroBitPin pin[0];
+ MicroBitPin P0;
+ MicroBitPin P1;
+ MicroBitPin P2;
+ MicroBitPin P3;
+ MicroBitPin P4;
+ MicroBitPin P5;
+ MicroBitPin P6;
+ MicroBitPin P7;
+ MicroBitPin P8;
+ MicroBitPin P9;
+ MicroBitPin P10;
+ MicroBitPin P11;
+ MicroBitPin P12;
+ MicroBitPin P13;
+ MicroBitPin P14;
+ MicroBitPin P15;
+ MicroBitPin P16;
+ MicroBitPin P19;
+ MicroBitPin P20;
+
+ /**
+ * Constructor.
+ *
+ * Create a representation of all given I/O pins on the edge connector
+ *
+ * Accepts a sequence of unique ID's used to distinguish events raised
+ * by MicroBitPin instances on the default EventModel.
+ */
+ MicroBitIO(int ID_P0, int ID_P1, int ID_P2,
+ int ID_P3, int ID_P4, int ID_P5,
+ int ID_P6, int ID_P7, int ID_P8,
+ int ID_P9, int ID_P10,int ID_P11,
+ int ID_P12,int ID_P13,int ID_P14,
+ int ID_P15,int ID_P16,int ID_P19,
+ int ID_P20);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitLightSensor.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,137 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_LIGHT_SENSOR_H
+#define MICROBIT_LIGHT_SENSOR_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "EventModel.h"
+#include "MicroBitMatrixMaps.h"
+
+#define MICROBIT_LIGHT_SENSOR_CHAN_NUM 3
+#define MICROBIT_LIGHT_SENSOR_AN_SET_TIME 4000
+#define MICROBIT_LIGHT_SENSOR_TICK_PERIOD 5
+
+#define MICROBIT_LIGHT_SENSOR_MAX_VALUE 338
+#define MICROBIT_LIGHT_SENSOR_MIN_VALUE 75
+
+/**
+ * Class definition for MicroBitLightSensor.
+ *
+ * This is an object that interleaves light sensing with MicroBitDisplay.
+ */
+class MicroBitLightSensor
+{
+
+ //contains the results from each section of the display
+ int results[MICROBIT_LIGHT_SENSOR_CHAN_NUM] = { 0 };
+
+ //holds the current channel (also used to index the results array)
+ uint8_t chan;
+
+ //a Timeout which triggers our analogReady() call
+ Timeout analogTrigger;
+
+ //a pointer the currently sensed pin, represented as an AnalogIn
+ AnalogIn* sensePin;
+
+ const MatrixMap &matrixMap;
+
+ /**
+ * After the startSensing method has been called, this method will be called
+ * MICROBIT_LIGHT_SENSOR_AN_SET_TIME after.
+ *
+ * It will then read from the currently selected channel using the AnalogIn
+ * that was configured in the startSensing method.
+ */
+ void analogReady();
+
+ /**
+ * Forcibly disables the AnalogIn, otherwise it will remain in possession
+ * of the GPIO channel it is using, meaning that the display will not be
+ * able to use a channel (COL).
+ *
+ * This is required as per PAN 3, details of which can be found here:
+ *
+ * https://www.nordicsemi.com/eng/nordic/download_resource/24634/5/88440387
+ */
+ void analogDisable();
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Create a representation of the light sensor.
+ *
+ * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates.
+ * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h.
+ */
+ MicroBitLightSensor(const MatrixMap &map);
+
+ /**
+ * This method returns a summed average of the three sections of the display.
+ *
+ * A section is defined as:
+ * ___________________
+ * | 1 | | 2 | | 3 |
+ * |___|___|___|___|___|
+ * | | | | | |
+ * |___|___|___|___|___|
+ * | 2 | | 3 | | 1 |
+ * |___|___|___|___|___|
+ * | | | | | |
+ * |___|___|___|___|___|
+ * | 3 | | 1 | | 2 |
+ * |___|___|___|___|___|
+ *
+ * Where each number represents a different section on the 5 x 5 matrix display.
+ *
+ * @return returns a value in the range 0 - 255 where 0 is dark, and 255
+ * is very bright
+ */
+ int read();
+
+ /**
+ * The method that is invoked by sending MICROBIT_DISPLAY_EVT_LIGHT_SENSE
+ * using the id MICROBIT_ID_DISPLAY.
+ *
+ * @note this can be manually driven by calling this member function, with
+ * a MicroBitEvent using the CREATE_ONLY option of the MicroBitEvent
+ * constructor.
+ */
+ void startSensing(MicroBitEvent);
+
+ /**
+ * A destructor for MicroBitLightSensor.
+ *
+ * The destructor removes the listener, used by MicroBitLightSensor from the default EventModel.
+ */
+ ~MicroBitLightSensor();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitMatrixMaps.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,157 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Definition of the LED Matrix maps supported.
+ * Each map represents the layou of a different device.
+ *
+ * Ensure only one of these is selected.
+ */
+
+#ifndef MICROBIT_MATRIX_MAPS_H
+#define MICROBIT_MATRIX_MAPS_H
+
+#include "MicroBitConfig.h"
+#include "mbed.h"
+
+#define NO_CONN 0
+
+/**
+ * Provides the mapping from Matrix ROW/COL to a linear X/Y buffer.
+ * It's arranged such that matrixMap[col, row] provides the [x,y] screen co-ord.
+ */
+
+struct MatrixPoint
+{
+ uint8_t x;
+ uint8_t y;
+};
+
+/**
+ * This struct presumes rows and columns are arranged contiguously...
+ */
+struct MatrixMap
+{
+ int width; // The physical width of the LED matrix, in pixels.
+ int height; // The physical height of the LED matrix, in pixels.
+ int rows; // The number of drive pins connected to LEDs.
+ int columns; // The number of sink pins connected to the LEDs.
+
+ PinName rowStart; // ID of the first drive pin.
+ PinName columnStart; // ID of the first sink pink.
+
+ const MatrixPoint *map; // Table mapping logical LED positions to physical positions.
+};
+
+/*
+ * Dimensions for well known micro:bit LED configurations
+ */
+#define MICROBIT_DISPLAY_WIDTH 5
+#define MICROBIT_DISPLAY_HEIGHT 5
+
+#if MICROBIT_DISPLAY_TYPE == MICROBUG_REFERENCE_DEVICE
+
+#define MICROBIT_DISPLAY_COLUMN_COUNT 5
+#define MICROBIT_DISPLAY_ROW_COUNT 5
+
+ const MatrixPoint microbitDisplayMap[MICROBIT_DISPLAY_ROW_COUNT * MICROBIT_DISPLAY_COLUMN_COUNT] =
+ {
+ {0,0},{0,1},{0,2},{0,3},{0,4},
+ {1,0},{1,1},{1,2},{1,3},{1,4},
+ {2,0},{2,1},{2,2},{2,3},{2,4},
+ {3,0},{3,1},{3,2},{3,3},{3,4},
+ {4,0},{4,1},{4,2},{4,3},{4,4}
+ };
+
+#endif
+
+#if MICROBIT_DISPLAY_TYPE == MICROBIT_3X9
+
+#define MICROBIT_DISPLAY_COLUMN_COUNT 9
+#define MICROBIT_DISPLAY_ROW_COUNT 3
+
+ const MatrixPoint microbitDisplayMap[MICROBIT_DISPLAY_ROW_COUNT * MICROBIT_DISPLAY_COLUMN_COUNT] =
+ {
+ {0,4},{0,3},{1,1},
+ {1,4},{4,2},{0,1},
+ {2,4},{3,2},{4,0},
+ {3,4},{2,2},{3,0},
+ {4,4},{1,2},{2,0},
+ {4,3},{0,2},{1,0},
+ {3,3},{4,1},{0,0},
+ {2,3},{3,1},{NO_CONN,NO_CONN},
+ {1,3},{2,1},{NO_CONN,NO_CONN}
+ };
+
+#endif
+
+#if MICROBIT_DISPLAY_TYPE == MICROBIT_SB1
+
+#define MICROBIT_DISPLAY_COLUMN_COUNT 3
+#define MICROBIT_DISPLAY_ROW_COUNT 9
+
+ const MatrixPoint microbitDisplayMap[MICROBIT_DISPLAY_ROW_COUNT * MICROBIT_DISPLAY_COLUMN_COUNT] =
+ {
+ {0,4},{1,4},{2,4},{3,4},{4,4},{4,3},{3,3},{2,3},{1,3},
+ {0,3},{4,2},{3,2},{2,2},{1,2},{0,2},{4,1},{3,1},{2,1},
+ {1,1},{0,1},{4,0},{3,0},{2,0},{1,0},{0,0},{NO_CONN,NO_CONN},{NO_CONN,NO_CONN}
+ };
+
+#endif
+
+#if MICROBIT_DISPLAY_TYPE == MICROBIT_SB2
+
+#define MICROBIT_DISPLAY_COLUMN_COUNT 9
+#define MICROBIT_DISPLAY_ROW_COUNT 3
+
+ const MatrixPoint microbitDisplayMap[MICROBIT_DISPLAY_ROW_COUNT * MICROBIT_DISPLAY_COLUMN_COUNT] =
+ {
+ {0,0},{4,2},{2,4},
+ {2,0},{0,2},{4,4},
+ {4,0},{2,2},{0,4},
+ {4,3},{1,0},{0,1},
+ {3,3},{3,0},{1,1},
+ {2,3},{3,4},{2,1},
+ {1,3},{1,4},{3,1},
+ {0,3},{NO_CONN,NO_CONN},{4,1},
+ {1,2},{NO_CONN,NO_CONN},{3,2}
+ };
+
+#endif
+
+//ROW1 and COL1 are defined in mbed classic:
+//https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/TARGET_NRF51_MICROBIT/PinNames.h
+const MatrixMap microbitMatrixMap =
+{
+ MICROBIT_DISPLAY_WIDTH,
+ MICROBIT_DISPLAY_HEIGHT,
+ MICROBIT_DISPLAY_ROW_COUNT,
+ MICROBIT_DISPLAY_COLUMN_COUNT,
+ ROW1,
+ COL1,
+ microbitDisplayMap
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitMessageBus.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,188 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_MESSAGE_BUS_H
+#define MICROBIT_MESSAGE_BUS_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitEvent.h"
+#include "MicroBitListener.h"
+#include "EventModel.h"
+
+/**
+ * Class definition for the MicroBitMessageBus.
+ *
+ * The MicroBitMessageBus is the common mechanism to deliver asynchronous events on the
+ * MicroBit platform. It serves a number of purposes:
+ *
+ * 1) It provides an eventing abstraction that is independent of the underlying substrate.
+ *
+ * 2) It provides a mechanism to decouple user code from trusted system code
+ * i.e. the basis of a message passing nano kernel.
+ *
+ * 3) It allows a common high level eventing abstraction across a range of hardware types.e.g. buttons, BLE...
+ *
+ * 4) It provides a mechanim for extensibility - new devices added via I/O pins can have OO based
+ * drivers and communicate via the message bus with minima impact on user level languages.
+ *
+ * 5) It allows for the possiblility of event / data aggregation, which in turn can save energy.
+ *
+ * It has the following design principles:
+ *
+ * 1) Maintain a low RAM footprint where possible
+ *
+ * 2) Make few assumptions about the underlying platform, but allow optimizations where possible.
+ */
+class MicroBitMessageBus : public EventModel, public MicroBitComponent
+{
+ public:
+
+ /**
+ * Default constructor.
+ *
+ * Adds itself as a fiber component, and also configures itself to be the
+ * default EventModel if defaultEventBus is NULL.
+ */
+ MicroBitMessageBus();
+
+ /**
+ * Queues the given event to be sent to all registered recipients.
+ *
+ * @param evt The event to send.
+ *
+ * @code
+ * MicroBitMessageBus bus;
+ *
+ * // Creates and sends the MicroBitEvent using bus.
+ * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * // Creates the MicrobitEvent, but delays the sending of that event.
+ * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY);
+ *
+ * bus.send(evt1);
+ *
+ * // This has the same effect!
+ * evt1.fire()
+ * @endcode
+ */
+ virtual int send(MicroBitEvent evt);
+
+ /**
+ * Internal function, used to deliver the given event to all relevant recipients.
+ * Normally, this is called once an event has been removed from the event queue.
+ *
+ * @param evt The event to send.
+ *
+ * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
+ * otherwise, all other (standard) listeners will be processed. Defaults to false.
+ *
+ * @return 1 if all matching listeners were processed, 0 if further processing is required.
+ *
+ * @note It is recommended that all external code uses the send() function instead of this function,
+ * or the constructors provided by MicrobitEvent.
+ */
+ int process(MicroBitEvent &evt, bool urgent = false);
+
+ /**
+ * 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.
+ */
+ virtual MicroBitListener *elementAt(int n);
+
+ /**
+ * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components.
+ */
+ ~MicroBitMessageBus();
+
+ /**
+ * Add the given MicroBitListener to the list of event handlers, unconditionally.
+ *
+ * @param listener The MicroBitListener to add.
+ *
+ * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
+ */
+ virtual int add(MicroBitListener *newListener);
+
+ /**
+ * Remove the given MicroBitListener from the list of event handlers.
+ *
+ * @param listener The MicroBitListener to remove.
+ *
+ * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
+ */
+ virtual int remove(MicroBitListener *newListener);
+
+ private:
+
+ MicroBitListener *listeners; // Chain of active listeners.
+ MicroBitEventQueueItem *evt_queue_head; // Head of queued events to be processed.
+ MicroBitEventQueueItem *evt_queue_tail; // Tail of queued events to be processed.
+ uint16_t nonce_val; // The last nonce issued.
+ uint16_t queueLength; // The number of events currently waiting to be processed.
+
+ /**
+ * Cleanup any MicroBitListeners marked for deletion from the list.
+ *
+ * @return The number of listeners removed from the list.
+ */
+ int deleteMarkedListeners();
+
+ /**
+ * Queue the given event for processing at a later time.
+ * Add the given event at the tail of our queue.
+ *
+ * @param The event to queue.
+ */
+ void queueEvent(MicroBitEvent &evt);
+
+ /**
+ * Extract the next event from the front of the event queue (if present).
+ *
+ * @return a pointer to the MicroBitEventQueueItem that is at the head of the list.
+ */
+ MicroBitEventQueueItem* dequeueEvent();
+
+ /**
+ * Periodic callback from MicroBit.
+ *
+ * Process at least one event from the event queue, if it is not empty.
+ * We then continue processing events until something appears on the runqueue.
+ */
+ virtual void idleTick();
+
+ /**
+ * Indicates whether or not we have any background work to do.
+ *
+ * @return 1 if there are any events waitingto be processed, 0 otherwise.
+ */
+ virtual int isIdleCallbackNeeded();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitMultiButton.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,176 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_MULTI_BUTTON_H
+#define MICROBIT_MULTI_BUTTON_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitButton.h"
+#include "EventModel.h"
+
+#define MICROBIT_MULTI_BUTTON_STATE_1 0x01
+#define MICROBIT_MULTI_BUTTON_STATE_2 0x02
+#define MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_1 0x04
+#define MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_2 0x08
+#define MICROBIT_MULTI_BUTTON_SUPRESSED_1 0X10
+#define MICROBIT_MULTI_BUTTON_SUPRESSED_2 0x20
+#define MICROBIT_MULTI_BUTTON_ATTACHED 0x40
+
+/**
+ * Class definition for MicroBitMultiButton.
+ *
+ * Represents a virtual button, capable of reacting to simultaneous presses of two
+ * other buttons.
+ */
+class MicroBitMultiButton : public MicroBitComponent
+{
+ uint16_t button1; // ID of the first button we're monitoring
+ uint16_t button2; // ID of the second button we're monitoring
+ MicroBitButtonEventConfiguration eventConfiguration; // Do we want to generate high level event (clicks), or defer this to another service.
+
+ /**
+ * Retrieves the button id for the alternate button id given.
+ *
+ * @param b the id of the button whose state we would like to retrieve.
+ *
+ * @return the other sub button id.
+ */
+ uint16_t otherSubButton(uint16_t b);
+
+ /**
+ * Determines if the given button id is marked as pressed.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if pressed, 0 if not.
+ */
+ int isSubButtonPressed(uint16_t button);
+
+ /**
+ * Determines if the given button id is marked as held.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if held, 0 if not.
+ */
+ int isSubButtonHeld(uint16_t button);
+
+ /**
+ * Determines if the given button id is marked as supressed.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if supressed, 0 if not.
+ */
+ int isSubButtonSupressed(uint16_t button);
+
+ /**
+ * Configures the button pressed state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+ void setButtonState(uint16_t button, int value);
+
+ /**
+ * Configures the button held state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+ void setHoldState(uint16_t button, int value);
+
+ /**
+ * Configures the button suppressed state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+ void setSupressedState(uint16_t button, int value);
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Create a representation of a virtual button, that generates events based upon the combination
+ * of two given buttons.
+ *
+ * @param button1 the unique ID of the first button to watch.
+ *
+ * @param button2 the unique ID of the second button to watch.
+ *
+ * @param id the unique EventModel id of this MicroBitMultiButton instance.
+ *
+ * @code
+ * multiButton(MICROBIT_ID_BUTTON_A, MICROBIT_ID_BUTTON_B, MICROBIT_ID_BUTTON_AB);
+ * @endcode
+ */
+ MicroBitMultiButton(uint16_t button1, uint16_t button2, uint16_t id);
+
+ /**
+ * Tests if this MicroBitMultiButton instance is virtually pressed.
+ *
+ * @return 1 if both physical buttons are pressed simultaneously.
+ *
+ * @code
+ * if(buttonAB.isPressed())
+ * display.scroll("Pressed!");
+ * @endcode
+ */
+ int isPressed();
+
+ /**
+ * Changes the event configuration of this button to the given MicroBitButtonEventConfiguration.
+ * All subsequent events generated by this button will then be informed by this configuraiton.
+ *
+ * @param config The new configuration for this button. Legal values are MICROBIT_BUTTON_ALL_EVENTS or MICROBIT_BUTTON_SIMPLE_EVENTS.
+ *
+ * @code
+ * // Configure a button to generate all possible events.
+ * buttonAB.setEventConfiguration(MICROBIT_BUTTON_ALL_EVENTS);
+ *
+ * // Configure a button to suppress MICROBIT_BUTTON_EVT_CLICK and MICROBIT_BUTTON_EVT_LONG_CLICK events.
+ * buttonAB.setEventConfiguration(MICROBIT_BUTTON_SIMPLE_EVENTS);
+ * @endcode
+ */
+ void setEventConfiguration(MicroBitButtonEventConfiguration config);
+
+ private:
+
+ /**
+ * A member function that is invoked when any event is detected from the two
+ * button IDs this MicrobitMultiButton instance was constructed with.
+ *
+ * @param evt the event received from the default EventModel.
+ */
+ void onButtonEvent(MicroBitEvent evt);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitPin.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,297 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_PIN_H
+#define MICROBIT_PIN_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+ // Status Field flags...
+#define IO_STATUS_DIGITAL_IN 0x01 // Pin is configured as a digital input, with no pull up.
+#define IO_STATUS_DIGITAL_OUT 0x02 // Pin is configured as a digital output
+#define IO_STATUS_ANALOG_IN 0x04 // Pin is Analog in
+#define IO_STATUS_ANALOG_OUT 0x08 // Pin is Analog out
+#define IO_STATUS_TOUCH_IN 0x10 // Pin is a makey-makey style touch sensor
+#define IO_STATUS_EVENTBUS_ENABLED 0x80 // Pin is will generate events on change
+
+//#defines for each edge connector pin
+#define MICROBIT_PIN_P0 P0_3 //P0 is the left most pad (ANALOG/DIGITAL) used to be P0_3 on green board
+#define MICROBIT_PIN_P1 P0_2 //P1 is the middle pad (ANALOG/DIGITAL)
+#define MICROBIT_PIN_P2 P0_1 //P2 is the right most pad (ANALOG/DIGITAL) used to be P0_1 on green board
+#define MICROBIT_PIN_P3 P0_4 //COL1 (ANALOG/DIGITAL)
+#define MICROBIT_PIN_P4 P0_5 //COL2 (ANALOG/DIGITAL)
+#define MICROBIT_PIN_P5 P0_17 //BTN_A
+#define MICROBIT_PIN_P6 P0_12 //COL9
+#define MICROBIT_PIN_P7 P0_11 //COL8
+#define MICROBIT_PIN_P8 P0_18 //PIN 18
+#define MICROBIT_PIN_P9 P0_10 //COL7
+#define MICROBIT_PIN_P10 P0_6 //COL3 (ANALOG/DIGITAL)
+#define MICROBIT_PIN_P11 P0_26 //BTN_B
+#define MICROBIT_PIN_P12 P0_20 //PIN 20
+#define MICROBIT_PIN_P13 P0_23 //SCK
+#define MICROBIT_PIN_P14 P0_22 //MISO
+#define MICROBIT_PIN_P15 P0_21 //MOSI
+#define MICROBIT_PIN_P16 P0_16 //PIN 16
+#define MICROBIT_PIN_P19 P0_0 //SCL
+#define MICROBIT_PIN_P20 P0_30 //SDA
+
+#define MICROBIT_PIN_MAX_OUTPUT 1023
+
+#define MICROBIT_PIN_MAX_SERVO_RANGE 180
+#define MICROBIT_PIN_DEFAULT_SERVO_RANGE 2000
+#define MICROBIT_PIN_DEFAULT_SERVO_CENTER 1500
+
+
+/**
+ * Pin capabilities enum.
+ * Used to determine the capabilities of each Pin as some can only be digital, or can be both digital and analogue.
+ */
+enum PinCapability{
+ PIN_CAPABILITY_DIGITAL = 0x01,
+ PIN_CAPABILITY_ANALOG = 0x02,
+ PIN_CAPABILITY_TOUCH = 0x04,
+ PIN_CAPABILITY_AD = PIN_CAPABILITY_DIGITAL | PIN_CAPABILITY_ANALOG,
+ PIN_CAPABILITY_ALL = PIN_CAPABILITY_DIGITAL | PIN_CAPABILITY_ANALOG | PIN_CAPABILITY_TOUCH
+
+};
+
+/**
+ * Class definition for MicroBitPin.
+ *
+ * Commonly represents an I/O pin on the edge connector.
+ */
+class MicroBitPin : public MicroBitComponent
+{
+ // The mbed object looking after this pin at any point in time (untyped due to dynamic behaviour).
+ void *pin;
+
+ PinCapability capability;
+
+ /**
+ * Disconnect any attached mBed IO from this pin.
+ *
+ * Used only when pin changes mode (i.e. Input/Output/Analog/Digital)
+ */
+ void disconnect();
+
+ /**
+ * Performs a check to ensure that the current Pin is in control of a
+ * DynamicPwm instance, and if it's not, allocates a new DynamicPwm instance.
+ */
+ int obtainAnalogChannel();
+
+ public:
+
+ // mbed PinName of this pin.
+ PinName name;
+
+ /**
+ * Constructor.
+ * Create a MicroBitPin instance, generally used to represent a pin on the edge connector.
+ *
+ * @param id the unique EventModel id of this component.
+ *
+ * @param name the mbed PinName for this MicroBitPin instance.
+ *
+ * @param capability the capabilities this MicroBitPin instance should have.
+ * (PIN_CAPABILITY_DIGITAL, PIN_CAPABILITY_ANALOG, PIN_CAPABILITY_TOUCH, PIN_CAPABILITY_AD, PIN_CAPABILITY_ALL)
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
+ * @endcode
+ */
+ MicroBitPin(int id, PinName name, PinCapability capability);
+
+ /**
+ * Configures this IO pin as a digital output (if necessary) and sets the pin to 'value'.
+ *
+ * @param value 0 (LO) or 1 (HI)
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have digital capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.setDigitalValue(1); // P0 is now HI
+ * @endcode
+ */
+ int setDigitalValue(int value);
+
+ /**
+ * Configures this IO pin as a digital input (if necessary) and tests its current value.
+ *
+ * @return 1 if this input is high, 0 if input is LO, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.getDigitalValue(); // P0 is either 0 or 1;
+ * @endcode
+ */
+ int getDigitalValue();
+
+ /**
+ * Configures this IO pin as an analog/pwm output, and change the output value to the given level.
+ *
+ * @param value the level to set on the output pin, in the range 0 - 1024
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+ int setAnalogValue(int value);
+
+ /**
+ * Configures this IO pin as an analog/pwm output (if necessary) and configures the period to be 20ms,
+ * with a duty cycle between 500 us and 2500 us.
+ *
+ * A value of 180 sets the duty cycle to be 2500us, and a value of 0 sets the duty cycle to be 500us by default.
+ *
+ * This range can be modified to fine tune, and also tolerate different servos.
+ *
+ * @param value the level to set on the output pin, in the range 0 - 180.
+ *
+ * @param range which gives the span of possible values the i.e. the lower and upper bounds (center +/- range/2). Defaults to MICROBIT_PIN_DEFAULT_SERVO_RANGE.
+ *
+ * @param center the center point from which to calculate the lower and upper bounds. Defaults to MICROBIT_PIN_DEFAULT_SERVO_CENTER
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+ int setServoValue(int value, int range = MICROBIT_PIN_DEFAULT_SERVO_RANGE, int center = MICROBIT_PIN_DEFAULT_SERVO_CENTER);
+
+ /**
+ * Configures this IO pin as an analogue input (if necessary), and samples the Pin for its analog value.
+ *
+ * @return the current analogue level on the pin, in the range 0 - 1024, or
+ * MICROBIT_NOT_SUPPORTED if the given pin does not have analog capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.getAnalogValue(); // P0 is a value in the range of 0 - 1024
+ * @endcode
+ */
+ 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.
+ *
+ * Users can also subscribe to MicroBitButton events generated from this pin.
+ *
+ * @return 1 if pin is touched, 0 if not, or MICROBIT_NOT_SUPPORTED if this pin does not support touch capability.
+ *
+ * @code
+ * MicroBitMessageBus bus;
+ *
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
+ * if(P0.isTouched())
+ * {
+ * //do something!
+ * }
+ *
+ * // subscribe to events generated by this pin!
+ * bus.listen(MICROBIT_ID_IO_P0, MICROBIT_BUTTON_EVT_CLICK, someFunction);
+ * @endcode
+ */
+ int isTouched();
+
+ /**
+ * Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
+ * and sets the pulse width, based on the value it is given.
+ *
+ * @param pulseWidth the desired pulse width in microseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+ int setServoPulseUs(int pulseWidth);
+
+ /**
+ * Configures the PWM period of the analog output to the given value.
+ *
+ * @param period The new period for the analog output in milliseconds.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+ int setAnalogPeriod(int period);
+
+ /**
+ * Configures the PWM period of the analog output to the given value.
+ *
+ * @param period The new period for the analog output in microseconds.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+ int setAnalogPeriodUs(int period);
+
+ /**
+ * Obtains the PWM period of the analog output in microseconds.
+ *
+ * @return the period on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+ int getAnalogPeriodUs();
+
+ /**
+ * Obtains the PWM period of the analog output in milliseconds.
+ *
+ * @return the period on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+ int getAnalogPeriod();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitRadio.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,227 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_RADIO_H
+#define MICROBIT_RADIO_H
+
+class MicroBitRadio;
+struct FrameBuffer;
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "PacketBuffer.h"
+#include "MicroBitRadioDatagram.h"
+#include "MicroBitRadioEvent.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * The nrf51822 RADIO module supports a number of proprietary modes of operation in addition to the typical BLE usage.
+ * This class uses one of these modes to enable simple, point to multipoint communication directly between micro:bits.
+ *
+ * TODO: The protocols implemented here do not currently perform any significant form of energy management,
+ * which means that they will consume far more energy than their BLE equivalent. Later versions of the protocol
+ * should look to address this through energy efficient broadcast techniques / sleep scheduling. In particular, the GLOSSY
+ * approach to efficienct rebroadcast and network synchronisation would likely provide an effective future step.
+ *
+ * TODO: Meshing should also be considered - again a GLOSSY approach may be effective here, and highly complementary to
+ * the master/slave arachitecture of BLE.
+ *
+ * TODO: This implementation only operates whilst the BLE stack is disabled. The nrf51822 provides a timeslot API to allow
+ * BLE to cohabit with other protocols. Future work to allow this colocation would be benefical, and would also allow for the
+ * creation of wireless BLE bridges.
+ *
+ * NOTE: This API does not contain any form of encryption, authentication or authorization. It's purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+
+// Status Flags
+#define MICROBIT_RADIO_STATUS_INITIALISED 0x0001
+
+// Default configuration values
+#define MICROBIT_RADIO_BASE_ADDRESS 0x75626974
+#define MICROBIT_RADIO_DEFAULT_GROUP 0
+#define MICROBIT_RADIO_DEFAULT_TX_POWER 6
+#define MICROBIT_RADIO_DEFAULT_FREQUENCY 7
+#define MICROBIT_RADIO_MAX_PACKET_SIZE 32
+#define MICROBIT_RADIO_HEADER_SIZE 4
+#define MICROBIT_RADIO_MAXIMUM_RX_BUFFERS 4
+
+// Known Protocol Numbers
+#define MICROBIT_RADIO_PROTOCOL_DATAGRAM 1 // A simple, single frame datagram. a little like UDP but with smaller packets. :-)
+#define MICROBIT_RADIO_PROTOCOL_EVENTBUS 2 // Transparent propogation of events from one micro:bit to another.
+
+// Events
+#define MICROBIT_RADIO_EVT_DATAGRAM 1 // Event to signal that a new datagram has been received.
+
+
+struct FrameBuffer
+{
+ uint8_t length; // The length of the remaining bytes in the packet. includes protocol/version/group fields, excluding the length field itself.
+ uint8_t version; // Protocol version code.
+ uint8_t group; // ID of the group to which this packet belongs.
+ uint8_t protocol; // Inner protocol number c.f. those issued by IANA for IP protocols
+
+ uint8_t payload[MICROBIT_RADIO_MAX_PACKET_SIZE]; // User / higher layer protocol data
+ FrameBuffer *next; // Linkage, to allow this and other protocols to queue packets pending processing.
+ uint8_t rssi; // Received signal strength of this frame.
+};
+
+
+class MicroBitRadio : MicroBitComponent
+{
+ uint8_t group; // The radio group to which this micro:bit belongs.
+ uint8_t queueDepth; // The number of packets in the receiver queue.
+ uint8_t rssi;
+ FrameBuffer *rxQueue; // A linear list of incoming packets, queued awaiting processing.
+ FrameBuffer *rxBuf; // A pointer to the buffer being actively used by the RADIO hardware.
+
+ public:
+ MicroBitRadioDatagram datagram; // A simple datagram service.
+ MicroBitRadioEvent event; // A simple event handling service.
+ static MicroBitRadio *instance; // A singleton reference, used purely by the interrupt service routine.
+
+ /**
+ * Constructor.
+ *
+ * Initialise the MicroBitRadio.
+ *
+ * @note This class is demand activated, as a result most resources are only
+ * committed if send/recv or event registrations calls are made.
+ */
+ MicroBitRadio(uint16_t id = MICROBIT_ID_RADIO);
+
+ /**
+ * Change the output power level of the transmitter to the given value.
+ *
+ * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
+ */
+ int setTransmitPower(int power);
+
+ /**
+ * Change the transmission and reception band of the radio to the given channel
+ *
+ * @param band a frequency band in the range 0 - 100. Each step is 1MHz wide, based at 2400MHz.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range,
+ * or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int setFrequencyBand(int band);
+
+ /**
+ * Retrieve a pointer to the currently allocated receive buffer. This is the area of memory
+ * actively being used by the radio hardware to store incoming data.
+ *
+ * @return a pointer to the current receive buffer.
+ */
+ FrameBuffer * getRxBuf();
+
+ /**
+ * Attempt to queue a buffer received by the radio hardware, if sufficient space is available.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if a replacement receiver buffer
+ * could not be allocated (either by policy or memory exhaustion).
+ */
+ int queueRxBuf();
+
+ /**
+ * Sets the RSSI for the most recent packet.
+ *
+ * @param rssi the new rssi value.
+ *
+ * @note should only be called from RADIO_IRQHandler...
+ */
+ int setRSSI(uint8_t rssi);
+
+ /**
+ * Retrieves the current RSSI for the most recent packet.
+ *
+ * @return the most recent RSSI value or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int getRSSI();
+
+ /**
+ * Initialises the radio for use as a multipoint sender/receiver
+ *
+ * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int enable();
+
+ /**
+ * Disables the radio for use as a multipoint sender/receiver.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int disable();
+
+ /**
+ * Sets the radio to listen to packets sent with the given group id.
+ *
+ * @param group The group to join. A micro:bit can only listen to one group ID at any time.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int setGroup(uint8_t group);
+
+ /**
+ * A background, low priority callback that is triggered whenever the processor is idle.
+ * Here, we empty our queue of received packets, and pass them onto higher level protocol handlers.
+ */
+ virtual void idleTick();
+
+ /**
+ * Determines the number of packets ready to be processed.
+ *
+ * @return The number of packets in the receive buffer.
+ */
+ int dataReady();
+
+ /**
+ * Retrieves the next packet from the receive buffer.
+ * If a data packet is available, then it will be returned immediately to
+ * the caller. This call will also dequeue the buffer.
+ *
+ * @return The buffer containing the the packet. If no data is available, NULL is returned.
+ *
+ * @note Once recv() has been called, it is the callers resposibility to
+ * delete the buffer when appropriate.
+ */
+ FrameBuffer* recv();
+
+ /**
+ * Transmits the given buffer onto the broadcast radio.
+ * The call will wait until the transmission of the packet has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+ int send(FrameBuffer *buffer);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitRadioDatagram.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,135 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_RADIO_DATAGRAM_H
+#define MICROBIT_RADIO_DATAGRAM_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitRadio.h"
+#include "ManagedString.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * This class provides the ability to broadcast simple text or binary messages to other micro:bits in the vicinity
+ * It is envisaged that this would provide the basis for children to experiment with building their own, simple,
+ * custom protocols.
+ *
+ * @note This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+class MicroBitRadioDatagram
+{
+ MicroBitRadio &radio; // The underlying radio module used to send and receive data.
+ FrameBuffer *rxQueue; // A linear list of incoming packets, queued awaiting processing.
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Creates an instance of a MicroBitRadioDatagram which offers the ability
+ * to broadcast simple text or binary messages to other micro:bits in the vicinity
+ *
+ * @param r The underlying radio module used to send and receive data.
+ */
+ MicroBitRadioDatagram(MicroBitRadio &r);
+
+ /**
+ * Retrieves packet payload data into the given buffer.
+ *
+ * If a data packet is already available, then it will be returned immediately to the caller.
+ * If no data is available then MICROBIT_INVALID_PARAMETER is returned.
+ *
+ * @param buf A pointer to a valid memory location where the received data is to be stored
+ *
+ * @param len The maximum amount of data that can safely be stored in 'buf'
+ *
+ * @return The length of the data stored, or MICROBIT_INVALID_PARAMETER if no data is available, or the memory regions provided are invalid.
+ */
+ int recv(uint8_t *buf, int len);
+
+ /**
+ * Retreives packet payload data into the given buffer.
+ *
+ * If a data packet is already available, then it will be returned immediately to the caller
+ * in the form of a PacketBuffer.
+ *
+ * @return the data received, or an empty PacketBuffer if no data is available.
+ */
+ PacketBuffer recv();
+
+ /**
+ * Transmits the given buffer onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param buffer The packet contents to transmit.
+ *
+ * @param len The number of bytes to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+ int send(uint8_t *buffer, int len);
+
+ /**
+ * Transmits the given string onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+ int send(PacketBuffer data);
+
+ /**
+ * Transmits the given string onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+ int send(ManagedString data);
+
+ /**
+ * Protocol handler callback. This is called when the radio receives a packet marked as a datagram.
+ *
+ * This function process this packet, and queues it for user reception.
+ */
+ void packetReceived();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitRadioEvent.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,143 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_RADIO_EVENT_H
+#define MICROBIT_RADIO_EVENT_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitRadio.h"
+#include "EventModel.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * This class provides the ability to extend the micro:bit's default EventModel to other micro:bits in the vicinity,
+ * in a very similar way to the MicroBitEventService for BLE interfaces.
+ *
+ * It is envisaged that this would provide the basis for children to experiment with building their own, simple,
+ * custom asynchronous events and actions.
+ *
+ * @note This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+class MicroBitRadioEvent
+{
+ bool suppressForwarding; // A private flag used to prevent event forwarding loops.
+ MicroBitRadio &radio; // A reference to the underlying radio module to use.
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Creates an instance of MicroBitRadioEvent which offers the ability to extend
+ * the micro:bit's default EventModel to other micro:bits in the vicinity.
+ *
+ * @param r The underlying radio module used to send and receive data.
+ */
+ MicroBitRadioEvent(MicroBitRadio &r);
+
+ /**
+ * Associates the given event with the radio channel.
+ *
+ * Once registered, all events matching the given registration sent to this micro:bit's
+ * default EventModel will be automatically retransmitted on the radio.
+ *
+ * @param id The id of the event to register.
+ *
+ * @param value the value of the event to register.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if no default EventModel is available.
+ *
+ * @note The wildcards MICROBIT_ID_ANY and MICROBIT_EVT_ANY can also be in place of the
+ * id and value fields.
+ */
+ int listen(uint16_t id, uint16_t value);
+
+ /**
+ * Associates the given event with the radio channel.
+ *
+ * Once registered, all events matching the given registration sent to the given
+ * EventModel will be automatically retransmitted on the radio.
+ *
+ * @param id The id of the events to register.
+ *
+ * @param value the value of the event to register.
+ *
+ * @param eventBus The EventModel to listen for events on.
+ *
+ * @return MICROBIT_OK on success.
+ *
+ * @note The wildcards MICROBIT_ID_ANY and MICROBIT_EVT_ANY can also be in place of the
+ * id and value fields.
+ */
+ int listen(uint16_t id, uint16_t value, EventModel &eventBus);
+
+ /**
+ * Disassociates the given event with the radio channel.
+ *
+ * @param id The id of the events to deregister.
+ *
+ * @param value The value of the event to deregister.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the default message bus does not exist.
+ *
+ * @note MICROBIT_EVT_ANY can be used to deregister all event values matching the given id.
+ */
+ int ignore(uint16_t id, uint16_t value);
+
+ /**
+ * Disassociates the given events with the radio channel.
+ *
+ * @param id The id of the events to deregister.
+ *
+ * @param value The value of the event to deregister.
+ *
+ * @param eventBus The EventModel to deregister on.
+ *
+ * @return MICROBIT_OK on success.
+ *
+ * @note MICROBIT_EVT_ANY can be used to deregister all event values matching the given id.
+ */
+ int ignore(uint16_t id, uint16_t value, EventModel &eventBus);
+
+ /**
+ * Protocol handler callback. This is called when the radio receives a packet marked as using the event protocol.
+ *
+ * This function process this packet, and fires the event contained inside onto the default EventModel.
+ */
+ void packetReceived();
+
+ /**
+ * Event handler callback. This is called whenever an event is received matching one of those registered through
+ * the registerEvent() method described above. Upon receiving such an event, it is wrapped into
+ * a radio packet and transmitted to any other micro:bits in the same group.
+ */
+ void eventReceived(MicroBitEvent e);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitSerial.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,587 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_SERIAL_H
+#define MICROBIT_SERIAL_H
+
+#include "mbed.h"
+#include "ManagedString.h"
+
+#define MICROBIT_SERIAL_DEFAULT_BAUD_RATE 115200
+#define MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE 20
+
+#define MICROBIT_SERIAL_EVT_DELIM_MATCH 1
+#define MICROBIT_SERIAL_EVT_HEAD_MATCH 2
+#define MICROBIT_SERIAL_EVT_RX_FULL 3
+
+#define MICROBIT_SERIAL_RX_IN_USE 1
+#define MICROBIT_SERIAL_TX_IN_USE 2
+#define MICROBIT_SERIAL_RX_BUFF_INIT 4
+#define MICROBIT_SERIAL_TX_BUFF_INIT 8
+
+
+enum MicroBitSerialMode
+{
+ ASYNC,
+ SYNC_SPINWAIT,
+ SYNC_SLEEP
+};
+
+/**
+ * Class definition for MicroBitSerial.
+ *
+ * Represents an instance of RawSerial which accepts micro:bit specific data types.
+ */
+class MicroBitSerial : public RawSerial
+{
+
+ //holds that state of the mutex locks for all MicroBitSerial instances.
+ static uint8_t status;
+
+ //holds the state of the baudrate for all MicroBitSerial instances.
+ static int baudrate;
+
+ //delimeters used for matching on receive.
+ ManagedString delimeters;
+
+ //a variable used when a user calls the eventAfter() method.
+ int rxBuffHeadMatch;
+
+ uint8_t *rxBuff;
+ uint8_t rxBuffSize;
+ volatile uint16_t rxBuffHead;
+ uint16_t rxBuffTail;
+
+
+ uint8_t *txBuff;
+ uint8_t txBuffSize;
+ uint16_t txBuffHead;
+ volatile uint16_t txBuffTail;
+
+ /**
+ * An internal interrupt callback for MicroBitSerial configured for when a
+ * character is received.
+ *
+ * Each time a character is received fill our circular buffer!
+ */
+ void dataReceived();
+
+ /**
+ * An internal interrupt callback for MicroBitSerial.
+ *
+ * Each time the Serial module's buffer is empty, write a character if we have
+ * characters to write.
+ */
+ void dataWritten();
+
+ /**
+ * An internal method to configure an interrupt on tx buffer and also
+ * a best effort copy operation to move bytes from a user buffer to our txBuff
+ *
+ * @param string a pointer to the first character of the users' buffer.
+ *
+ * @param len the length of the string, and ultimately the maximum number of bytes
+ * that will be copied dependent on the state of txBuff
+ *
+ * @return the number of bytes copied into the buffer.
+ */
+ int setTxInterrupt(uint8_t *string, int len);
+
+ /**
+ * Locks the mutex so that others can't use this serial instance for reception
+ */
+ void lockRx();
+
+ /**
+ * Locks the mutex so that others can't use this serial instance for transmission
+ */
+ void lockTx();
+
+ /**
+ * Unlocks the mutex so that others can use this serial instance for reception
+ */
+ void unlockRx();
+
+ /**
+ * Unlocks the mutex so that others can use this serial instance for transmission
+ */
+ void unlockTx();
+
+ /**
+ * We do not want to always have our buffers initialised, especially if users to not
+ * use them. We only bring them up on demand.
+ */
+ int initialiseRx();
+
+ /**
+ * We do not want to always have our buffers initialised, especially if users to not
+ * use them. We only bring them up on demand.
+ */
+ int initialiseTx();
+
+ /**
+ * An internal method that either spin waits if mode is set to SYNC_SPINWAIT
+ * or puts the fiber to sleep if the mode is set to SYNC_SLEEP
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP
+ */
+ void send(MicroBitSerialMode mode);
+
+ /**
+ * Reads a single character from the rxBuff
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - A character is read from the rxBuff if available, if there
+ * are no characters to be read, a value of zero is returned immediately.
+ *
+ * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there
+ * are no characters to be read, this method will spin
+ * (lock up the processor) until a character is available.
+ *
+ * SYNC_SLEEP - A character is read from the rxBuff if available, if there
+ * are no characters to be read, the calling fiber sleeps
+ * until there is a character available.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return a character from the circular buffer, or MICROBIT_NO_DATA is there
+ * are no characters in the buffer.
+ */
+ int getChar(MicroBitSerialMode mode);
+
+ /**
+ * An internal method that copies values from a circular buffer to a linear buffer.
+ *
+ * @param circularBuff a pointer to the source circular buffer
+ *
+ * @param circularBuffSize the size of the circular buffer
+ *
+ * @param linearBuff a pointer to the destination linear buffer
+ *
+ * @param tailPosition the tail position in the circular buffer you want to copy from
+ *
+ * @param headPosition the head position in the circular buffer you want to copy to
+ *
+ * @note this method assumes that the linear buffer has the appropriate amount of
+ * memory to contain the copy operation
+ */
+ void circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition);
+
+ public:
+
+ /**
+ * Constructor.
+ * Create an instance of MicroBitSerial
+ *
+ * @param tx the Pin to be used for transmission
+ *
+ * @param rx the Pin to be used for receiving data
+ *
+ * @param rxBufferSize the size of the buffer to be used for receiving bytes
+ *
+ * @param txBufferSize the size of the buffer to be used for transmitting bytes
+ *
+ * @code
+ * MicroBitSerial serial(USBTX, USBRX);
+ * @endcode
+ * @note the default baud rate is 115200. More API details can be found:
+ * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/SerialBase.h
+ * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/RawSerial.h
+ *
+ * Buffers aren't allocated until the first send or receive respectively.
+ */
+ MicroBitSerial(PinName tx, PinName rx, uint8_t rxBufferSize = MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE, uint8_t txBufferSize = MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE);
+
+ /**
+ * Sends a single character over the serial line.
+ *
+ * @param c the character to send
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - the character is copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - the character is copied into the txBuff and this method
+ * will spin (lock up the processor) until the character has
+ * been sent.
+ *
+ * SYNC_SLEEP - the character is copied into the txBuff and the fiber sleeps
+ * until the character has been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+ int sendChar(char c, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Sends a ManagedString over the serial line.
+ *
+ * @param s the string to send
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - bytes are copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - bytes are copied into the txBuff and this method
+ * will spin (lock up the processor) until all bytes
+ * have been sent.
+ *
+ * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps
+ * until all bytes have been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+ int send(ManagedString s, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Sends a buffer of known length over the serial line.
+ *
+ * @param buffer a pointer to the first character of the buffer
+ *
+ * @param len the number of bytes that are safely available to read.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - bytes are copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - bytes are copied into the txBuff and this method
+ * will spin (lock up the processor) until all bytes
+ * have been sent.
+ *
+ * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps
+ * until all bytes have been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+ int send(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Reads a single character from the rxBuff
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - A character is read from the rxBuff if available, if there
+ * are no characters to be read, a value of MICROBIT_NO_DATA is returned immediately.
+ *
+ * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there
+ * are no characters to be read, this method will spin
+ * (lock up the processor) until a character is available.
+ *
+ * SYNC_SLEEP - A character is read from the rxBuff if available, if there
+ * are no characters to be read, the calling fiber sleeps
+ * until there is a character available.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return a character, MICROBIT_SERIAL_IN_USE if another fiber is using the serial instance for reception,
+ * MICROBIT_NO_RESOURCES if buffer allocation did not complete successfully, or MICROBIT_NO_DATA if
+ * the rx buffer is empty and the mode given is ASYNC.
+ */
+ int read(MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Reads multiple characters from the rxBuff and returns them as a ManagedString
+ *
+ * @param size the number of characters to read.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, it will read however
+ * many characters there are available.
+ *
+ * SYNC_SPINWAIT - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, this method will spin
+ * (lock up the processor) until the desired number of characters have been read.
+ *
+ * SYNC_SLEEP - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, the calling fiber sleeps
+ * until the desired number of characters have been read.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return A ManagedString, or an empty ManagedString if an error was encountered during the read.
+ */
+ ManagedString read(int size, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Reads multiple characters from the rxBuff and fills a user buffer.
+ *
+ * @param buffer a pointer to a user allocated buffer.
+ *
+ * @param bufferLen the amount of data that can be safely stored
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, it will fill the buffer with however
+ * many characters there are available.
+ *
+ * SYNC_SPINWAIT - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, this method will spin (lock up the processor)
+ * and fill the buffer until the desired number of characters have been read.
+ *
+ * SYNC_SLEEP - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, the calling fiber sleeps
+ * until the desired number of characters have been read.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of characters read, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the instance for receiving.
+ */
+ int read(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * Reads until one of the delimeters matches a character in the rxBuff
+ *
+ * @param delimeters a ManagedString containing a sequence of delimeter characters e.g. ManagedString("\r\n")
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, it will return an Empty ManagedString.
+ *
+ * SYNC_SPINWAIT - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, this method will spin (lock up the processor) until a
+ * received character matches one of the delimeters.
+ *
+ * SYNC_SLEEP - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, the calling fiber sleeps until a character matching one
+ * of the delimeters is seen.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return A ManagedString containing the characters up to a delimeter, or an Empty ManagedString,
+ * if another fiber is currently using this instance for reception.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+ ManagedString readUntil(ManagedString delimeters, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE);
+
+ /**
+ * A wrapper around the inherited method "baud" so we can trap the baud rate
+ * as it changes and restore it if redirect() is called.
+ *
+ * @param baudrate the new baudrate. See:
+ * - https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/serial_api.c
+ * for permitted baud rates.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if baud rate is less than 0, otherwise MICROBIT_OK.
+ *
+ * @note the underlying implementation chooses the first allowable rate at or above that requested.
+ */
+ void baud(int baudrate);
+
+ /**
+ * A way of dynamically configuring the serial instance to use pins other than USBTX and USBRX.
+ *
+ * @param tx the new transmission pin.
+ *
+ * @param rx the new reception pin.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently transmitting or receiving, otherwise MICROBIT_OK.
+ */
+ int redirect(PinName tx, PinName rx);
+
+ /**
+ * Configures an event to be fired after "len" characters.
+ *
+ * @param len the number of characters to wait before triggering the event.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ */
+ int eventAfter(int len, MicroBitSerialMode mode = ASYNC);
+
+ /**
+ * Configures an event to be fired on a match with one of the delimeters.
+ *
+ * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+ int eventOn(ManagedString delimeters, MicroBitSerialMode mode = ASYNC);
+
+ /**
+ * Determines whether there is any data waiting in our Rx buffer.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ *
+ * @note We do not wrap the super's readable() method as we don't want to
+ * interfere with communities that use manual calls to serial.readable().
+ */
+ int isReadable();
+
+ /**
+ * Determines if we have space in our txBuff.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ *
+ * @note We do not wrap the super's writeable() method as we don't want to
+ * interfere with communities that use manual calls to serial.writeable().
+ */
+ int isWriteable();
+
+ /**
+ * Reconfigures the size of our rxBuff
+ *
+ * @param size the new size for our rxBuff
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for reception, otherwise MICROBIT_OK.
+ */
+ int setRxBufferSize(uint8_t size);
+
+ /**
+ * Reconfigures the size of our txBuff
+ *
+ * @param size the new size for our txBuff
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for transmission, otherwise MICROBIT_OK.
+ */
+ int setTxBufferSize(uint8_t size);
+
+ /**
+ * The size of our rx buffer in bytes.
+ *
+ * @return the current size of rxBuff in bytes
+ */
+ int getRxBufferSize();
+
+ /**
+ * The size of our tx buffer in bytes.
+ *
+ * @return the current size of txBuff in bytes
+ */
+ int getTxBufferSize();
+
+ /**
+ * Sets the tail to match the head of our circular buffer for reception,
+ * effectively clearing the reception buffer.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for reception, otherwise MICROBIT_OK.
+ */
+ int clearRxBuffer();
+
+ /**
+ * Sets the tail to match the head of our circular buffer for transmission,
+ * effectively clearing the transmission buffer.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for transmission, otherwise MICROBIT_OK.
+ */
+ int clearTxBuffer();
+
+ /**
+ * The number of bytes currently stored in our rx buffer waiting to be digested,
+ * by the user.
+ *
+ * @return The currently buffered number of bytes in our rxBuff.
+ */
+ int rxBufferedSize();
+
+ /**
+ * The number of bytes currently stored in our tx buffer waiting to be transmitted
+ * by the hardware.
+ *
+ * @return The currently buffered number of bytes in our txBuff.
+ */
+ int txBufferedSize();
+
+ /**
+ * Determines if the serial bus is currently in use by another fiber for reception.
+ *
+ * @return The state of our mutex lock for reception.
+ *
+ * @note Only one fiber can call read at a time
+ */
+ int rxInUse();
+
+ /**
+ * Determines if the serial bus is currently in use by another fiber for transmission.
+ *
+ * @return The state of our mutex lock for transmition.
+ *
+ * @note Only one fiber can call send at a time
+ */
+ int txInUse();
+
+ /**
+ * Detaches a previously configured interrupt
+ *
+ * @param interruptType one of Serial::RxIrq or Serial::TxIrq
+ */
+ void detach(Serial::IrqType interuptType);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitStorage.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,233 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_STORAGE_H
+#define MICROBIT_STORAGE_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "ManagedString.h"
+#include "ErrorNo.h"
+
+#define MICROBIT_STORAGE_MAGIC 0xCAFE
+
+#define MICROBIT_STORAGE_BLOCK_SIZE 48
+#define MICROBIT_STORAGE_KEY_SIZE 16
+#define MICROBIT_STORAGE_VALUE_SIZE MICROBIT_STORAGE_BLOCK_SIZE - MICROBIT_STORAGE_KEY_SIZE
+
+#define MICROBIT_STORAGE_STORE_PAGE_OFFSET 17 //Use the page just above the BLE Bond Data.
+#define MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET 19 //Use the page just below the BLE Bond Data.
+
+struct KeyValuePair
+{
+ uint8_t key[MICROBIT_STORAGE_KEY_SIZE] = { 0 };
+ uint8_t value[MICROBIT_STORAGE_VALUE_SIZE] = { 0 };
+};
+
+struct KeyValueStore
+{
+ uint32_t magic;
+ uint32_t size;
+
+ KeyValueStore(uint32_t magic, uint32_t size)
+ {
+ this->magic = magic;
+ this->size = size;
+ }
+
+ KeyValueStore()
+ {
+ this->magic = 0;
+ this->size = 0;
+ }
+};
+
+
+/**
+ * Class definition for the MicroBitStorage class.
+ * This allows reading and writing of small blocks of data to FLASH memory.
+ *
+ * This class operates as a key value store, it allows the retrieval, addition
+ * and deletion of KeyValuePairs.
+ *
+ * The first 8 bytes are reserved for the KeyValueStore struct which gives core
+ * information such as the number of KeyValuePairs in the store, and whether the
+ * store has been initialised.
+ *
+ * After the KeyValueStore struct, KeyValuePairs are arranged contiguously until
+ * the end of the block used as persistent storage.
+ *
+ * |-------8-------|--------48-------|-----|---------48--------|
+ * | KeyValueStore | KeyValuePair[0] | ... | KeyValuePair[N-1] |
+ * |---------------|-----------------|-----|-------------------|
+ */
+class MicroBitStorage
+{
+ /**
+ * Function for copying words from one location to another.
+ *
+ * @param from the address to copy data from.
+ *
+ * @param to the address to copy the data to.
+ *
+ * @param sizeInWords the number of words to copy
+ */
+ void flashCopy(uint32_t* from, uint32_t* to, int sizeInWords);
+
+ /**
+ * Function for populating the scratch page with a KeyValueStore.
+ *
+ * @param store the KeyValueStore struct to write to the scratch page.
+ */
+ void scratchKeyValueStore(KeyValueStore store);
+
+ /**
+ * Function for populating the scratch page with a KeyValuePair.
+ *
+ * @param pair the KeyValuePair struct to write to the scratch page.
+ *
+ * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer
+ * is used to determine the offset into the scratch page, where the KeyValuePair should
+ * be written.
+ */
+ void scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPointer);
+
+ public:
+
+ /**
+ * Default constructor.
+ *
+ * Creates an instance of MicroBitStorage which acts like a KeyValueStore
+ * that allows the retrieval, addition and deletion of KeyValuePairs.
+ */
+ MicroBitStorage();
+
+ /**
+ * Writes the given number of bytes to the address specified.
+ *
+ * @param buffer the data to write.
+ *
+ * @param address the location in memory to write to.
+ *
+ * @param length the number of bytes to write.
+ *
+ * @note currently not implemented.
+ */
+ int writeBytes(uint8_t *buffer, uint32_t address, int length);
+
+ /**
+ * Method for erasing a page in flash.
+ *
+ * @param page_address Address of the first word in the page to be erased.
+ */
+ void flashPageErase(uint32_t * page_address);
+
+ /**
+ * Method for writing a word of data in flash with a value.
+ *
+ * @param address Address of the word to change.
+ *
+ * @param value Value to be written to flash.
+ */
+ void flashWordWrite(uint32_t * address, uint32_t value);
+
+ /**
+ * Places a given key, and it's corresponding value into flash at the earliest
+ * available point.
+ *
+ * @param key the unique name that should be used as an identifier for the given data.
+ * The key is presumed to be null terminated.
+ *
+ * @param data a pointer to the beginning of the data to be persisted.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
+ */
+ int put(const char* key, uint8_t* data);
+
+ /**
+ * Places a given key, and it's corresponding value into flash at the earliest
+ * available point.
+ *
+ * @param key the unique name that should be used as an identifier for the given data.
+ *
+ * @param data a pointer to the beginning of the data to be persisted.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
+ */
+ int put(ManagedString key, uint8_t* data);
+
+ /**
+ * Retreives a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
+ * NULL if the key was not found in storage.
+ *
+ * @note it is up to the user to free memory after use.
+ */
+ KeyValuePair* get(const char* key);
+
+ /**
+ * Retreives a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
+ * NULL if the key was not found in storage.
+ *
+ * @note it is up to the user to free memory after use.
+ */
+ KeyValuePair* get(ManagedString key);
+
+ /**
+ * Removes a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
+ * was not found in flash.
+ */
+ int remove(const char* key);
+
+ /**
+ * Removes a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
+ * was not found in flash.
+ */
+ int remove(ManagedString key);
+
+ /**
+ * The size of the flash based KeyValueStore.
+ *
+ * @return the number of entries in the key value store
+ */
+ int size();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/drivers/MicroBitThermometer.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,180 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_THERMOMETER_H
+#define MICROBIT_THERMOMETER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitComponent.h"
+#include "MicroBitStorage.h"
+
+#define MICROBIT_THERMOMETER_PERIOD 1000
+
+
+#define MAG3110_SAMPLE_RATES 11
+
+/*
+ * Temperature events
+ */
+#define MICROBIT_THERMOMETER_EVT_UPDATE 1
+
+#define MICROBIT_THERMOMETER_ADDED_TO_IDLE 2
+
+/**
+ * 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;
+ int16_t offset;
+ MicroBitStorage* storage;
+
+ public:
+
+ /**
+ * Constructor.
+ * Create new MicroBitThermometer that gives an indication of the current temperature.
+ *
+ * @param _storage an instance of MicroBitStorage used to persist temperature offset data
+ *
+ * @param id the unique EventModel id of this component. Defaults to MICROBIT_ID_THERMOMETER.
+ *
+ * @code
+ * MicroBitStorage storage;
+ * MicroBitThermometer thermometer(storage);
+ * @endcode
+ */
+ MicroBitThermometer(MicroBitStorage& _storage, uint16_t id = MICROBIT_ID_THERMOMETER);
+
+ /**
+ * Constructor.
+ * Create new MicroBitThermometer that gives an indication of the current temperature.
+ *
+ * @param id the unique EventModel id of this component. Defaults to MICROBIT_ID_THERMOMETER.
+ *
+ * @code
+ * MicroBitThermometer thermometer;
+ * @endcode
+ */
+ MicroBitThermometer(uint16_t id = MICROBIT_ID_THERMOMETER);
+
+ /**
+ * Set the sample rate at which the temperatureis read (in ms).
+ *
+ * The default sample period is 1 second.
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @note the temperature is always read in the background, and is only updated
+ * when the processor is idle, or when the temperature is explicitly read.
+ */
+ void setPeriod(int period);
+
+ /**
+ * Reads the currently configured sample rate of the thermometer.
+ *
+ * @return The time between samples, in milliseconds.
+ */
+ int getPeriod();
+
+ /**
+ * Set the value that is used to offset the raw silicon temperature.
+ *
+ * @param offset the offset for the silicon temperature
+ *
+ * @return MICROBIT_OK on success
+ */
+ int setOffset(int offset);
+
+ /**
+ * Retreive the value that is used to offset the raw silicon temperature.
+ *
+ * @return the current offset.
+ */
+ int getOffset();
+
+ /**
+ * This member function fetches the raw silicon temperature, and calculates
+ * the value used to offset the raw silicon temperature based on a given temperature.
+ *
+ * @param calibrationTemp the temperature used to calculate the raw silicon temperature
+ * offset.
+ *
+ * @return MICROBIT_OK on success
+ */
+ int setCalibration(int calibrationTemp);
+
+ /**
+ * Gets the current temperature of the microbit.
+ *
+ * @return the current temperature, in degrees celsius.
+ *
+ * @code
+ * thermometer.getTemperature();
+ * @endcode
+ */
+ int getTemperature();
+
+ /**
+ * Updates the temperature sample of this instance of MicroBitThermometer
+ * only if isSampleNeeded() indicates that an update is required.
+ *
+ * This call also will add the thermometer to fiber components to receive
+ * periodic callbacks.
+ *
+ * @return MICROBIT_OK on success.
+ */
+ int updateSample();
+
+ /**
+ * Periodic callback from MicroBit idle thread.
+ */
+ virtual void idleTick();
+
+ /**
+ * Indicates if we'd like some processor time to sense the temperature.
+ *
+ * @returns 1 if we'd like some processor time, 0 otherwise.
+ */
+ virtual int isIdleCallbackNeeded();
+
+ private:
+
+ /**
+ * Determines if we're due to take another temperature reading
+ *
+ * @return 1 if we're due to take a temperature reading, 0 otherwise.
+ */
+ int isSampleNeeded();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/ManagedString.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,399 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MANAGED_STRING_H
+#define MANAGED_STRING_H
+
+#include "MicroBitConfig.h"
+#include "RefCounted.h"
+#include "PacketBuffer.h"
+
+struct StringData : RefCounted
+{
+ uint16_t len;
+ char data[0];
+};
+
+
+/**
+ * Class definition for a ManagedString.
+ *
+ * Uses basic reference counting to implement a copy-assignable, immutable string.
+ *
+ * This maps closely to the constructs found in many high level application languages,
+ * such as Touch Develop.
+ *
+ * Written from first principles here, for several reasons:
+ * 1) std::shared_ptr is not yet availiable on the ARMCC compiler
+ *
+ * 2) to reduce memory footprint - we don't need many of the other features in the std library
+ *
+ * 3) it makes an interesting case study for anyone interested in seeing how it works!
+ *
+ * 4) we need explicit reference counting to inter-op with low-level application langauge runtimes.
+ *
+ * 5) the reference counting needs to also work for read-only, flash-resident strings
+ */
+class ManagedString
+{
+ // StringData contains the reference count, the length, follwed by char[] data, all in one block.
+ // When referece count is 0xffff, then it's read only and should not be counted.
+ // Otherwise the block was malloc()ed.
+ // We control access to this to proide immutability and reference counting.
+ StringData *ptr;
+
+ public:
+
+ /**
+ * Constructor.
+ * Create a managed string from a specially prepared string literal.
+ *
+ * @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned.
+ *
+ * @code
+ * static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello";
+ * ManagedString s((StringData*)(void*)hello);
+ * @endcode
+ */
+ ManagedString(StringData *ptr);
+
+ /**
+ * Get current ptr, do not decr() it, and set the current instance to empty string.
+ *
+ * This is to be used by specialized runtimes which pass StringData around.
+ */
+ StringData *leakData();
+
+ /**
+ * Constructor.
+ *
+ * Create a managed string from a pointer to an 8-bit character buffer.
+ *
+ * The buffer is copied to ensure safe memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str The character array on which to base the new ManagedString.
+ *
+ * @code
+ * ManagedString s("abcdefg");
+ * @endcode
+ */
+ ManagedString(const char *str);
+
+ /**
+ * Constructor.
+ *
+ * Create a managed string from a given integer.
+ *
+ * @param value The integer from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s(20);
+ * @endcode
+ */
+ ManagedString(const int value);
+
+
+ /**
+ * Constructor.
+ * Create a managed string from a given char.
+ *
+ * @param value The character from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s('a');
+ * @endcode
+ */
+ ManagedString(const char value);
+
+ /**
+ * Constructor.
+ * Create a ManagedString from a PacketBuffer. All bytes in the
+ * PacketBuffer are added to the ManagedString.
+ *
+ * @param buffer The PacktBuffer from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s = radio.datagram.recv();
+ * @endcode
+ */
+ ManagedString(PacketBuffer buffer);
+
+ /**
+ * Constructor.
+ * Create a ManagedString from a pointer to an 8-bit character buffer of a given length.
+ *
+ * The buffer is copied to ensure sane memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str The character array on which to base the new ManagedString.
+ *
+ * @param length The length of the character array
+ *
+ * @code
+ * ManagedString s("abcdefg",7);
+ * @endcode
+ */
+ ManagedString(const char *str, const int16_t length);
+
+ /**
+ * Copy constructor.
+ * Makes a new ManagedString identical to the one supplied.
+ *
+ * Shares the character buffer and reference count with the supplied ManagedString.
+ *
+ * @param s The ManagedString to copy.
+ *
+ * @code
+ * ManagedString s("abcdefg");
+ * ManagedString p(s);
+ * @endcode
+ */
+ ManagedString(const ManagedString &s);
+
+ /**
+ * Default constructor.
+ *
+ * Create an empty ManagedString.
+ *
+ * @code
+ * ManagedString s();
+ * @endcode
+ */
+ ManagedString();
+
+ /**
+ * Destructor.
+ *
+ * Free this ManagedString, and decrement the reference count to the
+ * internal character buffer.
+ *
+ * If we're holding the last reference, also free the character buffer.
+ */
+ ~ManagedString();
+
+ /**
+ * Copy assign operation.
+ *
+ * Called when one ManagedString is assigned the value of another.
+ *
+ * If the ManagedString being assigned is already refering to a character buffer,
+ * decrement the reference count and free up the buffer as necessary.
+ *
+ * Then, update our character buffer to refer to that of the supplied ManagedString,
+ * and increase its reference count.
+ *
+ * @param s The ManagedString to copy.
+ *
+ * @code
+ * ManagedString s("abcd");
+ * ManagedString p("efgh");
+ * p = s // p now points to s, s' ref is incremented
+ * @endcode
+ */
+ ManagedString& operator = (const ManagedString& s);
+
+ /**
+ * Equality operation.
+ *
+ * Called when one ManagedString is tested to be equal to another using the '==' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ * ManagedString p("efgh");
+ *
+ * if(p == s)
+ * display.scroll("We are the same!");
+ * else
+ * display.scroll("We are different!"); //p is not equal to s - this will be called
+ * @endcode
+ */
+ bool operator== (const ManagedString& s);
+
+ /**
+ * Inequality operation.
+ *
+ * Called when one ManagedString is tested to be less than another using the '<' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is alphabetically less than to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("a");
+ * ManagedString p("b");
+ *
+ * if(s < p)
+ * display.scroll("a is before b!"); //a is before b
+ * else
+ * display.scroll("b is before a!");
+ * @endcode
+ */
+ bool operator< (const ManagedString& s);
+
+ /**
+ * Inequality operation.
+ *
+ * Called when one ManagedString is tested to be greater than another using the '>' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is alphabetically greater than to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("a");
+ * ManagedString p("b");
+ *
+ * if(p>a)
+ * display.scroll("b is after a!"); //b is after a
+ * else
+ * display.scroll("a is after b!");
+ * @endcode
+ */
+ bool operator> (const ManagedString& s);
+
+ /**
+ * Extracts a ManagedString from this string, at the position provided.
+ *
+ * @param start The index of the first character to extract, indexed from zero.
+ *
+ * @param length The number of characters to extract from the start position
+ *
+ * @return a ManagedString representing the requested substring.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcdefg");
+ *
+ * display.scroll(s.substring(0,2)) // displays "ab"
+ * @endcode
+ */
+ ManagedString substring(int16_t start, int16_t length);
+
+ /**
+ * Concatenates this string with the one provided.
+ *
+ * @param s The ManagedString to concatenate.
+ *
+ * @return a new ManagedString representing the joined strings.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ * ManagedString p("efgh")
+ *
+ * display.scroll(s + p) // scrolls "abcdefgh"
+ * @endcode
+ */
+ ManagedString operator+ (ManagedString& s);
+
+ /**
+ * Provides a character value at a given position in the string, indexed from zero.
+ *
+ * @param index The position of the character to return.
+ *
+ * @return the character at posisiton index, zero if index is invalid.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ *
+ * display.scroll(s.charAt(1)) // scrolls "b"
+ * @endcode
+ */
+ char charAt(int16_t index);
+
+
+ /**
+ * Provides an immutable 8 bit wide character buffer representing this string.
+ *
+ * @return a pointer to the character buffer.
+ */
+ const char *toCharArray() const
+ {
+ return ptr->data;
+ }
+
+ /**
+ * Determines the length of this ManagedString in characters.
+ *
+ * @return the length of the string in characters.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ *
+ * display.scroll(s.length()) // scrolls "4"
+ * @endcode
+ */
+ int16_t length() const
+ {
+ return ptr->len;
+ }
+
+ /**
+ * Empty String constant
+ */
+ static ManagedString EmptyString;
+
+ private:
+
+ /**
+ * Internal constructor helper.
+ *
+ * Configures this ManagedString to refer to the static EmptyString
+ */
+ void initEmpty();
+
+ /**
+ * Internal constructor helper.
+ *
+ * Creates this ManagedString based on a given null terminated char array.
+ */
+ void initString(const char *str);
+
+ /**
+ * Private Constructor.
+ *
+ * Create a managed string based on a concat of two strings.
+ * The buffer is copied to ensure sane memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str1 The first string on which to base the new ManagedString.
+ *
+ * @param str2 The second string on which to base the new ManagedString.
+ */
+ ManagedString(const ManagedString &s1, const ManagedString &s2);
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/ManagedType.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,279 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_MANAGED_TYPE_H
+#define MICROBIT_MANAGED_TYPE_H
+
+#include "MicroBitConfig.h"
+
+/**
+ * Class definition for a Generic Managed Type.
+ *
+ * Represents a reference counted object.
+ *
+ * @note When the destructor is called, delete is called on the object - implicitly calling the given objects destructor.
+ */
+template <class T>
+class ManagedType
+{
+protected:
+
+ int *ref;
+
+public:
+
+ T *object;
+
+ /**
+ * Constructor for the managed type, given a class space T.
+ *
+ * @param object the object that you would like to be ref counted - of class T
+ *
+ * @code
+ * T object = new T();
+ * ManagedType<T> mt(t);
+ * @endcode
+ */
+ ManagedType(T* object);
+
+ /**
+ * Default constructor for the managed type, given a class space T.
+ */
+ ManagedType();
+
+ /**
+ * Copy constructor for the managed type, given a class space T.
+ *
+ * @param t another managed type instance of class type T.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1(mt);
+ * @endcode
+ */
+ ManagedType(const ManagedType<T> &t);
+
+ /**
+ * Destructor for the managed type, given a class space T.
+ */
+ ~ManagedType();
+
+ /**
+ * Copy-assign member function for the managed type, given a class space.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1 = mt;
+ * @endcode
+ */
+ ManagedType<T>& operator = (const ManagedType<T>&i);
+
+ /**
+ * Returns the references to this ManagedType.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1(mt);
+ *
+ * mt.getReferences // this will be 2!
+ * @endcode
+ */
+ int getReferences();
+
+ /**
+ * De-reference operator overload. This makes modifying ref-counted POD
+ * easier.
+ *
+ * @code
+ * ManagedType<int> x = 0;
+ * *x = 1; // mutates the ref-counted integer
+ * @endcode
+ */
+ T& operator*() {
+ return *object;
+ }
+
+ /**
+ * Method call operator overload. This forwards the call to the underlying
+ * object.
+ *
+ * @code
+ * ManagedType<T> x = new T();
+ * x->m(); // resolves to T::m
+ */
+ T* operator->() {
+ if (object == NULL)
+ microbit_panic(MICROBIT_NULL_DEREFERENCE);
+ return object;
+ }
+
+ /**
+ * Shorthand for `x.operator->()`
+ */
+ T* get() {
+ return object;
+ }
+
+ /**
+ * A simple inequality overload to compare two ManagedType instances.
+ */
+ bool operator!=(const ManagedType<T>& x) {
+ return !(this == x);
+ }
+
+ /**
+ * A simple equality overload to compare two ManagedType instances.
+ */
+ bool operator==(const ManagedType<T>& x) {
+ return this->object == x.object;
+ }
+};
+
+/**
+ * Constructor for the managed type, given a class space T.
+ *
+ * @param object the object that you would like to be ref counted - of class T
+ *
+ * @code
+ * T object = new T();
+ * ManagedType<T> mt(t);
+ * @endcode
+ */
+template<typename T>
+ManagedType<T>::ManagedType(T* object)
+{
+ this->object = object;
+ ref = (int *)malloc(sizeof(int));
+ *ref = 1;
+}
+
+/**
+ * Default constructor for the managed type, given a class space T.
+ */
+template<typename T>
+ManagedType<T>::ManagedType()
+{
+ this->object = NULL;
+ ref = (int *)malloc(sizeof(int));
+ *ref = 0;
+}
+
+/**
+ * Copy constructor for the managed type, given a class space T.
+ *
+ * @param t another managed type instance of class type T.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1(mt);
+ * @endcode
+ */
+template<typename T>
+ManagedType<T>::ManagedType(const ManagedType<T> &t)
+{
+ this->object = t.object;
+ this->ref = t.ref;
+ (*ref)++;
+}
+
+/**
+ * Destructor for the managed type, given a class space T.
+ */
+template<typename T>
+ManagedType<T>::~ManagedType()
+{
+ // Special case - we were created using a default constructor, and never assigned a value.
+ if (*ref == 0)
+ {
+ // Simply destroy our reference counter and we're done.
+ free(ref);
+ }
+
+ // Normal case - we have a valid piece of data.
+ // Decrement our reference counter and free all allocated memory if we're deleting the last reference.
+ else if (--(*ref) == 0)
+ {
+ delete object;
+ free(ref);
+ }
+}
+
+/**
+ * Copy-assign member function for the managed type, given a class space.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1 = mt;
+ * @endcode
+ */
+template<typename T>
+ManagedType<T>& ManagedType<T>::operator = (const ManagedType<T>&t)
+{
+ if (this == &t)
+ return *this;
+
+ // Special case - we were created using a default constructor, and never assigned a value.
+ if (*ref == 0)
+ {
+ // Simply destroy our reference counter, as we're about to adopt another.
+ free(ref);
+ }
+
+ else if (--(*ref) == 0)
+ {
+ delete object;
+ free(ref);
+ }
+
+ object = t.object;
+ ref = t.ref;
+
+ (*ref)++;
+
+ return *this;
+}
+
+/**
+ * Returns the references to this ManagedType.
+ *
+ * @code
+ * T* object = new T();
+ * ManagedType<T> mt(t);
+ * ManagedType<T> mt1(mt);
+ *
+ * mt.getReferences // this will be 2!
+ * @endcode
+ */
+template<typename T>
+int ManagedType<T>::getReferences()
+{
+ return (*ref);
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/Matrix4.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,202 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_MATRIX4_H
+#define MICROBIT_MATRIX4_H
+
+#include "MicroBitConfig.h"
+
+/**
+* Class definition for a simple matrix, that is optimised for nx4 or 4xn matrices.
+*
+* This class is heavily optimised for these commonly used matrices as used in 3D geometry.
+* Whilst this class does support basic operations on matrices of any dimension, it is not intended as a
+* general purpose matrix class as inversion operations are only provided for 4x4 matrices.
+* For programmers needing more flexible Matrix support, the Matrix and MatrixMath classes from
+* Ernsesto Palacios provide a good basis:
+*
+* https://developer.mbed.org/cookbook/MatrixClass
+* https://developer.mbed.org/users/Yo_Robot/code/MatrixMath/
+*/
+class Matrix4
+{
+ float *data; // Linear buffer representing the matrix.
+ int rows; // The number of rows in the matrix.
+ int cols; // The number of columns in the matrix.
+
+public:
+
+ /**
+ * Constructor.
+ * Create a matrix of the given size.
+ *
+ * @param rows the number of rows in the matrix to be created.
+ *
+ * @param cols the number of columns in the matrix to be created.
+ *
+ * @code
+ * Matrix4(10, 4); // Creates a Matrix with 10 rows and 4 columns.
+ * @endcode
+ */
+ Matrix4(int rows, int cols);
+
+ /**
+ * Constructor.
+ * Create a matrix that is an identical copy of the given matrix.
+ *
+ * @param matrix The matrix to copy.
+ *
+ * @code
+ * Matrix newMatrix(matrix); .
+ * @endcode
+ */
+ Matrix4(const Matrix4 &matrix);
+
+ /**
+ * Determines the number of columns in this matrix.
+ *
+ * @return The number of columns in the matrix.
+ *
+ * @code
+ * int c = matrix.width();
+ * @endcode
+ */
+ int width();
+
+ /**
+ * Determines the number of rows in this matrix.
+ *
+ * @return The number of rows in the matrix.
+ *
+ * @code
+ * int r = matrix.height();
+ * @endcode
+ */
+ int height();
+
+ /**
+ * Reads the matrix element at the given position.
+ *
+ * @param row The row of the element to read.
+ *
+ * @param col The column of the element to read.
+ *
+ * @return The value of the matrix element at the given position. 0 is returned if the given index is out of range.
+ *
+ * @code
+ * float v = matrix.get(1,2);
+ * @endcode
+ */
+ float get(int row, int col);
+
+ /**
+ * Writes the matrix element at the given position.
+ *
+ * @param row The row of the element to write.
+ *
+ * @param col The column of the element to write.
+ *
+ * @param v The new value of the element.
+ *
+ * @code
+ * matrix.set(1,2,42.0);
+ * @endcode
+ */
+ void set(int row, int col, float v);
+
+ /**
+ * Transposes this matrix.
+ *
+ * @return the resultant matrix.
+ *
+ * @code
+ * matrix.transpose();
+ * @endcode
+ */
+ Matrix4 transpose();
+
+ /**
+ * Multiplies this matrix with the given matrix (if possible).
+ *
+ * @param matrix the matrix to multiply this matrix's values against.
+ *
+ * @param transpose Transpose the matrices before multiplication. Defaults to false.
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.multiply(matrixB);
+ * @endcode
+ */
+ Matrix4 multiply(Matrix4 &matrix, bool transpose = false);
+
+ /**
+ * Multiplies the transpose of this matrix with the given matrix (if possible).
+ *
+ * @param matrix the matrix to multiply this matrix's values against.
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.multiplyT(matrixB);
+ * @endcode
+ */
+ Matrix4 multiplyT(Matrix4 &matrix);
+
+ /**
+ * Performs an optimised inversion of a 4x4 matrix.
+ * Only 4x4 matrices are supported by this operation.
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.invert();
+ * @endcode
+ */
+ Matrix4 invert();
+
+ /**
+ * Destructor.
+ *
+ * Frees any memory consumed by this Matrix4 instance.
+ */
+ ~Matrix4();
+};
+
+/**
+ * Multiplies the transpose of this matrix with the given matrix (if possible).
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.multiplyT(matrixB);
+ * @endcode
+ */
+inline Matrix4 Matrix4::multiplyT(Matrix4 &matrix)
+{
+ return multiply(matrix, true);
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/MicroBitCoordinateSystem.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,67 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_COORDINATE_SYSTEM_H
+#define MICROBIT_COORDINATE_SYSTEM_H
+#include "MicroBitConfig.h"
+
+/**
+ * Co-ordinate systems that can be used.
+ * RAW: Unaltered data. Data will be returned directly from the accelerometer.
+ *
+ * SIMPLE_CARTESIAN: Data will be returned based on an easy to understand alignment, consistent with the cartesian system taught in schools.
+ * When held upright, facing the user:
+ *
+ * /
+ * +--------------------+ z
+ * | |
+ * | ..... |
+ * | * ..... * |
+ * ^ | ..... |
+ * | | |
+ * y +--------------------+ x-->
+ *
+ *
+ * NORTH_EAST_DOWN: Data will be returned based on the industry convention of the North East Down (NED) system.
+ * When held upright, facing the user:
+ *
+ * z
+ * +--------------------+ /
+ * | |
+ * | ..... |
+ * | * ..... * |
+ * ^ | ..... |
+ * | | |
+ * x +--------------------+ y-->
+ *
+ */
+enum MicroBitCoordinateSystem
+{
+ RAW,
+ SIMPLE_CARTESIAN,
+ NORTH_EAST_DOWN
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/MicroBitEvent.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,106 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_EVENT_H
+#define MICROBIT_EVENT_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+
+// Wildcard event codes
+#define MICROBIT_ID_ANY 0
+#define MICROBIT_EVT_ANY 0
+
+enum MicroBitEventLaunchMode
+{
+ CREATE_ONLY,
+ CREATE_AND_FIRE
+};
+
+#define MICROBIT_EVENT_DEFAULT_LAUNCH_MODE CREATE_AND_FIRE
+
+/**
+ * Class definition for a MicroBitEvent
+ *
+ * It represents a common event that is generated by the various components on the micro:bit.
+ */
+class MicroBitEvent
+{
+ public:
+
+ uint16_t source; // ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A.
+ uint16_t value; // Component specific code indicating the cause of the event.
+ uint32_t timestamp; // Time at which the event was generated. ms since power on.
+
+ /**
+ * Constructor.
+ *
+ * @param src The id of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A.
+ *
+ * @param value A component specific code indicating the cause of the event.
+ *
+ * @param mode Optional definition of how the event should be processed after construction (if at all):
+ * CREATE_ONLY: MicroBitEvent is initialised, and no further processing takes place.
+ * CREATE_AND_FIRE: MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!).
+ *
+ * @code
+ * // Create and launch an event using the default configuration
+ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * // Create an event only, do not fire onto an EventModel.
+ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,CREATE_AND_FIRE);
+ * @endcode
+ */
+ MicroBitEvent(uint16_t source, uint16_t value, MicroBitEventLaunchMode mode = MICROBIT_EVENT_DEFAULT_LAUNCH_MODE);
+
+ /**
+ * Default constructor - initialises all values, and sets timestamp to the current time.
+ */
+ MicroBitEvent();
+
+ /**
+ * Fires this MicroBitEvent onto the Default EventModel, or a custom one!
+ */
+ void fire();
+};
+
+/**
+ * Enclosing class to hold a chain of events.
+ */
+struct MicroBitEventQueueItem
+{
+ MicroBitEvent evt;
+ MicroBitEventQueueItem *next;
+
+ /**
+ * Constructor.
+ * Create a new MicroBitEventQueueItem.
+ *
+ * @param evt The event to be queued.
+ */
+ MicroBitEventQueueItem(MicroBitEvent evt);
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/MicroBitImage.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,480 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_IMAGE_H
+#define MICROBIT_IMAGE_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "ManagedString.h"
+#include "RefCounted.h"
+
+struct ImageData : RefCounted
+{
+ uint16_t width; // Width in pixels
+ uint16_t height; // Height in pixels
+ uint8_t data[0]; // 2D array representing the bitmap image
+};
+
+/**
+ * Class definition for a MicroBitImage.
+ *
+ * An MicroBitImage is a simple bitmap representation of an image.
+ * n.b. This is a mutable, managed type.
+ */
+class MicroBitImage
+{
+ ImageData *ptr; // Pointer to payload data
+
+
+ /**
+ * Internal constructor which provides sanity checking and initialises class properties.
+ *
+ * @param x the width of the image
+ *
+ * @param y the height of the image
+ *
+ * @param bitmap an array of integers that make up an image.
+ */
+ void init(const int16_t x, const int16_t y, const uint8_t *bitmap);
+
+ /**
+ * Internal constructor which defaults to the Empty Image instance variable
+ */
+ void init_empty();
+
+ public:
+ static MicroBitImage EmptyImage; // Shared representation of a null image.
+
+ /**
+ * Get current ptr, do not decr() it, and set the current instance to empty image.
+ *
+ * This is to be used by specialized runtimes which pass ImageData around.
+ */
+ ImageData *leakData();
+
+ /**
+ * Return a 2D array representing the bitmap image.
+ */
+ uint8_t *getBitmap()
+ {
+ return ptr->data;
+ }
+
+ /**
+ * Constructor.
+ * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr().
+ *
+ * @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned.
+ *
+ * @code
+ * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i((ImageData*)(void*)heart);
+ * @endcode
+ */
+ MicroBitImage(ImageData *ptr);
+
+ /**
+ * Default Constructor.
+ * Creates a new reference to the empty MicroBitImage bitmap
+ *
+ * @code
+ * MicroBitImage i(); //an empty image instance
+ * @endcode
+ */
+ MicroBitImage();
+
+
+ /**
+ * Copy Constructor.
+ * Add ourselves as a reference to an existing MicroBitImage.
+ *
+ * @param image The MicroBitImage to reference.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n");
+ * MicroBitImage i2(i); //points to i
+ * @endcode
+ */
+ MicroBitImage(const MicroBitImage &image);
+
+ /**
+ * Constructor.
+ * Create a blank bitmap representation of a given size.
+ *
+ * @param s A text based representation of the image given whitespace delimited numeric values.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * @endcode
+ */
+ explicit MicroBitImage(const char *s);
+
+ /**
+ * Constructor.
+ * Create a blank bitmap representation of a given size.
+ *
+ * @param x the width of the image.
+ *
+ * @param y the height of the image.
+ *
+ * Bitmap buffer is linear, with 8 bits per pixel, row by row,
+ * top to bottom with no word alignment. Stride is therefore the image width in pixels.
+ * in where w and h are width and height respectively, the layout is therefore:
+ *
+ * |[0,0]...[w,o][1,0]...[w,1] ... [[w,h]
+ *
+ * A copy of the image is made in RAM, as images are mutable.
+ */
+ MicroBitImage(const int16_t x, const int16_t y);
+
+ /**
+ * Constructor.
+ * Create a bitmap representation of a given size, based on a given buffer.
+ *
+ * @param x the width of the image.
+ *
+ * @param y the height of the image.
+ *
+ * @param bitmap a 2D array representing the image.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * @endcode
+ */
+ MicroBitImage(const int16_t x, const int16_t y, const uint8_t *bitmap);
+
+ /**
+ * Destructor.
+ *
+ * Removes buffer resources held by the instance.
+ */
+ ~MicroBitImage();
+
+ /**
+ * Copy assign operation.
+ *
+ * Called when one MicroBitImage is assigned the value of another using the '=' operator.
+ *
+ * Decrement our reference count and free up the buffer as necessary.
+ *
+ * Then, update our buffer to refer to that of the supplied MicroBitImage,
+ * and increase its reference count.
+ *
+ * @param s The MicroBitImage to reference.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * MicroBitImage i1();
+ * i1 = i; // i1 now references i
+ * @endcode
+ */
+ MicroBitImage& operator = (const MicroBitImage& i);
+
+
+ /**
+ * Equality operation.
+ *
+ * Called when one MicroBitImage is tested to be equal to another using the '==' operator.
+ *
+ * @param i The MicroBitImage to test ourselves against.
+ *
+ * @return true if this MicroBitImage is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * MicroBitImage i();
+ * MicroBitImage i1();
+ *
+ * if(i == i1) //will be true
+ * display.scroll("true");
+ * @endcode
+ */
+ bool operator== (const MicroBitImage& i);
+
+ /**
+ * Resets all pixels in this image to 0.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.clear();
+ * @endcode
+ */
+ void clear();
+
+ /**
+ * Sets the pixel at the given co-ordinates to a given value.
+ *
+ * @param x The co-ordinate of the pixel to change.
+ *
+ * @param y The co-ordinate of the pixel to change.
+ *
+ * @param value The new value of the pixel (the brightness level 0-255)
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.setPixelValue(0,0,255);
+ * @endcode
+ *
+ * @note all coordinates originate from the top left of an image.
+ */
+ int setPixelValue(int16_t x , int16_t y, uint8_t value);
+
+ /**
+ * Retreives the value of a given pixel.
+ *
+ * @param x The x co-ordinate of the pixel to read. Must be within the dimensions of the image.
+ *
+ * @param y The y co-ordinate of the pixel to read. Must be within the dimensions of the image.
+ *
+ * @return The value assigned to the given pixel location (the brightness level 0-255), or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.getPixelValue(0,0); //should be 0;
+ * @endcode
+ */
+ int getPixelValue(int16_t x , int16_t y);
+
+ /**
+ * Replaces the content of this image with that of a given 2D array representing
+ * the image.
+ *
+ * @param x the width of the image. Must be within the dimensions of the image.
+ *
+ * @param y the width of the image. Must be within the dimensions of the image.
+ *
+ * @param bitmap a 2D array representing the image.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i();
+ * i.printImage(0,0,heart);
+ * @endcode
+ *
+ * @note all coordinates originate from the top left of an image.
+ */
+ int printImage(int16_t x, int16_t y, const uint8_t *bitmap);
+
+ /**
+ * Pastes a given bitmap at the given co-ordinates.
+ *
+ * Any pixels in the relvant area of this image are replaced.
+ *
+ * @param image The MicroBitImage to paste.
+ *
+ * @param x The leftmost X co-ordinate in this image where the given image should be pasted. Defaults to 0.
+ *
+ * @param y The uppermost Y co-ordinate in this image where the given image should be pasted. Defaults to 0.
+ *
+ * @param alpha set to 1 if transparency clear pixels in given image should be treated as transparent. Set to 0 otherwise. Defaults to 0.
+ *
+ * @return The number of pixels written.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.paste(i, -5, 0); // a small heart
+ * @endcode
+ */
+ int paste(const MicroBitImage &image, int16_t x = 0, int16_t y = 0, uint8_t alpha = 0);
+
+ /**
+ * Prints a character to the display at the given location
+ *
+ * @param c The character to display.
+ *
+ * @param x The x co-ordinate of on the image to place the top left of the character. Defaults to 0.
+ *
+ * @param y The y co-ordinate of on the image to place the top left of the character. Defaults to 0.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i(5,5);
+ * i.print('a');
+ * @endcode
+ */
+ int print(char c, int16_t x = 0, int16_t y = 0);
+
+ /**
+ * Shifts the pixels in this Image a given number of pixels to the left.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.shiftLeft(5); // a small heart
+ * @endcode
+ */
+ int shiftLeft(int16_t n);
+
+ /**
+ * Shifts the pixels in this Image a given number of pixels to the right.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.shiftLeft(5); // a small heart
+ * i.shiftRight(5); // a big heart
+ * @endcode
+ */
+ int shiftRight(int16_t n);
+
+ /**
+ * Shifts the pixels in this Image a given number of pixels to upward.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.shiftUp(1);
+ * @endcode
+ */
+ int shiftUp(int16_t n);
+
+ /**
+ * Shifts the pixels in this Image a given number of pixels to downward.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.shiftDown(1);
+ * @endcode
+ */
+ int shiftDown(int16_t n);
+
+ /**
+ * Gets the width of this image.
+ *
+ * @return The width of this image.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.getWidth(); // equals 10...
+ * @endcode
+ */
+ int getWidth() const
+ {
+ return ptr->width;
+ }
+
+ /**
+ * Gets the height of this image.
+ *
+ * @return The height of this image.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.getHeight(); // equals 5...
+ * @endcode
+ */
+ int getHeight() const
+ {
+ return ptr->height;
+ }
+
+ /**
+ * Gets number of bytes in the bitmap, ie., width * height.
+ *
+ * @return The size of the bitmap.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.getSize(); // equals 50...
+ * @endcode
+ */
+ int getSize() const
+ {
+ return ptr->width * ptr->height;
+ }
+
+ /**
+ * Converts the bitmap to a csv ManagedString.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * uBit.serial.printString(i.toString()); // "0,1,0,1,0,0,0,0,0,0\n..."
+ * @endcode
+ */
+ ManagedString toString();
+
+ /**
+ * Crops the image to the given dimensions.
+ *
+ * @param startx the location to start the crop in the x-axis
+ *
+ * @param starty the location to start the crop in the y-axis
+ *
+ * @param width the width of the desired cropped region
+ *
+ * @param height the height of the desired cropped region
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.crop(0,0,2,2).toString() // "0,1\n1,1\n"
+ * @endcode
+ */
+ MicroBitImage crop(int startx, int starty, int finx, int finy);
+
+ /**
+ * Check if image is read-only (i.e., residing in flash).
+ */
+ bool isReadOnly();
+
+ /**
+ * Create a copy of the image bitmap. Used particularly, when isReadOnly() is true.
+ *
+ * @return an instance of MicroBitImage which can be modified independently of the current instance
+ */
+ MicroBitImage clone();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/PacketBuffer.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,269 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef MICROBIT_PACKET_BUFFER_H
+#define MICROBIT_PACKET_BUFFER_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "RefCounted.h"
+
+struct PacketData : RefCounted
+{
+ uint16_t rssi; // The radio signal strength this packet was received.
+ uint8_t length; // The length of the payload in bytes
+ uint8_t payload[0]; // User / higher layer protocol data
+};
+
+/**
+ * Class definition for a PacketBuffer.
+ * A PacketBuffer holds a series of bytes that can be sent or received from the MicroBitRadio channel.
+ *
+ * @note This is a mutable, managed type.
+ */
+class PacketBuffer
+{
+ PacketData *ptr; // Pointer to payload data
+
+ public:
+
+ /**
+ * Provide a pointer to a memory location containing the packet data.
+ *
+ * @return The contents of this packet, as an array of bytes.
+ */
+ uint8_t *getBytes();
+
+ /**
+ * Default Constructor.
+ * Creates an empty Packet Buffer.
+ *
+ * @code
+ * PacketBuffer p();
+ * @endcode
+ */
+ PacketBuffer();
+
+ /**
+ * Constructor.
+ * Creates a new PacketBuffer of the given size.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @code
+ * PacketBuffer p(16); // Creates a PacketBuffer 16 bytes long.
+ * @endcode
+ */
+ PacketBuffer(int length);
+
+ /**
+ * Constructor.
+ * Creates an empty Packet Buffer of the given size,
+ * and fills it with the data provided.
+ *
+ * @param data The data with which to fill the buffer.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @param rssi The radio signal strength at the time this packet was recieved. Defaults to 0.
+ *
+ * @code
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p(buf, 3); // Creates a PacketBuffer 3 bytes long.
+ * @endcode
+ */
+ PacketBuffer(uint8_t *data, int length, int rssi = 0);
+
+ /**
+ * Copy Constructor.
+ * Add ourselves as a reference to an existing PacketBuffer.
+ *
+ * @param buffer The PacketBuffer to reference.
+ *
+ * @code
+ * PacketBuffer p();
+ * PacketBuffer p2(p); // Refers to the same packet as p.
+ * @endcode
+ */
+ PacketBuffer(const PacketBuffer &buffer);
+
+ /**
+ * Internal constructor-initialiser.
+ *
+ * @param data The data with which to fill the buffer.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @param rssi The radio signal strength at the time this packet was recieved.
+ */
+ void init(uint8_t *data, int length, int rssi);
+
+ /**
+ * Destructor.
+ *
+ * Removes buffer resources held by the instance.
+ */
+ ~PacketBuffer();
+
+ /**
+ * Copy assign operation.
+ *
+ * Called when one PacketBuffer is assigned the value of another using the '=' operator.
+ *
+ * Decrements our reference count and free up the buffer as necessary.
+ *
+ * Then, update our buffer to refer to that of the supplied PacketBuffer,
+ * and increase its reference count.
+ *
+ * @param p The PacketBuffer to reference.
+ *
+ * @code
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p1(16);
+ * PacketBuffer p2(buf, 3);
+ *
+ * p1 = p2;
+ * @endcode
+ */
+ PacketBuffer& operator = (const PacketBuffer& p);
+
+ /**
+ * Array access operation (read).
+ *
+ * Called when a PacketBuffer is dereferenced with a [] operation.
+ *
+ * Transparently map this through to the underlying payload for elegance of programming.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * uint8_t data = p1[0];
+ * @endcode
+ */
+ uint8_t operator [] (int i) const;
+
+ /**
+ * Array access operation (modify).
+ *
+ * Called when a PacketBuffer is dereferenced with a [] operation.
+ *
+ * Transparently map this through to the underlying payload for elegance of programming.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1[0] = 42;
+ * @endcode
+ */
+ uint8_t& operator [] (int i);
+
+ /**
+ * Equality operation.
+ *
+ * Called when one PacketBuffer is tested to be equal to another using the '==' operator.
+ *
+ * @param p The PacketBuffer to test ourselves against.
+ *
+ * @return true if this PacketBuffer is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p1();
+ * PacketBuffer p2();
+ *
+ * if(p1 == p2) // will be true
+ * display.scroll("same!");
+ * @endcode
+ */
+ bool operator== (const PacketBuffer& p);
+
+ /**
+ * Sets the byte at the given index to value provided.
+ *
+ * @param position The index of the byte to change.
+ *
+ * @param value The new value of the byte (0-255).
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255.
+ * @endcode
+ */
+ int setByte(int position, uint8_t value);
+
+ /**
+ * Determines the value of the given byte in the packet.
+ *
+ * @param position The index of the byte to read.
+ *
+ * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255.
+ * p1.getByte(0); // Returns 255.
+ * @endcode
+ */
+ int getByte(int position);
+
+ /**
+ * Gets number of bytes in this buffer
+ *
+ * @return The size of the buffer in bytes.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.length(); // Returns 16.
+ * @endcode
+ */
+ int length();
+
+ /**
+ * Retrieves the received signal strength of this packet.
+ *
+ * @return The signal strength of the radio when this packet was received, in -dbM.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.getRSSI(); // Returns the received signal strength.
+ * @endcode
+ */
+ int getRSSI();
+
+ /**
+ * Sets the received signal strength of this packet.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setRSSI(37);
+ * @endcode
+ */
+ void setRSSI(uint8_t rssi);
+
+ static PacketBuffer EmptyPacket;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/inc/types/RefCounted.h Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,72 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef REF_COUNTED_H
+#define REF_COUNTED_H
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "MicroBitDevice.h"
+
+/**
+ * Base class for payload for ref-counted objects. Used by ManagedString and MicroBitImage.
+ * There is no constructor, as this struct is typically malloc()ed.
+ */
+struct RefCounted
+{
+public:
+
+ /**
+ * The high 15 bits hold the number of outstanding references. The lowest bit is always 1
+ * to make sure it doesn't look like vtable.
+ * Should never be even or one (object should be deleted then).
+ * When it's set to 0xffff, it means the object sits in flash and should not be counted.
+ */
+ uint16_t refCount;
+
+ /**
+ * Increment reference count.
+ */
+ void incr();
+
+ /**
+ * Decrement reference count.
+ */
+ void decr();
+
+ /**
+ * Initializes for one outstanding reference.
+ */
+ void init();
+
+ /**
+ * Checks if the object resides in flash memory.
+ *
+ * @return true if the object resides in flash memory, false otherwise.
+ */
+ bool isReadOnly();
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/module.json Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,28 @@
+{
+ "name": "microbit-dal",
+ "version": "2.0.0-rc1",
+ "license": "MIT",
+ "description": "The runtime library for the BBC micro:bit, developed by Lancaster University",
+ "keywords": [
+ "mbed-classic",
+ "microbit",
+ "runtime",
+ "library",
+ "lancaster",
+ "University"
+ ],
+ "author": "Joe Finney <j.finney@lancaster.ac.uk (mailto:j.finney@lancaster.ac.uk) >",
+ "homepage": "https://github.com/lancaster-university/microbit-dal/",
+ "dependencies": {
+ "mbed-classic": "lancaster-university/mbed-classic#microbit_hfclk+mb2",
+ "ble": "lancaster-university/BLE_API#v2.5.0+mb3",
+ "ble-nrf51822": "lancaster-university/nrf51822#v2.5.0+mb5",
+ "nrf51-sdk": "lancaster-university/nrf51-sdk#v2.2.0+mb3"
+ },
+ "extraIncludes": [
+ "inc/core",
+ "inc/types",
+ "inc/drivers",
+ "inc/bluetooth"
+ ]
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/CMakeLists.txt Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,104 @@
+# This file is no longer auto-generated to make the repository builds with GCC
+# and ARMCC no matter what.
+
+cmake_minimum_required(VERSION 2.8.12)
+
+enable_language(ASM)
+
+set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES
+ "core/MemberFunctionCallback.cpp"
+ "core/MicroBitCompat.cpp"
+ "core/MicroBitDevice.cpp"
+ "core/MicroBitFiber.cpp"
+ "core/MicroBitFont.cpp"
+ "core/MicroBitHeapAllocator.cpp"
+ "core/MicroBitListener.cpp"
+ "core/MicroBitSystemTimer.cpp"
+
+ "types/ManagedString.cpp"
+ "types/Matrix4.cpp"
+ "types/MicroBitEvent.cpp"
+ "types/MicroBitImage.cpp"
+ "types/PacketBuffer.cpp"
+ "types/RefCounted.cpp"
+
+ "drivers/DynamicPwm.cpp"
+ "drivers/MicroBitAccelerometer.cpp"
+ "drivers/MicroBitButton.cpp"
+ "drivers/MicroBitCompass.cpp"
+ "drivers/MicroBitCompassCalibrator.cpp"
+ "drivers/MicroBitDisplay.cpp"
+ "drivers/MicroBitI2C.cpp"
+ "drivers/MicroBitIO.cpp"
+ "drivers/MicroBitLightSensor.cpp"
+ "drivers/MicroBitMessageBus.cpp"
+ "drivers/MicroBitMultiButton.cpp"
+ "drivers/MicroBitPin.cpp"
+ "drivers/MicroBitRadio.cpp"
+ "drivers/MicroBitRadioDatagram.cpp"
+ "drivers/MicroBitRadioEvent.cpp"
+ "drivers/MicroBitSerial.cpp"
+ "drivers/MicroBitStorage.cpp"
+ "drivers/MicroBitThermometer.cpp"
+
+ "bluetooth/MicroBitAccelerometerService.cpp"
+ "bluetooth/MicroBitBLEManager.cpp"
+ "bluetooth/MicroBitButtonService.cpp"
+ "bluetooth/MicroBitDFUService.cpp"
+ "bluetooth/MicroBitEventService.cpp"
+ "bluetooth/MicroBitIOPinService.cpp"
+ "bluetooth/MicroBitLEDService.cpp"
+ "bluetooth/MicroBitMagnetometerService.cpp"
+ "bluetooth/MicroBitTemperatureService.cpp"
+ "bluetooth/MicroBitUARTService.cpp"
+)
+
+execute_process(WORKING_DIRECTORY "../../yotta_modules/${PROJECT_NAME}" COMMAND "git" "log" "--pretty=format:%h" "-n" "1" OUTPUT_VARIABLE git_hash)
+execute_process(WORKING_DIRECTORY "../../yotta_modules/${PROJECT_NAME}" COMMAND "git" "rev-parse" "--abbrev-ref" "HEAD" OUTPUT_VARIABLE git_branch OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+if ("${git_branch}" STREQUAL "master")
+ set(MICROBIT_DAL_VERSION_STRING "${YOTTA_MICROBIT_DAL_VERSION_STRING}")
+else()
+ set(MICROBIT_DAL_VERSION_STRING "${YOTTA_MICROBIT_DAL_VERSION_STRING}-${git_branch}-g${git_hash}")
+endif()
+
+set(MICROBIT_DAL_VERSION_FLAGS "-DMICROBIT_DAL_VERSION=\\\"${MICROBIT_DAL_VERSION_STRING}\\\"")
+
+set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MICROBIT_DAL_VERSION_FLAGS}")
+
+if (YOTTA_CFG_MICROBIT_CONFIGFILE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${YOTTA_FORCE_INCLUDE_FLAG} \"${YOTTA_CFG_MICROBIT_CONFIGFILE}\"")
+endif ()
+
+if(CMAKE_COMPILER_IS_GNUCC)
+ file(REMOVE "asm/CortexContextSwitch.s")
+ configure_file("asm/CortexContextSwitch.s.gcc" "asm/CortexContextSwitch.s" COPYONLY)
+else()
+ file(REMOVE "asm/CortexContextSwitch.s")
+ configure_file("asm/CortexContextSwitch.s.armcc" "asm/CortexContextSwitch.s" COPYONLY)
+endif()
+
+set(YOTTA_AUTO_MICROBIT-DAL_S_FILES
+ "asm/CortexContextSwitch.s"
+)
+
+add_library(microbit-dal
+ ${YOTTA_AUTO_MICROBIT-DAL_CPP_FILES}
+ ${YOTTA_AUTO_MICROBIT-DAL_S_FILES}
+)
+
+yotta_postprocess_target(LIBRARY microbit-dal)
+
+target_link_libraries(microbit-dal
+ mbed-classic
+ ble
+ ble-nrf51822
+)
+
+if(CMAKE_COMPILER_IS_GNUCC)
+ message("suppressing ALL warnings from mbed-classic, ble, ble-nrf51822 & nrf51-sdk")
+ target_compile_options(mbed-classic PRIVATE "-w")
+ target_compile_options(ble PRIVATE "-w")
+ target_compile_options(ble-nrf51822 PRIVATE "-w")
+ target_compile_options(nrf51-sdk PRIVATE "-w")
+endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/asm/CortexContextSwitch.s.armcc Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,293 @@ +; The MIT License (MIT) + +; Copyright (c) 2016 British Broadcasting Corporation. +; This software is provided by Lancaster University by arrangement with the BBC. + +; Permission is hereby granted, free of charge, to any person obtaining a +; copy of this software and associated documentation files (the "Software"), +; to deal in the Software without restriction, including without limitation +; the rights to use, copy, modify, merge, publish, distribute, sublicense, +; and/or sell copies of the Software, and to permit persons to whom the +; Software is furnished to do so, subject to the following conditions: + +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. + +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +; DEALINGS IN THE SOFTWARE. + + AREA asm_func, CODE, READONLY + +; Export our context switching subroutine as a C function for use in mbed + EXPORT swap_context + EXPORT save_context + EXPORT save_register_context + EXPORT restore_register_context + + ALIGN + +; R0 Contains a pointer to the TCB of the fibre being scheduled out. +; R1 Contains a pointer to the TCB of the fibre being scheduled in. +; R2 Contains a pointer to the base of the stack of the fibre being scheduled out. +; R3 Contains a pointer to the base of the stack of the fibre being scheduled in. + +swap_context + + ; Write our core registers into the TCB + ; First, store the general registers + + ; Skip this is we're given a NULL parameter for the TCB + CMP R0, #0 + BEQ store_context_complete + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + ; Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + ; Now the Stack and Link Register. + ; As this context is only intended for use with a fiber scheduler, + ; we don't need the PC. + MOV R6, SP + STR R6, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + +store_context_complete + ; Finally, Copy the stack. We do this to reduce RAM footprint, as stack is usually very small at the point + ; of scheduling, but we need a lot of capacity for interrupt handling and other functions. + + ; Skip this is we're given a NULL parameter for the stack. + CMP R2, #0 + BEQ store_stack_complete + + LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base. +store_stack + SUBS R4, #4 + SUBS R2, #4 + + LDR R5, [R4] + STR R5, [R2] + + CMP R4, R6 + BNE store_stack + +store_stack_complete + + ; + ; Now page in the new context. + ; Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. + ; + LDR R4, [R1, #56] + MOV LR, R4 + LDR R6, [R1, #52] + MOV SP, R6 + + ; Copy the stack in. + ; n.b. we do this after setting the SP to make comparisons easier. + + ; Skip this is we're given a NULL parameter for the stack. + CMP R3, #0 + BEQ restore_stack_complete + + LDR R4, [R1,#60] ; Load R4 with the fiber's defined stack_base. + +restore_stack + SUBS R4, #4 + SUBS R3, #4 + + LDR R5, [R3] + STR R5, [R4] + + CMP R4, R6 + BNE restore_stack + +restore_stack_complete + LDR R4, [R1, #48] + MOV R12, R4 + LDR R4, [R1, #44] + MOV R11, R4 + LDR R4, [R1, #40] + MOV R10, R4 + LDR R4, [R1, #36] + MOV R9, R4 + LDR R4, [R1, #32] + MOV R8, R4 + + LDR R7, [R1, #28] + LDR R6, [R1, #24] + LDR R5, [R1, #20] + LDR R4, [R1, #16] + LDR R3, [R1, #12] + LDR R2, [R1, #8] + LDR R0, [R1, #0] + LDR R1, [R1, #4] + + ; Return to caller (scheduler). + BX LR + + +; R0 Contains a pointer to the TCB of the fibre to snapshot +; R1 Contains a pointer to the base of the stack of the fibre being snapshotted + +save_context + + ; Write our core registers into the TCB + ; First, store the general registers + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + ; Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + ; Now the Stack and Link Register. + ; As this context is only intended for use with a fiber scheduler, + ; we don't need the PC. + MOV R6, SP + STR R6, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + + ; Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point + ; of sceduling, but we need a lot of capacity for interrupt handling and other functions. + + LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base. + +store_stack1 + SUBS R4, #4 + SUBS R1, #4 + + LDR R5, [R4] + STR R5, [R1] + + CMP R4, R6 + BNE store_stack1 + + ; Restore scratch registers. + + LDR R7, [R0, #28] + LDR R6, [R0, #24] + LDR R5, [R0, #20] + LDR R4, [R0, #16] + + ; Return to caller (scheduler). + BX LR + + +; R0 Contains a pointer to the TCB of the fiber to snapshot +save_register_context + + ; Write our core registers into the TCB + ; First, store the general registers + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + ; Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + ; Now the Stack Pointer and Link Register. + ; As this context is only intended for use with a fiber scheduler, + ; we don't need the PC. + MOV R4, SP + STR R4, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + + ; Restore scratch registers. + LDR R4, [R0, #16] + + ; Return to caller (scheduler). + BX LR + + +restore_register_context + + ; + ; Now page in the new context. + ; Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. + ; + LDR R4, [R0, #56] + MOV LR, R4 + LDR R4, [R0, #52] + MOV SP, R4 + + ; High registers... + LDR R4, [R0, #48] + MOV R12, R4 + LDR R4, [R0, #44] + MOV R11, R4 + LDR R4, [R0, #40] + MOV R10, R4 + LDR R4, [R0, #36] + MOV R9, R4 + LDR R4, [R0, #32] + MOV R8, R4 + + ; Low registers... + LDR R7, [R0, #28] + LDR R6, [R0, #24] + LDR R5, [R0, #20] + LDR R4, [R0, #16] + LDR R3, [R0, #12] + LDR R2, [R0, #8] + LDR R0, [R0, #0] + LDR R1, [R0, #4] + + ; Return to caller (normally the scheduler). + BX LR + + ALIGN + END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/asm/CortexContextSwitch.s.gcc Thu Apr 07 01:33:22 2016 +0100 @@ -0,0 +1,292 @@ +@ The MIT License (MIT) + +@ Copyright (c) 2016 British Broadcasting Corporation. +@ This software is provided by Lancaster University by arrangement with the BBC. + +@ Permission is hereby granted, free of charge, to any person obtaining a +@ copy of this software and associated documentation files (the "Software"), +@ to deal in the Software without restriction, including without limitation +@ the rights to use, copy, modify, merge, publish, distribute, sublicense, +@ and/or sell copies of the Software, and to permit persons to whom the +@ Software is furnished to do so, subject to the following conditions: + +@ The above copyright notice and this permission notice shall be included in +@ all copies or substantial portions of the Software. + +@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +@ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +@ DEALINGS IN THE SOFTWARE. + + .syntax unified + .cpu cortex-m0 + .thumb + .text + .align 2 + +@ Export our context switching subroutine as a C function for use in mbed + .global swap_context + .global save_context + .global save_register_context + .global restore_register_context + +@ R0 Contains a pointer to the TCB of the fibre being scheduled out. +@ R1 Contains a pointer to the TCB of the fibre being scheduled in. +@ R2 Contains a pointer to the base of the stack of the fibre being scheduled out. +@ R3 Contains a pointer to the base of the stack of the fibre being scheduled in. + +swap_context: + + @ Write our core registers into the TCB + @ First, store the general registers + + @ Skip this is we're given a NULL parameter for the TCB + CMP R0, #0 + BEQ store_context_complete + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + @ Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + @ Now the Stack and Link Register. + @ As this context is only intended for use with a fiber scheduler, + @ we don't need the PC. + MOV R6, SP + STR R6, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + +store_context_complete: + @ Finally, Copy the stack. We do this to reduce RAM footprint, as stack is usually very small at the point + @ of scheduling, but we need a lot of capacity for interrupt handling and other functions. + + @ Skip this is we're given a NULL parameter for the stack. + CMP R2, #0 + BEQ store_stack_complete + + LDR R4, [R0,#60] @ Load R4 with the fiber's defined stack_base. +store_stack: + SUBS R4, #4 + SUBS R2, #4 + + LDR R5, [R4] + STR R5, [R2] + + CMP R4, R6 + BNE store_stack + +store_stack_complete: + + @ + @ Now page in the new context. + @ Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. + @ + LDR R4, [R1, #56] + MOV LR, R4 + LDR R6, [R1, #52] + MOV SP, R6 + + @ Copy the stack in. + @ n.b. we do this after setting the SP to make comparisons easier. + + @ Skip this is we're given a NULL parameter for the stack. + CMP R3, #0 + BEQ restore_stack_complete + + LDR R4, [R1,#60] @ Load R4 with the fiber's defined stack_base. + +restore_stack: + SUBS R4, #4 + SUBS R3, #4 + + LDR R5, [R3] + STR R5, [R4] + + CMP R4, R6 + BNE restore_stack + +restore_stack_complete: + LDR R4, [R1, #48] + MOV R12, R4 + LDR R4, [R1, #44] + MOV R11, R4 + LDR R4, [R1, #40] + MOV R10, R4 + LDR R4, [R1, #36] + MOV R9, R4 + LDR R4, [R1, #32] + MOV R8, R4 + + LDR R7, [R1, #28] + LDR R6, [R1, #24] + LDR R5, [R1, #20] + LDR R4, [R1, #16] + LDR R3, [R1, #12] + LDR R2, [R1, #8] + LDR R0, [R1, #0] + LDR R1, [R1, #4] + + @ Return to caller (scheduler). + BX LR + + +@ R0 Contains a pointer to the TCB of the fibre to snapshot +@ R1 Contains a pointer to the base of the stack of the fibre being snapshotted + +save_context: + + @ Write our core registers into the TCB + @ First, store the general registers + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + @ Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + @ Now the Stack and Link Register. + @ As this context is only intended for use with a fiber scheduler, + @ we don't need the PC. + MOV R6, SP + STR R6, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + + @ Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point + @ of sceduling, but we need a lot of capacity for interrupt handling and other functions. + + LDR R4, [R0,#60] @ Load R4 with the fiber's defined stack_base. + +store_stack1: + SUBS R4, #4 + SUBS R1, #4 + + LDR R5, [R4] + STR R5, [R1] + + CMP R4, R6 + BNE store_stack1 + + @ Restore scratch registers. + + LDR R7, [R0, #28] + LDR R6, [R0, #24] + LDR R5, [R0, #20] + LDR R4, [R0, #16] + + @ Return to caller (scheduler). + BX LR + + +@ R0 Contains a pointer to the TCB of the fiber to snapshot +save_register_context: + + @ Write our core registers into the TCB + @ First, store the general registers + + STR R0, [R0,#0] + STR R1, [R0,#4] + STR R2, [R0,#8] + STR R3, [R0,#12] + STR R4, [R0,#16] + STR R5, [R0,#20] + STR R6, [R0,#24] + STR R7, [R0,#28] + + @ Now the high general purpose registers + MOV R4, R8 + STR R4, [R0,#32] + MOV R4, R9 + STR R4, [R0,#36] + MOV R4, R10 + STR R4, [R0,#40] + MOV R4, R11 + STR R4, [R0,#44] + MOV R4, R12 + STR R4, [R0,#48] + + @ Now the Stack Pointer and Link Register. + @ As this context is only intended for use with a fiber scheduler, + @ we don't need the PC. + MOV R4, SP + STR R4, [R0,#52] + MOV R4, LR + STR R4, [R0,#56] + + @ Restore scratch registers. + LDR R4, [R0, #16] + + @ Return to caller (scheduler). + BX LR + + +restore_register_context: + + @ + @ Now page in the new context. + @ Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler. + @ + LDR R4, [R0, #56] + MOV LR, R4 + LDR R4, [R0, #52] + MOV SP, R4 + + @ High registers... + LDR R4, [R0, #48] + MOV R12, R4 + LDR R4, [R0, #44] + MOV R11, R4 + LDR R4, [R0, #40] + MOV R10, R4 + LDR R4, [R0, #36] + MOV R9, R4 + LDR R4, [R0, #32] + MOV R8, R4 + + @ Low registers... + LDR R7, [R0, #28] + LDR R6, [R0, #24] + LDR R5, [R0, #20] + LDR R4, [R0, #16] + LDR R3, [R0, #12] + LDR R2, [R0, #8] + LDR R0, [R0, #0] + LDR R1, [R0, #4] + + @ Return to caller (normally the scheduler). + BX LR
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitAccelerometerService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,121 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the custom MicroBit Accelerometer Service.
+ * Provides a BLE service to remotely read the state of the accelerometer, and configure its behaviour.
+ */
+#include "MicroBitConfig.h"
+#include "ble/UUID.h"
+
+#include "MicroBitAccelerometerService.h"
+
+/**
+ * Constructor.
+ * Create a representation of the AccelerometerService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _accelerometer An instance of MicroBitAccelerometer.
+ */
+MicroBitAccelerometerService::MicroBitAccelerometerService(BLEDevice &_ble, MicroBitAccelerometer &_accelerometer) :
+ ble(_ble), accelerometer(_accelerometer)
+{
+ // Create the data structures that represent each of our characteristics in Soft Device.
+ 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,
+ sizeof(accelerometerPeriodCharacteristicBuffer),
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+
+ // Initialise our characteristic values.
+ accelerometerDataCharacteristicBuffer[0] = 0;
+ accelerometerDataCharacteristicBuffer[1] = 0;
+ accelerometerDataCharacteristicBuffer[2] = 0;
+ accelerometerPeriodCharacteristicBuffer = accelerometer.getPeriod();
+
+ // Set default security requirements
+ accelerometerDataCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ accelerometerPeriodCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ GattCharacteristic *characteristics[] = {&accelerometerDataCharacteristic, &accelerometerPeriodCharacteristic};
+ GattService service(MicroBitAccelerometerServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
+
+ ble.addService(service);
+
+ accelerometerDataCharacteristicHandle = accelerometerDataCharacteristic.getValueHandle();
+ accelerometerPeriodCharacteristicHandle = accelerometerPeriodCharacteristic.getValueHandle();
+
+ ble.gattServer().write(accelerometerDataCharacteristicHandle,(uint8_t *)accelerometerDataCharacteristicBuffer, sizeof(accelerometerDataCharacteristicBuffer));
+ ble.gattServer().write(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer));
+
+ ble.onDataWritten(this, &MicroBitAccelerometerService::onDataWritten);
+
+ if (EventModel::defaultEventBus)
+ EventModel::defaultEventBus->listen(MICROBIT_ID_ACCELEROMETER, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE, this, &MicroBitAccelerometerService::accelerometerUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+}
+
+/**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+void MicroBitAccelerometerService::onDataWritten(const GattWriteCallbackParams *params)
+{
+ if (params->handle == accelerometerPeriodCharacteristicHandle && params->len >= sizeof(accelerometerPeriodCharacteristicBuffer))
+ {
+ accelerometerPeriodCharacteristicBuffer = *((uint16_t *)params->data);
+ 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 = accelerometer.getPeriod();
+ ble.gattServer().write(accelerometerPeriodCharacteristicHandle, (const uint8_t *)&accelerometerPeriodCharacteristicBuffer, sizeof(accelerometerPeriodCharacteristicBuffer));
+ }
+}
+
+/**
+ * Accelerometer update callback
+ */
+void MicroBitAccelerometerService::accelerometerUpdate(MicroBitEvent)
+{
+ if (ble.getGapState().connected)
+ {
+ accelerometerDataCharacteristicBuffer[0] = accelerometer.getX();
+ accelerometerDataCharacteristicBuffer[1] = accelerometer.getY();
+ accelerometerDataCharacteristicBuffer[2] = 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
+};
+
+const uint8_t MicroBitAccelerometerServiceDataUUID[] = {
+ 0xe9,0x5d,0xca,0x4b,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitAccelerometerServicePeriodUUID[] = {
+ 0xe9,0x5d,0xfb,0x24,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitBLEManager.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,602 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitBLEManager.h"
+#include "MicroBitStorage.h"
+#include "MicroBitFiber.h"
+
+
+/* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ.
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "ble.h"
+
+extern "C"
+{
+#include "device_manager.h"
+uint32_t btle_set_gatt_table_size(uint32_t size);
+}
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+#define MICROBIT_PAIRING_FADE_SPEED 4
+
+const char* MICROBIT_BLE_MANUFACTURER = NULL;
+const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
+const char* MICROBIT_BLE_HARDWARE_VERSION = NULL;
+const char* MICROBIT_BLE_FIRMWARE_VERSION = MICROBIT_DAL_VERSION;
+const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
+const int8_t MICROBIT_BLE_POWER_LEVEL[] = {-30, -20, -16, -12, -8, -4, 0, 4};
+
+/*
+ * Many of the mbed interfaces we need to use only support callbacks to plain C functions, rather than C++ methods.
+ * So, we maintain a pointer to the MicroBitBLEManager that's in use. Ths way, we can still access resources on the micro:bit
+ * whilst keeping the code modular.
+ */
+static MicroBitBLEManager *manager = NULL; // Singleton reference to the BLE manager. many mbed BLE API callbacks still do not support member funcions yet. :-(
+static uint8_t deviceID = 255; // Unique ID for the peer that has connected to us.
+static Gap::Handle_t pairingHandle = 0; // The connection handle used during a pairing process. Used to ensure that connections are dropped elegantly.
+
+static void storeSystemAttributes(Gap::Handle_t handle)
+{
+ if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
+ {
+ ManagedString key("bleSysAttrs");
+
+ KeyValuePair* bleSysAttrs = manager->storage->get(key);
+
+ BLESysAttribute attrib;
+ BLESysAttributeStore attribStore;
+
+ uint16_t len = sizeof(attrib.sys_attr);
+
+ sd_ble_gatts_sys_attr_get(handle, attrib.sys_attr, &len, BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
+
+ //copy our stored sysAttrs
+ if(bleSysAttrs != NULL)
+ {
+ memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
+ delete bleSysAttrs;
+ }
+
+ //check if we need to update
+ if(memcmp(attribStore.sys_attrs[deviceID].sys_attr, attrib.sys_attr, len) != 0)
+ {
+ attribStore.sys_attrs[deviceID] = attrib;
+ manager->storage->put(key, (uint8_t *)&attribStore);
+ }
+ }
+}
+
+/**
+ * Callback when a BLE GATT disconnect occurs.
+ */
+static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason)
+{
+ storeSystemAttributes(reason->handle);
+
+ if (manager)
+ manager->advertise();
+}
+
+/**
+ * Callback when a BLE SYS_ATTR_MISSING.
+ */
+static void bleSysAttrMissingCallback(const GattSysAttrMissingCallbackParams *params)
+{
+ int complete = 0;
+ deviceID = 255;
+
+ dm_handle_t dm_handle = {0,0,0,0};
+
+ int ret = dm_handle_get(params->connHandle, &dm_handle);
+
+ if (ret == 0)
+ deviceID = dm_handle.device_id;
+
+ if(manager->storage != NULL && deviceID < MICROBIT_BLE_MAXIMUM_BONDS)
+ {
+ ManagedString key("bleSysAttrs");
+
+ KeyValuePair* bleSysAttrs = manager->storage->get(key);
+
+ BLESysAttributeStore attribStore;
+ BLESysAttribute attrib;
+
+ if(bleSysAttrs != NULL)
+ {
+ //restore our sysAttrStore
+ memcpy(&attribStore, bleSysAttrs->value, sizeof(BLESysAttributeStore));
+ delete bleSysAttrs;
+
+ attrib = attribStore.sys_attrs[deviceID];
+
+ ret = sd_ble_gatts_sys_attr_set(params->connHandle, attrib.sys_attr, sizeof(attrib.sys_attr), BLE_GATTS_SYS_ATTR_FLAG_SYS_SRVCS);
+
+ complete = 1;
+
+ if(ret == 0)
+ ret = sd_ble_gatts_service_changed(params->connHandle, 0x000c, 0xffff);
+ }
+ }
+
+ if (!complete)
+ sd_ble_gatts_sys_attr_set(params->connHandle, NULL, 0, 0);
+
+}
+
+static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
+{
+ (void) handle; /* -Wunused-param */
+
+ ManagedString passKey((const char *)passkey, SecurityManager::PASSKEY_LEN);
+
+ if (manager)
+ manager->pairingRequested(passKey);
+}
+
+static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
+{
+ (void) handle; /* -Wunused-param */
+
+ dm_handle_t dm_handle = {0,0,0,0};
+ int ret = dm_handle_get(handle, &dm_handle);
+
+ if (ret == 0)
+ deviceID = dm_handle.device_id;
+
+ if (manager)
+ {
+ pairingHandle = handle;
+ manager->pairingComplete(status == SecurityManager::SEC_STATUS_SUCCESS);
+ }
+}
+
+/**
+ * Constructor.
+ *
+ * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
+ *
+ * @param _storage an instance of MicroBitStorage used to persist sys attribute information. (This is required for compatability with iOS).
+ *
+ * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
+ * Hence, the init() member function should be used to initialise the BLE stack.
+ */
+MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage& _storage) :
+ storage(&_storage)
+{
+ manager = this;
+ this->ble = NULL;
+ this->pairingStatus = 0;
+}
+
+/**
+ * Constructor.
+ *
+ * Configure and manage the micro:bit's Bluetooth Low Energy (BLE) stack.
+ *
+ * @note The BLE stack *cannot* be brought up in a static context (the software simply hangs or corrupts itself).
+ * Hence, the init() member function should be used to initialise the BLE stack.
+ */
+MicroBitBLEManager::MicroBitBLEManager() :
+ storage(NULL)
+{
+ manager = this;
+ this->ble = NULL;
+ this->pairingStatus = 0;
+}
+
+/**
+ * When called, the micro:bit will begin advertising for a predefined period,
+ * MICROBIT_BLE_ADVERTISING_TIMEOUT seconds to bonded devices.
+ */
+void MicroBitBLEManager::advertise()
+{
+ if(ble)
+ ble->gap().startAdvertising();
+}
+
+/**
+ * Post constructor initialisation method as the BLE stack cannot be brought
+ * up in a static context.
+ *
+ * @param deviceName The name used when advertising
+ * @param serialNumber The serial number exposed by the device information service
+ * @param messageBus An instance of an EventModel, used during pairing.
+ * @param enableBonding If true, the security manager enabled bonding.
+ *
+ * @code
+ * bleManager.init(uBit.getName(), uBit.getSerial(), uBit.messageBus, true);
+ * @endcode
+ */
+void MicroBitBLEManager::init(ManagedString deviceName, ManagedString serialNumber, EventModel& messageBus, bool enableBonding)
+{
+ ManagedString BLEName("BBC micro:bit");
+ this->deviceName = deviceName;
+
+#if !(CONFIG_ENABLED(MICROBIT_BLE_WHITELIST))
+ ManagedString namePrefix(" [");
+ ManagedString namePostfix("]");
+ BLEName = BLEName + namePrefix + deviceName + namePostfix;
+#endif
+
+ // Start the BLE stack.
+#if CONFIG_ENABLED(MICROBIT_HEAP_REUSE_SD)
+ btle_set_gatt_table_size(MICROBIT_SD_GATT_TABLE_SIZE);
+#endif
+
+ ble = new BLEDevice();
+ ble->init();
+
+ // automatically restart advertising after a device disconnects.
+ ble->gap().onDisconnection(bleDisconnectionCallback);
+ ble->gattServer().onSysAttrMissing(bleSysAttrMissingCallback);
+
+ // Configure the stack to hold onto the CPU during critical timing events.
+ // mbed-classic performs __disable_irq() calls in its timers that can cause
+ // MIC failures on secure BLE channels...
+ ble_common_opt_radio_cpu_mutex_t opt;
+ opt.enable = 1;
+ sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
+
+#if CONFIG_ENABLED(MICROBIT_BLE_PRIVATE_ADDRESSES)
+ // Configure for private addresses, so kids' behaviour can't be easily tracked.
+ ble->gap().setAddress(BLEProtocol::AddressType::RANDOM_PRIVATE_RESOLVABLE, {0});
+#endif
+
+ // Setup our security requirements.
+ ble->securityManager().onPasskeyDisplay(passkeyDisplayCallback);
+ ble->securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
+ ble->securityManager().init(enableBonding, (SecurityManager::MICROBIT_BLE_SECURITY_LEVEL == SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM), SecurityManager::IO_CAPS_DISPLAY_ONLY);
+
+ if (enableBonding)
+ {
+ // If we're in pairing mode, review the size of the bond table.
+ int bonds = getBondCount();
+
+ // TODO: It would be much better to implement some sort of LRU/NFU policy here,
+ // but this isn't currently supported in mbed, so we'd need to layer break...
+
+ // If we're full, empty the bond table.
+ if (bonds >= MICROBIT_BLE_MAXIMUM_BONDS)
+ ble->securityManager().purgeAllBondingState();
+ }
+
+#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
+ // Configure a whitelist to filter all connection requetss from unbonded devices.
+ // Most BLE stacks only permit one connection at a time, so this prevents denial of service attacks.
+ BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
+ Gap::Whitelist_t whitelist;
+ whitelist.addresses = bondedAddresses;
+ whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
+
+ ble->securityManager().getAddressesFromBondTable(whitelist);
+
+ ble->gap().setWhitelist(whitelist);
+ ble->gap().setScanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST);
+ ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS);
+#endif
+
+ // Configure the radio at our default power level
+ setTransmitPower(MICROBIT_BLE_DEFAULT_TX_POWER);
+
+ // Bring up core BLE services.
+ new MicroBitDFUService(*ble);
+ DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, serialNumber.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
+ new MicroBitEventService(*ble, messageBus);
+
+
+ // Configure for high speed mode where possible.
+ Gap::ConnectionParams_t fast;
+ ble->getPreferredConnectionParams(&fast);
+ fast.minConnectionInterval = 8; // 10 ms
+ fast.maxConnectionInterval = 16; // 20 ms
+ fast.slaveLatency = 0;
+ ble->setPreferredConnectionParams(&fast);
+
+ // Setup advertising.
+#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
+#else
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+#endif
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
+ ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+ ble->setAdvertisingInterval(200);
+
+#if (MICROBIT_BLE_ADVERTISING_TIMEOUT > 0)
+ ble->gap().setAdvertisingTimeout(MICROBIT_BLE_ADVERTISING_TIMEOUT);
+#endif
+
+ // If we have whitelisting enabled, then prevent only enable advertising of we have any binded devices...
+ // This is to further protect kids' privacy. If no-one initiates BLE, then the device is unreachable.
+ // If whiltelisting is disabled, then we always advertise.
+#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
+ if (whitelist.size > 0)
+#endif
+ ble->startAdvertising();
+}
+
+/**
+ * Change the output power level of the transmitter to the given value.
+ *
+ * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
+ *
+ * @code
+ * // maximum transmission power.
+ * bleManager.setTransmitPower(7);
+ * @endcode
+ */
+int MicroBitBLEManager::setTransmitPower(int power)
+{
+ if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (ble->gap().setTxPower(MICROBIT_BLE_POWER_LEVEL[power]) != NRF_SUCCESS)
+ return MICROBIT_NOT_SUPPORTED;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Determines the number of devices currently bonded with this micro:bit.
+ * @return The number of active bonds.
+ */
+int MicroBitBLEManager::getBondCount()
+{
+ BLEProtocol::Address_t bondedAddresses[MICROBIT_BLE_MAXIMUM_BONDS];
+ Gap::Whitelist_t whitelist;
+ whitelist.addresses = bondedAddresses;
+ whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
+ ble->securityManager().getAddressesFromBondTable(whitelist);
+
+ return whitelist.bonds;
+}
+
+/**
+ * A request to pair has been received from a BLE device.
+ * If we're in pairing mode, display the passkey to the user.
+ * Also, purge the bonding table if it has reached capacity.
+ *
+ * @note for internal use only.
+ */
+void MicroBitBLEManager::pairingRequested(ManagedString passKey)
+{
+ // Update our mode to display the passkey.
+ this->passKey = passKey;
+ this->pairingStatus = MICROBIT_BLE_PAIR_REQUEST;
+}
+
+/**
+ * A pairing request has been sucessfully completed.
+ * If we're in pairing mode, display a success or failure message.
+ *
+ * @note for internal use only.
+ */
+void MicroBitBLEManager::pairingComplete(bool success)
+{
+ this->pairingStatus = MICROBIT_BLE_PAIR_COMPLETE;
+
+ if(success)
+ {
+ this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
+ fiber_add_idle_component(this);
+ }
+}
+
+/**
+ * Periodic callback in thread context.
+ * We use this here purely to safely issue a disconnect operation after a pairing operation is complete.
+ */
+void MicroBitBLEManager::idleTick()
+{
+ if (ble)
+ ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
+
+ fiber_remove_idle_component(this);
+}
+
+/**
+ * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming
+ * of the micro:bit in cases where BLE is disabled during normal operation.
+ *
+ * @param display An instance of MicroBitDisplay used when displaying pairing information.
+ * @param authorizationButton The button to use to authorise a pairing request.
+ *
+ * @code
+ * // initiate pairing mode
+ * bleManager.pairingMode(uBit.display, uBit.buttonA);
+ * @endcode
+ */
+void MicroBitBLEManager::pairingMode(MicroBitDisplay& display, MicroBitButton& authorisationButton)
+{
+ ManagedString namePrefix("BBC micro:bit [");
+ ManagedString namePostfix("]");
+ ManagedString BLEName = namePrefix + deviceName + namePostfix;
+
+ ManagedString msg("PAIRING MODE!");
+
+ int timeInPairingMode = 0;
+ int brightness = 255;
+ int fadeDirection = 0;
+
+ ble->gap().stopAdvertising();
+
+ // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices.
+#if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST)
+ BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS];
+ Gap::Whitelist_t whitelist;
+ whitelist.addresses = addresses;
+ whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS;
+ whitelist.size = 0;
+ ble->gap().setWhitelist(whitelist);
+ ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
+#endif
+
+ // Update the advertised name of this micro:bit to include the device name
+ ble->clearAdvertisingPayload();
+
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+ ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length());
+ ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+ ble->setAdvertisingInterval(200);
+
+ ble->gap().setAdvertisingTimeout(0);
+ ble->gap().startAdvertising();
+
+ // Stop any running animations on the display
+ display.stopAnimation();
+ display.scroll(msg);
+
+ // Display our name, visualised as a histogram in the display to aid identification.
+ showNameHistogram(display);
+
+ while(1)
+ {
+ if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)
+ {
+ timeInPairingMode = 0;
+ MicroBitImage arrow("0,0,255,0,0\n0,255,0,0,0\n255,255,255,255,255\n0,255,0,0,0\n0,0,255,0,0\n");
+ display.print(arrow,0,0,0);
+
+ if (fadeDirection == 0)
+ brightness -= MICROBIT_PAIRING_FADE_SPEED;
+ else
+ brightness += MICROBIT_PAIRING_FADE_SPEED;
+
+ if (brightness <= 40)
+ display.clear();
+
+ if (brightness <= 0)
+ fadeDirection = 1;
+
+ if (brightness >= 255)
+ fadeDirection = 0;
+
+ if (authorisationButton.isPressed())
+ {
+ pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST;
+ pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE;
+ }
+ }
+
+ if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE)
+ {
+ timeInPairingMode = 0;
+ display.setBrightness(255);
+ for (int i=0; i<passKey.length(); i++)
+ {
+ display.image.print(passKey.charAt(i),0,0);
+ fiber_sleep(800);
+ display.clear();
+ fiber_sleep(200);
+
+ if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
+ break;
+ }
+
+ fiber_sleep(1000);
+ }
+
+ if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE)
+ {
+ if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL)
+ {
+ MicroBitImage tick("0,0,0,0,0\n0,0,0,0,255\n0,0,0,255,0\n255,0,255,0,0\n0,255,0,0,0\n");
+ display.print(tick,0,0,0);
+ fiber_sleep(15000);
+ timeInPairingMode = MICROBIT_BLE_PAIRING_TIMEOUT * 30;
+
+ /*
+ * Disabled, as the API to return the number of active bonds is not reliable at present...
+ *
+ display.clear();
+ ManagedString c(getBondCount());
+ ManagedString c2("/");
+ ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS);
+ ManagedString c4("USED");
+
+ display.scroll(c+c2+c3+c4);
+ *
+ *
+ */
+ }
+ else
+ {
+ MicroBitImage cross("255,0,0,0,255\n0,255,0,255,0\n0,0,255,0,0\n0,255,0,255,0\n255,0,0,0,255\n");
+ display.print(cross,0,0,0);
+ }
+ }
+
+ fiber_sleep(100);
+ timeInPairingMode++;
+
+ if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30)
+ microbit_reset();
+ }
+}
+
+/**
+ * Displays the device's ID code as a histogram on the provided MicroBitDisplay instance.
+ *
+ * @param display The display instance used for displaying the histogram.
+ */
+void MicroBitBLEManager::showNameHistogram(MicroBitDisplay &display)
+{
+ uint32_t n = NRF_FICR->DEVICEID[1];
+ int ld = 1;
+ int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
+ int h;
+
+ display.clear();
+ for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
+ {
+ h = (n % d) / ld;
+
+ n -= h;
+ d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
+ ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
+
+ for (int j=0; j<h+1; j++)
+ display.image.setPixelValue(MICROBIT_DFU_HISTOGRAM_WIDTH-i-1, MICROBIT_DFU_HISTOGRAM_HEIGHT-j-1, 255);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitButtonService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,143 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * 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 "MicroBitConfig.h"
+#include "ble/UUID.h"
+
+#include "MicroBitButtonService.h"
+#include "MicroBitButton.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;
+
+ // Set default security requirements
+ buttonADataCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ buttonBDataCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ 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));
+
+ if (EventModel::defaultEventBus)
+ {
+ EventModel::defaultEventBus->listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, this, &MicroBitButtonService::buttonAUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ EventModel::defaultEventBus->listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, this, &MicroBitButtonService::buttonBUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ }
+}
+
+
+/**
+ * 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
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitDFUService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,137 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MicroBit Device Firmware Update loader.
+ *
+ * This is actually just a frontend to a memory resident nordic DFU loader.
+ *
+ * We rely on the BLE standard pairing processes to provide encryption and authentication.
+ * We assume any device that is paied with the micro:bit is authorized to reprogram the device.
+ *
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitDFUService.h"
+#include "ble/UUID.h"
+#include "MicroBitConfig.h"
+
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+/*
+ * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+extern "C" {
+#include "dfu_app_handler.h"
+}
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+
+/**
+ * Constructor.
+ * Initialise the Device Firmware Update service.
+ * @param _ble The instance of a BLE device that we're running on.
+ */
+MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
+ ble(_ble)
+{
+ // Opcodes can be issued here to control the MicroBitDFU Service, as defined above.
+ GattCharacteristic microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte, 0, sizeof(uint8_t),
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+
+ controlByte = 0x00;
+
+ // Set default security requirements
+ microBitDFUServiceControlCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ GattCharacteristic *characteristics[] = {µBitDFUServiceControlCharacteristic};
+ GattService service(MicroBitDFUServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
+
+ ble.addService(service);
+
+ microBitDFUServiceControlCharacteristicHandle = microBitDFUServiceControlCharacteristic.getValueHandle();
+
+ ble.gattServer().write(microBitDFUServiceControlCharacteristicHandle, &controlByte, sizeof(uint8_t));
+ ble.gattServer().onDataWritten(this, &MicroBitDFUService::onDataWritten);
+}
+
+/**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
+{
+ if (params->handle == microBitDFUServiceControlCharacteristicHandle)
+ {
+ if(params->len > 0 && params->data[0] == MICROBIT_DFU_OPCODE_START_DFU)
+ {
+ // TODO: Raise a SYSTEM event here.
+ //uBit.display.stopAnimation();
+ //uBit.display.clear();
+
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ printf(" ACTIVATING BOOTLOADER.\n");
+#endif
+
+ // Perform an explicit disconnection to assist our peer to reconnect to the DFU service
+ ble.disconnect(Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
+
+ wait_ms(1000);
+
+ // Call bootloader_start implicitly trough a event handler call
+ // it is a work around for bootloader_start not being public in sdk 8.1
+ ble_dfu_t p_dfu;
+ ble_dfu_evt_t p_evt;
+
+ p_dfu.conn_handle = params->connHandle;
+ p_evt.ble_dfu_evt_type = BLE_DFU_START;
+
+ dfu_app_on_dfu_evt(&p_dfu, &p_evt);
+ }
+ }
+}
+
+
+/**
+ * UUID definitions for BLE Services and Characteristics.
+ */
+const uint8_t MicroBitDFUServiceUUID[] = {
+ 0xe9,0x5d,0x93,0xb0,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitDFUServiceControlCharacteristicUUID[] = {
+ 0xe9,0x5d,0x93,0xb1,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitEventService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,188 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MicroBit BLE Event Service.
+ * Provides a BLE gateway onto an Event Model.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitEventService.h"
+#include "ble/UUID.h"
+#include "ExternalEvents.h"
+#include "MicroBitFiber.h"
+
+/**
+ * Constructor.
+ * Create a representation of the EventService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _messageBus An instance of an EventModel which events will be mirrored from.
+ */
+MicroBitEventService::MicroBitEventService(BLEDevice &_ble, EventModel &_messageBus) :
+ ble(_ble),messageBus(_messageBus)
+{
+ GattCharacteristic microBitEventCharacteristic(MicroBitEventServiceMicroBitEventCharacteristicUUID, (uint8_t *)µBitEventBuffer, 0, sizeof(EventServiceEvent),
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
+
+ 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 = microBitRequirementsBuffer = clientRequirementsBuffer = clientEventBuffer;
+
+ messageBusListenerOffset = 0;
+
+ // Set default security requirements
+ microBitEventCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ clientEventCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ clientRequirementsCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ microBitRequirementsCharacteristic->requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ 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);
+
+ fiber_add_idle_component(this);
+}
+
+
+/**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+void MicroBitEventService::onDataWritten(const GattWriteCallbackParams *params)
+{
+ int len = params->len;
+ EventServiceEvent *e = (EventServiceEvent *)params->data;
+
+ if (params->handle == clientEventCharacteristicHandle) {
+
+ // Read and fire all events...
+ while (len >= 4)
+ {
+ MicroBitEvent evt(e->type, e->reason);
+ len-=4;
+ e++;
+ }
+ return;
+ }
+
+ if (params->handle == clientRequirementsCharacteristicHandle) {
+ // Read and register for all the events given...
+ while (len >= 4)
+ {
+ messageBus.listen(e->type, e->reason, this, &MicroBitEventService::onMicroBitEvent, MESSAGE_BUS_LISTENER_IMMEDIATE);
+
+ len-=4;
+ e++;
+ }
+ return;
+ }
+}
+
+/**
+ * Callback. Invoked when any events are sent on the microBit message bus.
+ */
+void MicroBitEventService::onMicroBitEvent(MicroBitEvent evt)
+{
+ EventServiceEvent *e = µBitEventBuffer;
+
+ if (ble.getGapState().connected) {
+ e->type = evt.source;
+ e->reason = evt.value;
+
+ 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;
+ messageBus.ignore(MICROBIT_ID_ANY, MICROBIT_EVT_ANY, this, &MicroBitEventService::onMicroBitEvent);
+ }
+}
+
+/**
+ * Read callback on microBitRequirements characteristic.
+ *
+ * Used to iterate through the events that the code on this micro:bit is interested in.
+ */
+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 = 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[] = {
+ 0xe9,0x5d,0x97,0x75,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+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
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitIOPinService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,330 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the custom MicroBit IOPin Service.
+ * Provides a BLE service to remotely read the state of the I/O Pin, and configure its behaviour.
+ */
+#include "MicroBitConfig.h"
+#include "ble/UUID.h"
+
+#include "MicroBitIOPinService.h"
+#include "MicroBitFiber.h"
+
+/**
+ * Constructor.
+ * Create a representation of the IOPinService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _io An instance of MicroBitIO that this service will use to perform
+ * I/O operations.
+ */
+MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble, MicroBitIO &_io) :
+ ble(_ble), io(_io)
+{
+ // 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));
+
+ // Set default security requirements
+ ioPinServiceADCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ ioPinServiceIOCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ ioPinServiceDataCharacteristic->requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ 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);
+ fiber_add_idle_component(this);
+}
+
+/**
+ * Determines if the given pin was configured as a digital pin by the BLE ADPinConfigurationCharacterisitic.
+ *
+ * @param i the enumeration of the pin to test
+ * @return 1 if this pin is configured as digital, 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 ADPinConfigurationCharacterisitic.
+ *
+ * @param i the enumeration of the pin to test
+ * @return 1 if this pin is configured as analog, 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 i 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 i 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))
+ io.pin[i].getDigitalValue();
+ //MicroBitIOPins[i]->getDigitalValue();
+
+ if(isAnalog(i) && isInput(i))
+ io.pin[i].getAnalogValue();
+ //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))
+ io.pin[i].getDigitalValue();
+ //MicroBitIOPins[i]->getDigitalValue();
+
+ if(isAnalog(i) && isInput(i))
+ io.pin[i].getAnalogValue();
+ //MicroBitIOPins[i]->getAnalogValue();
+ }
+ }
+
+ if (params->handle == ioPinServiceDataCharacteristic->getValueHandle())
+ {
+ // We have some pin data to change...
+ uint16_t 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))
+ io.pin[data->pin].setDigitalValue(data->value);
+ //MicroBitIOPins[data->pin]->setDigitalValue(data->value);
+ else
+ io.pin[data->pin].setAnalogValue(data->value*4);
+ //MicroBitIOPins[data->pin]->setAnalogValue(data->value*4);
+ }
+
+ data++;
+ len -= sizeof(IOData);
+ }
+ }
+}
+
+/**
+ * Callback. invoked when the BLE data characteristic is read.
+ *
+ * Reads all the pins marked as inputs, and updates the data stored in the characteristic.
+ */
+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 = io.pin[i].getDigitalValue();
+ //value = MicroBitIOPins[i]->getDigitalValue();
+ else
+ value = io.pin[i].getAnalogValue();
+ //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. Notify any connected
+ * device with any changes.
+ */
+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 = io.pin[i].getDigitalValue();
+ //value = MicroBitIOPins[i]->getDigitalValue();
+ else
+ value = io.pin[i].getAnalogValue();
+ //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
+};
+*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitLEDService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,150 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the custom MicroBit LED Service.
+ * Provides a BLE service to remotely read and write the state of the LED display.
+ */
+#include "MicroBitConfig.h"
+#include "ble/UUID.h"
+
+#include "MicroBitLEDService.h"
+
+/**
+ * Constructor.
+ * Create a representation of the LEDService
+ * @param _ble The instance of a BLE device that we're running on.
+ * @param _display An instance of MicroBitDisplay to interface with.
+ */
+MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble, MicroBitDisplay &_display) :
+ ble(_ble), display(_display),
+ matrixCharacteristic(MicroBitLEDServiceMatrixUUID, (uint8_t *)&matrixCharacteristicBuffer, 0, sizeof(matrixCharacteristicBuffer),
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ)
+{
+ // Create the data structures that represent each of our characteristics in Soft Device.
+ GattCharacteristic textCharacteristic(MicroBitLEDServiceTextUUID, (uint8_t *)textCharacteristicBuffer, 0, MICROBIT_BLE_MAXIMUM_SCROLLTEXT,
+ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+
+ GattCharacteristic scrollingSpeedCharacteristic(MicroBitLEDServiceScrollingSpeedUUID, (uint8_t *)&scrollingSpeedCharacteristicBuffer, 0,
+ sizeof(scrollingSpeedCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
+
+ // Initialise our characteristic values.
+ memclr(matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer));
+ textCharacteristicBuffer[0] = 0;
+ scrollingSpeedCharacteristicBuffer = MICROBIT_DEFAULT_SCROLL_SPEED;
+
+ matrixCharacteristic.setReadAuthorizationCallback(this, &MicroBitLEDService::onDataRead);
+
+ // Set default security requirements
+ matrixCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ textCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ scrollingSpeedCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ GattCharacteristic *characteristics[] = {&matrixCharacteristic, &textCharacteristic, &scrollingSpeedCharacteristic};
+ GattService service(MicroBitLEDServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
+
+ ble.addService(service);
+
+ matrixCharacteristicHandle = matrixCharacteristic.getValueHandle();
+ textCharacteristicHandle = textCharacteristic.getValueHandle();
+ scrollingSpeedCharacteristicHandle = scrollingSpeedCharacteristic.getValueHandle();
+
+ 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);
+}
+
+
+/**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+void MicroBitLEDService::onDataWritten(const GattWriteCallbackParams *params)
+{
+ uint8_t *data = (uint8_t *)params->data;
+
+ if (params->handle == matrixCharacteristicHandle && params->len > 0 && params->len < 6)
+ {
+ for (int y=0; y<params->len; y++)
+ for (int x=0; x<5; x++)
+ display.image.setPixelValue(x, y, (data[y] & (0x01 << (4-x))) ? 255 : 0);
+ }
+
+ else if (params->handle == textCharacteristicHandle)
+ {
+ // Create a ManagedString representation from the UTF8 data.
+ // We do this explicitly to control the length (in case the string is not NULL terminated!)
+ ManagedString s((char *)params->data, params->len);
+
+ // Start the string scrolling and we're done.
+ display.scrollAsync(s, (int) scrollingSpeedCharacteristicBuffer);
+ }
+
+ else if (params->handle == scrollingSpeedCharacteristicHandle && params->len >= sizeof(scrollingSpeedCharacteristicBuffer))
+ {
+ // Read the speed requested, and store it locally.
+ // We use this as the speed for all scroll operations subsquently initiated from BLE.
+ scrollingSpeedCharacteristicBuffer = *((uint16_t *)params->data);
+ }
+}
+
+/**
+ * Callback. Invoked when any of our attributes are read via BLE.
+ */
+void MicroBitLEDService::onDataRead(GattReadAuthCallbackParams *params)
+{
+ if (params->handle == matrixCharacteristicHandle)
+ {
+ for (int y=0; y<5; y++)
+ {
+ matrixCharacteristicBuffer[y] = 0;
+
+ for (int x=0; x<5; x++)
+ {
+ if (display.image.getPixelValue(x, y))
+ matrixCharacteristicBuffer[y] |= 0x01 << (4-x);
+ }
+ }
+
+ ble.gattServer().write(matrixCharacteristicHandle, (const uint8_t *)&matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer));
+ }
+}
+
+
+const uint8_t MicroBitLEDServiceUUID[] = {
+ 0xe9,0x5d,0xd9,0x1d,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitLEDServiceMatrixUUID[] = {
+ 0xe9,0x5d,0x7b,0x77,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitLEDServiceTextUUID[] = {
+ 0xe9,0x5d,0x93,0xee,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitLEDServiceScrollingSpeedUUID[] = {
+ 0xe9,0x5d,0x0d,0x2d,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitMagnetometerService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,156 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the MicroBit BLE Magnetometer Service.
+ * Provides access to live magnetometer data via BLE, and provides basic configuration options.
+ */
+#include "MicroBitConfig.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.
+ * @param _compass An instance of MicroBitCompass to use as our Magnetometer source.
+ */
+MicroBitMagnetometerService::MicroBitMagnetometerService(BLEDevice &_ble, MicroBitCompass &_compass) :
+ ble(_ble), compass(_compass)
+{
+ // 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);
+
+ // Initialise our characteristic values.
+ magnetometerDataCharacteristicBuffer[0] = 0;
+ magnetometerDataCharacteristicBuffer[1] = 0;
+ magnetometerDataCharacteristicBuffer[2] = 0;
+ magnetometerBearingCharacteristicBuffer = 0;
+ magnetometerPeriodCharacteristicBuffer = compass.getPeriod();
+
+ // Set default security requirements
+ magnetometerDataCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ magnetometerBearingCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ magnetometerPeriodCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ 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);
+ if (EventModel::defaultEventBus)
+ {
+ EventModel::defaultEventBus->listen(MICROBIT_ID_COMPASS, MICROBIT_COMPASS_EVT_DATA_UPDATE, this, &MicroBitMagnetometerService::magnetometerUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ EventModel::defaultEventBus->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)
+{
+ if (ble.getGapState().connected)
+ {
+ magnetometerDataCharacteristicBuffer[0] = compass.getX();
+ magnetometerDataCharacteristicBuffer[1] = compass.getY();
+ magnetometerDataCharacteristicBuffer[2] = compass.getZ();
+ magnetometerPeriodCharacteristicBuffer = compass.getPeriod();
+
+ ble.gattServer().write(magnetometerPeriodCharacteristicHandle, (const uint8_t *)&magnetometerPeriodCharacteristicBuffer, sizeof(magnetometerPeriodCharacteristicBuffer));
+ ble.gattServer().notify(magnetometerDataCharacteristicHandle,(uint8_t *)magnetometerDataCharacteristicBuffer, sizeof(magnetometerDataCharacteristicBuffer));
+
+ if (compass.isCalibrated())
+ {
+ magnetometerBearingCharacteristicBuffer = (uint16_t) compass.heading();
+ 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)
+{
+ // Reconfigure the compass. This might take a while...
+ compass.setPeriod(magnetometerPeriodCharacteristicBuffer);
+
+ // The compass will choose the nearest sample period to that we've specified.
+ // Read the ACTUAL sample period back.
+ magnetometerPeriodCharacteristicBuffer = 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
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitTemperatureService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,115 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the custom MicroBit Temperature Service.
+ * Provides a BLE service to remotely read the silicon temperature of the nRF51822.
+ */
+#include "MicroBitConfig.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.
+ * @param _thermometer An instance of MicroBitThermometer to use as our temperature source.
+ */
+MicroBitTemperatureService::MicroBitTemperatureService(BLEDevice &_ble, MicroBitThermometer &_thermometer) :
+ ble(_ble), thermometer(_thermometer)
+{
+ // 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);
+
+ GattCharacteristic temperaturePeriodCharacteristic(MicroBitTemperatureServicePeriodUUID, (uint8_t *)&temperaturePeriodCharacteristicBuffer, 0,
+ sizeof(temperaturePeriodCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
+
+ // Initialise our characteristic values.
+ temperatureDataCharacteristicBuffer = 0;
+ temperaturePeriodCharacteristicBuffer = thermometer.getPeriod();
+
+ // Set default security requirements
+ temperatureDataCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+ temperaturePeriodCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL);
+
+ GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic, &temperaturePeriodCharacteristic};
+ GattService service(MicroBitTemperatureServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
+
+ ble.addService(service);
+
+ temperatureDataCharacteristicHandle = temperatureDataCharacteristic.getValueHandle();
+ temperaturePeriodCharacteristicHandle = temperaturePeriodCharacteristic.getValueHandle();
+
+ ble.gattServer().write(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer));
+ ble.gattServer().write(temperaturePeriodCharacteristicHandle,(uint8_t *)&temperaturePeriodCharacteristicBuffer, sizeof(temperaturePeriodCharacteristicBuffer));
+
+ ble.onDataWritten(this, &MicroBitTemperatureService::onDataWritten);
+ if (EventModel::defaultEventBus)
+ EventModel::defaultEventBus->listen(MICROBIT_ID_THERMOMETER, MICROBIT_THERMOMETER_EVT_UPDATE, this, &MicroBitTemperatureService::temperatureUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+}
+
+/**
+ * Temperature update callback
+ */
+void MicroBitTemperatureService::temperatureUpdate(MicroBitEvent)
+{
+ if (ble.getGapState().connected)
+ {
+ temperatureDataCharacteristicBuffer = thermometer.getTemperature();
+ ble.gattServer().notify(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer));
+ }
+}
+
+/**
+ * Callback. Invoked when any of our attributes are written via BLE.
+ */
+void MicroBitTemperatureService::onDataWritten(const GattWriteCallbackParams *params)
+{
+ if (params->handle == temperaturePeriodCharacteristicHandle && params->len >= sizeof(temperaturePeriodCharacteristicBuffer))
+ {
+ temperaturePeriodCharacteristicBuffer = *((uint16_t *)params->data);
+ thermometer.setPeriod(temperaturePeriodCharacteristicBuffer);
+
+ // 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.
+ temperaturePeriodCharacteristicBuffer = thermometer.getPeriod();
+ ble.gattServer().write(temperaturePeriodCharacteristicHandle, (const uint8_t *)&temperaturePeriodCharacteristicBuffer, sizeof(temperaturePeriodCharacteristicBuffer));
+ }
+}
+
+
+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,0x92,0x50,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
+
+const uint8_t MicroBitTemperatureServicePeriodUUID[] = {
+ 0xe9,0x5d,0x1b,0x25,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/bluetooth/MicroBitUARTService.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,539 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the custom MicroBit UART Service.
+ * Provides a BLE service that acts as a UART port, enabling the reception and transmission
+ * of an arbitrary number of bytes.
+ */
+
+#include "ble/UUID.h"
+
+#include "ExternalEvents.h"
+#include "MicroBitUARTService.h"
+#include "MicroBitFiber.h"
+#include "ErrorNo.h"
+#include "NotifyEvents.h"
+
+static uint8_t txBufferHead = 0;
+static uint8_t txBufferTail = 0;
+
+static GattCharacteristic* rxCharacteristic = NULL;
+
+/**
+ * A callback function for whenever a Bluetooth device consumes our RX Buffer
+ */
+void on_confirmation_received_callback(uint16_t handle)
+{
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ SERIAL_DEBUG->printf("RECEIVED!! %d \r\n",handle);
+#endif
+ if(handle == rxCharacteristic->getValueAttribute().getHandle())
+ {
+ txBufferTail = txBufferHead;
+ MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
+ }
+}
+
+/**
+ * Constructor for the UARTService.
+ * @param _ble an instance of BLEDevice
+ * @param rxBufferSize the size of the rxBuffer
+ * @param txBufferSize the size of the txBuffer
+ *
+ * @note defaults to 20
+ */
+MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble)
+{
+
+ txBuffer = (uint8_t *)malloc(txBufferSize);
+ rxBuffer = (uint8_t *)malloc(rxBufferSize);
+
+ rxBufferHead = 0;
+ rxBufferTail = 0;
+ this->rxBufferSize = rxBufferSize;
+
+ txBufferHead = 0;
+ txBufferTail = 0;
+ this->txBufferSize = txBufferSize;
+
+ GattCharacteristic txCharacteristic(UARTServiceTXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
+
+ rxCharacteristic = new GattCharacteristic(UARTServiceRXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
+
+ GattCharacteristic *charTable[] = {&txCharacteristic, rxCharacteristic};
+
+ GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+
+ _ble.addService(uartService);
+
+ this->txCharacteristicHandle = txCharacteristic.getValueAttribute().getHandle();
+
+ _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
+ _ble.gattServer().onConfirmationReceived(on_confirmation_received_callback);
+}
+
+/**
+ * A callback function for whenever a Bluetooth device writes to our TX characteristic.
+ */
+void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) {
+ if (params->handle == this->txCharacteristicHandle)
+ {
+ uint16_t bytesWritten = params->len;
+
+ for(int byteIterator = 0; byteIterator < bytesWritten; byteIterator++)
+ {
+ int newHead = (rxBufferHead + 1) % rxBufferSize;
+
+ if(newHead != rxBufferTail)
+ {
+ char c = params->data[byteIterator];
+
+ int delimeterOffset = 0;
+ int delimLength = this->delimeters.length();
+
+ //iterate through our delimeters (if any) to see if there is a match
+ while(delimeterOffset < delimLength)
+ {
+ //fire an event if there is to block any waiting fibers
+ if(this->delimeters.charAt(delimeterOffset) == c)
+ MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
+
+ delimeterOffset++;
+ }
+
+ rxBuffer[rxBufferHead] = c;
+
+ rxBufferHead = newHead;
+
+ if(rxBufferHead == rxBuffHeadMatch)
+ {
+ rxBuffHeadMatch = -1;
+ MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
+ }
+ }
+ else
+ MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL);
+ }
+ }
+}
+
+/**
+ * An internal method that copies values from a circular buffer to a linear buffer.
+ *
+ * @param circularBuff a pointer to the source circular buffer
+ * @param circularBuffSize the size of the circular buffer
+ * @param linearBuff a pointer to the destination linear buffer
+ * @param tailPosition the tail position in the circular buffer you want to copy from
+ * @param headPosition the head position in the circular buffer you want to copy to
+ *
+ * @note this method assumes that the linear buffer has the appropriate amount of
+ * memory to contain the copy operation
+ */
+void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition)
+{
+ int toBuffIndex = 0;
+
+ while(tailPosition != headPosition)
+ {
+ linearBuff[toBuffIndex++] = circularBuff[tailPosition];
+
+ tailPosition = (tailPosition + 1) % circularBuffSize;
+ }
+}
+
+/**
+ * Retreives a single character from our RxBuffer.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read a single character, and return immediately
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
+ */
+int MicroBitUARTService::getc(MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if(mode == ASYNC)
+ {
+ if(!isReadable())
+ return MICROBIT_NO_DATA;
+ }
+
+ if(mode == SYNC_SLEEP)
+ {
+ if(!isReadable())
+ eventAfter(1, mode);
+ }
+
+ char c = rxBuffer[rxBufferTail];
+
+ rxBufferTail = (rxBufferTail + 1) % rxBufferSize;
+
+ return c;
+}
+
+/**
+ * places a single character into our transmission buffer,
+ *
+ * @param c the character to transmit
+ *
+ * @return the number of characters written (0, or 1).
+ */
+int MicroBitUARTService::putc(char c)
+{
+ return (send((uint8_t *)&c, 1) == 1) ? 1 : EOF;
+}
+
+/**
+ * Copies characters into the buffer used for Transmitting to the central device.
+ *
+ * @param buf a buffer containing length number of bytes.
+ * @param length the size of the buffer.
+ *
+ * @return the number of characters copied into the buffer
+ *
+ * @note no modes for sending are available at the moment, due to interrupt overhead.
+ */
+int MicroBitUARTService::send(const uint8_t *buf, int length)
+{
+ if(length < 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ int bytesWritten = 0;
+
+ if (ble.getGapState().connected) {
+
+ for(int bufferIterator = 0; bufferIterator < length; bufferIterator++)
+ {
+ int nextHead = (txBufferHead + 1) % txBufferSize;
+
+ if(nextHead != txBufferTail)
+ {
+ txBuffer[txBufferHead] = buf[bufferIterator];
+
+ txBufferHead = nextHead;
+
+ bytesWritten++;
+ }
+ }
+
+ int size = txBufferedSize();
+
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ SERIAL_DEBUG->printf("tx size: %d", size);
+#endif
+
+ uint8_t temp[size] = { 0 };
+
+ circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead);
+
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ for(int i = 0; i < size; i++)
+ SERIAL_DEBUG->printf("%c",temp[i]);
+#endif
+
+ ble.gattServer().write(rxCharacteristic->getValueAttribute().getHandle(), temp, size);
+ }
+
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ SERIAL_DEBUG->printf("written: %d \r\n",bytesWritten);
+#endif
+
+ return bytesWritten;
+}
+
+/**
+ * Copies characters into the buffer used for Transmitting to the central device.
+ *
+ * @param s the string to transmit
+ *
+ * @return the number of characters copied into the buffer
+ *
+ * @note no modes for sending are available at the moment, due to interrupt overhead.
+ */
+int MicroBitUARTService::send(ManagedString s)
+{
+ return send((uint8_t *)s.toCharArray(), s.length());
+}
+
+/**
+ * Reads a number of characters from the rxBuffer and fills user given buffer.
+ *
+ * @param buf a pointer to a buffer of len bytes.
+ * @param len the size of the user allocated buffer
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read all available characters, and return immediately
+ * until the buffer limit is reached
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all determine whether the given number of characters
+ * are available in our buffer, if not, it will set an event and sleep
+ * until the number of characters are avaialable.
+ *
+ * @return the number of characters digested
+ */
+int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ int i = 0;
+
+ if(mode == ASYNC)
+ {
+ int c;
+
+ while((c = getc(mode)) > 0 && i < len)
+ {
+ buf[i] = c;
+ i++;
+ }
+ }
+
+ if(mode == SYNC_SLEEP)
+ {
+ if(len > rxBufferedSize())
+ eventAfter(len - rxBufferedSize(), mode);
+
+ while(i < len)
+ {
+ buf[i] = (char)getc(mode);
+ i++;
+ }
+ }
+
+ return i;
+}
+
+/**
+ * Reads a number of characters from the rxBuffer and returns them as a ManagedString
+ *
+ * @param len the number of characters to read.
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt to read all available characters, and return immediately
+ * until the buffer limit is reached
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all determine whether the given number of characters
+ * are available in our buffer, if not, it will set an event and sleep
+ * until the number of characters are avaialable.
+ *
+ * @return an empty ManagedString on error, or a ManagedString containing characters
+ */
+ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode)
+{
+ uint8_t buf[len + 1] = { 0 };
+
+ int ret = read(buf, len, mode);
+
+ if(ret < 1)
+ return ManagedString();
+
+ return ManagedString((const char *)buf);
+}
+
+/**
+ * Reads characters until a character matches one of the given delimeters
+ *
+ * @param delimeters the number of characters to match against
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will attempt read the immediate buffer, and look for a match.
+ * If there isn't, an empty ManagedString will be returned.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
+ * if a match is not found, it will block on an event, fired when a
+ * character is matched.
+ *
+ * @return an empty ManagedString on error, or a ManagedString containing characters
+ */
+ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ int localTail = rxBufferTail;
+ int preservedTail = rxBufferTail;
+
+ int foundIndex = -1;
+
+ //ASYNC mode just iterates through our stored characters checking for any matches.
+ while(localTail != rxBufferHead && foundIndex == -1)
+ {
+ //we use localTail to prevent modification of the actual tail.
+ char c = rxBuffer[localTail];
+
+ for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
+ if(delimeters.charAt(delimeterIterator) == c)
+ foundIndex = localTail;
+
+ localTail = (localTail + 1) % rxBufferSize;
+ }
+
+ //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a
+ //matching character.
+ if(mode == SYNC_SLEEP && foundIndex == -1)
+ {
+ eventOn(delimeters, mode);
+
+ foundIndex = rxBufferHead - 1;
+
+ this->delimeters = ManagedString();
+ }
+
+ if(foundIndex >= 0)
+ {
+ //calculate our local buffer size
+ int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail;
+
+ uint8_t localBuff[localBuffSize + 1] = { 0 };
+
+ circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex);
+
+ //plus one for the character we listened for...
+ rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize;
+
+ return ManagedString((char *)localBuff, localBuffSize);
+ }
+
+ return ManagedString();
+}
+
+/**
+ * Configures an event to be fired on a match with one of the delimeters.
+ *
+ * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //configure our head match...
+ this->delimeters = delimeters;
+
+ //block!
+ if(mode == SYNC_SLEEP)
+ fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures an event to be fired after "len" characters.
+ *
+ * @param len the number of characters to wait before triggering the event
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ */
+int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //configure our head match...
+ this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize;
+
+ //block!
+ if(mode == SYNC_SLEEP)
+ fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Determines if we have space in our rxBuff.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ *
+ * @note the reason we do not wrap the super's readable() method is so that we
+ * don't interfere with communities that use manual calls to uBit.serial.readable()
+ */
+int MicroBitUARTService::isReadable()
+{
+ return (rxBufferTail != rxBufferHead) ? 1 : 0;
+}
+
+/**
+ * @return The currently buffered number of bytes in our rxBuff.
+ */
+int MicroBitUARTService::rxBufferedSize()
+{
+ if(rxBufferTail > rxBufferHead)
+ return (rxBufferSize - rxBufferTail) + rxBufferHead;
+
+ return rxBufferHead - rxBufferTail;
+}
+
+/**
+ * @return The currently buffered number of bytes in our txBuff.
+ */
+int MicroBitUARTService::txBufferedSize()
+{
+ if(txBufferTail > txBufferHead)
+ return (txBufferSize - txBufferTail) + txBufferHead;
+
+ return txBufferHead - txBufferTail;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MemberFunctionCallback.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,58 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MemberFunctionCallback.
+ *
+ * C++ member functions (also known as methods) have a more complex
+ * representation than normal C functions. This class allows a reference to
+ * a C++ member function to be stored then called at a later date.
+ *
+ * This class is used extensively by the MicroBitMessageBus to deliver
+ * events to C++ methods.
+ */
+
+#include "MicroBitConfig.h"
+#include "MemberFunctionCallback.h"
+
+/**
+ * Calls the method reference held by this MemberFunctionCallback.
+ *
+ * @param e The event to deliver to the method
+ */
+void MemberFunctionCallback::fire(MicroBitEvent e)
+{
+ invoke(object, method, e);
+}
+
+/**
+ * A comparison of two MemberFunctionCallback objects.
+ *
+ * @return true if the given MemberFunctionCallback is equivalent to this one, false otherwise.
+ */
+bool MemberFunctionCallback::operator==(const MemberFunctionCallback &mfc)
+{
+ return (object == mfc.object && (memcmp(method,mfc.method,sizeof(method))==0));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitCompat.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,101 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * This file contains functions used to maintain compatability and portability.
+ * It also contains constants that are used elsewhere in the DAL.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitCompat.h"
+#include "ErrorNo.h"
+
+
+/**
+ * Performs an in buffer reverse of a given char array.
+ *
+ * @param s the string to reverse.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int string_reverse(char *s)
+{
+ //sanity check...
+ if(s == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ char *j;
+ int c;
+
+ j = s + strlen(s) - 1;
+
+ while(s < j)
+ {
+ c = *s;
+ *s++ = *j;
+ *j-- = c;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Converts a given integer into a string representation.
+ *
+ * @param n The number to convert.
+ *
+ * @param s A pointer to the buffer where the resulting string will be stored.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int itoa(int n, char *s)
+{
+ int i = 0;
+ int positive = (n >= 0);
+
+ if (s == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Record the sign of the number,
+ // Ensure our working value is positive.
+ if (positive)
+ n = -n;
+
+ // Calculate each character, starting with the LSB.
+ do {
+ s[i++] = abs(n % 10) + '0';
+ } while (abs(n /= 10) > 0);
+
+ // Add a negative sign as needed
+ if (!positive)
+ s[i++] = '-';
+
+ // Terminate the string.
+ s[i] = '\0';
+
+ // Flip the order.
+ string_reverse(s);
+
+ return MICROBIT_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitDevice.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,375 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Compatibility / portability funcitons and constants for the MicroBit DAL.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitButton.h"
+#include "MicroBitDevice.h"
+#include "MicroBitFont.h"
+#include "mbed.h"
+#include "ErrorNo.h"
+
+/*
+ * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "nrf_soc.h"
+#include "nrf_sdm.h"
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+static char friendly_name[MICROBIT_NAME_LENGTH+1];
+static const uint8_t panicFace[5] = {0x1B, 0x1B,0x0,0x0E,0x11};
+static int panic_timeout = 0;
+static uint32_t random_value = 0;
+
+/**
+ * Determines if a BLE stack is currently running.
+ *
+ * @return true is a bluetooth stack is operational, false otherwise.
+ */
+bool ble_running()
+{
+ uint8_t t;
+ sd_softdevice_is_enabled(&t);
+ return t==1;
+}
+
+/**
+ * Derived a unique, consistent serial number of this device from internal data.
+ *
+ * @return the serial number of this device.
+ */
+uint32_t microbit_serial_number()
+{
+ return NRF_FICR->DEVICEID[1];
+}
+
+/**
+ * Derive the friendly name for this device, based on its serial number.
+ *
+ * @return the serial number of this device.
+ */
+char* microbit_friendly_name()
+{
+ const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
+ {
+ {'z', 'v', 'g', 'p', 't'},
+ {'u', 'o', 'i', 'e', 'a'},
+ {'z', 'v', 'g', 'p', 't'},
+ {'u', 'o', 'i', 'e', 'a'},
+ {'z', 'v', 'g', 'p', 't'}
+ };
+
+ // We count right to left, so create a pointer to the end of the buffer.
+ char *name = friendly_name;
+ name += MICROBIT_NAME_LENGTH;
+
+ // Terminate the string.
+ *name = 0;
+
+ // Derive our name from the nrf51822's unique ID.
+ uint32_t n = microbit_serial_number();
+ int ld = 1;
+ int d = MICROBIT_NAME_CODE_LETTERS;
+ int h;
+
+ for (int i=0; i<MICROBIT_NAME_LENGTH; i++)
+ {
+ h = (n % d) / ld;
+ n -= h;
+ d *= MICROBIT_NAME_CODE_LETTERS;
+ ld *= MICROBIT_NAME_CODE_LETTERS;
+ *--name = codebook[i][h];
+ }
+
+ return friendly_name;
+}
+
+/**
+ * Perform a hard reset of the micro:bit.
+ */
+void
+microbit_reset()
+{
+ NVIC_SystemReset();
+}
+
+/**
+ * Determine the version of microbit-dal currently running.
+ * @return a pointer to a character buffer containing a representation of the semantic version number.
+ */
+const char *
+microbit_dal_version()
+{
+ return MICROBIT_DAL_VERSION;
+}
+
+/**
+ * Defines the length of time that the device will remain in a error state before resetting.
+ *
+ * @param iteration The number of times the error code will be displayed before resetting. Set to zero to remain in error state forever.
+ *
+ * @code
+ * microbit_panic_timeout(4);
+ * @endcode
+ */
+void microbit_panic_timeout(int iterations)
+{
+ panic_timeout = iterations;
+}
+
+/**
+ * Disables all interrupts and user processing.
+ * Displays "=(" and an accompanying status code on the default display.
+ * @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
+ *
+ * @code
+ * microbit_panic(20);
+ * @endcode
+ */
+void microbit_panic(int statusCode)
+{
+ DigitalIn resetButton(MICROBIT_PIN_BUTTON_RESET);
+ resetButton.mode(PullUp);
+
+ uint32_t row_mask = 0;
+ uint32_t col_mask = 0;
+ uint32_t row_reset = 0x01 << microbitMatrixMap.rowStart;
+ uint32_t row_data = row_reset;
+ uint8_t count = panic_timeout ? panic_timeout : 1;
+ uint8_t strobeRow = 0;
+
+ row_mask = 0;
+ for (int i = microbitMatrixMap.rowStart; i < microbitMatrixMap.rowStart + microbitMatrixMap.rows; i++)
+ row_mask |= 0x01 << i;
+
+ for (int i = microbitMatrixMap.columnStart; i < microbitMatrixMap.columnStart + microbitMatrixMap.columns; i++)
+ col_mask |= 0x01 << i;
+
+ PortOut LEDMatrix(Port0, row_mask | col_mask);
+
+ if(statusCode < 0 || statusCode > 255)
+ statusCode = 0;
+
+ __disable_irq(); //stop ALL interrupts
+
+
+ //point to the font stored in Flash
+ const unsigned char * fontLocation = MicroBitFont::defaultFont;
+
+ //get individual digits of status code, and place it into a single array/
+ const uint8_t* chars[MICROBIT_PANIC_ERROR_CHARS] = { panicFace, fontLocation+((((statusCode/100 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode/10 % 10)+48)-MICROBIT_FONT_ASCII_START) * 5), fontLocation+((((statusCode % 10)+48)-MICROBIT_FONT_ASCII_START) * 5)};
+
+ //enter infinite loop.
+ while(count)
+ {
+ //iterate through our chars :)
+ for(int characterCount = 0; characterCount < MICROBIT_PANIC_ERROR_CHARS; characterCount++)
+ {
+ int outerCount = 0;
+
+ //display the current character
+ while(outerCount < 500)
+ {
+ uint32_t col_data = 0;
+
+ int i = 0;
+
+ //if we have hit the row limit - reset both the bit mask and the row variable
+ if(strobeRow == microbitMatrixMap.rows)
+ {
+ strobeRow = 0;
+ row_data = row_reset;
+ }
+
+ // Calculate the bitpattern to write.
+ for (i = 0; i < microbitMatrixMap.columns; i++)
+ {
+ int index = (i * microbitMatrixMap.rows) + strobeRow;
+
+ int bitMsk = 0x10 >> microbitMatrixMap.map[index].x; //chars are right aligned but read left to right
+ int y = microbitMatrixMap.map[index].y;
+
+ if(chars[characterCount][y] & bitMsk)
+ col_data |= (1 << i);
+ }
+
+ col_data = ~col_data << microbitMatrixMap.columnStart & col_mask;
+
+ LEDMatrix = col_data | row_data;
+
+ //burn cycles
+ i = 1000;
+ while(i>0)
+ {
+ // Check if the reset button has been pressed. Interrupts are disabled, so the normal method can't be relied upon...
+ if (resetButton == 0)
+ microbit_reset();
+
+ i--;
+ }
+
+ //update the bit mask and row count
+ row_data <<= 1;
+ strobeRow++;
+ outerCount++;
+ }
+ }
+
+ if (panic_timeout)
+ count--;
+ }
+
+ microbit_reset();
+}
+
+/**
+ * Generate a random number in the given range.
+ * We use a simple Galois LFSR random number generator here,
+ * as a Galois LFSR is sufficient for our applications, and much more lightweight
+ * than the hardware random number generator built int the processor, which takes
+ * a long time and uses a lot of energy.
+ *
+ * KIDS: You shouldn't use this is the real world to generte cryptographic keys though...
+ * have a think why not. :-)
+ *
+ * @param max the upper range to generate a number for. This number cannot be negative.
+ *
+ * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE if max is <= 0.
+ *
+ * @code
+ * microbit_random(200); //a number between 0 and 199
+ * @endcode
+ */
+int microbit_random(int max)
+{
+ uint32_t m, result;
+
+ if(max <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Our maximum return value is actually one less than passed
+ max--;
+
+ do {
+ m = (uint32_t)max;
+ result = 0;
+ do {
+ // Cycle the LFSR (Linear Feedback Shift Register).
+ // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!),
+ // For those interested, it's documented in his paper:
+ // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors"
+ // https://www.schneier.com/paper-pseudorandom-sequence.html
+ uint32_t rnd = random_value;
+
+ rnd = ((((rnd >> 31)
+ ^ (rnd >> 6)
+ ^ (rnd >> 4)
+ ^ (rnd >> 2)
+ ^ (rnd >> 1)
+ ^ rnd)
+ & 0x0000001)
+ << 31 )
+ | (rnd >> 1);
+
+ random_value = rnd;
+
+ result = ((result << 1) | (rnd & 0x00000001));
+ } while(m >>= 1);
+ } while (result > (uint32_t)max);
+
+ return result;
+}
+
+/**
+ * Seed the random number generator (RNG).
+ *
+ * This function uses the NRF51822's in built cryptographic random number generator to seed a Galois LFSR.
+ * We do this as the hardware RNG is relatively high power, and is locked out by the BLE stack internally,
+ * with a less than optimal application interface. A Galois LFSR is sufficient for our
+ * applications, and much more lightweight.
+ */
+void microbit_seed_random()
+{
+ random_value = 0;
+
+ if(ble_running())
+ {
+ // If Bluetooth is enabled, we need to go through the Nordic software to safely do this.
+ uint32_t result = sd_rand_application_vector_get((uint8_t*)&random_value, sizeof(random_value));
+
+ // If we couldn't get the random bytes then at least make the seed non-zero.
+ if (result != NRF_SUCCESS)
+ random_value = 0xBBC5EED;
+ }
+ else
+ {
+ // Othwerwise we can access the hardware RNG directly.
+
+ // Start the Random number generator. No need to leave it running... I hope. :-)
+ NRF_RNG->TASKS_START = 1;
+
+ for(int i = 0; i < 4; i++)
+ {
+ // Clear the VALRDY EVENT
+ NRF_RNG->EVENTS_VALRDY = 0;
+
+ // Wait for a number ot be generated.
+ while(NRF_RNG->EVENTS_VALRDY == 0);
+
+ random_value = (random_value << 8) | ((int) NRF_RNG->VALUE);
+ }
+
+ // Disable the generator to save power.
+ NRF_RNG->TASKS_STOP = 1;
+ }
+}
+
+/**
+ * Seed the pseudo random number generator (RNG) using the given 32-bit value.
+ * This function does not use the NRF51822's in built cryptographic random number generator.
+ *
+ * @param seed The value to use as a seed.
+ */
+void microbit_seed_random(uint32_t seed)
+{
+ random_value = seed;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitFiber.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,964 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Functionality definitions for the MicroBit Fiber scheduler.
+ *
+ * This lightweight, non-preemptive scheduler provides a simple threading mechanism for two main purposes:
+ *
+ * 1) To provide a clean abstraction for application languages to use when building async behaviour (callbacks).
+ * 2) To provide ISR decoupling for EventModel events generated in an ISR context.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitFiber.h"
+#include "MicroBitSystemTimer.h"
+
+/*
+ * Statically allocated values used to create and destroy Fibers.
+ * required to be defined here to allow persistence during context switches.
+ */
+Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
+static Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
+static Fiber *idleFiber = NULL; // the idle task - performs a power efficient sleep, and system maintenance tasks.
+
+/*
+ * Scheduler state.
+ */
+static Fiber *runQueue = NULL; // The list of runnable fibers.
+static Fiber *sleepQueue = NULL; // The list of blocked fibers waiting on a fiber_sleep() operation.
+static Fiber *waitQueue = NULL; // The list of blocked fibers waiting on an event.
+static Fiber *fiberPool = NULL; // Pool of unused fibers, just waiting for a job to do.
+
+/*
+ * Scheduler wide flags
+ */
+static uint8_t fiber_flags = 0;
+
+
+/*
+ * Fibers may perform wait/notify semantics on events. If set, these operations will be permitted on this EventModel.
+ */
+static EventModel *messageBus = NULL;
+
+// Array of components which are iterated during idle thread execution, isIdleCallbackNeeded is polled during a systemTick.
+static MicroBitComponent* idleThreadComponents[MICROBIT_IDLE_COMPONENTS];
+
+/**
+ * Utility function to add the currenty running fiber to the given queue.
+ *
+ * Perform a simple add at the head, to avoid complexity,
+ *
+ * Queues are normally very short, so maintaining a doubly linked, sorted list typically outweighs the cost of
+ * brute force searching.
+ *
+ * @param f The fiber to add to the queue
+ *
+ * @param queue The run queue to add the fiber to.
+ */
+void queue_fiber(Fiber *f, Fiber **queue)
+{
+ __disable_irq();
+
+ // Record which queue this fiber is on.
+ f->queue = queue;
+
+ // Add the fiber to the tail of the queue. Although this involves scanning the
+ // list, it results in fairer scheduling.
+ if (*queue == NULL)
+ {
+ f->next = NULL;
+ f->prev = NULL;
+ *queue = f;
+ }
+ else
+ {
+ // Scan to the end of the queue.
+ // We don't maintain a tail pointer to save RAM (queues are nrmally very short).
+ Fiber *last = *queue;
+
+ while (last->next != NULL)
+ last = last->next;
+
+ last->next = f;
+ f->prev = last;
+ f->next = NULL;
+ }
+
+ __enable_irq();
+}
+
+/**
+ * Utility function to the given fiber from whichever queue it is currently stored on.
+ *
+ * @param f the fiber to remove.
+ */
+void dequeue_fiber(Fiber *f)
+{
+ // If this fiber is already dequeued, nothing the there's nothing to do.
+ if (f->queue == NULL)
+ return;
+
+ // Remove this fiber fromm whichever queue it is on.
+ __disable_irq();
+
+ if (f->prev != NULL)
+ f->prev->next = f->next;
+ else
+ *(f->queue) = f->next;
+
+ if(f->next)
+ f->next->prev = f->prev;
+
+ f->next = NULL;
+ f->prev = NULL;
+ f->queue = NULL;
+
+ __enable_irq();
+
+}
+
+/**
+ * Allocates a fiber from the fiber pool if availiable. Otherwise, allocates a new one from the heap.
+ */
+Fiber *getFiberContext()
+{
+ Fiber *f;
+
+ __disable_irq();
+
+ if (fiberPool != NULL)
+ {
+ f = fiberPool;
+ dequeue_fiber(f);
+ // dequeue_fiber() exits with irqs enabled, so no need to do this again!
+ }
+ else
+ {
+ __enable_irq();
+
+ f = new Fiber();
+
+ if (f == NULL)
+ return NULL;
+
+ f->stack_bottom = 0;
+ f->stack_top = 0;
+ }
+
+ // Ensure this fiber is in suitable state for reuse.
+ f->flags = 0;
+ f->tcb.stack_base = CORTEX_M0_STACK_BASE;
+
+ return f;
+}
+
+
+/**
+ * Initialises the Fiber scheduler.
+ * Creates a Fiber context around the calling thread, and adds it to the run queue as the current thread.
+ *
+ * This function must be called once only from the main thread, and before any other Fiber operation.
+ *
+ * @param _messageBus An event model, used to direct the priorities of the scheduler.
+ */
+void scheduler_init(EventModel &_messageBus)
+{
+ // If we're already initialised, then nothing to do.
+ if (fiber_scheduler_running())
+ return;
+
+ // Store a reference to the messageBus provided.
+ // This parameter will be NULL if we're being run without a message bus.
+ messageBus = &_messageBus;
+
+ // Create a new fiber context
+ currentFiber = getFiberContext();
+
+ // Add ourselves to the run queue.
+ queue_fiber(currentFiber, &runQueue);
+
+ // Create the IDLE fiber.
+ // Configure the fiber to directly enter the idle task.
+ idleFiber = getFiberContext();
+ idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
+ idleFiber->tcb.LR = (uint32_t) &idle_task;
+
+ if (messageBus)
+ {
+ // Register to receive events in the NOTIFY channel - this is used to implement wait-notify semantics
+ messageBus->listen(MICROBIT_ID_NOTIFY, MICROBIT_EVT_ANY, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ messageBus->listen(MICROBIT_ID_NOTIFY_ONE, MICROBIT_EVT_ANY, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ }
+
+ // register a period callback to drive the scheduler and any other registered components.
+ new MicroBitSystemTimerCallback(scheduler_tick);
+
+ fiber_flags |= MICROBIT_SCHEDULER_RUNNING;
+}
+
+/**
+ * Determines if the fiber scheduler is operational.
+ *
+ * @return 1 if the fber scheduler is running, 0 otherwise.
+ */
+int fiber_scheduler_running()
+{
+ if (fiber_flags & MICROBIT_SCHEDULER_RUNNING)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * The timer callback, called from interrupt context once every SYSTEM_TICK_PERIOD_MS milliseconds.
+ * This function checks to determine if any fibers blocked on the sleep queue need to be woken up
+ * and made runnable.
+ */
+void scheduler_tick()
+{
+ Fiber *f = sleepQueue;
+ Fiber *t;
+
+ // Check the sleep queue, and wake up any fibers as necessary.
+ while (f != NULL)
+ {
+ t = f->next;
+
+ if (system_timer_current_time() >= f->context)
+ {
+ // Wakey wakey!
+ dequeue_fiber(f);
+ queue_fiber(f,&runQueue);
+ }
+
+ f = t;
+ }
+}
+
+/**
+ * Event callback. Called from an instance of MicroBitMessageBus whenever an event is raised.
+ *
+ * This function checks to determine if any fibers blocked on the wait queue need to be woken up
+ * and made runnable due to the event.
+ *
+ * @param evt the event that has just been raised on an instance of MicroBitMessageBus.
+ */
+void scheduler_event(MicroBitEvent evt)
+{
+ Fiber *f = waitQueue;
+ Fiber *t;
+ int notifyOneComplete = 0;
+
+ // This should never happen.
+ // It is however, safe to simply ignore any events provided, as if no messageBus if recorded,
+ // no fibers are permitted to block on events.
+ if (messageBus == NULL)
+ return;
+
+ // Check the wait queue, and wake up any fibers as necessary.
+ while (f != NULL)
+ {
+ t = f->next;
+
+ // extract the event data this fiber is blocked on.
+ uint16_t id = f->context & 0xFFFF;
+ uint16_t value = (f->context & 0xFFFF0000) >> 16;
+
+ // Special case for the NOTIFY_ONE channel...
+ if ((evt.source == MICROBIT_ID_NOTIFY_ONE && id == MICROBIT_ID_NOTIFY) && (value == MICROBIT_EVT_ANY || value == evt.value))
+ {
+ if (!notifyOneComplete)
+ {
+ // Wakey wakey!
+ dequeue_fiber(f);
+ queue_fiber(f,&runQueue);
+ notifyOneComplete = 1;
+ }
+ }
+
+ // Normal case.
+ else if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value))
+ {
+ // Wakey wakey!
+ dequeue_fiber(f);
+ queue_fiber(f,&runQueue);
+ }
+
+ f = t;
+ }
+
+ // Unregister this event, as we've woken up all the fibers with this match.
+ if (evt.source != MICROBIT_ID_NOTIFY && evt.source != MICROBIT_ID_NOTIFY_ONE)
+ messageBus->ignore(evt.source, evt.value, scheduler_event);
+}
+
+
+/**
+ * Blocks the calling thread for the given period of time.
+ * The calling thread will be immediateley descheduled, and placed onto a
+ * wait queue until the requested amount of time has elapsed.
+ *
+ * @param t The period of time to sleep, in milliseconds.
+ *
+ * @note the fiber will not be be made runnable until after the elapsed time, but there
+ * are no guarantees precisely when the fiber will next be scheduled.
+ */
+void fiber_sleep(unsigned long t)
+{
+ Fiber *f = currentFiber;
+
+ // If the scheduler is not running, then simply perform a spin wait and exit.
+ if (!fiber_scheduler_running())
+ {
+ wait_ms(t);
+ return;
+ }
+
+ // Sleep is a blocking call, so if we're in a fork on block context,
+ // it's time to spawn a new fiber...
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
+ {
+ // Allocate a new fiber. This will come from the fiber pool if availiable,
+ // else a new one will be allocated on the heap.
+ forkedFiber = getFiberContext();
+
+ // If we're out of memory, there's nothing we can do.
+ // keep running in the context of the current thread as a best effort.
+ if (forkedFiber != NULL)
+ f = forkedFiber;
+ }
+
+ // Calculate and store the time we want to wake up.
+ f->context = system_timer_current_time() + t;
+
+ // Remove fiber from the run queue
+ dequeue_fiber(f);
+
+ // Add fiber to the sleep queue. We maintain strict ordering here to reduce lookup times.
+ queue_fiber(f, &sleepQueue);
+
+ // Finally, enter the scheduler.
+ schedule();
+}
+
+/**
+ * Blocks the calling thread until the specified event is raised.
+ * The calling thread will be immediateley descheduled, and placed onto a
+ * wait queue until the requested event is received.
+ *
+ * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
+ *
+ * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
+ *
+ * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
+ *
+ * @code
+ * fiber_wait_for_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ * @endcode
+ *
+ * @note the fiber will not be be made runnable until after the event is raised, but there
+ * are no guarantees precisely when the fiber will next be scheduled.
+ */
+int fiber_wait_for_event(uint16_t id, uint16_t value)
+{
+ int ret = fiber_wake_on_event(id, value);
+
+ if(ret == MICROBIT_OK)
+ schedule();
+
+ return ret;
+}
+
+/**
+ * Configures the fiber context for the current fiber to block on an event ID
+ * and value, but does not deschedule the fiber.
+ *
+ * @param id The ID field of the event to listen for (e.g. MICROBIT_ID_BUTTON_A)
+ *
+ * @param value The value of the event to listen for (e.g. MICROBIT_BUTTON_EVT_CLICK)
+ *
+ * @return MICROBIT_OK, or MICROBIT_NOT_SUPPORTED if the fiber scheduler is not running, or associated with an EventModel.
+ *
+ * @code
+ * fiber_wake_on_event(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * //perform some time critical operation.
+ *
+ * //deschedule the current fiber manually, waiting for the previously configured event.
+ * schedule();
+ * @endcode
+ */
+int fiber_wake_on_event(uint16_t id, uint16_t value)
+{
+ Fiber *f = currentFiber;
+
+ if (messageBus == NULL || !fiber_scheduler_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Sleep is a blocking call, so if we'r ein a fork on block context,
+ // it's time to spawn a new fiber...
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
+ {
+ // Allocate a TCB from the new fiber. This will come from the tread pool if availiable,
+ // else a new one will be allocated on the heap.
+ forkedFiber = getFiberContext();
+
+ // If we're out of memory, there's nothing we can do.
+ // keep running in the context of the current thread as a best effort.
+ if (forkedFiber != NULL)
+ f = forkedFiber;
+ }
+
+ // Encode the event data in the context field. It's handy having a 32 bit core. :-)
+ f->context = value << 16 | id;
+
+ // Remove ourselve from the run queue
+ dequeue_fiber(f);
+
+ // 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.
+ // Special case for teh notify channel, as we always stay registered for that.
+ if (id != MICROBIT_ID_NOTIFY && id != MICROBIT_ID_NOTIFY_ONE)
+ messageBus->listen(id, value, scheduler_event, MESSAGE_BUS_LISTENER_IMMEDIATE);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Executes the given function asynchronously if necessary.
+ *
+ * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
+ * that complete very quickly, bringing unecessary RAM overhead.
+ *
+ * This function takes a snapshot of the current processor context, then attempts to optimistically call the given function directly.
+ * We only create an additional fiber if that function performs a block operation.
+ *
+ * @param entry_fn The function to execute.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int invoke(void (*entry_fn)(void))
+{
+ // Validate our parameters.
+ if (entry_fn == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (!fiber_scheduler_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
+ {
+ // If we attempt a fork on block whilst already in fork n block context,
+ // simply launch a fiber to deal with the request and we're done.
+ create_fiber(entry_fn);
+ return MICROBIT_OK;
+ }
+
+ // Snapshot current context, but also update the Link Register to
+ // refer to our calling function.
+ save_register_context(¤tFiber->tcb);
+
+ // If we're here, there are two possibilities:
+ // 1) We're about to attempt to execute the user code
+ // 2) We've already tried to execute the code, it blocked, and we've backtracked.
+
+ // If we're returning from the user function and we forked another fiber then cleanup and exit.
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
+ {
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
+ return MICROBIT_OK;
+ }
+
+ // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
+ // execute the function directly. If the code tries to block, we detect this and
+ // spawn a thread to deal with it.
+ currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
+ entry_fn();
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
+
+ // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
+ // The fiber will then re-enter the scheduler, so no need for further cleanup.
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
+ release_fiber();
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Executes the given function asynchronously if necessary, and offers the ability to provide a parameter.
+ *
+ * Fibers are often used to run event handlers, however many of these event handlers are very simple functions
+ * that complete very quickly, bringing unecessary RAM. overhead
+ *
+ * This function takes a snapshot of the current fiber context, then attempt to optimistically call the given function directly.
+ * We only create an additional fiber if that function performs a block operation.
+ *
+ * @param entry_fn The function to execute.
+ *
+ * @param param an untyped parameter passed into the entry_fn and completion_fn.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ */
+int invoke(void (*entry_fn)(void *), void *param)
+{
+ // Validate our parameters.
+ if (entry_fn == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (!fiber_scheduler_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ if (currentFiber->flags & (MICROBIT_FIBER_FLAG_FOB | MICROBIT_FIBER_FLAG_PARENT | MICROBIT_FIBER_FLAG_CHILD))
+ {
+ // If we attempt a fork on block whilst already in a fork on block context,
+ // simply launch a fiber to deal with the request and we're done.
+ create_fiber(entry_fn, param);
+ return MICROBIT_OK;
+ }
+
+ // Snapshot current context, but also update the Link Register to
+ // refer to our calling function.
+ save_register_context(¤tFiber->tcb);
+
+ // If we're here, there are two possibilities:
+ // 1) We're about to attempt to execute the user code
+ // 2) We've already tried to execute the code, it blocked, and we've backtracked.
+
+ // If we're returning from the user function and we forked another fiber then cleanup and exit.
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
+ {
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_PARENT;
+ return MICROBIT_OK;
+ }
+
+ // Otherwise, we're here for the first time. Enter FORK ON BLOCK mode, and
+ // execute the function directly. If the code tries to block, we detect this and
+ // spawn a thread to deal with it.
+ currentFiber->flags |= MICROBIT_FIBER_FLAG_FOB;
+ entry_fn(param);
+ currentFiber->flags &= ~MICROBIT_FIBER_FLAG_FOB;
+
+ // If this is is an exiting fiber that for spawned to handle a blocking call, recycle it.
+ // The fiber will then re-enter the scheduler, so no need for further cleanup.
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_CHILD)
+ release_fiber(param);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Launches a fiber.
+ *
+ * @param ep the entry point for the fiber.
+ *
+ * @param cp the completion routine after ep has finished execution
+ */
+void launch_new_fiber(void (*ep)(void), void (*cp)(void))
+{
+ // Execute the thread's entrypoint
+ ep();
+
+ // Execute the thread's completion routine;
+ cp();
+
+ // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
+ release_fiber();
+}
+
+/**
+ * Launches a fiber with a parameter
+ *
+ * @param ep the entry point for the fiber.
+ *
+ * @param cp the completion routine after ep has finished execution
+ *
+ * @param pm the parameter to provide to ep and cp.
+ */
+void launch_new_fiber_param(void (*ep)(void *), void (*cp)(void *), void *pm)
+{
+ // Execute the thread's entrypoint.
+ ep(pm);
+
+ // Execute the thread's completion routine.
+ cp(pm);
+
+ // If we get here, then the completion routine didn't recycle the fiber... so do it anyway. :-)
+ release_fiber(pm);
+}
+
+Fiber *__create_fiber(uint32_t ep, uint32_t cp, uint32_t pm, int parameterised)
+{
+ // Validate our parameters.
+ if (ep == 0 || cp == 0)
+ return NULL;
+
+ // Allocate a TCB from the new fiber. This will come from the fiber pool if availiable,
+ // else a new one will be allocated on the heap.
+ Fiber *newFiber = getFiberContext();
+
+ // If we're out of memory, there's nothing we can do.
+ if (newFiber == NULL)
+ return NULL;
+
+ newFiber->tcb.R0 = (uint32_t) ep;
+ newFiber->tcb.R1 = (uint32_t) cp;
+ newFiber->tcb.R2 = (uint32_t) pm;
+
+ // Set the stack and assign the link register to refer to the appropriate entry point wrapper.
+ newFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
+ newFiber->tcb.LR = parameterised ? (uint32_t) &launch_new_fiber_param : (uint32_t) &launch_new_fiber;
+
+ // Add new fiber to the run queue.
+ queue_fiber(newFiber, &runQueue);
+
+ return newFiber;
+}
+
+/**
+ * Creates a new Fiber, and launches it.
+ *
+ * @param entry_fn The function the new Fiber will begin execution in.
+ *
+ * @param completion_fn The function called when the thread completes execution of entry_fn.
+ * Defaults to release_fiber.
+ *
+ * @return The new Fiber, or NULL if the operation could not be completed.
+ */
+Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void))
+{
+ if (!fiber_scheduler_running())
+ return NULL;
+
+ return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, 0, 0);
+}
+
+
+/**
+ * Creates a new parameterised Fiber, and launches it.
+ *
+ * @param entry_fn The function the new Fiber will begin execution in.
+ *
+ * @param param an untyped parameter passed into the entry_fn and completion_fn.
+ *
+ * @param completion_fn The function called when the thread completes execution of entry_fn.
+ * Defaults to release_fiber.
+ *
+ * @return The new Fiber, or NULL if the operation could not be completed.
+ */
+Fiber *create_fiber(void (*entry_fn)(void *), void *param, void (*completion_fn)(void *))
+{
+ if (!fiber_scheduler_running())
+ return NULL;
+
+ return __create_fiber((uint32_t) entry_fn, (uint32_t)completion_fn, (uint32_t) param, 1);
+}
+
+/**
+ * Exit point for all fibers.
+ *
+ * Any fiber reaching the end of its entry function will return here for recycling.
+ */
+void release_fiber(void *)
+{
+ if (!fiber_scheduler_running())
+ return;
+
+ release_fiber();
+}
+
+/**
+ * Exit point for all fibers.
+ *
+ * Any fiber reaching the end of its entry function will return here for recycling.
+ */
+void release_fiber(void)
+{
+ if (!fiber_scheduler_running())
+ return;
+
+ // Remove ourselves form the runqueue.
+ dequeue_fiber(currentFiber);
+
+ // Add ourselves to the list of free fibers
+ queue_fiber(currentFiber, &fiberPool);
+
+ // Find something else to do!
+ schedule();
+}
+
+/**
+ * Resizes the stack allocation of the current fiber if necessary to hold the system stack.
+ *
+ * If the stack allocation is large enough to hold the current system stack, then this function does nothing.
+ * Otherwise, the the current allocation of the fiber is freed, and a larger block is allocated.
+ *
+ * @param f The fiber context to verify.
+ *
+ * @return The stack depth of the given fiber.
+ */
+void verify_stack_size(Fiber *f)
+{
+ // Ensure the stack buffer is large enough to hold the stack Reallocate if necessary.
+ uint32_t stackDepth;
+ uint32_t bufferSize;
+
+ // Calculate the stack depth.
+ stackDepth = f->tcb.stack_base - ((uint32_t) __get_MSP());
+
+ // Calculate the size of our allocated stack buffer
+ bufferSize = f->stack_top - f->stack_bottom;
+
+ // If we're too small, increase our buffer size.
+ if (bufferSize < stackDepth)
+ {
+ // To ease heap churn, we choose the next largest multple of 32 bytes.
+ bufferSize = (stackDepth + 32) & 0xffffffe0;
+
+ // Release the old memory
+ if (f->stack_bottom != 0)
+ free((void *)f->stack_bottom);
+
+ // Allocate a new one of the appropriate size.
+ f->stack_bottom = (uint32_t) malloc(bufferSize);
+
+ // Recalculate where the top of the stack is and we're done.
+ f->stack_top = f->stack_bottom + bufferSize;
+ }
+}
+
+/**
+ * Determines if any fibers are waiting to be scheduled.
+ *
+ * @return The number of fibers currently on the run queue
+ */
+int scheduler_runqueue_empty()
+{
+ return (runQueue == NULL);
+}
+
+/**
+ * Calls the Fiber scheduler.
+ * The calling Fiber will likely be blocked, and control given to another waiting fiber.
+ * Call this function to yield control of the processor when you have nothing more to do.
+ */
+void schedule()
+{
+ if (!fiber_scheduler_running())
+ return;
+
+ // First, take a reference to the currently running fiber;
+ Fiber *oldFiber = currentFiber;
+
+ // First, see if we're in Fork on Block context. If so, we simply want to store the full context
+ // of the currently running thread in a newly created fiber, and restore the context of the
+ // currently running fiber, back to the point where it entered FOB.
+
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_FOB)
+ {
+ // Record that the fibers have a parent/child relationship
+ currentFiber->flags |= MICROBIT_FIBER_FLAG_PARENT;
+ forkedFiber->flags |= MICROBIT_FIBER_FLAG_CHILD;
+
+ // Define the stack base of the forked fiber to be align with the entry point of the parent fiber
+ forkedFiber->tcb.stack_base = currentFiber->tcb.SP;
+
+ // Ensure the stack allocation of the new fiber is large enough
+ verify_stack_size(forkedFiber);
+
+ // Store the full context of this fiber.
+ save_context(&forkedFiber->tcb, forkedFiber->stack_top);
+
+ // We may now be either the newly created thread, or the one that created it.
+ // if the MICROBIT_FIBER_FLAG_PARENT flag is still set, we're the old thread, so
+ // restore the current fiber to its stored context and we're done.
+ if (currentFiber->flags & MICROBIT_FIBER_FLAG_PARENT)
+ restore_register_context(¤tFiber->tcb);
+
+ // If we're the new thread, we must have been unblocked by the scheduler, so simply return
+ // and continue processing.
+ return;
+ }
+
+ // We're in a normal scheduling context, so perform a round robin algorithm across runnable fibers.
+ // OK - if we've nothing to do, then run the IDLE task (power saving sleep)
+ if (runQueue == NULL)
+ currentFiber = idleFiber;
+
+ else if (currentFiber->queue == &runQueue)
+ // If the current fiber is on the run queue, round robin.
+ currentFiber = currentFiber->next == NULL ? runQueue : currentFiber->next;
+
+ else
+ // Otherwise, just pick the head of the run queue.
+ currentFiber = runQueue;
+
+ if (currentFiber == idleFiber && oldFiber->flags & MICROBIT_FIBER_FLAG_DO_NOT_PAGE)
+ {
+ // Run the idle task right here using the old fiber's stack.
+ // Keep idling while the runqueue is empty, or there is data to process.
+
+ // Run in the context of the original fiber, to preserve state of flags...
+ // as we are running on top of this fiber's stack.
+ currentFiber = oldFiber;
+
+ do
+ {
+ idle();
+ }
+ while (runQueue == NULL);
+
+ // Switch to a non-idle fiber.
+ // If this fiber is the same as the old one then there'll be no switching at all.
+ currentFiber = runQueue;
+ }
+
+ // Swap to the context of the chosen fiber, and we're done.
+ // Don't bother with the overhead of switching if there's only one fiber on the runqueue!
+ if (currentFiber != oldFiber)
+ {
+ // Special case for the idle task, as we don't maintain a stack context (just to save memory).
+ if (currentFiber == idleFiber)
+ {
+ idleFiber->tcb.SP = CORTEX_M0_STACK_BASE - 0x04;
+ idleFiber->tcb.LR = (uint32_t) &idle_task;
+ }
+
+ if (oldFiber == idleFiber)
+ {
+ // Just swap in the new fiber, and discard changes to stack and register context.
+ swap_context(NULL, ¤tFiber->tcb, 0, currentFiber->stack_top);
+ }
+ else
+ {
+ // Ensure the stack allocation of the fiber being scheduled out is large enough
+ verify_stack_size(oldFiber);
+
+ // Schedule in the new fiber.
+ swap_context(&oldFiber->tcb, ¤tFiber->tcb, oldFiber->stack_top, currentFiber->stack_top);
+ }
+ }
+}
+
+/**
+ * Adds a component to the array of idle thread components, which are processed
+ * when the run queue is empty.
+ *
+ * The system timer will poll isIdleCallbackNeeded on each component to determine
+ * if the scheduler should schedule the idle_task imminently.
+ *
+ * @param component The component to add to the array.
+ *
+ * @return MICROBIT_OK on success or MICROBIT_NO_RESOURCES if the fiber components array is full.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
+ *
+ * fiber_add_idle_component(accelerometer);
+ * @endcode
+ */
+int fiber_add_idle_component(MicroBitComponent *component)
+{
+ int i = 0;
+
+ while(idleThreadComponents[i] != NULL && i < MICROBIT_IDLE_COMPONENTS)
+ i++;
+
+ if(i == MICROBIT_IDLE_COMPONENTS)
+ return MICROBIT_NO_RESOURCES;
+
+ idleThreadComponents[i] = component;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Remove a component from the array of idle thread components
+ *
+ * @param component The component to remove from the idle component array.
+ *
+ * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * // heap allocated - otherwise it will be paged out!
+ * MicroBitAccelerometer* accelerometer = new MicroBitAccelerometer(i2c);
+ *
+ * fiber_add_idle_component(accelerometer);
+ *
+ * fiber_remove_idle_component(accelerometer);
+ * @endcode
+ */
+int fiber_remove_idle_component(MicroBitComponent *component)
+{
+ int i = 0;
+
+ while(idleThreadComponents[i] != component && i < MICROBIT_IDLE_COMPONENTS)
+ i++;
+
+ if(i == MICROBIT_IDLE_COMPONENTS)
+ return MICROBIT_INVALID_PARAMETER;
+
+ idleThreadComponents[i] = NULL;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Set of tasks to perform when idle.
+ * Service any background tasks that are required, and attempt a power efficient sleep.
+ */
+void idle()
+{
+ // Service background tasks
+ for(int i = 0; i < MICROBIT_IDLE_COMPONENTS; i++)
+ if(idleThreadComponents[i] != NULL)
+ idleThreadComponents[i]->idleTick();
+
+ // If the above did create any useful work, enter power efficient sleep.
+ if(scheduler_runqueue_empty())
+ __WFE();
+}
+
+/**
+ * The idle task, which is called when the runtime has no fibers that require execution.
+ *
+ * This function typically calls idle().
+ */
+void idle_task()
+{
+ while(1)
+ {
+ idle();
+ schedule();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitFont.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,99 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MicrobitFont
+ * This class represents a font that can be used by the display to render text.
+ *
+ * A MicroBitFont is 5x5.
+ * Each Row is represented by a byte in the array.
+ *
+ * Row Format:
+ * ================================================================
+ * | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
+ * ================================================================
+ * | N/A | N/A | N/A | Col 1 | Col 2 | Col 3 | Col 4 | Col 5 |
+ * | 0x80 | 0x40 | 0x20 | 0x10 | 0x08 | 0x04 | 0x02 | 0x01 |
+ *
+ * Example: { 0x08, 0x08, 0x08, 0x0, 0x08 }
+ *
+ * The above will produce an exclaimation mark on the second column in form the left.
+ *
+ * We could compress further, but the complexity of decode would likely outweigh the gains.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitFont.h"
+
+const unsigned char pendolino3[475] = {
+0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x0, 0x8, 0xa, 0x4a, 0x40, 0x0, 0x0, 0xa, 0x5f, 0xea, 0x5f, 0xea, 0xe, 0xd9, 0x2e, 0xd3, 0x6e, 0x19, 0x32, 0x44, 0x89, 0x33, 0xc, 0x92, 0x4c, 0x92, 0x4d, 0x8, 0x8, 0x0, 0x0, 0x0, 0x4, 0x88, 0x8, 0x8, 0x4, 0x8, 0x4, 0x84, 0x84, 0x88, 0x0, 0xa, 0x44, 0x8a, 0x40, 0x0, 0x4, 0x8e, 0xc4, 0x80, 0x0, 0x0, 0x0, 0x4, 0x88, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x22, 0x44, 0x88, 0x10, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x4, 0x8c, 0x84, 0x84, 0x8e, 0x1c, 0x82, 0x4c, 0x90, 0x1e, 0x1e, 0xc2, 0x44, 0x92, 0x4c, 0x6, 0xca, 0x52, 0x5f, 0xe2, 0x1f, 0xf0, 0x1e, 0xc1, 0x3e, 0x2, 0x44, 0x8e, 0xd1, 0x2e, 0x1f, 0xe2, 0x44, 0x88, 0x10, 0xe, 0xd1, 0x2e, 0xd1, 0x2e, 0xe, 0xd1, 0x2e, 0xc4, 0x88, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x4, 0x80, 0x4, 0x88, 0x2, 0x44, 0x88, 0x4, 0x82, 0x0, 0xe, 0xc0, 0xe, 0xc0, 0x8, 0x4, 0x82, 0x44, 0x88, 0xe, 0xd1, 0x26, 0xc0, 0x4, 0xe, 0xd1, 0x35, 0xb3, 0x6c, 0xc, 0x92, 0x5e, 0xd2, 0x52, 0x1c, 0x92, 0x5c, 0x92, 0x5c, 0xe, 0xd0, 0x10, 0x10, 0xe, 0x1c, 0x92, 0x52, 0x52, 0x5c, 0x1e, 0xd0, 0x1c, 0x90, 0x1e, 0x1e, 0xd0, 0x1c, 0x90, 0x10, 0xe, 0xd0, 0x13, 0x71, 0x2e, 0x12, 0x52, 0x5e, 0xd2, 0x52, 0x1c, 0x88, 0x8, 0x8, 0x1c, 0x1f, 0xe2, 0x42, 0x52, 0x4c, 0x12, 0x54, 0x98, 0x14, 0x92, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x11, 0x3b, 0x75, 0xb1, 0x31, 0x11, 0x39, 0x35, 0xb3, 0x71, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x1c, 0x92, 0x5c, 0x90, 0x10, 0xc, 0x92, 0x52, 0x4c, 0x86, 0x1c, 0x92, 0x5c, 0x92, 0x51, 0xe, 0xd0, 0xc, 0x82, 0x5c, 0x1f, 0xe4, 0x84, 0x84, 0x84, 0x12, 0x52, 0x52, 0x52, 0x4c, 0x11, 0x31, 0x31, 0x2a, 0x44, 0x11, 0x31, 0x35, 0xbb, 0x71, 0x12, 0x52, 0x4c, 0x92, 0x52, 0x11, 0x2a, 0x44, 0x84, 0x84, 0x1e, 0xc4, 0x88, 0x10, 0x1e, 0xe, 0xc8, 0x8, 0x8, 0xe, 0x10, 0x8, 0x4, 0x82, 0x41, 0xe, 0xc2, 0x42, 0x42, 0x4e, 0x4, 0x8a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x8, 0x4, 0x80, 0x0, 0x0, 0x0, 0xe, 0xd2, 0x52, 0x4f, 0x10, 0x10, 0x1c, 0x92, 0x5c, 0x0, 0xe, 0xd0, 0x10, 0xe, 0x2, 0x42, 0x4e, 0xd2, 0x4e, 0xc, 0x92, 0x5c, 0x90, 0xe, 0x6, 0xc8, 0x1c, 0x88, 0x8, 0xe, 0xd2, 0x4e, 0xc2, 0x4c, 0x10, 0x10, 0x1c, 0x92, 0x52, 0x8, 0x0, 0x8, 0x8, 0x8, 0x2, 0x40, 0x2, 0x42, 0x4c, 0x10, 0x14, 0x98, 0x14, 0x92, 0x8, 0x8, 0x8, 0x8, 0x6, 0x0, 0x1b, 0x75, 0xb1, 0x31, 0x0, 0x1c, 0x92, 0x52, 0x52, 0x0, 0xc, 0x92, 0x52, 0x4c, 0x0, 0x1c, 0x92, 0x5c, 0x90, 0x0, 0xe, 0xd2, 0x4e, 0xc2, 0x0, 0xe, 0xd0, 0x10, 0x10, 0x0, 0x6, 0xc8, 0x4, 0x98, 0x8, 0x8, 0xe, 0xc8, 0x7, 0x0, 0x12, 0x52, 0x52, 0x4f, 0x0, 0x11, 0x31, 0x2a, 0x44, 0x0, 0x11, 0x31, 0x35, 0xbb, 0x0, 0x12, 0x4c, 0x8c, 0x92, 0x0, 0x11, 0x2a, 0x44, 0x98, 0x0, 0x1e, 0xc4, 0x88, 0x1e, 0x6, 0xc4, 0x8c, 0x84, 0x86, 0x8, 0x8, 0x8, 0x8, 0x8, 0x18, 0x8, 0xc, 0x88, 0x18, 0x0, 0x0, 0xc, 0x83, 0x60};
+
+
+const unsigned char* MicroBitFont::defaultFont = pendolino3;
+MicroBitFont MicroBitFont::systemFont = MicroBitFont(defaultFont, MICROBIT_FONT_ASCII_END);
+
+/**
+ * Constructor.
+ *
+ * Sets the font represented by this font object.
+ *
+ * @param font A pointer to the beginning of the new font.
+ *
+ * @param asciiEnd the char value at which this font finishes.
+ */
+MicroBitFont::MicroBitFont(const unsigned char* characters, int asciiEnd)
+{
+ this->characters = characters;
+ this->asciiEnd = asciiEnd;
+}
+
+/**
+ * Default Constructor.
+ *
+ * Configures the default font for the display to use.
+ */
+MicroBitFont::MicroBitFont()
+{
+ this->characters = defaultFont;
+ this->asciiEnd = MICROBIT_FONT_ASCII_END;
+}
+
+/**
+ * Modifies the current system font to the given instance of MicroBitFont.
+ *
+ * @param font the new font that will be used to render characters on the display.
+ */
+void MicroBitFont::setSystemFont(MicroBitFont font)
+{
+ MicroBitFont::systemFont = font;
+}
+
+/**
+ * Retreives the font object used for rendering characters on the display.
+ */
+MicroBitFont MicroBitFont::getSystemFont()
+{
+ return MicroBitFont::systemFont;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitHeapAllocator.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,405 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * A simple 32 bit block based memory allocator. This allows one or more memory segments to
+ * be designated as heap storage, and is designed to run in a static memory area or inside the standard C
+ * heap for use by the micro:bit runtime. This is required for several reasons:
+ *
+ * 1) It reduces memory fragmentation due to the high churn sometime placed on the heap
+ * by ManagedTypes, fibers and user code. Underlying heap implentations are often have very simplistic
+ * allocation pilicies and suffer from fragmentation in prolonged use - which can cause programs to
+ * stop working after a period of time. The algorithm implemented here is simple, but highly tolerant to
+ * large amounts of churn.
+ *
+ * 2) It allows us to reuse the 8K of SRAM set aside for SoftDevice as additional heap storage
+ * when BLE is not in use.
+ *
+ * 3) It gives a simple example of how memory allocation works! :-)
+ *
+ * P.S. This is a very simple allocator, therefore not without its weaknesses. Why don't you consider
+ * what these are, and consider the tradeoffs against simplicity...
+ *
+ * @note The need for this should be reviewed in the future, if a different memory allocator is
+ * made availiable in the mbed platform.
+ *
+ * TODO: Consider caching recently freed blocks to improve allocation time.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitHeapAllocator.h"
+#include "MicroBitDevice.h"
+#include "ErrorNo.h"
+
+struct HeapDefinition
+{
+ uint32_t *heap_start; // Physical address of the start of this heap.
+ uint32_t *heap_end; // Physical address of the end of this heap.
+};
+
+// A list of all active heap regions, and their dimensions in memory.
+HeapDefinition heap[MICROBIT_MAXIMUM_HEAPS] = { };
+uint8_t heap_count = 0;
+
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+// Diplays a usage summary about a given heap...
+void microbit_heap_print(HeapDefinition &heap)
+{
+ uint32_t blockSize;
+ uint32_t *block;
+ int totalFreeBlock = 0;
+ int totalUsedBlock = 0;
+ int cols = 0;
+
+ if (heap.heap_start == NULL)
+ {
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("--- HEAP NOT INITIALISED ---\n");
+ return;
+ }
+
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_start : %p\n", heap.heap_start);
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_end : %p\n", heap.heap_end);
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("heap_size : %d\n", (int)heap.heap_end - (int)heap.heap_start);
+
+ // Disable IRQ temporarily to ensure no race conditions!
+ __disable_irq();
+
+ block = heap.heap_start;
+ while (block < heap.heap_end)
+ {
+ blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE;
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("[%c:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4);
+ if (cols++ == 20)
+ {
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\n");
+ cols = 0;
+ }
+
+ if (*block & MICROBIT_HEAP_BLOCK_FREE)
+ totalFreeBlock += blockSize;
+ else
+ totalUsedBlock += blockSize;
+
+ block += blockSize;
+ }
+
+ // Enable Interrupts
+ __enable_irq();
+
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\n");
+
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("mb_total_free : %d\n", totalFreeBlock*4);
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("mb_total_used : %d\n", totalUsedBlock*4);
+}
+
+
+// Diagnostics function. Displays a usage summary about all initialised heaps.
+void microbit_heap_print()
+{
+ for (int i=0; i < heap_count; i++)
+ {
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("\nHEAP %d: \n", i);
+ microbit_heap_print(heap[i]);
+ }
+}
+#endif
+
+void microbit_initialise_heap(HeapDefinition &heap)
+{
+ // Simply mark the entire heap as free.
+ *heap.heap_start = ((uint32_t) heap.heap_end - (uint32_t) heap.heap_start) / MICROBIT_HEAP_BLOCK_SIZE;
+ *heap.heap_start |= MICROBIT_HEAP_BLOCK_FREE;
+}
+
+/**
+ * Create and initialise a given memory region as for heap storage.
+ * After this is called, any future calls to malloc, new, free or delete may use the new heap.
+ * The heap allocator will attempt to allocate memory from heaps in the order that they are created.
+ * i.e. memory will be allocated from first heap created until it is full, then the second heap, and so on.
+ *
+ * @param start The start address of memory to use as a heap region.
+ *
+ * @param end The end address of memory to use as a heap region.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated.
+ *
+ * @note Only code that #includes MicroBitHeapAllocator.h will use this heap. This includes all micro:bit runtime
+ * code, and user code targetting the runtime. External code can choose to include this file, or
+ * simply use the standard heap.
+ */
+int microbit_create_heap(uint32_t start, uint32_t end)
+{
+ // Ensure we don't exceed the maximum number of heap segments.
+ if (heap_count == MICROBIT_MAXIMUM_HEAPS)
+ return MICROBIT_NO_RESOURCES;
+
+ // Sanity check. Ensure range is valid, large enough and word aligned.
+ if (end <= start || end - start < MICROBIT_HEAP_BLOCK_SIZE*2 || end % 4 != 0 || start % 4 != 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Disable IRQ temporarily to ensure no race conditions!
+ __disable_irq();
+
+ // Record the dimensions of this new heap
+ heap[heap_count].heap_start = (uint32_t *)start;
+ heap[heap_count].heap_end = (uint32_t *)end;
+
+ // Initialise the heap as being completely empty and available for use.
+ microbit_initialise_heap(heap[heap_count]);
+ heap_count++;
+
+ // Enable Interrupts
+ __enable_irq();
+
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+ microbit_heap_print();
+#endif
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Create and initialise a heap region within the current the heap region specified
+ * by the linker script.
+ *
+ * If the requested amount is not available, then the amount requested will be reduced
+ * automatically to fit the space available.
+ *
+ * @param ratio The proportion of the underlying heap to allocate.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the heap could not be allocated.
+ */
+int microbit_create_nested_heap(float ratio)
+{
+ uint32_t length;
+ void *p;
+
+ if (ratio <= 0.0 || ratio > 1.0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Snapshot something at the top of the main heap.
+ p = native_malloc(sizeof(uint32_t));
+
+ // Estimate the size left in our heap, taking care to ensure it lands on a word boundary.
+ length = (uint32_t) (((float)(MICROBIT_HEAP_END - (uint32_t)p)) * ratio);
+ length &= 0xFFFFFFFC;
+
+ // Release our reference pointer.
+ native_free(p);
+ p = NULL;
+
+ // Allocate memory for our heap.
+ // We iteratively reduce the size of memory are allocate until it fits within available space.
+ while (p == NULL)
+ {
+ p = native_malloc(length);
+ if (p == NULL)
+ {
+ length -= 32;
+ if (length <= 0)
+ return MICROBIT_NO_RESOURCES;
+ }
+ }
+
+ uint32_t start = (uint32_t) p;
+ microbit_create_heap(start, start + length);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Attempt to allocate a given amount of memory from any of our configured heap areas.
+ *
+ * @param size The amount of memory, in bytes, to allocate.
+ *
+ * @return A pointer to the allocated memory, or NULL if insufficient memory is available.
+ */
+void *microbit_malloc(size_t size, HeapDefinition &heap)
+{
+ uint32_t blockSize = 0;
+ uint32_t blocksNeeded = size % MICROBIT_HEAP_BLOCK_SIZE == 0 ? size / MICROBIT_HEAP_BLOCK_SIZE : size / MICROBIT_HEAP_BLOCK_SIZE + 1;
+ uint32_t *block;
+ uint32_t *next;
+
+ if (size <= 0)
+ return NULL;
+
+ // Account for the index block;
+ blocksNeeded++;
+
+ // Disable IRQ temporarily to ensure no race conditions!
+ __disable_irq();
+
+ // We implement a first fit algorithm with cache to handle rapid churn...
+ // We also defragment free blocks as we search, to optimise this and future searches.
+ block = heap.heap_start;
+ while (block < heap.heap_end)
+ {
+ // If the block is used, then keep looking.
+ if(!(*block & MICROBIT_HEAP_BLOCK_FREE))
+ {
+ block += *block;
+ continue;
+ }
+
+ blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE;
+
+ // We have a free block. Let's see if the subsequent ones are too. If so, we can merge...
+ next = block + blockSize;
+
+ while (*next & MICROBIT_HEAP_BLOCK_FREE)
+ {
+ if (next >= heap.heap_end)
+ break;
+
+ // We can merge!
+ blockSize += (*next & ~MICROBIT_HEAP_BLOCK_FREE);
+ *block = blockSize | MICROBIT_HEAP_BLOCK_FREE;
+
+ next = block + blockSize;
+ }
+
+ // We have a free block. Let's see if it's big enough.
+ // If so, we have a winner.
+ if (blockSize >= blocksNeeded)
+ break;
+
+ // Otherwise, keep looking...
+ block += blockSize;
+ }
+
+ // We're full!
+ if (block >= heap.heap_end)
+ {
+ __enable_irq();
+ return NULL;
+ }
+
+ // If we're at the end of memory or have very near match then mark the whole segment as in use.
+ if (blockSize <= blocksNeeded+1 || block+blocksNeeded+1 >= heap.heap_end)
+ {
+ // Just mark the whole block as used.
+ *block &= ~MICROBIT_HEAP_BLOCK_FREE;
+ }
+ else
+ {
+ // We need to split the block.
+ uint32_t *splitBlock = block + blocksNeeded;
+ *splitBlock = blockSize - blocksNeeded;
+ *splitBlock |= MICROBIT_HEAP_BLOCK_FREE;
+
+ *block = blocksNeeded;
+ }
+
+ // Enable Interrupts
+ __enable_irq();
+
+ return block+1;
+}
+
+/**
+ * Release a given area of memory from the heap.
+ *
+ * @param mem The memory area to release.
+ */
+void *microbit_malloc(size_t size)
+{
+ void *p;
+
+ // Assign the memory from the first heap created that has space.
+ for (int i=0; i < heap_count; i++)
+ {
+ p = microbit_malloc(size, heap[i]);
+ if (p != NULL)
+ {
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: ALLOCATED: %d [%p]\n", size, p);
+#endif
+ return p;
+ }
+ }
+
+ // If we reach here, then either we have no memory available, or our heap spaces
+ // haven't been initialised. Either way, we try the native allocator.
+
+ p = native_malloc(size);
+ if (p != NULL)
+ {
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+ // Keep everything trasparent if we've not been initialised yet
+ if (heap_count > 0)
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p);
+#endif
+ return p;
+ }
+
+ // We're totally out of options (and memory!).
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+ // Keep everything transparent if we've not been initialised yet
+ if (heap_count > 0)
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_malloc: OUT OF MEMORY [%d]\n", size);
+#endif
+
+#if CONFIG_ENABLED(MICROBIT_PANIC_HEAP_FULL)
+ microbit_panic(MICROBIT_OOM);
+#endif
+
+ return NULL;
+}
+
+/**
+ * Release a given area of memory from the heap.
+ *
+ * @param mem The memory area to release.
+ */
+void microbit_free(void *mem)
+{
+ uint32_t *memory = (uint32_t *)mem;
+ uint32_t *cb = memory-1;
+
+#if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG)
+ if (heap_count > 0)
+ if(SERIAL_DEBUG) SERIAL_DEBUG->printf("microbit_free: %p\n", mem);
+#endif
+ // Sanity check.
+ if (memory == NULL)
+ return;
+
+ // If this memory was created from a heap registered with us, free it.
+ for (int i=0; i < heap_count; i++)
+ {
+ if(memory > heap[i].heap_start && memory < heap[i].heap_end)
+ {
+ // The memory block given is part of this heap, so we can simply
+ // flag that this memory area is now free, and we're done.
+ *cb |= MICROBIT_HEAP_BLOCK_FREE;
+ return;
+ }
+ }
+
+ // If we reach here, then the memory is not part of any registered heap.
+ // Forward it to the native heap allocator, and let nature take its course...
+ native_free(mem);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitListener.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,122 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * This structure defines a MicroBitListener used to invoke functions, or member
+ * functions if an instance of EventModel receives an event whose id and value
+ * match this MicroBitListener's id and value.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitListener.h"
+
+/**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param handler A function pointer to call when the event is detected.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent), uint16_t flags)
+{
+ this->id = id;
+ this->value = value;
+ this->cb = handler;
+ this->cb_arg = NULL;
+ this->flags = flags;
+ this->next = NULL;
+ this->evt_queue = NULL;
+}
+
+/**
+ * Constructor.
+ *
+ * Create a new Message Bus Listener, this constructor accepts an additional
+ * parameter "arg", which is passed to the handler.
+ *
+ * @param id The ID of the component you want to listen to.
+ *
+ * @param value The event value you would like to listen to from that component
+ *
+ * @param handler A function pointer to call when the event is detected.
+ *
+ * @param arg A pointer to some data that will be given to the handler.
+ *
+ * @param flags User specified, implementation specific flags, that allow behaviour of this events listener
+ * to be tuned.
+ */
+MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg, uint16_t flags)
+{
+ this->id = id;
+ this->value = value;
+ this->cb_param = handler;
+ this->cb_arg = arg;
+ this->flags = flags | MESSAGE_BUS_LISTENER_PARAMETERISED;
+ this->next = NULL;
+ this->evt_queue = NULL;
+}
+
+/**
+ * Destructor. Ensures all resources used by this listener are freed.
+ */
+MicroBitListener::~MicroBitListener()
+{
+ if(this->flags & MESSAGE_BUS_LISTENER_METHOD)
+ delete cb_method;
+}
+
+/**
+ * Queues and event up to be processed.
+ *
+ * @param e The event to queue
+ */
+void MicroBitListener::queue(MicroBitEvent e)
+{
+ int queueDepth;
+
+ MicroBitEventQueueItem *p = evt_queue;
+
+ if (evt_queue == NULL)
+ evt_queue = new MicroBitEventQueueItem(e);
+ else
+ {
+ queueDepth = 1;
+
+ while (p->next != NULL)
+ {
+ p = p->next;
+ queueDepth++;
+ }
+
+ if (queueDepth < MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
+ p->next = new MicroBitEventQueueItem(e);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/core/MicroBitSystemTimer.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,181 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Definitions for the MicroBit system timer.
+ *
+ * This module provides:
+ *
+ * 1) a concept of global system time since power up
+ * 2) a simple periodic multiplexing API for the underlying mbed implementation.
+ *
+ * The latter is useful to avoid costs associated with multiple mbed Ticker instances
+ * in microbit-dal components, as each incurs a significant additional RAM overhead (circa 80 bytes).
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitSystemTimer.h"
+#include "ErrorNo.h"
+
+/*
+ * Time since power on. Measured in milliseconds.
+ * When stored as an unsigned long, this gives us approx 50 days between rollover, which is ample. :-)
+ */
+static unsigned long ticks = 0;
+static unsigned int tick_period = 0;
+
+// Array of components which are iterated during a system tick
+static MicroBitComponent* systemTickComponents[MICROBIT_SYSTEM_COMPONENTS];
+
+// Periodic callback interrupt
+static Ticker *timer = NULL;
+
+
+/**
+ * Initialises the system wide timer.
+ *
+ * This must be called before any components register to receive periodic periodic callbacks.
+ *
+ * @param timer_period The initial period between interrupts, in millseconds.
+ *
+ * @return MICROBIT_OK on success.
+ */
+int system_timer_init(int period)
+{
+ if (timer == NULL)
+ timer = new Ticker();
+
+ return system_timer_set_period(period);
+}
+
+/**
+ * Reconfigures the system wide timer to the given period in milliseconds.
+ *
+ * @param period the new period of the timer in milliseconds
+ *
+ * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if period < 1
+ */
+int system_timer_set_period(int period)
+{
+ if (period < 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If a timer is already running, ensure it is disabled before reconfiguring.
+ if (tick_period)
+ timer->detach();
+
+ // register a period callback to drive the scheduler and any other registered components.
+ tick_period = period;
+ timer->attach_us(system_timer_tick, period * 1000);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Accessor to obtain the current tick period in milliseconds
+ *
+ * @return the current tick period in milliseconds
+ */
+int system_timer_get_period()
+{
+ return tick_period;
+}
+
+/**
+ * Determines the time since the device was powered on.
+ *
+ * @return the current time since power on in milliseconds
+ */
+unsigned long system_timer_current_time()
+{
+ return ticks;
+}
+
+/**
+ * Timer callback. Called from interrupt context, once per period.
+ *
+ * Simply checks to determine if any fibers blocked on the sleep queue need to be woken up
+ * and made runnable.
+ */
+void system_timer_tick()
+{
+ // increment our real-time counter.
+ ticks += system_timer_get_period();
+
+ // Update any components registered for a callback
+ for(int i = 0; i < MICROBIT_SYSTEM_COMPONENTS; i++)
+ if(systemTickComponents[i] != NULL)
+ systemTickComponents[i]->systemTick();
+}
+
+/**
+ * Add a component to the array of system components. This component will then receive
+ * periodic callbacks, once every tick period.
+ *
+ * @param component The component to add.
+ *
+ * @return MICROBIT_OK on success. MICROBIT_NO_RESOURCES is returned if the component array is full.
+ *
+ * @note The callback will be in interrupt context.
+ */
+int system_timer_add_component(MicroBitComponent *component)
+{
+ int i = 0;
+
+ // If we haven't been initialized, bring up the timer with the default period.
+ if (timer == NULL)
+ system_timer_init(SYSTEM_TICK_PERIOD_MS);
+
+ while(systemTickComponents[i] != NULL && i < MICROBIT_SYSTEM_COMPONENTS)
+ i++;
+
+ if(i == MICROBIT_SYSTEM_COMPONENTS)
+ return MICROBIT_NO_RESOURCES;
+
+ systemTickComponents[i] = component;
+ return MICROBIT_OK;
+}
+
+/**
+ * Remove a component from the array of system components. This component will no longer receive
+ * periodic callbacks.
+ *
+ * @param component The component to remove.
+ *
+ * @return MICROBIT_OK on success. MICROBIT_INVALID_PARAMETER is returned if the given component has not been previously added.
+ */
+int system_timer_remove_component(MicroBitComponent *component)
+{
+ int i = 0;
+
+ while(systemTickComponents[i] != component && i < MICROBIT_SYSTEM_COMPONENTS)
+ i++;
+
+ if(i == MICROBIT_SYSTEM_COMPONENTS)
+ return MICROBIT_INVALID_PARAMETER;
+
+ systemTickComponents[i] = NULL;
+
+ return MICROBIT_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/DynamicPwm.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,331 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for DynamicPwm.
+ *
+ * This class addresses a few issues found in the underlying libraries.
+ * This provides the ability for a neat, clean swap between PWM channels.
+ */
+
+#include "MicroBitConfig.h"
+#include "DynamicPwm.h"
+#include "MicroBitPin.h"
+#include "ErrorNo.h"
+
+DynamicPwm* DynamicPwm::pwms[NO_PWMS] = { NULL };
+
+uint8_t DynamicPwm::lastUsed = NO_PWMS+1; //set it to out of range i.e. 4 so we know it hasn't been used yet.
+
+uint16_t DynamicPwm::sharedPeriod = 0; //set the shared period to an unknown state
+
+/**
+ * Reassigns an already operational PWM channel to the given pin.
+ *
+ * @param pin The desired pin to begin a PWM wave.
+ *
+ * @param oldPin The pin to stop running a PWM wave.
+ *
+ * @param channel_number The GPIOTE channel being used to drive this PWM channel
+ *
+ * TODO: Merge into mbed, at a later date.
+ */
+void gpiote_reinit(PinName pin, PinName oldPin, uint8_t channel_number)
+{
+ // Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
+ NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
+ | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
+ | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
+ | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
+ | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
+
+ NRF_GPIO->OUTCLR = (1 << oldPin);
+ NRF_GPIO->OUTCLR = (1 << pin);
+
+ /* Finally configure the channel as the caller expects. If OUTINIT works, the channel is configured properly.
+ If it does not, the channel output inheritance sets the proper level. */
+
+ NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
+ ((uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos) |
+ ((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
+ ((uint32_t)GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos); // ((uint32_t)GPIOTE_CONFIG_OUTINIT_High <<
+ // GPIOTE_CONFIG_OUTINIT_Pos);//
+
+ /* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
+ __NOP();
+ __NOP();
+ __NOP();
+
+ NRF_TIMER2->CC[channel_number] = 0;
+}
+
+/**
+ * An internal constructor used when allocating a new DynamicPwm instance.
+ *
+ * @param pin the name of the pin for the pwm to target
+ *
+ * @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
+ * or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
+ */
+DynamicPwm::DynamicPwm(PinName pin, PwmPersistence persistence) : PwmOut(pin)
+{
+ this->flags = persistence;
+}
+
+/**
+ * Redirects the pwm channel to point at a different pin.
+ *
+ * @param pin the desired pin to output a PWM wave.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->redirect(p0); // pwm is now produced on p0
+ * @endcode
+ */
+void DynamicPwm::redirect(PinName pin)
+{
+ gpiote_reinit(pin, _pwm.pin, (uint8_t)_pwm.pwm);
+ this->_pwm.pin = pin;
+}
+
+/**
+ * Creates a new DynamicPwm instance, or reuses an existing instance that
+ * has a persistence level of PWM_PERSISTENCE_TRANSIENT.
+ *
+ * @param pin the name of the pin for the pwm to target
+ *
+ * @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
+ * or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
+ *
+ * @return a pointer to the first available free pwm channel - or the first one that can be reallocated. If
+ * no channels are available, NULL is returned.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * @endcode
+ */
+DynamicPwm* DynamicPwm::allocate(PinName pin, PwmPersistence persistence)
+{
+ //try to find a blank spot first
+ for(int i = 0; i < NO_PWMS; i++)
+ {
+ if(pwms[i] == NULL)
+ {
+ lastUsed = i;
+ pwms[i] = new DynamicPwm(pin, persistence);
+ return pwms[i];
+ }
+ }
+
+ //no blank spot.. try to find a transient PWM
+ int channelIterator = (lastUsed + 1 > NO_PWMS - 1) ? 0 : lastUsed + 1;
+
+ while(channelIterator != lastUsed)
+ {
+ if(pwms[channelIterator]->flags & PWM_PERSISTENCE_TRANSIENT)
+ {
+ lastUsed = channelIterator;
+ pwms[channelIterator]->flags = persistence;
+ pwms[channelIterator]->redirect(pin);
+ return pwms[channelIterator];
+ }
+
+ channelIterator = (channelIterator + 1 > NO_PWMS - 1) ? 0 : channelIterator + 1;
+ }
+
+ //if we haven't found a free one, we must try to allocate the last used...
+ if(pwms[lastUsed]->flags & PWM_PERSISTENCE_TRANSIENT)
+ {
+ pwms[lastUsed]->flags = persistence;
+ pwms[lastUsed]->redirect(pin);
+ return pwms[lastUsed];
+ }
+
+ //well if we have no transient channels - we can't give any away! :( return null
+ return (DynamicPwm*)NULL;
+}
+
+/**
+ * Frees this DynamicPwm instance for reuse.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate();
+ * pwm->release();
+ * @endcode
+ */
+void DynamicPwm::release()
+{
+ //free the pwm instance.
+ NRF_GPIOTE->CONFIG[(uint8_t) _pwm.pwm] = 0;
+ pwmout_free(&_pwm);
+ this->flags = PWM_PERSISTENCE_TRANSIENT;
+
+ //set the pointer to this object to null...
+ for(int i =0; i < NO_PWMS; i++)
+ if(pwms[i] == this)
+ {
+ delete pwms[i];
+ pwms[i] = NULL;
+ }
+}
+
+/**
+ * A lightweight wrapper around the super class' write in order to capture the value
+ *
+ * @param value the duty cycle percentage in floating point format.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate();
+ * pwm->write(0.5);
+ * @endcode
+ */
+int DynamicPwm::write(float value){
+
+ if(value < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ PwmOut::write(value);
+ lastValue = value;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Retreives the PinName associated with this DynamicPwm instance.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // returns the PinName n.
+ * pwm->getPinName();
+ * @endcode
+ *
+ * @note This should be used to check that the DynamicPwm instance has not
+ * been reallocated for use in another part of a program.
+ */
+PinName DynamicPwm::getPinName()
+{
+ return _pwm.pin;
+}
+
+/**
+ * Retreives the last value that has been written to this DynamicPwm instance.
+ * in the range 0 - 1023 inclusive.
+ *
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->write(0.5);
+ *
+ * // will return 512.
+ * pwm->getValue();
+ * @endcode
+ */
+int DynamicPwm::getValue()
+{
+ return (float)lastValue * float(MICROBIT_PIN_MAX_OUTPUT);
+}
+
+/**
+ * Retreives the current period in use by the entire PWM module in microseconds.
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->getPeriod();
+ * @endcode
+ */
+int DynamicPwm::getPeriodUs()
+{
+ return sharedPeriod;
+}
+
+/**
+ * Retreives the current period in use by the entire PWM module in milliseconds.
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ * pwm->setPeriodUs(20000);
+ *
+ * // will return 20000
+ * pwm->getPeriod();
+ * @endcode
+ */
+int DynamicPwm::getPeriod()
+{
+ return getPeriodUs() / 1000;
+}
+
+/**
+ * Sets the period used by the WHOLE PWM module.
+ *
+ * @param period the desired period in microseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if period is out of range
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // period now is 20ms
+ * pwm->setPeriodUs(20000);
+ * @endcode
+ *
+ * @note Any changes to the period will AFFECT ALL CHANNELS.
+ */
+int DynamicPwm::setPeriodUs(int period)
+{
+ if(period < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //#HACK this forces mbed to update the pulse width calculation.
+ period_us(period);
+ write(lastValue);
+ sharedPeriod = period;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
+ *
+ * @param period the desired period in milliseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if period is out of range
+ *
+ * Example:
+ * @code
+ * DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
+ *
+ * // period now is 20ms
+ * pwm->setPeriod(20);
+ * @endcode
+ */
+int DynamicPwm::setPeriod(int period)
+{
+ return setPeriodUs(period * 1000);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitAccelerometer.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,710 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBit Accelerometer.
+ *
+ * Represents an implementation of the Freescale MMA8653 3 axis accelerometer
+ * Also includes basic data caching and on demand activation.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitAccelerometer.h"
+#include "ErrorNo.h"
+#include "MicroBitConfig.h"
+#include "MicroBitEvent.h"
+#include "MicroBitCompat.h"
+#include "MicroBitFiber.h"
+
+/**
+ * Configures the accelerometer for G range and sample rate defined
+ * in this object. The nearest values are chosen to those defined
+ * that are supported by the hardware. The instance variables are then
+ * updated to reflect reality.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the accelerometer could not be configured.
+ */
+int MicroBitAccelerometer::configure()
+{
+ const MMA8653SampleRangeConfig *actualSampleRange;
+ const MMA8653SampleRateConfig *actualSampleRate;
+ int result;
+
+ // First find the nearest sample rate to that specified.
+ actualSampleRate = &MMA8653SampleRate[MMA8653_SAMPLE_RATES-1];
+ for (int i=MMA8653_SAMPLE_RATES-1; i>=0; i--)
+ {
+ if(MMA8653SampleRate[i].sample_period < this->samplePeriod * 1000)
+ break;
+
+ actualSampleRate = &MMA8653SampleRate[i];
+ }
+
+ // Now find the nearest sample range to that specified.
+ actualSampleRange = &MMA8653SampleRange[MMA8653_SAMPLE_RANGES-1];
+ for (int i=MMA8653_SAMPLE_RANGES-1; i>=0; i--)
+ {
+ if(MMA8653SampleRange[i].sample_range < this->sampleRange)
+ break;
+
+ actualSampleRange = &MMA8653SampleRange[i];
+ }
+
+ // OK, we have the correct data. Update our local state.
+ this->samplePeriod = actualSampleRate->sample_period / 1000;
+ this->sampleRange = actualSampleRange->sample_range;
+
+ // Now configure the accelerometer accordingly.
+ // First place the device into standby mode, so it can be configured.
+ result = writeCommand(MMA8653_CTRL_REG1, 0x00);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ // Enable high precisiosn mode. This consumes a bit more power, but still only 184 uA!
+ result = writeCommand(MMA8653_CTRL_REG2, 0x10);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ // Enable the INT1 interrupt pin.
+ result = writeCommand(MMA8653_CTRL_REG4, 0x01);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ // Select the DATA_READY event source to be routed to INT1
+ result = writeCommand(MMA8653_CTRL_REG5, 0x01);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ // Configure for the selected g range.
+ result = writeCommand(MMA8653_XYZ_DATA_CFG, actualSampleRange->xyz_data_cfg);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ // Bring the device back online, with 10bit wide samples at the requested frequency.
+ result = writeCommand(MMA8653_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01);
+ if (result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Issues a standard, 2 byte I2C command write to the accelerometer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to write to.
+ *
+ * @param value The value to write.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
+ */
+int MicroBitAccelerometer::writeCommand(uint8_t reg, uint8_t value)
+{
+ uint8_t command[2];
+ command[0] = reg;
+ command[1] = value;
+
+ return i2c.write(address, (const char *)command, 2);
+}
+
+/**
+ * Issues a read command, copying data into the specified buffer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to access.
+ *
+ * @param buffer Memory area to read the data into.
+ *
+ * @param length The number of bytes to read.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
+ */
+int MicroBitAccelerometer::readCommand(uint8_t reg, uint8_t* buffer, int length)
+{
+ int result;
+
+ if (buffer == NULL || length <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ result = i2c.write(address, (const char *)®, 1, true);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ result = i2c.read(address, (char *)buffer, length);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Constructor.
+ * Create a software abstraction of an accelerometer.
+ *
+ * @param _i2c an instance of MicroBitI2C used to communicate with the onboard accelerometer.
+ *
+ * @param address the default I2C address of the accelerometer. Defaults to: MMA8653_DEFAULT_ADDR.
+ *
+ * @param id the unique EventModel id of this component. Defaults to: MICROBIT_ID_ACCELEROMETER
+ *
+ * @code
+ * MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c);
+ * @endcode
+ */
+MicroBitAccelerometer::MicroBitAccelerometer(MicroBitI2C& _i2c, uint16_t address, uint16_t id) : sample(), int1(MICROBIT_PIN_ACCEL_DATA_READY), i2c(_i2c)
+{
+ // Store our identifiers.
+ this->id = id;
+ this->status = 0;
+ this->address = address;
+
+ // Update our internal state for 50Hz at +/- 2g (50Hz has a period af 20ms).
+ this->samplePeriod = 20;
+ this->sampleRange = 2;
+
+ // Initialise gesture history
+ this->sigma = 0;
+ this->lastGesture = GESTURE_NONE;
+ this->currentGesture = GESTURE_NONE;
+ this->shake.x = 0;
+ this->shake.y = 0;
+ this->shake.z = 0;
+ this->shake.count = 0;
+ this->shake.timer = 0;
+
+ // Configure and enable the accelerometer.
+ if (this->configure() == MICROBIT_OK)
+ status |= MICROBIT_COMPONENT_RUNNING;
+}
+
+/**
+ * Attempts to read the 8 bit ID from the accelerometer, this can be used for
+ * validation purposes.
+ *
+ * @return the 8 bit ID returned by the accelerometer, or MICROBIT_I2C_ERROR if the request fails.
+ *
+ * @code
+ * accelerometer.whoAmI();
+ * @endcode
+ */
+int MicroBitAccelerometer::whoAmI()
+{
+ uint8_t data;
+ int result;
+
+ result = readCommand(MMA8653_WHOAMI, &data, 1);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ return (int)data;
+}
+
+/**
+ * Reads the acceleration data from the accelerometer, and stores it in our buffer.
+ * This only happens if the accelerometer indicates that it has new data via int1.
+ *
+ * On first use, this member function will attempt to add this component to the
+ * list of fiber components in order to constantly update the values stored
+ * by this object.
+ *
+ * This technique is called lazy instantiation, and it means that we do not
+ * obtain the overhead from non-chalantly adding this component to fiber components.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the read request fails.
+ */
+int MicroBitAccelerometer::updateSample()
+{
+ if(!(status & MICROBIT_ACCEL_ADDED_TO_IDLE))
+ {
+ fiber_add_idle_component(this);
+ status |= MICROBIT_ACCEL_ADDED_TO_IDLE;
+ }
+
+ // Poll interrupt line from accelerometer.
+ // n.b. Default is Active LO. Interrupt is cleared in data read.
+ if(!int1)
+ {
+ int8_t data[6];
+ int result;
+
+ result = readCommand(MMA8653_OUT_X_MSB, (uint8_t *)data, 6);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ // read MSB values...
+ sample.x = data[0];
+ sample.y = data[2];
+ sample.z = data[4];
+
+ // Normalize the data in the 0..1024 range.
+ sample.x *= 8;
+ sample.y *= 8;
+ sample.z *= 8;
+
+#if CONFIG_ENABLED(USE_ACCEL_LSB)
+ // Add in LSB values.
+ sample.x += (data[1] / 64);
+ sample.y += (data[3] / 64);
+ sample.z += (data[5] / 64);
+#endif
+
+ // Scale into millig (approx!)
+ sample.x *= this->sampleRange;
+ sample.y *= this->sampleRange;
+ sample.z *= this->sampleRange;
+
+ // Indicate that pitch and roll data is now stale, and needs to be recalculated if needed.
+ status &= ~MICROBIT_ACCEL_PITCH_ROLL_VALID;
+
+ // Update gesture tracking
+ updateGesture();
+
+ // Indicate that a new sample is available
+ MicroBitEvent e(id, MICROBIT_ACCELEROMETER_EVT_DATA_UPDATE);
+ }
+
+ return MICROBIT_OK;
+};
+
+/**
+ * A service function.
+ * It calculates the current scalar acceleration of the device (x^2 + y^2 + z^2).
+ * It does not, however, square root the result, as this is a relatively high cost operation.
+ *
+ * This is left to application code should it be needed.
+ *
+ * @return the sum of the square of the acceleration of the device across all axes.
+ */
+int MicroBitAccelerometer::instantaneousAccelerationSquared()
+{
+ updateSample();
+
+ // Use pythagoras theorem to determine the combined force acting on the device.
+ return (int)sample.x*(int)sample.x + (int)sample.y*(int)sample.y + (int)sample.z*(int)sample.z;
+}
+
+/**
+ * Service function.
+ * Determines a 'best guess' posture of the device based on instantaneous data.
+ *
+ * This makes no use of historic data, and forms this input to the filter implemented in updateGesture().
+ *
+ * @return A 'best guess' of the current posture of the device, based on instanataneous data.
+ */
+BasicGesture MicroBitAccelerometer::instantaneousPosture()
+{
+ int force = instantaneousAccelerationSquared();
+ bool shakeDetected = false;
+
+ // Test for shake events.
+ // We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by
+ // a string acceleration to the right, then we can infer a shake. Similarly, we can do this for each acxis (left/right, up/down, in/out).
+ //
+ // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device
+ // has been shaken.
+ if ((getX() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (getX() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x))
+ {
+ shakeDetected = true;
+ shake.x = !shake.x;
+ }
+
+ if ((getY() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (getY() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y))
+ {
+ shakeDetected = true;
+ shake.y = !shake.y;
+ }
+
+ if ((getZ() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (getZ() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z))
+ {
+ shakeDetected = true;
+ shake.z = !shake.z;
+ }
+
+ if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD && ++shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD)
+ shake.shaken = 1;
+
+ if (++shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING)
+ {
+ shake.timer = 0;
+ if (shake.count > 0)
+ {
+ if(--shake.count == 0)
+ shake.shaken = 0;
+ }
+ }
+
+ if (shake.shaken)
+ return GESTURE_SHAKE;
+
+ if (force < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD)
+ return GESTURE_FREEFALL;
+
+ if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD)
+ return GESTURE_3G;
+
+ if (force > MICROBIT_ACCELEROMETER_6G_THRESHOLD)
+ return GESTURE_6G;
+
+ if (force > MICROBIT_ACCELEROMETER_8G_THRESHOLD)
+ return GESTURE_8G;
+
+ // Determine our posture.
+ if (getX() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_LEFT;
+
+ if (getX() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_RIGHT;
+
+ if (getY() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_DOWN;
+
+ if (getY() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_UP;
+
+ if (getZ() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_FACE_UP;
+
+ if (getZ() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE))
+ return GESTURE_FACE_DOWN;
+
+ return GESTURE_NONE;
+}
+
+/**
+ * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote
+ * stability.
+ */
+void MicroBitAccelerometer::updateGesture()
+{
+ // Determine what it looks like we're doing based on the latest sample...
+ BasicGesture g = instantaneousPosture();
+
+ // Perform some low pass filtering to reduce jitter from any detected effects
+ if (g == currentGesture)
+ {
+ if (sigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
+ sigma++;
+ }
+ else
+ {
+ currentGesture = g;
+ sigma = 0;
+ }
+
+ // If we've reached threshold, update our record and raise the relevant event...
+ if (currentGesture != lastGesture && sigma >= MICROBIT_ACCELEROMETER_GESTURE_DAMPING)
+ {
+ lastGesture = currentGesture;
+ MicroBitEvent e(MICROBIT_ID_GESTURE, lastGesture);
+ }
+}
+
+/**
+ * Attempts to set the sample rate of the accelerometer to the specified value (in ms).
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
+ *
+ * @code
+ * // sample rate is now 20 ms.
+ * accelerometer.setPeriod(20);
+ * @endcode
+ *
+ * @note The requested rate may not be possible on the hardware. In this case, the
+ * nearest lower rate is chosen.
+ */
+int MicroBitAccelerometer::setPeriod(int period)
+{
+ this->samplePeriod = period;
+ return this->configure();
+}
+
+/**
+ * Reads the currently configured sample rate of the accelerometer.
+ *
+ * @return The time between samples, in milliseconds.
+ */
+int MicroBitAccelerometer::getPeriod()
+{
+ return (int)samplePeriod;
+}
+
+/**
+ * Attempts to set the sample range of the accelerometer to the specified value (in g).
+ *
+ * @param range The requested sample range of samples, in g.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR is the request fails.
+ *
+ * @code
+ * // the sample range of the accelerometer is now 8G.
+ * accelerometer.setRange(8);
+ * @endcode
+ *
+ * @note The requested range may not be possible on the hardware. In this case, the
+ * nearest lower range is chosen.
+ */
+int MicroBitAccelerometer::setRange(int range)
+{
+ this->sampleRange = range;
+ return this->configure();
+}
+
+/**
+ * Reads the currently configured sample range of the accelerometer.
+ *
+ * @return The sample range, in g.
+ */
+int MicroBitAccelerometer::getRange()
+{
+ return (int)sampleRange;
+}
+
+/**
+ * Reads the value of the X axis from the latest update retrieved from the accelerometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The force measured in the X axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getX();
+ * @endcode
+ */
+int MicroBitAccelerometer::getX(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case SIMPLE_CARTESIAN:
+ return -sample.x;
+
+ case NORTH_EAST_DOWN:
+ return sample.y;
+
+ case RAW:
+ default:
+ return sample.x;
+ }
+}
+
+/**
+ * Reads the value of the Y axis from the latest update retrieved from the accelerometer.
+ *
+ * @return The force measured in the Y axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getY();
+ * @endcode
+ */
+int MicroBitAccelerometer::getY(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case SIMPLE_CARTESIAN:
+ return -sample.y;
+
+ case NORTH_EAST_DOWN:
+ return -sample.x;
+
+ case RAW:
+ default:
+ return sample.y;
+ }
+}
+
+/**
+ * Reads the value of the Z axis from the latest update retrieved from the accelerometer.
+ *
+ * @return The force measured in the Z axis, in milli-g.
+ *
+ * @code
+ * accelerometer.getZ();
+ * @endcode
+ */
+int MicroBitAccelerometer::getZ(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case NORTH_EAST_DOWN:
+ return -sample.z;
+
+ case SIMPLE_CARTESIAN:
+ case RAW:
+ default:
+ return sample.z;
+ }
+}
+
+/**
+ * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The pitch of the device, in degrees.
+ *
+ * @code
+ * accelerometer.getPitch();
+ * @endcode
+ */
+int MicroBitAccelerometer::getPitch()
+{
+ return (int) ((360*getPitchRadians()) / (2*PI));
+}
+
+/**
+ * Provides a rotation compensated pitch of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The pitch of the device, in radians.
+ *
+ * @code
+ * accelerometer.getPitchRadians();
+ * @endcode
+ */
+float MicroBitAccelerometer::getPitchRadians()
+{
+ if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID))
+ recalculatePitchRoll();
+
+ return pitch;
+}
+
+/**
+ * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The roll of the device, in degrees.
+ *
+ * @code
+ * accelerometer.getRoll();
+ * @endcode
+ */
+int MicroBitAccelerometer::getRoll()
+{
+ return (int) ((360*getRollRadians()) / (2*PI));
+}
+
+/**
+ * Provides a rotation compensated roll of the device, based on the latest update retrieved from the accelerometer.
+ *
+ * @return The roll of the device, in radians.
+ *
+ * @code
+ * accelerometer.getRollRadians();
+ * @endcode
+ */
+float MicroBitAccelerometer::getRollRadians()
+{
+ if (!(status & MICROBIT_ACCEL_PITCH_ROLL_VALID))
+ recalculatePitchRoll();
+
+ return roll;
+}
+
+/**
+ * Recalculate roll and pitch values for the current sample.
+ *
+ * @note We only do this at most once per sample, as the necessary trigonemteric functions are rather
+ * heavyweight for a CPU without a floating point unit.
+ */
+void MicroBitAccelerometer::recalculatePitchRoll()
+{
+ float x = (float) getX(NORTH_EAST_DOWN);
+ float y = (float) getY(NORTH_EAST_DOWN);
+ float z = (float) getZ(NORTH_EAST_DOWN);
+
+ roll = atan2(getY(NORTH_EAST_DOWN), getZ(NORTH_EAST_DOWN));
+ pitch = atan(-x / (y*sin(roll) + z*cos(roll)));
+ status |= MICROBIT_ACCEL_PITCH_ROLL_VALID;
+}
+
+/**
+ * Retrieves the last recorded gesture.
+ *
+ * @return The last gesture that was detected.
+ *
+ * Example:
+ * @code
+ * MicroBitDisplay display;
+ *
+ * if (accelerometer.getGesture() == SHAKE)
+ * display.scroll("SHAKE!");
+ * @endcode
+ */
+BasicGesture MicroBitAccelerometer::getGesture()
+{
+ return lastGesture;
+}
+
+/**
+ * A periodic callback invoked by the fiber scheduler idle thread.
+ *
+ * Internally calls updateSample().
+ */
+void MicroBitAccelerometer::idleTick()
+{
+ updateSample();
+}
+
+/**
+ * Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
+ *
+ * We check if any data is ready for reading by checking the interrupt flag on the accelerometer.
+ */
+int MicroBitAccelerometer::isIdleCallbackNeeded()
+{
+ return !int1;
+}
+
+/**
+ * Destructor for MicroBitAccelerometer, where we deregister from the array of fiber components.
+ */
+MicroBitAccelerometer::~MicroBitAccelerometer()
+{
+ fiber_remove_idle_component(this);
+}
+
+const MMA8653SampleRangeConfig MMA8653SampleRange[MMA8653_SAMPLE_RANGES] = {
+ {2, 0},
+ {4, 1},
+ {8, 2}
+};
+
+const MMA8653SampleRateConfig MMA8653SampleRate[MMA8653_SAMPLE_RATES] = {
+ {1250, 0x00},
+ {2500, 0x08},
+ {5000, 0x10},
+ {10000, 0x18},
+ {20000, 0x20},
+ {80000, 0x28},
+ {160000, 0x30},
+ {640000, 0x38}
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitButton.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,162 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitButton.h"
+#include "MicroBitSystemTimer.h"
+
+/**
+ * Constructor.
+ *
+ * Create a software representation of a button.
+ *
+ * @param name the physical pin on the processor that should be used as input.
+ *
+ * @param id the ID of the new MicroBitButton object.
+ *
+ * @param eventConfiguration Configures the events that will be generated by this MicroBitButton instance.
+ * Defaults to MICROBIT_BUTTON_ALL_EVENTS.
+ *
+ * @param mode the configuration of internal pullups/pulldowns, as defined in the mbed PinMode class. PullNone by default.
+ *
+ * @code
+ * buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
+ * @endcode
+ */
+MicroBitButton::MicroBitButton(PinName name, uint16_t id, MicroBitButtonEventConfiguration eventConfiguration, PinMode mode) : pin(name, mode)
+{
+ this->id = id;
+ this->name = name;
+ this->eventConfiguration = eventConfiguration;
+ this->downStartTime = 0;
+ this->sigma = 0;
+ system_timer_add_component(this);
+}
+
+/**
+ * Changes the event configuration used by this button to the given MicroBitButtonEventConfiguration.
+ *
+ * All subsequent events generated by this button will then be informed by this configuraiton.
+ *
+ * @param config The new configuration for this button. Legal values are MICROBIT_BUTTON_ALL_EVENTS or MICROBIT_BUTTON_SIMPLE_EVENTS.
+ *
+ * Example:
+ * @code
+ * // Configure a button to generate all possible events.
+ * buttonA.setEventConfiguration(MICROBIT_BUTTON_ALL_EVENTS);
+ *
+ * // Configure a button to suppress MICROBIT_BUTTON_EVT_CLICK and MICROBIT_BUTTON_EVT_LONG_CLICK events.
+ * buttonA.setEventConfiguration(MICROBIT_BUTTON_SIMPLE_EVENTS);
+ * @endcode
+ */
+void MicroBitButton::setEventConfiguration(MicroBitButtonEventConfiguration config)
+{
+ this->eventConfiguration = config;
+}
+
+/**
+ * periodic callback from MicroBit system timer.
+ *
+ * Check for state change for this button, and fires various events on a state change.
+ */
+void MicroBitButton::systemTick()
+{
+ //
+ // If the pin is pulled low (touched), increment our culumative counter.
+ // otherwise, decrement it. We're essentially building a lazy follower here.
+ // This makes the output debounced for buttons, and desensitizes touch sensors
+ // (particularly in environments where there is mains noise!)
+ //
+ if(!pin)
+ {
+ if (sigma < MICROBIT_BUTTON_SIGMA_MAX)
+ sigma++;
+ }
+ else
+ {
+ if (sigma > MICROBIT_BUTTON_SIGMA_MIN)
+ sigma--;
+ }
+
+ // Check to see if we have off->on state change.
+ if(sigma > MICROBIT_BUTTON_SIGMA_THRESH_HI && !(status & MICROBIT_BUTTON_STATE))
+ {
+ // Record we have a state change, and raise an event.
+ status |= MICROBIT_BUTTON_STATE;
+ MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN);
+
+ //Record the time the button was pressed.
+ downStartTime = system_timer_current_time();
+ }
+
+ // Check to see if we have on->off state change.
+ if(sigma < MICROBIT_BUTTON_SIGMA_THRESH_LO && (status & MICROBIT_BUTTON_STATE))
+ {
+ status = 0;
+ MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_UP);
+
+ if (eventConfiguration == MICROBIT_BUTTON_ALL_EVENTS)
+ {
+ //determine if this is a long click or a normal click and send event
+ if((system_timer_current_time() - downStartTime) >= MICROBIT_BUTTON_LONG_CLICK_TIME)
+ MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_LONG_CLICK);
+ else
+ MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK);
+ }
+ }
+
+ //if button is pressed and the hold triggered event state is not triggered AND we are greater than the button debounce value
+ if((status & MICROBIT_BUTTON_STATE) && !(status & MICROBIT_BUTTON_STATE_HOLD_TRIGGERED) && (system_timer_current_time() - downStartTime) >= MICROBIT_BUTTON_HOLD_TIME)
+ {
+ //set the hold triggered event flag
+ status |= MICROBIT_BUTTON_STATE_HOLD_TRIGGERED;
+
+ //fire hold event
+ MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_HOLD);
+ }
+}
+
+/**
+ * Tests if this Button is currently pressed.
+ *
+ * @code
+ * if(buttonA.isPressed())
+ * display.scroll("Pressed!");
+ * @endcode
+ *
+ * @return 1 if this button is pressed, 0 otherwise.
+ */
+int MicroBitButton::isPressed()
+{
+ return status & MICROBIT_BUTTON_STATE ? 1 : 0;
+}
+
+/**
+ * Destructor for MicroBitButton, where we deregister this instance from the array of fiber components.
+ */
+MicroBitButton::~MicroBitButton()
+{
+ system_timer_remove_component(this);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitCompass.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,778 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBit Compass.
+ *
+ * Represents an implementation of the Freescale MAG3110 I2C Magnetmometer.
+ * Also includes basic caching, calibration and on demand activation.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitCompass.h"
+#include "MicroBitFiber.h"
+#include "ErrorNo.h"
+
+/**
+ * An initialisation member function used by the many constructors of MicroBitCompass.
+ *
+ * @param id the unique identifier for this compass instance.
+ *
+ * @param address the base address of the magnetometer on the i2c bus.
+ */
+void MicroBitCompass::init(uint16_t id, uint16_t address)
+{
+ this->id = id;
+ this->address = address;
+
+ // Select 10Hz update rate, with oversampling, and enable the device.
+ this->samplePeriod = 100;
+ this->configure();
+
+ // Assume that we have no calibration information.
+ status &= ~MICROBIT_COMPASS_STATUS_CALIBRATED;
+
+ if(this->storage != NULL)
+ {
+ KeyValuePair *calibrationData = storage->get(ManagedString("compassCal"));
+
+ if(calibrationData != NULL)
+ {
+ CompassSample storedSample = CompassSample();
+
+ memcpy(&storedSample, calibrationData->value, sizeof(CompassSample));
+
+ setCalibration(storedSample);
+
+ delete calibrationData;
+ }
+ }
+
+ // Indicate that we're up and running.
+ status |= MICROBIT_COMPONENT_RUNNING;
+}
+
+/**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _accelerometer an instance of the accelerometer, used for tilt compensation.
+ *
+ * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer(i2c);
+ *
+ * MicroBitStorage storage;
+ *
+ * MicroBitCompass compass(i2c, accelerometer, storage);
+ * @endcode
+ */
+MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, MicroBitStorage& _storage, uint16_t address, uint16_t id) :
+ average(),
+ sample(),
+ int1(MICROBIT_PIN_COMPASS_DATA_READY),
+ i2c(_i2c),
+ accelerometer(&_accelerometer),
+ storage(&_storage)
+{
+ init(id, address);
+}
+
+/**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _accelerometer an instance of the accelerometer, used for tilt compensation.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitAccelerometer accelerometer(i2c);
+ *
+ * MicroBitCompass compass(i2c, accelerometer, storage);
+ * @endcode
+ */
+MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitAccelerometer& _accelerometer, uint16_t address, uint16_t id) :
+ average(),
+ sample(),
+ int1(MICROBIT_PIN_COMPASS_DATA_READY),
+ i2c(_i2c),
+ accelerometer(&_accelerometer),
+ storage(NULL)
+{
+ init(id, address);
+}
+
+/**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param _storage an instance of MicroBitStorage, used to persist calibration data across resets.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitStorage storage;
+ *
+ * MicroBitCompass compass(i2c, storage);
+ * @endcode
+ */
+MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, MicroBitStorage& _storage, uint16_t address, uint16_t id) :
+ average(),
+ sample(),
+ int1(MICROBIT_PIN_COMPASS_DATA_READY),
+ i2c(_i2c),
+ accelerometer(NULL),
+ storage(&_storage)
+{
+ init(id, address);
+}
+
+/**
+ * Constructor.
+ * Create a software representation of an e-compass.
+ *
+ * @param _i2c an instance of i2c, which the compass is accessible from.
+ *
+ * @param address the default address for the compass register on the i2c bus. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @param id the ID of the new MicroBitCompass object. Defaults to MAG3110_DEFAULT_ADDR.
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ *
+ * MicroBitCompass compass(i2c);
+ * @endcode
+ */
+MicroBitCompass::MicroBitCompass(MicroBitI2C& _i2c, uint16_t address, uint16_t id) :
+ average(),
+ sample(),
+ int1(MICROBIT_PIN_COMPASS_DATA_READY),
+ i2c(_i2c),
+ accelerometer(NULL),
+ storage(NULL)
+{
+ init(id, address);
+}
+
+/**
+ * Issues a standard, 2 byte I2C command write to the accelerometer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to write to.
+ *
+ * @param value The value to write.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if the the write request failed.
+ */
+int MicroBitCompass::writeCommand(uint8_t reg, uint8_t value)
+{
+ uint8_t command[2];
+ command[0] = reg;
+ command[1] = value;
+
+ return i2c.write(address, (const char *)command, 2);
+}
+
+/**
+ * Issues a read command, copying data into the specified buffer.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the register to access.
+ *
+ * @param buffer Memory area to read the data into.
+ *
+ * @param length The number of bytes to read.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER or MICROBIT_I2C_ERROR if the the read request failed.
+ */
+int MicroBitCompass::readCommand(uint8_t reg, uint8_t* buffer, int length)
+{
+ int result;
+
+ if (buffer == NULL || length <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ result = i2c.write(address, (const char *)®, 1, true);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ result = i2c.read(address, (char *)buffer, length);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Issues a read of a given address, and returns the value.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the 16 bit register to access.
+ *
+ * @return The register value, interpreted as a 16 but signed value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed.
+ */
+int MicroBitCompass::read16(uint8_t reg)
+{
+ uint8_t cmd[2];
+ int result;
+
+ cmd[0] = reg;
+ result = i2c.write(address, (const char *)cmd, 1);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+
+ result = i2c.read(address, (char *)cmd, 2);
+ if (result !=0)
+ return MICROBIT_I2C_ERROR;
+
+ return (int16_t) ((cmd[1] | (cmd[0] << 8))); //concatenate the MSB and LSB
+}
+
+/**
+ * Issues a read of a given address, and returns the value.
+ *
+ * Blocks the calling thread until complete.
+ *
+ * @param reg The address of the 16 bit register to access.
+ *
+ * @return The register value, interpreted as a 8 bit unsigned value, or MICROBIT_I2C_ERROR if the magnetometer could not be accessed.
+ */
+int MicroBitCompass::read8(uint8_t reg)
+{
+ uint8_t data;
+ int result;
+
+ data = 0;
+ result = readCommand(reg, (uint8_t*) &data, 1);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+ return data;
+}
+
+/**
+ * Calculates a tilt compensated bearing of the device, using the accelerometer.
+ */
+int MicroBitCompass::tiltCompensatedBearing()
+{
+ // Precompute the tilt compensation parameters to improve readability.
+ float phi = accelerometer->getRollRadians();
+ float theta = accelerometer->getPitchRadians();
+
+ float x = (float) getX(NORTH_EAST_DOWN);
+ float y = (float) getY(NORTH_EAST_DOWN);
+ float z = (float) getZ(NORTH_EAST_DOWN);
+
+ // Precompute cos and sin of pitch and roll angles to make the calculation a little more efficient.
+ float sinPhi = sin(phi);
+ float cosPhi = cos(phi);
+ float sinTheta = sin(theta);
+ float cosTheta = cos(theta);
+
+ float bearing = (360*atan2(z*sinPhi - y*cosPhi, x*cosTheta + y*sinTheta*sinPhi + z*sinTheta*cosPhi)) / (2*PI);
+
+ if (bearing < 0)
+ bearing += 360.0;
+
+ return (int) bearing;
+}
+
+/**
+ * Calculates a non-tilt compensated bearing of the device.
+ */
+int MicroBitCompass::basicBearing()
+{
+ updateSample();
+
+ float bearing = (atan2((double)(sample.y - average.y),(double)(sample.x - average.x)))*180/PI;
+
+ if (bearing < 0)
+ bearing += 360.0;
+
+ return (int)(360.0 - bearing);
+}
+
+/**
+ * Gets the current heading of the device, relative to magnetic north.
+ *
+ * If the compass is not calibrated, it will raise the MICROBIT_COMPASS_EVT_CALIBRATE event.
+ *
+ * Users wishing to implement their own calibration algorithms should listen for this event,
+ * using MESSAGE_BUS_LISTENER_IMMEDIATE model. This ensures that calibration is complete before
+ * the user program continues.
+ *
+ * @return the current heading, in degrees. Or MICROBIT_CALIBRATION_IN_PROGRESS if the compass is calibrating.
+ *
+ * @code
+ * compass.heading();
+ * @endcode
+ */
+int MicroBitCompass::heading()
+{
+ if(status & MICROBIT_COMPASS_STATUS_CALIBRATING)
+ return MICROBIT_CALIBRATION_IN_PROGRESS;
+
+ if(!(status & MICROBIT_COMPASS_STATUS_CALIBRATED))
+ calibrate();
+
+ if(accelerometer != NULL)
+ return tiltCompensatedBearing();
+
+ return basicBearing();
+}
+
+/**
+ * Updates the local sample, only if the compass indicates that
+ * data is stale.
+ *
+ * @note Can be used to trigger manual updates, if the device is running without a scheduler.
+ * Also called internally by all get[X,Y,Z]() member functions.
+ */
+int MicroBitCompass::updateSample()
+{
+ /**
+ * Adds the compass to idle, if it hasn't been added already.
+ * This is an optimisation so that the compass is only added on first 'use'.
+ */
+ if(!(status & MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE))
+ {
+ fiber_add_idle_component(this);
+ status |= MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE;
+ }
+
+ // Poll interrupt line from compass (Active HI).
+ // Interrupt is cleared on data read of MAG_OUT_X_MSB.
+ if(int1)
+ {
+ sample.x = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_X_MSB));
+ sample.y = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_Y_MSB));
+ sample.z = MAG3110_NORMALIZE_SAMPLE((int) read16(MAG_OUT_Z_MSB));
+
+ // Indicate that a new sample is available
+ MicroBitEvent e(id, MICROBIT_COMPASS_EVT_DATA_UPDATE);
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Periodic callback from MicroBit idle thread.
+ *
+ * Calls updateSample().
+ */
+void MicroBitCompass::idleTick()
+{
+ updateSample();
+}
+
+/**
+ * Reads the value of the X axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the X axis, in nano teslas.
+ *
+ * @code
+ * compass.getX();
+ * @endcode
+ */
+int MicroBitCompass::getX(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case SIMPLE_CARTESIAN:
+ return sample.x - average.x;
+
+ case NORTH_EAST_DOWN:
+ return -(sample.y - average.y);
+
+ case RAW:
+ default:
+ return sample.x;
+ }
+}
+
+/**
+ * Reads the value of the Y axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the Y axis, in nano teslas.
+ *
+ * @code
+ * compass.getY();
+ * @endcode
+ */
+int MicroBitCompass::getY(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case SIMPLE_CARTESIAN:
+ return -(sample.y - average.y);
+
+ case NORTH_EAST_DOWN:
+ return (sample.x - average.x);
+
+ case RAW:
+ default:
+ return sample.y;
+ }
+}
+
+/**
+ * Reads the value of the Z axis from the latest update retrieved from the magnetometer.
+ *
+ * @param system The coordinate system to use. By default, a simple cartesian system is provided.
+ *
+ * @return The magnetic force measured in the Z axis, in nano teslas.
+ *
+ * @code
+ * compass.getZ();
+ * @endcode
+ */
+int MicroBitCompass::getZ(MicroBitCoordinateSystem system)
+{
+ updateSample();
+
+ switch (system)
+ {
+ case SIMPLE_CARTESIAN:
+ case NORTH_EAST_DOWN:
+ return -(sample.z - average.z);
+
+ case RAW:
+ default:
+ return sample.z;
+ }
+}
+
+/**
+ * Determines the overall magnetic field strength based on the latest update from the magnetometer.
+ *
+ * @return The magnetic force measured across all axis, in nano teslas.
+ *
+ * @code
+ * compass.getFieldStrength();
+ * @endcode
+ */
+int MicroBitCompass::getFieldStrength()
+{
+ double x = getX();
+ double y = getY();
+ double z = getZ();
+
+ return (int) sqrt(x*x + y*y + z*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.
+ *
+ * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be configured.
+ */
+int MicroBitCompass::configure()
+{
+ const MAG3110SampleRateConfig *actualSampleRate;
+ int result;
+
+ // First, take the device offline, so it can be configured.
+ result = writeCommand(MAG_CTRL_REG1, 0x00);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+ // Wait for the part to enter standby mode...
+ while(1)
+ {
+ // Read the status of the part...
+ // If we can't communicate with it over I2C, pass on the error.
+ result = this->read8(MAG_SYSMOD);
+ if (result == MICROBIT_I2C_ERROR)
+ return MICROBIT_I2C_ERROR;
+
+ // if the part in in standby, we're good to carry on.
+ if((result & 0x03) == 0)
+ break;
+
+ // Perform a power efficient sleep...
+ fiber_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;
+ result = writeCommand(MAG_CTRL_REG2, 0xA0);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+
+ // Bring the device online, with the requested sample frequency.
+ result = writeCommand(MAG_CTRL_REG1, actualSampleRate->ctrl_reg1 | 0x01);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Attempts to set the sample rate of the compass to the specified value (in ms).
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @return MICROBIT_OK or MICROBIT_I2C_ERROR if the magnetometer could not be updated.
+ *
+ * @code
+ * // sample rate is now 20 ms.
+ * compass.setPeriod(20);
+ * @endcode
+ *
+ * @note The requested rate may not be possible on the hardware. In this case, the
+ * nearest lower rate is chosen.
+ */
+int MicroBitCompass::setPeriod(int period)
+{
+ this->samplePeriod = period;
+ return 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 read the 8 bit ID from the magnetometer, this can be used for
+ * validation purposes.
+ *
+ * @return the 8 bit ID returned by the magnetometer, or MICROBIT_I2C_ERROR if the request fails.
+ *
+ * @code
+ * compass.whoAmI();
+ * @endcode
+ */
+int MicroBitCompass::whoAmI()
+{
+ uint8_t data;
+ int result;
+
+ result = readCommand(MAG_WHOAMI, &data, 1);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+ return (int)data;
+}
+
+/**
+ * Reads the current die temperature of the compass.
+ *
+ * @return the temperature in degrees celsius, or MICROBIT_I2C_ERROR if the temperature reading could not be retreived
+ * from the accelerometer.
+ */
+int MicroBitCompass::readTemperature()
+{
+ int8_t temperature;
+ int result;
+
+ result = readCommand(MAG_DIE_TEMP, (uint8_t *)&temperature, 1);
+ if (result != MICROBIT_OK)
+ return MICROBIT_I2C_ERROR;
+
+ return temperature;
+}
+
+/**
+ * Perform a calibration of the compass.
+ *
+ * This method will be called automatically if a user attempts to read a compass value when
+ * the compass is uncalibrated. It can also be called at any time by the user.
+ *
+ * The method will only return once the compass has been calibrated.
+ *
+ * @return MICROBIT_OK, MICROBIT_I2C_ERROR if the magnetometer could not be accessed,
+ * or MICROBIT_CALIBRATION_REQUIRED if the calibration algorithm failed to complete successfully.
+ *
+ * @note THIS MUST BE CALLED TO GAIN RELIABLE VALUES FROM THE COMPASS
+ */
+int MicroBitCompass::calibrate()
+{
+ // Only perform one calibration process at a time.
+ if(isCalibrating())
+ return MICROBIT_CALIBRATION_IN_PROGRESS;
+
+ updateSample();
+
+ // Delete old calibration data
+ clearCalibration();
+
+ // Record that we've started calibrating.
+ status |= MICROBIT_COMPASS_STATUS_CALIBRATING;
+
+ // Launch any registred calibration alogrithm visialisation
+ MicroBitEvent(id, MICROBIT_COMPASS_EVT_CALIBRATE);
+
+ // Record that we've finished calibrating.
+ status &= ~MICROBIT_COMPASS_STATUS_CALIBRATING;
+
+ // If there are no changes to our sample data, we either have no calibration algorithm, or it couldn't complete succesfully.
+ if(!(status & MICROBIT_COMPASS_STATUS_CALIBRATED))
+ return MICROBIT_CALIBRATION_REQUIRED;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configure the compass to use the calibration data that is supplied to this call.
+ *
+ * Calibration data is comprised of the perceived zero offset of each axis of the compass.
+ *
+ * After calibration this should now take into account trimming errors in the magnetometer,
+ * and any "hard iron" offsets on the device.
+ *
+ * @param calibration A CompassSample containing the offsets for the x, y and z axis.
+ */
+void MicroBitCompass::setCalibration(CompassSample calibration)
+{
+ if(this->storage != NULL)
+ this->storage->put(ManagedString("compassCal"), (uint8_t *)&calibration);
+
+ average = calibration;
+ status |= MICROBIT_COMPASS_STATUS_CALIBRATED;
+}
+
+/**
+ * Provides the calibration data currently in use by the compass.
+ *
+ * More specifically, the x, y and z zero offsets of the compass.
+ *
+ * @return calibration A CompassSample containing the offsets for the x, y and z axis.
+ */
+CompassSample MicroBitCompass::getCalibration()
+{
+ return average;
+}
+
+/**
+ * Returns 0 or 1. 1 indicates that the compass is calibrated, zero means the compass requires calibration.
+ */
+int MicroBitCompass::isCalibrated()
+{
+ return status & MICROBIT_COMPASS_STATUS_CALIBRATED;
+}
+
+/**
+ * Returns 0 or 1. 1 indicates that the compass is calibrating, zero means the compass is not currently calibrating.
+ */
+int MicroBitCompass::isCalibrating()
+{
+ return status & MICROBIT_COMPASS_STATUS_CALIBRATING;
+}
+
+/**
+ * Clears the calibration held in persistent storage, and sets the calibrated flag to zero.
+ */
+void MicroBitCompass::clearCalibration()
+{
+ status &= ~MICROBIT_COMPASS_STATUS_CALIBRATED;
+}
+
+/**
+ * Returns 0 or 1. 1 indicates data is waiting to be read, zero means data is not ready to be read.
+ */
+int MicroBitCompass::isIdleCallbackNeeded()
+{
+ // 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;
+}
+
+/**
+ * Destructor for MicroBitCompass, where we deregister this instance from the array of fiber components.
+ */
+MicroBitCompass::~MicroBitCompass()
+{
+ fiber_remove_idle_component(this);
+}
+
+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
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitCompassCalibrator.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,188 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitCompassCalibrator.h"
+#include "EventModel.h"
+#include "Matrix4.h"
+
+/**
+ * Constructor.
+ *
+ * Create an object capable of calibrating the compass.
+ *
+ * The algorithm uses an accelerometer to ensure that a broad range of sample data has been gathered
+ * from the compass module, then performs a least mean squares optimisation of the
+ * results to determine the calibration data for the compass.
+ *
+ * The LED matrix display is used to provide feedback to the user on the gestures required.
+ *
+ * @param compass The compass instance to calibrate.
+ *
+ * @param accelerometer The accelerometer to gather contextual data from.
+ *
+ * @param display The LED matrix to display user feedback on.
+ */
+MicroBitCompassCalibrator::MicroBitCompassCalibrator(MicroBitCompass& _compass, MicroBitAccelerometer& _accelerometer, MicroBitDisplay& _display) : compass(_compass), accelerometer(_accelerometer), display(_display)
+{
+ if (EventModel::defaultEventBus)
+ EventModel::defaultEventBus->listen(MICROBIT_ID_COMPASS, MICROBIT_COMPASS_EVT_CALIBRATE, this, &MicroBitCompassCalibrator::calibrate, MESSAGE_BUS_LISTENER_IMMEDIATE);
+}
+
+/**
+ * Performs a simple game that in parallel, calibrates the compass.
+ *
+ * This function is executed automatically when the user requests a compass bearing, and compass calibration is required.
+ *
+ * This function is, by design, synchronous and only returns once calibration is complete.
+ */
+void MicroBitCompassCalibrator::calibrate(MicroBitEvent)
+{
+ struct Point
+ {
+ uint8_t x;
+ uint8_t y;
+ uint8_t on;
+ };
+
+ const int PERIMETER_POINTS = 12;
+ const int PIXEL1_THRESHOLD = 200;
+ const int PIXEL2_THRESHOLD = 800;
+
+ wait_ms(100);
+
+ Matrix4 X(PERIMETER_POINTS, 4);
+ Point perimeter[PERIMETER_POINTS] = {{1,0,0}, {2,0,0}, {3,0,0}, {4,1,0}, {4,2,0}, {4,3,0}, {3,4,0}, {2,4,0}, {1,4,0}, {0,3,0}, {0,2,0}, {0,1,0}};
+ Point cursor = {2,2,0};
+
+ MicroBitImage img(5,5);
+ MicroBitImage smiley("0,255,0,255,0\n0,255,0,255,0\n0,0,0,0,0\n255,0,0,0,255\n0,255,255,255,0\n");
+ int samples = 0;
+
+ // Firstly, we need to take over the display. Ensure all active animations are paused.
+ display.stopAnimation();
+ display.scrollAsync("DRAW A CIRCLE");
+
+ for (int i=0; i<110; i++)
+ wait_ms(100);
+
+ display.stopAnimation();
+ display.clear();
+
+ while(samples < PERIMETER_POINTS)
+ {
+ // update our model of the flash status of the user controlled pixel.
+ cursor.on = (cursor.on + 1) % 4;
+
+ // take a snapshot of the current accelerometer data.
+ int x = accelerometer.getX();
+ int y = accelerometer.getY();
+
+ // Wait a little whie for the button state to stabilise (one scheduler tick).
+ wait_ms(10);
+
+ // Deterine the position of the user controlled pixel on the screen.
+ if (x < -PIXEL2_THRESHOLD)
+ cursor.x = 0;
+ else if (x < -PIXEL1_THRESHOLD)
+ cursor.x = 1;
+ else if (x > PIXEL2_THRESHOLD)
+ cursor.x = 4;
+ else if (x > PIXEL1_THRESHOLD)
+ cursor.x = 3;
+ else
+ cursor.x = 2;
+
+ if (y < -PIXEL2_THRESHOLD)
+ cursor.y = 0;
+ else if (y < -PIXEL1_THRESHOLD)
+ cursor.y = 1;
+ else if (y > PIXEL2_THRESHOLD)
+ cursor.y = 4;
+ else if (y > PIXEL1_THRESHOLD)
+ cursor.y = 3;
+ else
+ cursor.y = 2;
+
+ img.clear();
+
+ // Turn on any pixels that have been visited.
+ for (int i=0; i<PERIMETER_POINTS; i++)
+ if (perimeter[i].on)
+ img.setPixelValue(perimeter[i].x, perimeter[i].y, 255);
+
+ // Update the pixel at the users position.
+ img.setPixelValue(cursor.x, cursor.y, 255);
+
+ // Update the buffer to the screen.
+ display.image.paste(img,0,0,0);
+
+ // test if we need to update the state at the users position.
+ for (int i=0; i<PERIMETER_POINTS; i++)
+ {
+ if (cursor.x == perimeter[i].x && cursor.y == perimeter[i].y && !perimeter[i].on)
+ {
+ // Record the sample data for later processing...
+ X.set(samples, 0, compass.getX(RAW));
+ X.set(samples, 1, compass.getY(RAW));
+ X.set(samples, 2, compass.getZ(RAW));
+ X.set(samples, 3, 1);
+
+ // Record that this pixel has been visited.
+ perimeter[i].on = 1;
+ samples++;
+ }
+ }
+
+ wait_ms(100);
+ }
+
+ // We have enough sample data to make a fairly accurate calibration.
+ // We use a Least Mean Squares approximation, as detailed in Freescale application note AN2426.
+
+ // Firstly, calculate the square of each sample.
+ Matrix4 Y(X.height(), 1);
+ for (int i = 0; i < X.height(); i++)
+ {
+ float v = X.get(i, 0)*X.get(i, 0) + X.get(i, 1)*X.get(i, 1) + X.get(i, 2)*X.get(i, 2);
+ Y.set(i, 0, v);
+ }
+
+ // Now perform a Least Squares Approximation.
+ Matrix4 Alpha = X.multiplyT(X).invert();
+ Matrix4 Gamma = X.multiplyT(Y);
+ Matrix4 Beta = Alpha.multiply(Gamma);
+
+ // The result contains the approximate zero point of each axis, but doubled.
+ // Halve each sample, and record this as the compass calibration data.
+ CompassSample cal ((int)(Beta.get(0,0) / 2), (int)(Beta.get(1,0) / 2), (int)(Beta.get(2,0) / 2));
+ compass.setCalibration(cal);
+
+ // Show a smiley to indicate that we're done, and continue on with the user program.
+ display.clear();
+ display.printAsync(smiley, 0, 0, 0, 1500);
+ wait_ms(1000);
+ display.clear();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitDisplay.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,1218 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBitDisplay.
+ *
+ * A MicroBitDisplay represents the LED matrix array on the micro:bit.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitDisplay.h"
+#include "MicroBitSystemTimer.h"
+#include "MicroBitFiber.h"
+#include "ErrorNo.h"
+#include "NotifyEvents.h"
+
+const int greyScaleTimings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {1, 23, 70, 163, 351, 726, 1476, 2976};
+
+/**
+ * Constructor.
+ *
+ * Create a software representation the micro:bit's 5x5 LED matrix.
+ * The display is initially blank.
+ *
+ * @param id The id the display should use when sending events on the MessageBus. Defaults to MICROBIT_ID_DISPLAY.
+ *
+ * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates.
+ * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * @endcode
+ */
+MicroBitDisplay::MicroBitDisplay(uint16_t id, const MatrixMap &map) :
+ matrixMap(map),
+ image(map.width*2,map.height)
+{
+ uint32_t row_mask;
+
+ this->id = id;
+ this->width = map.width;
+ this->height = map.height;
+ this->rotation = MICROBIT_DISPLAY_ROTATION_0;
+
+ row_mask = 0;
+ col_mask = 0;
+ strobeRow = 0;
+ row_mask = 0;
+
+ for (int i = matrixMap.rowStart; i < matrixMap.rowStart + matrixMap.rows; i++)
+ row_mask |= 0x01 << i;
+
+ for (int i = matrixMap.columnStart; i < matrixMap.columnStart + matrixMap.columns; i++)
+ col_mask |= 0x01 << i;
+
+ LEDMatrix = new PortOut(Port0, row_mask | col_mask);
+
+ this->greyscaleBitMsk = 0x01;
+ this->timingCount = 0;
+ this->setBrightness(MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS);
+ this->mode = DISPLAY_MODE_BLACK_AND_WHITE;
+ this->animationMode = ANIMATION_MODE_NONE;
+ this->lightSensor = NULL;
+
+ system_timer_add_component(this);
+
+ status |= MICROBIT_COMPONENT_RUNNING;
+}
+
+/**
+ * Internal frame update method, used to strobe the display.
+ *
+ * TODO: Write a more efficient, complementary variation of this method for the case where
+ * MICROBIT_DISPLAY_ROW_COUNT > MICROBIT_DISPLAY_COLUMN_COUNT.
+ */
+void MicroBitDisplay::systemTick()
+{
+ if(!(status & MICROBIT_COMPONENT_RUNNING))
+ return;
+
+ if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
+ {
+ renderWithLightSense();
+ return;
+ }
+
+ // Move on to the next row.
+ strobeRow++;
+
+ //reset the row counts and bit mask when we have hit the max.
+ if(strobeRow == matrixMap.rows)
+ strobeRow = 0;
+
+ if(mode == DISPLAY_MODE_BLACK_AND_WHITE)
+ render();
+
+ if(mode == DISPLAY_MODE_GREYSCALE)
+ {
+ greyscaleBitMsk = 0x01;
+ timingCount = 0;
+ renderGreyscale();
+ }
+
+ // Update text and image animations if we need to.
+ this->animationUpdate();
+}
+
+void MicroBitDisplay::renderFinish()
+{
+ *LEDMatrix = 0;
+}
+
+void MicroBitDisplay::render()
+{
+ // Simple optimisation.
+ // If display is at zero brightness, there's nothing to do.
+ if(brightness == 0)
+ return;
+
+ // Calculate the bitpattern to write.
+ uint32_t row_data = 0x01 << (microbitMatrixMap.rowStart + strobeRow);
+ uint32_t col_data = 0;
+
+ for (int i = 0; i < matrixMap.columns; i++)
+ {
+ int index = (i * matrixMap.rows) + strobeRow;
+
+ int x = matrixMap.map[index].x;
+ int y = matrixMap.map[index].y;
+ int t = x;
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_90)
+ {
+ x = width - 1 - y;
+ y = t;
+ }
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_180)
+ {
+ x = width - 1 - x;
+ y = height - 1 - y;
+ }
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_270)
+ {
+ x = y;
+ y = height - 1 - t;
+ }
+
+ if(image.getBitmap()[y*(width*2)+x])
+ col_data |= (1 << i);
+ }
+
+ // Invert column bits (as we're sinking not sourcing power), and mask off any unused bits.
+ col_data = ~col_data << matrixMap.columnStart & col_mask;
+
+ // Write the new bit pattern
+ *LEDMatrix = col_data | row_data;
+
+ //timer does not have enough resolution for brightness of 1. 23.53 us
+ if(brightness != MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS && brightness > MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
+ renderTimer.attach_us(this, &MicroBitDisplay::renderFinish, (((brightness * 950) / (MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS)) * system_timer_get_period()));
+
+ //this will take around 23us to execute
+ if(brightness <= MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS)
+ renderFinish();
+}
+
+void MicroBitDisplay::renderWithLightSense()
+{
+ //reset the row counts and bit mask when we have hit the max.
+ if(strobeRow == matrixMap.rows + 1)
+ {
+ MicroBitEvent(id, MICROBIT_DISPLAY_EVT_LIGHT_SENSE);
+ strobeRow = 0;
+ }
+ else
+ {
+ render();
+ this->animationUpdate();
+
+ // Move on to the next row.
+ strobeRow++;
+ }
+
+}
+
+void MicroBitDisplay::renderGreyscale()
+{
+ uint32_t row_data = 0x01 << (microbitMatrixMap.rowStart + strobeRow);
+ uint32_t col_data = 0;
+
+ // Calculate the bitpattern to write.
+ for (int i = 0; i < matrixMap.columns; i++)
+ {
+ int index = (i * matrixMap.rows) + strobeRow;
+
+ int x = matrixMap.map[index].x;
+ int y = matrixMap.map[index].y;
+ int t = x;
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_90)
+ {
+ x = width - 1 - y;
+ y = t;
+ }
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_180)
+ {
+ x = width - 1 - x;
+ y = height - 1 - y;
+ }
+
+ if(rotation == MICROBIT_DISPLAY_ROTATION_270)
+ {
+ x = y;
+ y = height - 1 - t;
+ }
+
+ if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk)
+ col_data |= (1 << i);
+ }
+
+ // Invert column bits (as we're sinking not sourcing power), and mask off any unused bits.
+ col_data = ~col_data << matrixMap.columnStart & col_mask;
+
+ // Write the new bit pattern
+ *LEDMatrix = col_data | row_data;
+
+ if(timingCount > MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH-1)
+ return;
+
+ greyscaleBitMsk <<= 1;
+
+ if(timingCount < 3)
+ {
+ wait_us(greyScaleTimings[timingCount++]);
+ renderGreyscale();
+ return;
+ }
+ renderTimer.attach_us(this,&MicroBitDisplay::renderGreyscale, greyScaleTimings[timingCount++]);
+}
+
+/**
+ * Periodic callback, that we use to perform any animations we have running.
+ */
+void
+MicroBitDisplay::animationUpdate()
+{
+ // If there's no ongoing animation, then nothing to do.
+ if (animationMode == ANIMATION_MODE_NONE)
+ return;
+
+ animationTick += system_timer_get_period();
+
+ if(animationTick >= animationDelay)
+ {
+ animationTick = 0;
+
+ if (animationMode == ANIMATION_MODE_SCROLL_TEXT)
+ this->updateScrollText();
+
+ if (animationMode == ANIMATION_MODE_PRINT_TEXT)
+ this->updatePrintText();
+
+ if (animationMode == ANIMATION_MODE_SCROLL_IMAGE)
+ this->updateScrollImage();
+
+ if (animationMode == ANIMATION_MODE_ANIMATE_IMAGE)
+ this->updateAnimateImage();
+
+ if(animationMode == ANIMATION_MODE_PRINT_CHARACTER)
+ {
+ animationMode = ANIMATION_MODE_NONE;
+ this->sendAnimationCompleteEvent();
+ }
+ }
+}
+
+/**
+ * Broadcasts an event onto the defult EventModel indicating that the
+ * current animation has completed.
+ */
+void MicroBitDisplay::sendAnimationCompleteEvent()
+{
+ // Signal that we've completed an animation.
+ MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
+
+ // Wake up a fiber that was blocked on the animation (if any).
+ MicroBitEvent(MICROBIT_ID_NOTIFY_ONE, MICROBIT_DISPLAY_EVT_FREE);
+}
+
+/**
+ * Internal scrollText update method.
+ * Shift the screen image by one pixel to the left. If necessary, paste in the next char.
+ */
+void MicroBitDisplay::updateScrollText()
+{
+ image.shiftLeft(1);
+ scrollingPosition++;
+
+ if (scrollingPosition == width + MICROBIT_DISPLAY_SPACING)
+ {
+ scrollingPosition = 0;
+
+ image.print(scrollingChar < scrollingText.length() ? scrollingText.charAt(scrollingChar) : ' ',width,0);
+
+ if (scrollingChar > scrollingText.length())
+ {
+ animationMode = ANIMATION_MODE_NONE;
+ this->sendAnimationCompleteEvent();
+ return;
+ }
+ scrollingChar++;
+ }
+}
+
+/**
+ * Internal printText update method.
+ * Paste the next character in the string.
+ */
+void MicroBitDisplay::updatePrintText()
+{
+ image.print(printingChar < printingText.length() ? printingText.charAt(printingChar) : ' ',0,0);
+
+ if (printingChar > printingText.length())
+ {
+ animationMode = ANIMATION_MODE_NONE;
+
+ this->sendAnimationCompleteEvent();
+ return;
+ }
+
+ printingChar++;
+}
+
+/**
+ * Internal scrollImage update method.
+ * Paste the stored bitmap at the appropriate point.
+ */
+void MicroBitDisplay::updateScrollImage()
+{
+ image.clear();
+
+ if (((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered) || scrollingImageStride == 0)
+ {
+ animationMode = ANIMATION_MODE_NONE;
+ this->sendAnimationCompleteEvent();
+
+ return;
+ }
+
+ scrollingImagePosition += scrollingImageStride;
+ scrollingImageRendered = true;
+}
+
+/**
+ * Internal animateImage update method.
+ * Paste the stored bitmap at the appropriate point and stop on the last frame.
+ */
+void MicroBitDisplay::updateAnimateImage()
+{
+ //wait until we have rendered the last position to give a continuous animation.
+ if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered)
+ {
+ animationMode = ANIMATION_MODE_NONE;
+ this->clear();
+ this->sendAnimationCompleteEvent();
+ return;
+ }
+
+ if(scrollingImagePosition > 0)
+ image.shiftLeft(-scrollingImageStride);
+
+ image.paste(scrollingImage, scrollingImagePosition, 0, 0);
+
+ if(scrollingImageStride == 0)
+ {
+ animationMode = ANIMATION_MODE_NONE;
+ this->sendAnimationCompleteEvent();
+ }
+
+ scrollingImageRendered = true;
+
+ scrollingImagePosition += scrollingImageStride;
+}
+
+/**
+ * Resets the current given animation.
+ */
+void MicroBitDisplay::stopAnimation()
+{
+ // Reset any ongoing animation.
+ if (animationMode != ANIMATION_MODE_NONE)
+ {
+ animationMode = ANIMATION_MODE_NONE;
+
+ // Indicate that we've completed an animation.
+ MicroBitEvent(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE);
+
+ // Wake up aall fibers that may blocked on the animation (if any).
+ MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
+ }
+
+ // Clear the display and setup the animation timers.
+ this->image.clear();
+}
+
+/**
+ * Blocks the current fiber until the display is available (i.e. does not effect is being displayed).
+ * Animations are queued until their time to display.
+ */
+void MicroBitDisplay::waitForFreeDisplay()
+{
+ // If there's an ongoing animation, wait for our turn to display.
+ if (animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
+ fiber_wait_for_event(MICROBIT_ID_NOTIFY, MICROBIT_DISPLAY_EVT_FREE);
+}
+
+/**
+ * Blocks the current fiber until the current animation has finished.
+ * If the scheduler is not running, this call will essentially perform a spinning wait.
+ */
+void MicroBitDisplay::fiberWait()
+{
+ if (fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE) == MICROBIT_NOT_SUPPORTED)
+ while(animationMode != ANIMATION_MODE_NONE && animationMode != ANIMATION_MODE_STOPPED)
+ __WFE();
+}
+
+/**
+ * Prints the given character to the display, if it is not in use.
+ *
+ * @param c The character to display.
+ *
+ * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
+ * or until the Displays next use.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY is the screen is in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync('p');
+ * display.printAsync('p',100);
+ * @endcode
+ */
+int MicroBitDisplay::printCharAsync(char c, int delay)
+{
+ //sanitise this value
+ if(delay < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If the display is free, it's our turn to display.
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ image.print(c, 0, 0);
+
+ if (delay > 0)
+ {
+ animationDelay = delay;
+ animationTick = 0;
+ animationMode = ANIMATION_MODE_PRINT_CHARACTER;
+ }
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Prints the given ManagedString to the display, one character at a time.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Must be > 0.
+ * Defaults to: MICROBIT_DEFAULT_PRINT_SPEED.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync("abc123",400);
+ * @endcode
+ */
+int MicroBitDisplay::printAsync(ManagedString s, int delay)
+{
+ if (s.length() == 1)
+ return printCharAsync(s.charAt(0));
+
+ //sanitise this value
+ if (delay <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ printingChar = 0;
+ printingText = s;
+ animationDelay = delay;
+ animationTick = 0;
+
+ animationMode = ANIMATION_MODE_PRINT_TEXT;
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Prints the given image to the display, if the display is not in use.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param i The image to display.
+ *
+ * @param x The horizontal position on the screen to display the image. Defaults to 0.
+ *
+ * @param y The vertical position on the screen to display the image. Defaults to 0.
+ *
+ * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults to 0.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.print(i,400);
+ * @endcode
+ */
+int MicroBitDisplay::printAsync(MicroBitImage i, int x, int y, int alpha, int delay)
+{
+ if(delay < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ image.paste(i, x, y, alpha);
+
+ if(delay > 0)
+ {
+ animationDelay = delay;
+ animationTick = 0;
+ animationMode = ANIMATION_MODE_PRINT_CHARACTER;
+ }
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Prints the given character to the display.
+ *
+ * @param c The character to display.
+ *
+ * @param delay Optional parameter - the time for which to show the character. Zero displays the character forever,
+ * or until the Displays next use.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.printAsync('p');
+ * display.printAsync('p',100);
+ * @endcode
+ */
+int MicroBitDisplay::printChar(char c, int delay)
+{
+ if (delay < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ this->printCharAsync(c, delay);
+
+ if (delay > 0)
+ fiberWait();
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Prints the given string to the display, one character at a time.
+ *
+ * Blocks the calling thread until all the text has been displayed.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_PRINT_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.print("abc123",400);
+ * @endcode
+ */
+int MicroBitDisplay::print(ManagedString s, int delay)
+{
+ //sanitise this value
+ if(delay <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ if (s.length() == 1)
+ {
+ return printCharAsync(s.charAt(0));
+ }
+ else
+ {
+ this->printAsync(s, delay);
+ fiberWait();
+ }
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Prints the given image to the display.
+ * Blocks the calling thread until all the image has been displayed.
+ *
+ * @param i The image to display.
+ *
+ * @param x The horizontal position on the screen to display the image. Defaults to 0.
+ *
+ * @param y The vertical position on the screen to display the image. Defaults to 0.
+ *
+ * @param alpha Treats the brightness level '0' as transparent. Defaults to 0.
+ *
+ * @param delay The time to display the image for, or zero to show the image forever. Defaults to 0.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.print(i,400);
+ * @endcode
+ */
+int MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay)
+{
+ if(delay < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ this->printAsync(i, x, y, alpha, delay);
+
+ if (delay > 0)
+ fiberWait();
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Scrolls the given string to the display, from right to left.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.scrollAsync("abc123",100);
+ * @endcode
+ */
+int MicroBitDisplay::scrollAsync(ManagedString s, int delay)
+{
+ //sanitise this value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If the display is free, it's our turn to display.
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ scrollingPosition = width-1;
+ scrollingChar = 0;
+ scrollingText = s;
+
+ animationDelay = delay;
+ animationTick = 0;
+ animationMode = ANIMATION_MODE_SCROLL_TEXT;
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Scrolls the given image across the display, from right to left.
+ * Returns immediately, and executes the animation asynchronously.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time between updates, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the display is already in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.scrollAsync(i,100,1);
+ * @endcode
+ */
+int MicroBitDisplay::scrollAsync(MicroBitImage image, int delay, int stride)
+{
+ //sanitise the delay value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If the display is free, it's our turn to display.
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ scrollingImagePosition = stride < 0 ? width : -image.getWidth();
+ scrollingImageStride = stride;
+ scrollingImage = image;
+ scrollingImageRendered = false;
+
+ animationDelay = stride == 0 ? 0 : delay;
+ animationTick = 0;
+ animationMode = ANIMATION_MODE_SCROLL_IMAGE;
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Scrolls the given string across the display, from right to left.
+ * Blocks the calling thread until all text has been displayed.
+ *
+ * @param s The string to display.
+ *
+ * @param delay The time to delay between characters, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * display.scroll("abc123",100);
+ * @endcode
+ */
+int MicroBitDisplay::scroll(ManagedString s, int delay)
+{
+ //sanitise this value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ // Start the effect.
+ this->scrollAsync(s, delay);
+
+ // Wait for completion.
+ fiberWait();
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Scrolls the given image across the display, from right to left.
+ * Blocks the calling thread until all the text has been displayed.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time between updates, in milliseconds. Defaults
+ * to: MICROBIT_DEFAULT_SCROLL_SPEED.
+ *
+ * @param stride The number of pixels to shift by in each update. Defaults to MICROBIT_DEFAULT_SCROLL_STRIDE.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicrobitImage i("1,1,1,1,1\n1,1,1,1,1\n");
+ * display.scroll(i,100,1);
+ * @endcode
+ */
+int MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride)
+{
+ //sanitise the delay value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ // Start the effect.
+ this->scrollAsync(image, delay, stride);
+
+ // Wait for completion.
+ fiberWait();
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
+ * Returns immediately.
+ *
+ * @param image The image to display.
+ *
+ * @param delay The time to delay between each update of the display, in milliseconds.
+ *
+ * @param stride The number of pixels to shift by in each update.
+ *
+ * @param startingPosition the starting position on the display for the animation
+ * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
+ *
+ * @return MICROBIT_OK, MICROBIT_BUSY if the screen is in use, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const int heart_w = 10;
+ * const int heart_h = 5;
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, };
+ *
+ * MicroBitImage i(heart_w,heart_h,heart);
+ * display.animateAsync(i,100,5);
+ * @endcode
+ */
+int MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, int startingPosition)
+{
+ //sanitise the delay value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If the display is free, we can display.
+ if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED)
+ {
+ // Assume right to left functionality, to align with scrollString()
+ stride = -stride;
+
+ //calculate starting position which is offset by the stride
+ scrollingImagePosition = (startingPosition == MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS) ? MICROBIT_DISPLAY_WIDTH + stride : startingPosition;
+ scrollingImageStride = stride;
+ scrollingImage = image;
+ scrollingImageRendered = false;
+
+ animationDelay = stride == 0 ? 0 : delay;
+ animationTick = delay-1;
+ animationMode = ANIMATION_MODE_ANIMATE_IMAGE;
+ }
+ else
+ {
+ return MICROBIT_BUSY;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * "Animates" the current image across the display with a given stride, finishing on the last frame of the animation.
+ * Blocks the calling thread until the animation is complete.
+ *
+ *
+ * @param delay The time to delay between each update of the display, in milliseconds.
+ *
+ * @param stride The number of pixels to shift by in each update.
+ *
+ * @param startingPosition the starting position on the display for the animation
+ * to begin at. Defaults to MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS.
+ *
+ * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const int heart_w = 10;
+ * const int heart_h = 5;
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, };
+ *
+ * MicroBitImage i(heart_w,heart_h,heart);
+ * display.animate(i,100,5);
+ * @endcode
+ */
+int MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int startingPosition)
+{
+ //sanitise the delay value
+ if(delay <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // If there's an ongoing animation, wait for our turn to display.
+ this->waitForFreeDisplay();
+
+ // If the display is free, it's our turn to display.
+ // If someone called stopAnimation(), then we simply skip...
+ if (animationMode == ANIMATION_MODE_NONE)
+ {
+ // Start the effect.
+ this->animateAsync(image, delay, stride, startingPosition);
+
+ // Wait for completion.
+ //TODO: Put this in when we merge tight-validation
+ //if (delay > 0)
+ fiberWait();
+ }
+ else
+ {
+ return MICROBIT_CANCELLED;
+ }
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Configures the brightness of the display.
+ *
+ * @param b The brightness to set the brightness to, in the range 0 - 255.
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER
+ *
+ * @code
+ * display.setBrightness(255); //max brightness
+ * @endcode
+ */
+int MicroBitDisplay::setBrightness(int b)
+{
+ //sanitise the brightness level
+ if(b < 0 || b > 255)
+ return MICROBIT_INVALID_PARAMETER;
+
+ this->brightness = b;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures the mode of the display.
+ *
+ * @param mode The mode to swap the display into. One of: DISPLAY_MODE_GREYSCALE,
+ * DISPLAY_MODE_BLACK_AND_WHITE, DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE
+ *
+ * @code
+ * display.setDisplayMode(DISPLAY_MODE_GREYSCALE); //per pixel brightness
+ * @endcode
+ */
+void MicroBitDisplay::setDisplayMode(DisplayMode mode)
+{
+ if(mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
+ {
+ //to reduce the artifacts on the display - increase the tick
+ if(system_timer_get_period() != MICROBIT_LIGHT_SENSOR_TICK_PERIOD)
+ system_timer_set_period(MICROBIT_LIGHT_SENSOR_TICK_PERIOD);
+ }
+
+ if(this->mode == DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE && mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
+ {
+ delete this->lightSensor;
+
+ this->lightSensor = NULL;
+ }
+
+ this->mode = mode;
+}
+
+/**
+ * Retrieves the mode of the display.
+ *
+ * @return the current mode of the display
+ */
+int MicroBitDisplay::getDisplayMode()
+{
+ return this->mode;
+}
+
+/**
+ * Fetches the current brightness of this display.
+ *
+ * @return the brightness of this display, in the range 0..255.
+ *
+ * @code
+ * display.getBrightness(); //the current brightness
+ * @endcode
+ */
+int MicroBitDisplay::getBrightness()
+{
+ return this->brightness;
+}
+
+/**
+ * Rotates the display to the given position.
+ *
+ * Axis aligned values only.
+ *
+ * @code
+ * display.rotateTo(MICROBIT_DISPLAY_ROTATION_180); //rotates 180 degrees from original orientation
+ * @endcode
+ */
+void MicroBitDisplay::rotateTo(DisplayRotation rotation)
+{
+ this->rotation = rotation;
+}
+
+/**
+ * Enables or disables the display entirely, and releases the pins for other uses.
+ *
+ * @param enableDisplay true to enabled the display, or false to disable it.
+ */
+void MicroBitDisplay::setEnable(bool enableDisplay)
+{
+ // If we're already in the correct state, then there's nothing to do.
+ if(((status & MICROBIT_COMPONENT_RUNNING) && enableDisplay) || (!(status & MICROBIT_COMPONENT_RUNNING) && !enableDisplay))
+ return;
+
+ uint32_t rmask = 0;
+ uint32_t cmask = 0;
+
+ for (int i = matrixMap.rowStart; i < matrixMap.rowStart + matrixMap.rows; i++)
+ rmask |= 0x01 << i;
+
+ for (int i = matrixMap.columnStart; i < matrixMap.columnStart + matrixMap.columns; i++)
+ cmask |= 0x01 << i;
+
+ if (enableDisplay)
+ {
+ PortOut p(Port0, rmask | cmask);
+ status |= MICROBIT_COMPONENT_RUNNING;
+ }
+ else
+ {
+ PortIn p(Port0, rmask | cmask);
+ p.mode(PullNone);
+ status &= ~MICROBIT_COMPONENT_RUNNING;
+ }
+}
+
+/**
+ * Enables the display, should only be called if the display is disabled.
+ *
+ * @code
+ * display.enable(); //Enables the display mechanics
+ * @endcode
+ *
+ * @note Only enables the display if the display is currently disabled.
+ */
+void MicroBitDisplay::enable()
+{
+ setEnable(true);
+}
+
+/**
+ * Disables the display, which releases control of the GPIO pins used by the display,
+ * which are exposed on the edge connector.
+ *
+ * @code
+ * display.disable(); //disables the display
+ * @endcode
+ *
+ * @note Only disables the display if the display is currently enabled.
+ */
+void MicroBitDisplay::disable()
+{
+ setEnable(false);
+}
+
+/**
+ * Clears the display of any remaining pixels.
+ *
+ * `display.image.clear()` can also be used!
+ *
+ * @code
+ * display.clear(); //clears the display
+ * @endcode
+ */
+void MicroBitDisplay::clear()
+{
+ image.clear();
+}
+
+/**
+ * Updates the font that will be used for display operations.
+ *
+ * @param font the new font that will be used to render characters.
+ *
+ * @note DEPRECATED! Please use MicroBitFont::setSystemFont() instead.
+ */
+void MicroBitDisplay::setFont(MicroBitFont font)
+{
+ MicroBitFont::setSystemFont(font);
+}
+
+/**
+ * Retrieves the font object used for rendering characters on the display.
+ *
+ * @note DEPRECATED! Please use MicroBitFont::getSystemFont() instead.
+ */
+MicroBitFont MicroBitDisplay::getFont()
+{
+ return MicroBitFont::getSystemFont();
+}
+
+/**
+ * Captures the bitmap currently being rendered on the display.
+ *
+ * @return a MicroBitImage containing the captured data.
+ */
+MicroBitImage MicroBitDisplay::screenShot()
+{
+ return image.crop(0,0,MICROBIT_DISPLAY_WIDTH,MICROBIT_DISPLAY_HEIGHT);
+}
+
+/**
+ * Gives a representative figure of the light level in the current environment
+ * where are micro:bit is situated.
+ *
+ * Internally, it constructs an instance of a MicroBitLightSensor if not already configured
+ * and sets the display mode to DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE.
+ *
+ * This also changes the tickPeriod to MICROBIT_LIGHT_SENSOR_TICK_SPEED so
+ * that the display does not suffer from artifacts.
+ *
+ * @return an indicative light level in the range 0 - 255.
+ *
+ * @note this will return 0 on the first call to this method, a light reading
+ * will be available after the display has activated the light sensor for the
+ * first time.
+ */
+int MicroBitDisplay::readLightLevel()
+{
+ if(mode != DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE)
+ {
+ setDisplayMode(DISPLAY_MODE_BLACK_AND_WHITE_LIGHT_SENSE);
+ this->lightSensor = new MicroBitLightSensor(matrixMap);
+ }
+
+ return this->lightSensor->read();
+}
+
+/**
+ * Destructor for MicroBitDisplay, where we deregister this instance from the array of system components.
+ */
+MicroBitDisplay::~MicroBitDisplay()
+{
+ system_timer_remove_component(this);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitI2C.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,134 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitI2C.h"
+#include "ErrorNo.h"
+#include "twi_master.h"
+#include "nrf_delay.h"
+
+/**
+ * Constructor.
+ *
+ * Create an instance of MicroBitI2C for I2C communication.
+ *
+ * @param sda the Pin to be used for SDA
+ *
+ * @param scl the Pin to be used for SCL
+ *
+ * @code
+ * MicroBitI2C i2c(I2C_SDA0, I2C_SCL0);
+ * @endcode
+ *
+ * @note This class presents a wrapped mbed call to capture failed I2C operations caused by a known silicon bug in the nrf51822.
+ * Attempts to automatically reset and restart the I2C hardware if this case is detected.
+ *
+ * For reference see PAN56 in:
+ *
+ * https://www.nordicsemi.com/eng/nordic/Products/nRF51822/PAN-nRF51822/24634
+ *
+ * v2.0 through to v2.4
+ */
+MicroBitI2C::MicroBitI2C(PinName sda, PinName scl) : I2C(sda,scl)
+{
+ this->retries = 0;
+}
+
+/**
+ * Performs a complete read transaction. The bottom bit of the address is forced to 1 to indicate a read.
+ *
+ * @param address 8-bit I2C slave address [ addr | 1 ]
+ *
+ * @param data A pointer to a byte buffer used for storing retrieved data.
+ *
+ * @param length Number of bytes to read.
+ *
+ * @param repeated if true, stop is not sent at the end. Defaults to false.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if an unresolved read failure is detected.
+ */
+int MicroBitI2C::read(int address, char *data, int length, bool repeated)
+{
+ int result = I2C::read(address,data,length,repeated);
+
+ //0 indicates a success, presume failure
+ while(result != 0 && retries < MICROBIT_I2C_MAX_RETRIES)
+ {
+ _i2c.i2c->EVENTS_ERROR = 0;
+ _i2c.i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
+ _i2c.i2c->POWER = 0;
+ nrf_delay_us(5);
+ _i2c.i2c->POWER = 1;
+ _i2c.i2c->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
+ twi_master_init_and_clear();
+ result = I2C::read(address,data,length,repeated);
+ retries++;
+ }
+
+ if(result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ retries = 0;
+ return MICROBIT_OK;
+}
+
+/**
+ * Performs a complete write transaction. The bottom bit of the address is forced to 0 to indicate a write.
+ *
+ * @param address 8-bit I2C slave address [ addr | 0 ]
+ *
+ * @param data A pointer to a byte buffer containing the data to write.
+ *
+ * @param length Number of bytes to write
+ *
+ * @param repeated if true, stop is not sent at the end. Defaults to false.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_I2C_ERROR if an unresolved write failure is detected.
+ */
+int MicroBitI2C::write(int address, const char *data, int length, bool repeated)
+{
+ int result = I2C::write(address,data,length,repeated);
+
+ //0 indicates a success, presume failure
+ while(result != 0 && retries < MICROBIT_I2C_MAX_RETRIES)
+ {
+ _i2c.i2c->EVENTS_ERROR = 0;
+ _i2c.i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos;
+ _i2c.i2c->POWER = 0;
+ nrf_delay_us(5);
+ _i2c.i2c->POWER = 1;
+ _i2c.i2c->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_ENABLE_ENABLE_Pos;
+
+ twi_master_init_and_clear();
+ result = I2C::write(address,data,length,repeated);
+ retries++;
+ }
+
+ if(result != 0)
+ return MICROBIT_I2C_ERROR;
+
+ retries = 0;
+ return MICROBIT_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitIO.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,70 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBit IO.
+ *
+ * Represents a collection of all I/O pins on the edge connector.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitIO.h"
+
+/**
+ * Constructor.
+ *
+ * Create a representation of all given I/O pins on the edge connector
+ *
+ * Accepts a sequence of unique ID's used to distinguish events raised
+ * by MicroBitPin instances on the default EventModel.
+ */
+MicroBitIO::MicroBitIO(int ID_P0, int ID_P1, int ID_P2,
+ int ID_P3, int ID_P4, int ID_P5,
+ int ID_P6, int ID_P7, int ID_P8,
+ int ID_P9, int ID_P10,int ID_P11,
+ int ID_P12,int ID_P13,int ID_P14,
+ int ID_P15,int ID_P16,int ID_P19,
+ int ID_P20) :
+ P0 (ID_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH)
+ P1 (ID_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH)
+ P2 (ID_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH)
+ P3 (ID_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_AD), //COL1 (ANALOG/DIGITAL)
+ P4 (ID_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL)
+ P5 (ID_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_DIGITAL), //BTN_A
+ P6 (ID_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_DIGITAL), //ROW2
+ P7 (ID_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_DIGITAL), //ROW1
+ P8 (ID_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_DIGITAL), //PIN 18
+ P9 (ID_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_DIGITAL), //ROW3
+ P10(ID_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_AD), //COL3 (ANALOG/DIGITAL)
+ P11(ID_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_DIGITAL), //BTN_B
+ P12(ID_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_DIGITAL), //PIN 20
+ P13(ID_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_DIGITAL), //SCK
+ P14(ID_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_DIGITAL), //MISO
+ P15(ID_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_DIGITAL), //MOSI
+ P16(ID_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL), //PIN 16
+ P19(ID_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_DIGITAL), //SCL
+ P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_DIGITAL) //SDA
+{
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitLightSensor.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,177 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBitLightSensor.
+ *
+ * This is an object that interleaves light sensing with MicroBitDisplay.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitLightSensor.h"
+#include "MicroBitDisplay.h"
+
+/**
+ * After the startSensing method has been called, this method will be called
+ * MICROBIT_LIGHT_SENSOR_AN_SET_TIME after.
+ *
+ * It will then read from the currently selected channel using the AnalogIn
+ * that was configured in the startSensing method.
+ */
+void MicroBitLightSensor::analogReady()
+{
+ this->results[chan] = this->sensePin->read_u16();
+
+ analogDisable();
+
+ DigitalOut((PinName)(matrixMap.columnStart + chan)).write(1);
+
+ chan++;
+
+ chan = chan % MICROBIT_LIGHT_SENSOR_CHAN_NUM;
+}
+
+/**
+ * Forcibly disables the AnalogIn, otherwise it will remain in possession
+ * of the GPIO channel it is using, meaning that the display will not be
+ * able to use a channel (COL).
+ *
+ * This is required as per PAN 3, details of which can be found here:
+ *
+ * https://www.nordicsemi.com/eng/nordic/download_resource/24634/5/88440387
+ */
+void MicroBitLightSensor::analogDisable()
+{
+ NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;
+
+ NRF_ADC->CONFIG = (ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos) |
+ (ADC_CONFIG_INPSEL_SupplyTwoThirdsPrescaling << ADC_CONFIG_INPSEL_Pos) |
+ (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
+ (ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
+ (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
+}
+
+/**
+ * Constructor.
+ *
+ * Create a representation of the light sensor.
+ *
+ * @param map The mapping information that relates pin inputs/outputs to physical screen coordinates.
+ * Defaults to microbitMatrixMap, defined in MicroBitMatrixMaps.h.
+ */
+MicroBitLightSensor::MicroBitLightSensor(const MatrixMap &map) :
+ analogTrigger(),
+ matrixMap(map)
+{
+ this->chan = 0;
+
+ if (EventModel::defaultEventBus)
+ EventModel::defaultEventBus->listen(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing, MESSAGE_BUS_LISTENER_IMMEDIATE);
+
+ this->sensePin = NULL;
+}
+
+/**
+ * This method returns a summed average of the three sections of the display.
+ *
+ * A section is defined as:
+ * ___________________
+ * | 1 | | 2 | | 3 |
+ * |___|___|___|___|___|
+ * | | | | | |
+ * |___|___|___|___|___|
+ * | 2 | | 3 | | 1 |
+ * |___|___|___|___|___|
+ * | | | | | |
+ * |___|___|___|___|___|
+ * | 3 | | 1 | | 2 |
+ * |___|___|___|___|___|
+ *
+ * Where each number represents a different section on the 5 x 5 matrix display.
+ *
+ * @return returns a value in the range 0 - 255 where 0 is dark, and 255
+ * is very bright
+ */
+int MicroBitLightSensor::read()
+{
+ int sum = 0;
+
+ for(int i = 0; i < MICROBIT_LIGHT_SENSOR_CHAN_NUM; i++)
+ sum += results[i];
+
+ int average = sum / MICROBIT_LIGHT_SENSOR_CHAN_NUM;
+
+ average = min(average, MICROBIT_LIGHT_SENSOR_MAX_VALUE);
+
+ average = max(average, MICROBIT_LIGHT_SENSOR_MIN_VALUE);
+
+ int inverted = (MICROBIT_LIGHT_SENSOR_MAX_VALUE - average) + MICROBIT_LIGHT_SENSOR_MIN_VALUE;
+
+ int a = 0;
+
+ int b = 255;
+
+ int normalised = a + ((((inverted - MICROBIT_LIGHT_SENSOR_MIN_VALUE)) * (b - a))/ (MICROBIT_LIGHT_SENSOR_MAX_VALUE - MICROBIT_LIGHT_SENSOR_MIN_VALUE));
+
+ return normalised;
+}
+
+/**
+ * The method that is invoked by sending MICROBIT_DISPLAY_EVT_LIGHT_SENSE
+ * using the id MICROBIT_ID_DISPLAY.
+ *
+ * @note this can be manually driven by calling this member function, with
+ * a MicroBitEvent using the CREATE_ONLY option of the MicroBitEvent
+ * constructor.
+ */
+void MicroBitLightSensor::startSensing(MicroBitEvent)
+{
+ for(int rowCount = 0; rowCount < matrixMap.rows; rowCount++)
+ DigitalOut((PinName)(matrixMap.rowStart + rowCount)).write(0);
+
+ PinName currentPin = (PinName)(matrixMap.columnStart + chan);
+
+ DigitalOut(currentPin).write(1);
+
+ DigitalIn(currentPin, PullNone).~DigitalIn();
+
+ if(this->sensePin != NULL)
+ delete this->sensePin;
+
+ this->sensePin = new AnalogIn(currentPin);
+
+ analogTrigger.attach_us(this, &MicroBitLightSensor::analogReady, MICROBIT_LIGHT_SENSOR_AN_SET_TIME);
+}
+
+/**
+ * A destructor for MicroBitLightSensor.
+ *
+ * The destructor removes the listener, used by MicroBitLightSensor from the default EventModel.
+ */
+MicroBitLightSensor::~MicroBitLightSensor()
+{
+ if (EventModel::defaultEventBus)
+ EventModel::defaultEventBus->ignore(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitMessageBus.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,552 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the MicroBitMessageBus.
+ *
+ * The MicroBitMessageBus is the common mechanism to deliver asynchronous events on the
+ * MicroBit platform. It serves a number of purposes:
+ *
+ * 1) It provides an eventing abstraction that is independent of the underlying substrate.
+ *
+ * 2) It provides a mechanism to decouple user code from trusted system code
+ * i.e. the basis of a message passing nano kernel.
+ *
+ * 3) It allows a common high level eventing abstraction across a range of hardware types.e.g. buttons, BLE...
+ *
+ * 4) It provides a mechanim for extensibility - new devices added via I/O pins can have OO based
+ * drivers and communicate via the message bus with minima impact on user level languages.
+ *
+ * 5) It allows for the possiblility of event / data aggregation, which in turn can save energy.
+ *
+ * It has the following design principles:
+ *
+ * 1) Maintain a low RAM footprint where possible
+ *
+ * 2) Make few assumptions about the underlying platform, but allow optimizations where possible.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitMessageBus.h"
+#include "MicroBitFiber.h"
+#include "ErrorNo.h"
+
+/**
+ * Default constructor.
+ *
+ * Adds itself as a fiber component, and also configures itself to be the
+ * default EventModel if defaultEventBus is NULL.
+ */
+MicroBitMessageBus::MicroBitMessageBus()
+{
+ this->listeners = NULL;
+ this->evt_queue_head = NULL;
+ this->evt_queue_tail = NULL;
+ this->queueLength = 0;
+
+ fiber_add_idle_component(this);
+
+ if(EventModel::defaultEventBus == NULL)
+ EventModel::defaultEventBus = this;
+}
+
+/**
+ * Invokes a callback on a given MicroBitListener
+ *
+ * Internal wrapper function, used to enable
+ * parameterised callbacks through the fiber scheduler.
+ */
+void async_callback(void *param)
+{
+ MicroBitListener *listener = (MicroBitListener *)param;
+
+ // OK, now we need to decide how to behave depending on our configuration.
+ // If this a fiber f already active within this listener then check our
+ // configuration to determine the correct course of action.
+ //
+
+ if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
+ {
+ // Drop this event, if that's how we've been configured.
+ if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
+ return;
+
+ // Queue this event up for later, if that's how we've been configured.
+ if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
+ {
+ listener->queue(listener->evt);
+ return;
+ }
+ }
+
+ // Determine the calling convention for the callback, and invoke...
+ // C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
+
+ // Record that we have a fiber going into this listener...
+ listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
+
+ while (1)
+ {
+ // Firstly, check for a method callback into an object.
+ if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
+ listener->cb_method->fire(listener->evt);
+
+ // Now a parameterised C function
+ else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
+ listener->cb_param(listener->evt, listener->cb_arg);
+
+ // We must have a plain C function
+ else
+ listener->cb(listener->evt);
+
+ // If there are more events to process, dequeue the next one and process it.
+ if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
+ {
+ MicroBitEventQueueItem *item = listener->evt_queue;
+
+ listener->evt = item->evt;
+ listener->evt_queue = listener->evt_queue->next;
+ delete item;
+
+ // We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
+ schedule();
+ }
+ else
+ break;
+ }
+
+ // The fiber of exiting... clear our state.
+ listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
+}
+
+/**
+ * Queue the given event for processing at a later time.
+ * Add the given event at the tail of our queue.
+ *
+ * @param The event to queue.
+ */
+void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
+{
+ int processingComplete;
+
+ MicroBitEventQueueItem *prev = evt_queue_tail;
+
+ // Now process all handler regsitered as URGENT.
+ // These pre-empt the queue, and are useful for fast, high priority services.
+ processingComplete = this->process(evt, true);
+
+ // If we've already processed all event handlers, we're all done.
+ // No need to queue the event.
+ if (processingComplete)
+ return;
+
+ // If we need to queue, but there is no space, then there's nothg we can do.
+ if (queueLength >= MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
+ return;
+
+ // Otherwise, we need to queue this event for later processing...
+ // We queue this event at the tail of the queue at the point where we entered queueEvent()
+ // This is important as the processing above *may* have generated further events, and
+ // we want to maintain ordering of events.
+ MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
+
+ // The queue was empty when we entered this function, so queue our event at the start of the queue.
+ __disable_irq();
+
+ if (prev == NULL)
+ {
+ item->next = evt_queue_head;
+ evt_queue_head = item;
+ }
+ else
+ {
+ item->next = prev->next;
+ prev->next = item;
+ }
+
+ if (item->next == NULL)
+ evt_queue_tail = item;
+
+ queueLength++;
+
+ __enable_irq();
+}
+
+/**
+ * Extract the next event from the front of the event queue (if present).
+ *
+ * @return a pointer to the MicroBitEventQueueItem that is at the head of the list.
+ */
+MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
+{
+ MicroBitEventQueueItem *item = NULL;
+
+ __disable_irq();
+
+ if (evt_queue_head != NULL)
+ {
+ item = evt_queue_head;
+ evt_queue_head = item->next;
+
+ if (evt_queue_head == NULL)
+ evt_queue_tail = NULL;
+
+ queueLength--;
+ }
+
+ __enable_irq();
+
+
+ return item;
+}
+
+/**
+ * Cleanup any MicroBitListeners marked for deletion from the list.
+ *
+ * @return The number of listeners removed from the list.
+ */
+int MicroBitMessageBus::deleteMarkedListeners()
+{
+ MicroBitListener *l, *p;
+ int removed = 0;
+
+ l = listeners;
+ p = NULL;
+
+ // Walk this list of event handlers. Delete any that match the given listener.
+ while (l != NULL)
+ {
+ if (l->flags & MESSAGE_BUS_LISTENER_DELETING && !l->flags & MESSAGE_BUS_LISTENER_BUSY)
+ {
+ if (p == NULL)
+ listeners = l->next;
+ else
+ p->next = l->next;
+
+ // delete the listener.
+ MicroBitListener *t = l;
+ l = l->next;
+
+ delete t;
+ removed++;
+
+ continue;
+ }
+
+ p = l;
+ l = l->next;
+ }
+
+ return removed;
+}
+
+/**
+ * Periodic callback from MicroBit.
+ *
+ * Process at least one event from the event queue, if it is not empty.
+ * We then continue processing events until something appears on the runqueue.
+ */
+void MicroBitMessageBus::idleTick()
+{
+ // Clear out any listeners marked for deletion
+ this->deleteMarkedListeners();
+
+ MicroBitEventQueueItem *item = this->dequeueEvent();
+
+ // 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 to all standard event listeners.
+ this->process(item->evt);
+
+ // Free the queue item.
+ delete item;
+
+ // If we have created some useful work to do, we stop processing.
+ // This helps to minimise the number of blocked fibers we create at any point in time, therefore
+ // also reducing the RAM footprint.
+ if(!scheduler_runqueue_empty())
+ break;
+
+ // Pull the next event to process, if there is one.
+ item = this->dequeueEvent();
+ }
+}
+
+/**
+ * Indicates whether or not we have any background work to do.
+ *
+ * @return 1 if there are any events waitingto be processed, 0 otherwise.
+ */
+int MicroBitMessageBus::isIdleCallbackNeeded()
+{
+ return !(evt_queue_head == NULL);
+}
+
+/**
+ * Queues the given event to be sent to all registered recipients.
+ *
+ * @param evt The event to send.
+ *
+ * @code
+ * MicroBitMessageBus bus;
+ *
+ * // Creates and sends the MicroBitEvent using bus.
+ * MicrobitEvent evt(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * // Creates the MicrobitEvent, but delays the sending of that event.
+ * MicrobitEvent evt1(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, CREATE_ONLY);
+ *
+ * bus.send(evt1);
+ *
+ * // This has the same effect!
+ * evt1.fire()
+ * @endcode
+ */
+int MicroBitMessageBus::send(MicroBitEvent evt)
+{
+ // We simply queue processing of the event until we're scheduled in normal thread context.
+ // We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
+ // hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
+ this->queueEvent(evt);
+ return MICROBIT_OK;
+}
+
+/**
+ * Internal function, used to deliver the given event to all relevant recipients.
+ * Normally, this is called once an event has been removed from the event queue.
+ *
+ * @param evt The event to send.
+ *
+ * @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
+ * otherwise, all other (standard) listeners will be processed. Defaults to false.
+ *
+ * @return 1 if all matching listeners were processed, 0 if further processing is required.
+ *
+ * @note It is recommended that all external code uses the send() function instead of this function,
+ * or the constructors provided by MicrobitEvent.
+ */
+int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
+{
+ MicroBitListener *l;
+ int complete = 1;
+ bool listenerUrgent;
+
+ l = listeners;
+ while (l != NULL)
+ {
+ if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
+ {
+ listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE;
+ if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING))
+ {
+ 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 || !fiber_scheduler_running())
+ async_callback(l);
+ else
+ invoke(async_callback, l);
+ }
+ else
+ {
+ complete = 0;
+ }
+ }
+
+ l = l->next;
+ }
+
+ return complete;
+}
+
+/**
+ * Add the given MicroBitListener to the list of event handlers, unconditionally.
+ *
+ * @param listener The MicroBitListener to add.
+ *
+ * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
+ */
+int MicroBitMessageBus::add(MicroBitListener *newListener)
+{
+ MicroBitListener *l, *p;
+ int methodCallback;
+
+ //handler can't be NULL!
+ if (newListener == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ l = listeners;
+
+ // Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
+ // registered in a that will already capture these events. If we do, silently ignore.
+
+ // We always check the ID, VALUE and CB_METHOD fields.
+ // If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
+ while (l != NULL)
+ {
+ methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
+
+ if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
+ {
+ // We have a perfect match for this event listener already registered.
+ // If it's marked for deletion, we simply resurrect the listener, and we're done.
+ // Either way, we return an error code, as the *new* listener should be released...
+ if(l->flags & MESSAGE_BUS_LISTENER_DELETING)
+ l->flags &= ~MESSAGE_BUS_LISTENER_DELETING;
+
+ return MICROBIT_NOT_SUPPORTED;
+ }
+
+ l = l->next;
+ }
+
+ // We have a valid, new event handler. Add it to the list.
+ // if listeners is null - we can automatically add this listener to the list at the beginning...
+ if (listeners == NULL)
+ {
+ listeners = newListener;
+ MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
+
+ return MICROBIT_OK;
+ }
+
+ // We maintain an ordered list of listeners.
+ // The chain is held stictly in increasing order of ID (first level), then value code (second level).
+ // Find the correct point in the chain for this event.
+ // Adding a listener is a rare occurance, so we just walk the list...
+
+ p = listeners;
+ l = listeners;
+
+ while (l != NULL && l->id < newListener->id)
+ {
+ p = l;
+ l = l->next;
+ }
+
+ while (l != NULL && l->id == newListener->id && l->value < newListener->value)
+ {
+ p = l;
+ l = l->next;
+ }
+
+ //add at front of list
+ if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
+ {
+ newListener->next = p;
+
+ //this new listener is now the front!
+ listeners = newListener;
+ }
+
+ //add after p
+ else
+ {
+ newListener->next = p->next;
+ p->next = newListener;
+ }
+
+ MicroBitEvent(MICROBIT_ID_MESSAGE_BUS_LISTENER, newListener->id);
+ return MICROBIT_OK;
+}
+
+/**
+ * Remove the given MicroBitListener from the list of event handlers.
+ *
+ * @param listener The MicroBitListener to remove.
+ *
+ * @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
+ */
+int MicroBitMessageBus::remove(MicroBitListener *listener)
+{
+ MicroBitListener *l;
+ int removed = 0;
+
+ //handler can't be NULL!
+ if (listener == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ l = listeners;
+
+ // Walk this list of event handlers. Delete any that match the given listener.
+ while (l != NULL)
+ {
+ 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)))
+ {
+ if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
+ {
+ // Found a match. mark this to be removed from the list.
+ l->flags |= MESSAGE_BUS_LISTENER_DELETING;
+ removed++;
+ }
+ }
+ }
+
+ l = l->next;
+ }
+
+ if (removed > 0)
+ return MICROBIT_OK;
+ else
+ return MICROBIT_INVALID_PARAMETER;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * Destructor for MicroBitMessageBus, where we deregister this instance from the array of fiber components.
+ */
+MicroBitMessageBus::~MicroBitMessageBus()
+{
+ fiber_remove_idle_component(this);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitMultiButton.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,298 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBitMultiButton.
+ *
+ * Represents a virtual button, capable of reacting to simultaneous presses of two
+ * other buttons.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitMultiButton.h"
+
+/**
+ * Constructor.
+ *
+ * Create a representation of a virtual button, that generates events based upon the combination
+ * of two given buttons.
+ *
+ * @param button1 the unique ID of the first button to watch.
+ *
+ * @param button2 the unique ID of the second button to watch.
+ *
+ * @param id the unique EventModel id of this MicroBitMultiButton instance.
+ *
+ * @code
+ * multiButton(MICROBIT_ID_BUTTON_A, MICROBIT_ID_BUTTON_B, MICROBIT_ID_BUTTON_AB);
+ * @endcode
+ */
+MicroBitMultiButton::MicroBitMultiButton(uint16_t button1, uint16_t button2, uint16_t id)
+{
+ this->id = id;
+ this->button1 = button1;
+ this->button2 = button2;
+ this->eventConfiguration = MICROBIT_BUTTON_SIMPLE_EVENTS;
+
+ if (EventModel::defaultEventBus)
+ {
+ EventModel::defaultEventBus->listen(button1, MICROBIT_EVT_ANY, this, &MicroBitMultiButton::onButtonEvent, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ EventModel::defaultEventBus->listen(button2, MICROBIT_EVT_ANY, this, &MicroBitMultiButton::onButtonEvent, MESSAGE_BUS_LISTENER_IMMEDIATE);
+ }
+}
+
+/**
+ * Retrieves the button id for the alternate button id given.
+ *
+ * @param b the id of the button whose state we would like to retrieve.
+ *
+ * @return the other sub button id.
+ */
+uint16_t MicroBitMultiButton::otherSubButton(uint16_t b)
+{
+ return (b == button1 ? button2 : button1);
+}
+
+/**
+ * Determines if the given button id is marked as pressed.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if pressed, 0 if not.
+ */
+int MicroBitMultiButton::isSubButtonPressed(uint16_t button)
+{
+ if (button == button1)
+ return status & MICROBIT_MULTI_BUTTON_STATE_1;
+
+ if (button == button2)
+ return status & MICROBIT_MULTI_BUTTON_STATE_2;
+
+ return 0;
+}
+
+/**
+ * Determines if the given button id is marked as held.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if held, 0 if not.
+ */
+int MicroBitMultiButton::isSubButtonHeld(uint16_t button)
+{
+ if (button == button1)
+ return status & MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_1;
+
+ if (button == button2)
+ return status & MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_2;
+
+ return 0;
+}
+
+/**
+ * Determines if the given button id is marked as supressed.
+ *
+ * @param button the id of the button whose state we would like to retrieve.
+ *
+ * @return 1 if supressed, 0 if not.
+ */
+int MicroBitMultiButton::isSubButtonSupressed(uint16_t button)
+{
+ if (button == button1)
+ return status & MICROBIT_MULTI_BUTTON_SUPRESSED_1;
+
+ if (button == button2)
+ return status & MICROBIT_MULTI_BUTTON_SUPRESSED_2;
+
+ return 0;
+}
+
+/**
+ * Configures the button pressed state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+void MicroBitMultiButton::setButtonState(uint16_t button, int value)
+{
+ if (button == button1)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_STATE_1;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_STATE_1;
+ }
+
+ if (button == button2)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_STATE_2;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_STATE_2;
+ }
+}
+
+/**
+ * Configures the button held state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+void MicroBitMultiButton::setHoldState(uint16_t button, int value)
+{
+ if (button == button1)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_1;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_1;
+ }
+
+ if (button == button2)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_2;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_HOLD_TRIGGERED_2;
+ }
+}
+
+/**
+ * Configures the button suppressed state for the given button id.
+ *
+ * @param button the id of the button whose state requires updating.
+ *
+ * @param value the value to set for this buttons state. (Transformed into a logical 0 or 1).
+ */
+void MicroBitMultiButton::setSupressedState(uint16_t button, int value)
+{
+ if (button == button1)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_SUPRESSED_1;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_SUPRESSED_1;
+ }
+
+ if (button == button2)
+ {
+ if (value)
+ status |= MICROBIT_MULTI_BUTTON_SUPRESSED_2;
+ else
+ status &= ~MICROBIT_MULTI_BUTTON_SUPRESSED_2;
+ }
+}
+
+/**
+ * Changes the event configuration of this button to the given MicroBitButtonEventConfiguration.
+ * All subsequent events generated by this button will then be informed by this configuraiton.
+ *
+ * @param config The new configuration for this button. Legal values are MICROBIT_BUTTON_ALL_EVENTS or MICROBIT_BUTTON_SIMPLE_EVENTS.
+ *
+ * @code
+ * // Configure a button to generate all possible events.
+ * buttonAB.setEventConfiguration(MICROBIT_BUTTON_ALL_EVENTS);
+ *
+ * // Configure a button to suppress MICROBIT_BUTTON_EVT_CLICK and MICROBIT_BUTTON_EVT_LONG_CLICK events.
+ * buttonAB.setEventConfiguration(MICROBIT_BUTTON_SIMPLE_EVENTS);
+ * @endcode
+ */
+void MicroBitMultiButton::setEventConfiguration(MicroBitButtonEventConfiguration config)
+{
+ this->eventConfiguration = config;
+}
+
+/**
+ * A member function that is invoked when any event is detected from the two
+ * button IDs this MicrobitMultiButton instance was constructed with.
+ *
+ * @param evt the event received from the default EventModel.
+ */
+void MicroBitMultiButton::onButtonEvent(MicroBitEvent evt)
+{
+ int button = evt.source;
+ int otherButton = otherSubButton(button);
+
+ switch(evt.value)
+ {
+ case MICROBIT_BUTTON_EVT_DOWN:
+ setButtonState(button, 1);
+ if(isSubButtonPressed(otherButton))
+ MicroBitEvent e(id, MICROBIT_BUTTON_EVT_DOWN);
+
+ break;
+
+ case MICROBIT_BUTTON_EVT_HOLD:
+ setHoldState(button, 1);
+ if(isSubButtonHeld(otherButton))
+ MicroBitEvent e(id, MICROBIT_BUTTON_EVT_HOLD);
+
+ break;
+
+ case MICROBIT_BUTTON_EVT_UP:
+ if(isSubButtonPressed(otherButton))
+ {
+ MicroBitEvent e(id, MICROBIT_BUTTON_EVT_UP);
+
+ if (isSubButtonHeld(button) && isSubButtonHeld(otherButton))
+ MicroBitEvent e(id, MICROBIT_BUTTON_EVT_LONG_CLICK);
+ else
+ MicroBitEvent e(id, MICROBIT_BUTTON_EVT_CLICK);
+
+ setSupressedState(otherButton, 1);
+ }
+ else if (!isSubButtonSupressed(button) && eventConfiguration == MICROBIT_BUTTON_ALL_EVENTS)
+ {
+ if (isSubButtonHeld(button))
+ MicroBitEvent e(button, MICROBIT_BUTTON_EVT_LONG_CLICK);
+ else
+ MicroBitEvent e(button, MICROBIT_BUTTON_EVT_CLICK);
+ }
+
+ setButtonState(button, 0);
+ setHoldState(button, 0);
+ setSupressedState(button, 0);
+
+ break;
+
+ }
+}
+
+
+/**
+ * Tests if this MicroBitMultiButton instance is virtually pressed.
+ *
+ * @return 1 if both physical buttons are pressed simultaneously.
+ *
+ * @code
+ * if(buttonAB.isPressed())
+ * display.scroll("Pressed!");
+ * @endcode
+ */
+int MicroBitMultiButton::isPressed()
+{
+ return ((status & MICROBIT_MULTI_BUTTON_STATE_1) && (status & MICROBIT_MULTI_BUTTON_STATE_2));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitPin.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,432 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for MicroBitPin.
+ *
+ * Commonly represents an I/O pin on the edge connector.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitPin.h"
+#include "MicroBitButton.h"
+#include "DynamicPwm.h"
+#include "ErrorNo.h"
+
+/**
+ * Constructor.
+ * Create a MicroBitPin instance, generally used to represent a pin on the edge connector.
+ *
+ * @param id the unique EventModel id of this component.
+ *
+ * @param name the mbed PinName for this MicroBitPin instance.
+ *
+ * @param capability the capabilities this MicroBitPin instance should have.
+ * (PIN_CAPABILITY_DIGITAL, PIN_CAPABILITY_ANALOG, PIN_CAPABILITY_TOUCH, PIN_CAPABILITY_AD, PIN_CAPABILITY_ALL)
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
+ * @endcode
+ */
+MicroBitPin::MicroBitPin(int id, PinName name, PinCapability capability)
+{
+ //set mandatory attributes
+ this->id = id;
+ this->name = name;
+ this->capability = capability;
+
+ // Power up in a disconnected, low power state.
+ // If we're unused, this is how it will stay...
+ this->status = 0x00;
+ this->pin = NULL;
+
+}
+
+/**
+ * Disconnect any attached mBed IO from this pin.
+ *
+ * Used only when pin changes mode (i.e. Input/Output/Analog/Digital)
+ */
+void MicroBitPin::disconnect()
+{
+ // This is a bit ugly, but rarely used code.
+ // It would be much better to use some polymorphism here, but the mBed I/O classes aren't arranged in an inheritance hierarchy... yet. :-)
+ if (status & IO_STATUS_DIGITAL_IN)
+ delete ((DigitalIn *)pin);
+
+ if (status & IO_STATUS_DIGITAL_OUT)
+ delete ((DigitalOut *)pin);
+
+ if (status & IO_STATUS_ANALOG_IN){
+ NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled; // forcibly disable the ADC - BUG in mbed....
+ delete ((AnalogIn *)pin);
+ }
+
+ if (status & IO_STATUS_ANALOG_OUT)
+ {
+ if(((DynamicPwm *)pin)->getPinName() == name)
+ ((DynamicPwm *)pin)->release();
+ }
+
+ if (status & IO_STATUS_TOUCH_IN)
+ delete ((MicroBitButton *)pin);
+
+ this->pin = NULL;
+ this->status = status & IO_STATUS_EVENTBUS_ENABLED; //retain event bus status
+}
+
+/**
+ * Configures this IO pin as a digital output (if necessary) and sets the pin to 'value'.
+ *
+ * @param value 0 (LO) or 1 (HI)
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have digital capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.setDigitalValue(1); // P0 is now HI
+ * @endcode
+ */
+int MicroBitPin::setDigitalValue(int value)
+{
+ // Check if this pin has a digital mode...
+ if(!(PIN_CAPABILITY_DIGITAL & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Ensure we have a valid value.
+ if (value < 0 || value > 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Move into a Digital input state if necessary.
+ if (!(status & IO_STATUS_DIGITAL_OUT)){
+ disconnect();
+ pin = new DigitalOut(name);
+ status |= IO_STATUS_DIGITAL_OUT;
+ }
+
+ // Write the value.
+ ((DigitalOut *)pin)->write(value);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures this IO pin as a digital input (if necessary) and tests its current value.
+ *
+ * @return 1 if this input is high, 0 if input is LO, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.getDigitalValue(); // P0 is either 0 or 1;
+ * @endcode
+ */
+int MicroBitPin::getDigitalValue()
+{
+ //check if this pin has a digital mode...
+ if(!(PIN_CAPABILITY_DIGITAL & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Move into a Digital input state if necessary.
+ if (!(status & IO_STATUS_DIGITAL_IN)){
+ disconnect();
+ pin = new DigitalIn(name,PullDown);
+ status |= IO_STATUS_DIGITAL_IN;
+ }
+
+ return ((DigitalIn *)pin)->read();
+}
+
+int MicroBitPin::obtainAnalogChannel()
+{
+ // Move into an analogue input state if necessary, if we are no longer the focus of a DynamicPWM instance, allocate ourselves again!
+ if (!(status & IO_STATUS_ANALOG_OUT) || !(((DynamicPwm *)pin)->getPinName() == name)){
+ disconnect();
+ pin = (void *)DynamicPwm::allocate(name);
+ status |= IO_STATUS_ANALOG_OUT;
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures this IO pin as an analog/pwm output, and change the output value to the given level.
+ *
+ * @param value the level to set on the output pin, in the range 0 - 1024
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+int MicroBitPin::setAnalogValue(int value)
+{
+ //check if this pin has an analogue mode...
+ if(!(PIN_CAPABILITY_ANALOG & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ //sanitise the level value
+ if(value < 0 || value > MICROBIT_PIN_MAX_OUTPUT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ float level = (float)value / float(MICROBIT_PIN_MAX_OUTPUT);
+
+ //obtain use of the DynamicPwm instance, if it has changed / configure if we do not have one
+ if(obtainAnalogChannel() == MICROBIT_OK)
+ return ((DynamicPwm *)pin)->write(level);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures this IO pin as an analog/pwm output (if necessary) and configures the period to be 20ms,
+ * with a duty cycle between 500 us and 2500 us.
+ *
+ * A value of 180 sets the duty cycle to be 2500us, and a value of 0 sets the duty cycle to be 500us by default.
+ *
+ * This range can be modified to fine tune, and also tolerate different servos.
+ *
+ * @param value the level to set on the output pin, in the range 0 - 180.
+ *
+ * @param range which gives the span of possible values the i.e. the lower and upper bounds (center +/- range/2). Defaults to MICROBIT_PIN_DEFAULT_SERVO_RANGE.
+ *
+ * @param center the center point from which to calculate the lower and upper bounds. Defaults to MICROBIT_PIN_DEFAULT_SERVO_CENTER
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+int MicroBitPin::setServoValue(int value, int range, int center)
+{
+ //check if this pin has an analogue mode...
+ if(!(PIN_CAPABILITY_ANALOG & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ //sanitise the servo level
+ if(value < 0 || range < 1 || center < 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //clip - just in case
+ if(value > MICROBIT_PIN_MAX_SERVO_RANGE)
+ value = MICROBIT_PIN_MAX_SERVO_RANGE;
+
+ //calculate the lower bound based on the midpoint
+ int lower = (center - (range / 2)) * 1000;
+
+ value = value * 1000;
+
+ //add the percentage of the range based on the value between 0 and 180
+ int scaled = lower + (range * (value / MICROBIT_PIN_MAX_SERVO_RANGE));
+
+ return setServoPulseUs(scaled / 1000);
+}
+
+/**
+ * Configures this IO pin as an analogue input (if necessary), and samples the Pin for its analog value.
+ *
+ * @return the current analogue level on the pin, in the range 0 - 1024, or
+ * MICROBIT_NOT_SUPPORTED if the given pin does not have analog capability.
+ *
+ * @code
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_BOTH);
+ * P0.getAnalogValue(); // P0 is a value in the range of 0 - 1024
+ * @endcode
+ */
+int MicroBitPin::getAnalogValue()
+{
+ //check if this pin has an analogue mode...
+ if(!(PIN_CAPABILITY_ANALOG & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Move into an analogue input state if necessary.
+ if (!(status & IO_STATUS_ANALOG_IN)){
+ disconnect();
+ pin = new AnalogIn(name);
+ status |= IO_STATUS_ANALOG_IN;
+ }
+
+ //perform a read!
+ 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.
+ *
+ * Users can also subscribe to MicroBitButton events generated from this pin.
+ *
+ * @return 1 if pin is touched, 0 if not, or MICROBIT_NOT_SUPPORTED if this pin does not support touch capability.
+ *
+ * @code
+ * MicroBitMessageBus bus;
+ *
+ * MicroBitPin P0(MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL);
+ * if(P0.isTouched())
+ * {
+ * //do something!
+ * }
+ *
+ * // subscribe to events generated by this pin!
+ * bus.listen(MICROBIT_ID_IO_P0, MICROBIT_BUTTON_EVT_CLICK, someFunction);
+ * @endcode
+ */
+int MicroBitPin::isTouched()
+{
+ //check if this pin has a touch mode...
+ if(!(PIN_CAPABILITY_TOUCH & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Move into a touch input state if necessary.
+ if (!(status & IO_STATUS_TOUCH_IN)){
+ disconnect();
+ pin = new MicroBitButton(name, id);
+ status |= IO_STATUS_TOUCH_IN;
+ }
+
+ return ((MicroBitButton *)pin)->isPressed();
+}
+
+/**
+ * Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
+ * and sets the pulse width, based on the value it is given.
+ *
+ * @param pulseWidth the desired pulse width in microseconds.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
+ * if the given pin does not have analog capability.
+ */
+int MicroBitPin::setServoPulseUs(int pulseWidth)
+{
+ //check if this pin has an analogue mode...
+ if(!(PIN_CAPABILITY_ANALOG & capability))
+ return MICROBIT_NOT_SUPPORTED;
+
+ //sanitise the pulse width
+ if(pulseWidth < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //Check we still have the control over the DynamicPwm instance
+ if(obtainAnalogChannel() == MICROBIT_OK)
+ {
+ //check if the period is set to 20ms
+ if(((DynamicPwm *)pin)->getPeriodUs() != MICROBIT_DEFAULT_PWM_PERIOD)
+ ((DynamicPwm *)pin)->setPeriodUs(MICROBIT_DEFAULT_PWM_PERIOD);
+
+ ((DynamicPwm *)pin)->pulsewidth_us(pulseWidth);
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures the PWM period of the analog output to the given value.
+ *
+ * @param period The new period for the analog output in microseconds.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+int MicroBitPin::setAnalogPeriodUs(int period)
+{
+ if (!(status & IO_STATUS_ANALOG_OUT))
+ return MICROBIT_NOT_SUPPORTED;
+
+ return ((DynamicPwm *)pin)->setPeriodUs(period);
+}
+
+/**
+ * Configures the PWM period of the analog output to the given value.
+ *
+ * @param period The new period for the analog output in milliseconds.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+int MicroBitPin::setAnalogPeriod(int period)
+{
+ return setAnalogPeriodUs(period*1000);
+}
+
+/**
+ * Obtains the PWM period of the analog output in microseconds.
+ *
+ * @return the period on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+int MicroBitPin::getAnalogPeriodUs()
+{
+ if (!(status & IO_STATUS_ANALOG_OUT))
+ return MICROBIT_NOT_SUPPORTED;
+
+ return ((DynamicPwm *)pin)->getPeriodUs();
+}
+
+/**
+ * Obtains the PWM period of the analog output in milliseconds.
+ *
+ * @return the period on success, or MICROBIT_NOT_SUPPORTED if the
+ * given pin is not configured as an analog output.
+ */
+int MicroBitPin::getAnalogPeriod()
+{
+ return getAnalogPeriodUs()/1000;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitRadio.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,512 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitRadio.h"
+#include "MicroBitComponent.h"
+#include "EventModel.h"
+#include "MicroBitDevice.h"
+#include "ErrorNo.h"
+#include "MicroBitFiber.h"
+#include "MicroBitBLEManager.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * The nrf51822 RADIO module supports a number of proprietary modes of operation oher than the typical BLE usage.
+ * This class uses one of these modes to enable simple, point to multipoint communication directly between micro:bits.
+ *
+ * TODO: The protocols implemented here do not currently perform any significant form of energy management,
+ * which means that they will consume far more energy than their BLE equivalent. Later versions of the protocol
+ * should look to address this through energy efficient broadcast techbiques / sleep scheduling. In particular, the GLOSSY
+ * approach to efficient rebroadcast and network synchronisation would likely provide an effective future step.
+ *
+ * TODO: Meshing should also be considered - again a GLOSSY approach may be effective here, and highly complementary to
+ * the master/slave arachitecture of BLE.
+ *
+ * TODO: This implementation may only operated whilst the BLE stack is disabled. The nrf51822 provides a timeslot API to allow
+ * BLE to cohabit with other protocols. Future work to allow this colocation would be benefical, and would also allow for the
+ * creation of wireless BLE bridges.
+ *
+ * NOTE: This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+
+MicroBitRadio* MicroBitRadio::instance = NULL;
+
+extern "C" void RADIO_IRQHandler(void)
+{
+ // Move on to the next buffer, if possible.
+ MicroBitRadio::instance->queueRxBuf();
+ NRF_RADIO->PACKETPTR = (uint32_t) MicroBitRadio::instance->getRxBuf();
+
+ if(NRF_RADIO->EVENTS_READY)
+ {
+ NRF_RADIO->EVENTS_READY = 0;
+
+ // Start listening and wait for the END event
+ NRF_RADIO->TASKS_START = 1;
+ }
+
+ if(NRF_RADIO->EVENTS_END)
+ {
+ NRF_RADIO->EVENTS_END = 0;
+
+ if(NRF_RADIO->CRCSTATUS == 1)
+ {
+ uint8_t sample = NRF_RADIO->RSSISAMPLE;
+
+ MicroBitRadio::instance->setRSSI(sample);
+ }
+
+ // Start listening and wait for the END event
+ NRF_RADIO->TASKS_START = 1;
+ }
+}
+
+/**
+ * Constructor.
+ *
+ * Initialise the MicroBitRadio.
+ *
+ * @note This class is demand activated, as a result most resources are only
+ * committed if send/recv or event registrations calls are made.
+ */
+MicroBitRadio::MicroBitRadio(uint16_t id) : datagram(*this), event (*this)
+{
+ this->id = id;
+ this->status = 0;
+ this->group = 0;
+ this->queueDepth = 0;
+ this->rssi = 0;
+ this->rxQueue = NULL;
+ this->rxBuf = NULL;
+
+ instance = this;
+}
+
+/**
+ * Change the output power level of the transmitter to the given value.
+ *
+ * @param power a value in the range 0..7, where 0 is the lowest power and 7 is the highest.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range.
+ */
+int MicroBitRadio::setTransmitPower(int power)
+{
+ if (power < 0 || power >= MICROBIT_BLE_POWER_LEVELS)
+ return MICROBIT_INVALID_PARAMETER;
+
+ NRF_RADIO->TXPOWER = (uint32_t)MICROBIT_BLE_POWER_LEVEL[power];
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Change the transmission and reception band of the radio to the given channel
+ *
+ * @param band a frequency band in the range 0 - 100. Each step is 1MHz wide, based at 2400MHz.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the value is out of range,
+ * or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::setFrequencyBand(int band)
+{
+ if (ble_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ if (band < 0 || band > 100)
+ return MICROBIT_INVALID_PARAMETER;
+
+ NRF_RADIO->FREQUENCY = (uint32_t)band;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Retrieve a pointer to the currently allocated receive buffer. This is the area of memory
+ * actively being used by the radio hardware to store incoming data.
+ *
+ * @return a pointer to the current receive buffer.
+ */
+FrameBuffer* MicroBitRadio::getRxBuf()
+{
+ return rxBuf;
+}
+
+/**
+ * Attempt to queue a buffer received by the radio hardware, if sufficient space is available.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if a replacement receiver buffer
+ * could not be allocated (either by policy or memory exhaustion).
+ */
+int MicroBitRadio::queueRxBuf()
+{
+ if (rxBuf == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (queueDepth >= MICROBIT_RADIO_MAXIMUM_RX_BUFFERS)
+ return MICROBIT_NO_RESOURCES;
+
+ // Store the received RSSI value in the frame
+ rxBuf->rssi = getRSSI();
+
+ // Ensure that a replacement buffer is available before queuing.
+ FrameBuffer *newRxBuf = new FrameBuffer();
+
+ if (newRxBuf == NULL)
+ return MICROBIT_NO_RESOURCES;
+
+ // We add to the tail of the queue to preserve causal ordering.
+ rxBuf->next = NULL;
+
+ if (rxQueue == NULL)
+ {
+ rxQueue = rxBuf;
+ }
+ else
+ {
+ FrameBuffer *p = rxQueue;
+ while (p->next != NULL)
+ p = p->next;
+
+ p->next = rxBuf;
+ }
+
+ // Increase our received packet count
+ queueDepth++;
+
+ // Allocate a new buffer for the receiver hardware to use. the old on will be passed on to higher layer protocols/apps.
+ rxBuf = newRxBuf;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Sets the RSSI for the most recent packet.
+ *
+ * @param rssi the new rssi value.
+ *
+ * @note should only be called from RADIO_IRQHandler...
+ */
+int MicroBitRadio::setRSSI(uint8_t rssi)
+{
+ if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
+ return MICROBIT_NOT_SUPPORTED;
+
+ this->rssi = rssi;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Retrieves the current RSSI for the most recent packet.
+ *
+ * @return the most recent RSSI value or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::getRSSI()
+{
+ if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
+ return MICROBIT_NOT_SUPPORTED;
+
+ return this->rssi;
+}
+
+/**
+ * Initialises the radio for use as a multipoint sender/receiver
+ *
+ * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::enable()
+{
+ // If the device is already initialised, then there's nothing to do.
+ if (status & MICROBIT_RADIO_STATUS_INITIALISED)
+ return MICROBIT_OK;
+
+ // Only attempt to enable this radio mode if BLE is disabled.
+ if (ble_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ // If this is the first time we've been enable, allocate out receive buffers.
+ if (rxBuf == NULL)
+ rxBuf = new FrameBuffer();
+
+ if (rxBuf == NULL)
+ return MICROBIT_NO_RESOURCES;
+
+ // Enable the High Frequency clock on the processor. This is a pre-requisite for
+ // the RADIO module. Without this clock, no communication is possible.
+ NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
+ NRF_CLOCK->TASKS_HFCLKSTART = 1;
+ while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
+
+ // Bring up the nrf51822 RADIO module in Nordic's proprietary 1MBps packet radio mode.
+ setTransmitPower(MICROBIT_RADIO_DEFAULT_TX_POWER);
+ setFrequencyBand(MICROBIT_RADIO_DEFAULT_FREQUENCY);
+
+ // Configure for 1Mbps throughput.
+ // This may sound excessive, but running a high data rates reduces the chances of collisions...
+ NRF_RADIO->MODE = RADIO_MODE_MODE_Nrf_1Mbit;
+
+ // Configure the addresses we use for this protocol. We run ANONYMOUSLY at the core.
+ // A 40 bit addresses is used. The first 32 bits match the ASCII character code for "uBit".
+ // Statistically, this provides assurance to avoid other similar 2.4GHz protocols that may be in the vicinity.
+ // We also map the assigned 8-bit GROUP id into the PREFIX field. This allows the RADIO hardware to perform
+ // address matching for us, and only generate an interrupt when a packet matching our group is received.
+ NRF_RADIO->BASE0 = MICROBIT_RADIO_BASE_ADDRESS;
+
+ // Join the default group. This will configure the remaining byte in the RADIO hardware module.
+ setGroup(MICROBIT_RADIO_DEFAULT_GROUP);
+
+ // The RADIO hardware module supports the use of multiple addresses, but as we're running anonymously, we only need one.
+ // Configure the RADIO module to use the default address (address 0) for both send and receive operations.
+ NRF_RADIO->TXADDRESS = 0;
+ NRF_RADIO->RXADDRESSES = 1;
+
+ // Packet layout configuration. The nrf51822 has a highly capable and flexible RADIO module that, in addition to transmission
+ // and reception of data, also contains a LENGTH field, two optional additional 1 byte fields (S0 and S1) and a CRC calculation.
+ // Configure the packet format for a simple 8 bit length field and no additional fields.
+ NRF_RADIO->PCNF0 = 0x00000008;
+ NRF_RADIO->PCNF1 = 0x02040000 | MICROBIT_RADIO_MAX_PACKET_SIZE;
+
+ // Most communication channels contain some form of checksum - a mathematical calculation taken based on all the data
+ // in a packet, that is also sent as part of the packet. When received, this calculation can be repeated, and the results
+ // from the sender and receiver compared. If they are different, then some corruption of the data ahas happened in transit,
+ // and we know we can't trust it. The nrf51822 RADIO uses a CRC for this - a very effective checksum calculation.
+ //
+ // Enable automatic 16bit CRC generation and checking, and configure how the CRC is calculated.
+ NRF_RADIO->CRCCNF = RADIO_CRCCNF_LEN_Two;
+ NRF_RADIO->CRCINIT = 0xFFFF;
+ NRF_RADIO->CRCPOLY = 0x11021;
+
+ // Set the start random value of the data whitening algorithm. This can be any non zero number.
+ NRF_RADIO->DATAWHITEIV = 0x18;
+
+ // Set up the RADIO module to read and write from our internal buffer.
+ NRF_RADIO->PACKETPTR = (uint32_t)rxBuf;
+
+ // Configure the hardware to issue an interrupt whenever a task is complete (e.g. send/receive).
+ NRF_RADIO->INTENSET = 0x00000008;
+ NVIC_ClearPendingIRQ(RADIO_IRQn);
+ NVIC_EnableIRQ(RADIO_IRQn);
+
+ NRF_RADIO->SHORTS |= RADIO_SHORTS_ADDRESS_RSSISTART_Msk;
+
+ // Start listening for the next packet
+ NRF_RADIO->EVENTS_READY = 0;
+ NRF_RADIO->TASKS_RXEN = 1;
+ while(NRF_RADIO->EVENTS_READY == 0);
+
+ NRF_RADIO->EVENTS_END = 0;
+ NRF_RADIO->TASKS_START = 1;
+
+ // register ourselves for a callback event, in order to empty the receive queue.
+ fiber_add_idle_component(this);
+
+ // Done. Record that our RADIO is configured.
+ status |= MICROBIT_RADIO_STATUS_INITIALISED;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Disables the radio for use as a multipoint sender/receiver.
+ *
+ * @return MICROBIT_OK on success, MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::disable()
+{
+ // Only attempt to enable.disable the radio if the protocol is alreayd running.
+ if (ble_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ if (!(status & MICROBIT_RADIO_STATUS_INITIALISED))
+ return MICROBIT_OK;
+
+ // Disable interrupts and STOP any ongoing packet reception.
+ NVIC_DisableIRQ(RADIO_IRQn);
+
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ NRF_RADIO->TASKS_DISABLE = 1;
+ while(NRF_RADIO->EVENTS_DISABLED == 0);
+
+ // deregister ourselves from the callback event used to empty the receive queue.
+ fiber_remove_idle_component(this);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Sets the radio to listen to packets sent with the given group id.
+ *
+ * @param group The group to join. A micro:bit can only listen to one group ID at any time.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::setGroup(uint8_t group)
+{
+ if (ble_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ // Record our group id locally
+ this->group = group;
+
+ // Also append it to the address of this device, to allow the RADIO module to filter for us.
+ NRF_RADIO->PREFIX0 = (uint32_t)group;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * A background, low priority callback that is triggered whenever the processor is idle.
+ * Here, we empty our queue of received packets, and pass them onto higher level protocol handlers.
+ */
+void MicroBitRadio::idleTick()
+{
+ // Walk the list of packets and process each one.
+ while(rxQueue)
+ {
+ FrameBuffer *p = rxQueue;
+
+ switch (p->protocol)
+ {
+ case MICROBIT_RADIO_PROTOCOL_DATAGRAM:
+ datagram.packetReceived();
+ break;
+
+ case MICROBIT_RADIO_PROTOCOL_EVENTBUS:
+ event.packetReceived();
+ break;
+
+ default:
+ MicroBitEvent(MICROBIT_ID_RADIO_DATA_READY, p->protocol);
+ }
+
+ // If the packet was processed, it will have been recv'd, and taken from the queue.
+ // If this was a packet for an unknown protocol, it will still be there, so simply free it.
+ if (p == rxQueue)
+ {
+ recv();
+ delete p;
+ }
+ }
+}
+
+/**
+ * Determines the number of packets ready to be processed.
+ *
+ * @return The number of packets in the receive buffer.
+ */
+int MicroBitRadio::dataReady()
+{
+ return queueDepth;
+}
+
+/**
+ * Retrieves the next packet from the receive buffer.
+ * If a data packet is available, then it will be returned immediately to
+ * the caller. This call will also dequeue the buffer.
+ *
+ * @return The buffer containing the the packet. If no data is available, NULL is returned.
+ *
+ * @note Once recv() has been called, it is the callers resposibility to
+ * delete the buffer when appropriate.
+ */
+FrameBuffer* MicroBitRadio::recv()
+{
+ FrameBuffer *p = rxQueue;
+
+ if (p)
+ {
+ rxQueue = rxQueue->next;
+ queueDepth--;
+ }
+
+ return p;
+}
+
+/**
+ * Transmits the given buffer onto the broadcast radio.
+ * The call will wait until the transmission of the packet has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NOT_SUPPORTED if the BLE stack is running.
+ */
+int MicroBitRadio::send(FrameBuffer *buffer)
+{
+ if (ble_running())
+ return MICROBIT_NOT_SUPPORTED;
+
+ if (buffer == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if (buffer->length > MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE - 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Firstly, disable the Radio interrupt. We want to wait until the trasmission completes.
+ NVIC_DisableIRQ(RADIO_IRQn);
+
+ // Turn off the transceiver.
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ NRF_RADIO->TASKS_DISABLE = 1;
+ while(NRF_RADIO->EVENTS_DISABLED == 0);
+
+ // Configure the radio to send the buffer provided.
+ NRF_RADIO->PACKETPTR = (uint32_t) buffer;
+
+ // Turn on the transmitter, and wait for it to signal that it's ready to use.
+ NRF_RADIO->EVENTS_READY = 0;
+ NRF_RADIO->TASKS_TXEN = 1;
+ while (NRF_RADIO->EVENTS_READY == 0);
+
+ // Start transmission and wait for end of packet.
+ NRF_RADIO->TASKS_START = 1;
+ NRF_RADIO->EVENTS_END = 0;
+ while(NRF_RADIO->EVENTS_END == 0);
+
+ // Return the radio to using the default receive buffer
+ NRF_RADIO->PACKETPTR = (uint32_t) rxBuf;
+
+ // Turn off the transmitter.
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ NRF_RADIO->TASKS_DISABLE = 1;
+ while(NRF_RADIO->EVENTS_DISABLED == 0);
+
+ // Start listening for the next packet
+ NRF_RADIO->EVENTS_READY = 0;
+ NRF_RADIO->TASKS_RXEN = 1;
+ while(NRF_RADIO->EVENTS_READY == 0);
+
+ NRF_RADIO->EVENTS_END = 0;
+ NRF_RADIO->TASKS_START = 1;
+
+ // Re-enable the Radio interrupt.
+ NVIC_ClearPendingIRQ(RADIO_IRQn);
+ NVIC_EnableIRQ(RADIO_IRQn);
+
+ return MICROBIT_OK;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitRadioDatagram.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,203 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitRadio.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * This class provides the ability to broadcast simple text or binary messages to other micro:bits in the vicinity
+ * It is envisaged that this would provide the basis for children to experiment with building their own, simple,
+ * custom protocols.
+ *
+ * @note This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+
+/**
+* Constructor.
+*
+* Creates an instance of a MicroBitRadioDatagram which offers the ability
+* to broadcast simple text or binary messages to other micro:bits in the vicinity
+*
+* @param r The underlying radio module used to send and receive data.
+*/
+MicroBitRadioDatagram::MicroBitRadioDatagram(MicroBitRadio &r) : radio(r)
+{
+ this->rxQueue = NULL;
+}
+
+/**
+ * Retrieves packet payload data into the given buffer.
+ *
+ * If a data packet is already available, then it will be returned immediately to the caller.
+ * If no data is available then MICROBIT_INVALID_PARAMETER is returned.
+ *
+ * @param buf A pointer to a valid memory location where the received data is to be stored
+ *
+ * @param len The maximum amount of data that can safely be stored in 'buf'
+ *
+ * @return The length of the data stored, or MICROBIT_INVALID_PARAMETER if no data is available, or the memory regions provided are invalid.
+ */
+int MicroBitRadioDatagram::recv(uint8_t *buf, int len)
+{
+ if (buf == NULL || rxQueue == NULL || len < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Take the first buffer from the queue.
+ FrameBuffer *p = rxQueue;
+ rxQueue = rxQueue->next;
+
+ int l = min(len, p->length - (MICROBIT_RADIO_HEADER_SIZE - 1));
+
+ // Fill in the buffer provided, if possible.
+ memcpy(buf, p->payload, l);
+
+ delete p;
+ return l;
+}
+
+/**
+ * Retreives packet payload data into the given buffer.
+ *
+ * If a data packet is already available, then it will be returned immediately to the caller
+ * in the form of a PacketBuffer.
+ *
+ * @return the data received, or an empty PacketBuffer if no data is available.
+ */
+PacketBuffer MicroBitRadioDatagram::recv()
+{
+ if (rxQueue == NULL)
+ return PacketBuffer::EmptyPacket;
+
+ FrameBuffer *p = rxQueue;
+ rxQueue = rxQueue->next;
+
+ PacketBuffer packet(p->payload, p->length - (MICROBIT_RADIO_HEADER_SIZE - 1), p->rssi);
+
+ delete p;
+ return packet;
+}
+
+/**
+ * Transmits the given buffer onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param buffer The packet contents to transmit.
+ *
+ * @param len The number of bytes to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+int MicroBitRadioDatagram::send(uint8_t *buffer, int len)
+{
+ if (buffer == NULL || len < 0 || len > MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE - 1)
+ return MICROBIT_INVALID_PARAMETER;
+
+ FrameBuffer buf;
+
+ buf.length = len + MICROBIT_RADIO_HEADER_SIZE - 1;
+ buf.version = 1;
+ buf.group = 0;
+ buf.protocol = MICROBIT_RADIO_PROTOCOL_DATAGRAM;
+ memcpy(buf.payload, buffer, len);
+
+ return radio.send(&buf);
+}
+
+/**
+ * Transmits the given string onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+int MicroBitRadioDatagram::send(PacketBuffer data)
+{
+ return send((uint8_t *)data.getBytes(), data.length());
+}
+
+/**
+ * Transmits the given string onto the broadcast radio.
+ *
+ * This is a synchronous call that will wait until the transmission of the packet
+ * has completed before returning.
+ *
+ * @param data The packet contents to transmit.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid,
+ * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`.
+ */
+int MicroBitRadioDatagram::send(ManagedString data)
+{
+ return send((uint8_t *)data.toCharArray(), data.length());
+}
+
+/**
+ * Protocol handler callback. This is called when the radio receives a packet marked as a datagram.
+ *
+ * This function process this packet, and queues it for user reception.
+ */
+void MicroBitRadioDatagram::packetReceived()
+{
+ FrameBuffer *packet = radio.recv();
+ int queueDepth = 0;
+
+ // We add to the tail of the queue to preserve causal ordering.
+ packet->next = NULL;
+
+ if (rxQueue == NULL)
+ {
+ rxQueue = packet;
+ }
+ else
+ {
+ FrameBuffer *p = rxQueue;
+ while (p->next != NULL)
+ {
+ p = p->next;
+ queueDepth++;
+ }
+
+ if (queueDepth >= MICROBIT_RADIO_MAXIMUM_RX_BUFFERS)
+ {
+ delete packet;
+ return;
+ }
+
+ p->next = packet;
+ }
+
+ MicroBitEvent(MICROBIT_ID_RADIO, MICROBIT_RADIO_EVT_DATAGRAM);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitRadioEvent.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,175 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitRadio.h"
+
+/**
+ * Provides a simple broadcast radio abstraction, built upon the raw nrf51822 RADIO module.
+ *
+ * This class provides the ability to extend the micro:bit's default EventModel to other micro:bits in the vicinity,
+ * in a very similar way to the MicroBitEventService for BLE interfaces.
+ *
+ * It is envisaged that this would provide the basis for children to experiment with building their own, simple,
+ * custom asynchronous events and actions.
+ *
+ * @note This API does not contain any form of encryption, authentication or authorisation. Its purpose is solely for use as a
+ * teaching aid to demonstrate how simple communications operates, and to provide a sandpit through which learning can take place.
+ * For serious applications, BLE should be considered a substantially more secure alternative.
+ */
+
+/**
+ * Constructor.
+ *
+ * Creates an instance of MicroBitRadioEvent which offers the ability to extend
+ * the micro:bit's default EventModel to other micro:bits in the vicinity.
+ *
+ * @param r The underlying radio module used to send and receive data.
+ */
+MicroBitRadioEvent::MicroBitRadioEvent(MicroBitRadio &r) : radio(r)
+{
+ this->suppressForwarding = false;
+}
+
+/**
+ * Associates the given event with the radio channel.
+ *
+ * Once registered, all events matching the given registration sent to this micro:bit's
+ * default EventModel will be automatically retransmitted on the radio.
+ *
+ * @param id The id of the event to register.
+ *
+ * @param value the value of the event to register.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if no default EventModel is available.
+ *
+ * @note The wildcards MICROBIT_ID_ANY and MICROBIT_EVT_ANY can also be in place of the
+ * id and value fields.
+ */
+int MicroBitRadioEvent::listen(uint16_t id, uint16_t value)
+{
+ if (EventModel::defaultEventBus)
+ return listen(id, value, *EventModel::defaultEventBus);
+
+ return MICROBIT_NO_RESOURCES;
+}
+
+/**
+ * Associates the given event with the radio channel.
+ *
+ * Once registered, all events matching the given registration sent to the given
+ * EventModel will be automatically retransmitted on the radio.
+ *
+ * @param id The id of the events to register.
+ *
+ * @param value the value of the event to register.
+ *
+ * @param eventBus The EventModel to listen for events on.
+ *
+ * @return MICROBIT_OK on success.
+ *
+ * @note The wildcards MICROBIT_ID_ANY and MICROBIT_EVT_ANY can also be in place of the
+ * id and value fields.
+ */
+int MicroBitRadioEvent::listen(uint16_t id, uint16_t value, EventModel &eventBus)
+{
+ return eventBus.listen(id, value, this, &MicroBitRadioEvent::eventReceived, MESSAGE_BUS_LISTENER_IMMEDIATE);
+}
+
+/**
+ * Disassociates the given event with the radio channel.
+ *
+ * @param id The id of the events to deregister.
+ *
+ * @param value The value of the event to deregister.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the default message bus does not exist.
+ *
+ * @note MICROBIT_EVT_ANY can be used to deregister all event values matching the given id.
+ */
+int MicroBitRadioEvent::ignore(uint16_t id, uint16_t value)
+{
+ if (EventModel::defaultEventBus)
+ return ignore(id, value, *EventModel::defaultEventBus);
+
+ return MICROBIT_INVALID_PARAMETER;
+}
+
+/**
+ * Disassociates the given events with the radio channel.
+ *
+ * @param id The id of the events to deregister.
+ *
+ * @param value The value of the event to deregister.
+ *
+ * @param eventBus The EventModel to deregister on.
+ *
+ * @return MICROBIT_OK on success.
+ *
+ * @note MICROBIT_EVT_ANY can be used to deregister all event values matching the given id.
+ */
+int MicroBitRadioEvent::ignore(uint16_t id, uint16_t value, EventModel &eventBus)
+{
+ return eventBus.ignore(id, value, this, &MicroBitRadioEvent::eventReceived);
+}
+
+
+/**
+ * Protocol handler callback. This is called when the radio receives a packet marked as using the event protocol.
+ *
+ * This function process this packet, and fires the event contained inside onto the default EventModel.
+ */
+void MicroBitRadioEvent::packetReceived()
+{
+ FrameBuffer *p = radio.recv();
+ MicroBitEvent *e = (MicroBitEvent *) p->payload;
+
+ suppressForwarding = true;
+ e->fire();
+ suppressForwarding = false;
+
+ delete p;
+}
+
+/**
+ * Event handler callback. This is called whenever an event is received matching one of those registered through
+ * the registerEvent() method described above. Upon receiving such an event, it is wrapped into
+ * a radio packet and transmitted to any other micro:bits in the same group.
+ */
+void MicroBitRadioEvent::eventReceived(MicroBitEvent e)
+{
+ if(suppressForwarding)
+ return;
+
+ FrameBuffer buf;
+
+ buf.length = sizeof(MicroBitEvent) + MICROBIT_RADIO_HEADER_SIZE - 1;
+ buf.version = 1;
+ buf.group = 0;
+ buf.protocol = MICROBIT_RADIO_PROTOCOL_EVENTBUS;
+ memcpy(buf.payload, (const uint8_t *)&e, sizeof(MicroBitEvent));
+
+ radio.send(&buf);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitSerial.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,1099 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "mbed.h"
+#include "MicroBitSerial.h"
+#include "ErrorNo.h"
+#include "MicroBitComponent.h"
+#include "MicroBitFiber.h"
+#include "NotifyEvents.h"
+
+uint8_t MicroBitSerial::status = 0;
+
+int MicroBitSerial::baudrate = 0;
+
+/**
+ * Constructor.
+ * Create an instance of MicroBitSerial
+ *
+ * @param tx the Pin to be used for transmission
+ *
+ * @param rx the Pin to be used for receiving data
+ *
+ * @param rxBufferSize the size of the buffer to be used for receiving bytes
+ *
+ * @param txBufferSize the size of the buffer to be used for transmitting bytes
+ *
+ * @code
+ * MicroBitSerial serial(USBTX, USBRX);
+ * @endcode
+ * @note the default baud rate is 115200. More API details can be found:
+ * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/SerialBase.h
+ * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/RawSerial.h
+ *
+ * Buffers aren't allocated until the first send or receive respectively.
+ */
+MicroBitSerial::MicroBitSerial(PinName tx, PinName rx, uint8_t rxBufferSize, uint8_t txBufferSize) : RawSerial(tx,rx), delimeters()
+{
+ this->rxBuffSize = rxBufferSize;
+ this->txBuffSize = txBufferSize;
+
+ this->rxBuff = NULL;
+ this->txBuff = NULL;
+
+ this->rxBuffHead = 0;
+ this->rxBuffTail = 0;
+
+ this->txBuffHead = 0;
+ this->txBuffTail = 0;
+
+ this->rxBuffHeadMatch = -1;
+
+ this->baud(MICROBIT_SERIAL_DEFAULT_BAUD_RATE);
+
+#if CONFIG_ENABLED(MICROBIT_DBG)
+ SERIAL_DEBUG = this;
+#endif
+
+}
+
+/**
+ * An internal interrupt callback for MicroBitSerial configured for when a
+ * character is received.
+ *
+ * Each time a character is received fill our circular buffer!
+ */
+void MicroBitSerial::dataReceived()
+{
+ if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT))
+ return;
+
+ //get the received character
+ char c = getc();
+
+ int delimeterOffset = 0;
+ int delimLength = this->delimeters.length();
+
+ //iterate through our delimeters (if any) to see if there is a match
+ while(delimeterOffset < delimLength)
+ {
+ //fire an event if there is to block any waiting fibers
+ if(this->delimeters.charAt(delimeterOffset) == c)
+ MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH);
+
+ delimeterOffset++;
+ }
+
+ uint16_t newHead = (rxBuffHead + 1) % rxBuffSize;
+
+ //look ahead to our newHead value to see if we are about to collide with the tail
+ if(newHead != rxBuffTail)
+ {
+ //if we are not, store the character, and update our actual head.
+ this->rxBuff[rxBuffHead] = c;
+ rxBuffHead = newHead;
+
+ //if we have any fibers waiting for a specific number of characters, unblock them
+ if(rxBuffHeadMatch >= 0)
+ if(rxBuffHead == rxBuffHeadMatch)
+ {
+ rxBuffHeadMatch = -1;
+ MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_HEAD_MATCH);
+ }
+ }
+ else
+ //otherwise, our buffer is full, send an event to the user...
+ MicroBitEvent(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_RX_FULL);
+}
+
+/**
+ * An internal interrupt callback for MicroBitSerial.
+ *
+ * Each time the Serial module's buffer is empty, write a character if we have
+ * characters to write.
+ */
+void MicroBitSerial::dataWritten()
+{
+ if(txBuffTail == txBuffHead || !(status & MICROBIT_SERIAL_TX_BUFF_INIT))
+ return;
+
+ //send our current char
+ putc(txBuff[txBuffTail]);
+
+ uint16_t nextTail = (txBuffTail + 1) % txBuffSize;
+
+ //unblock any waiting fibers that are waiting for transmission to finish.
+ if(nextTail == txBuffHead)
+ {
+ MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY);
+ detach(Serial::IrqType::TxIrq);
+ }
+
+ //update our tail!
+ txBuffTail = nextTail;
+}
+
+/**
+ * An internal method to configure an interrupt on tx buffer and also
+ * a best effort copy operation to move bytes from a user buffer to our txBuff
+ *
+ * @param string a pointer to the first character of the users' buffer.
+ *
+ * @param len the length of the string, and ultimately the maximum number of bytes
+ * that will be copied dependent on the state of txBuff
+ *
+ * @return the number of bytes copied into the buffer.
+ */
+int MicroBitSerial::setTxInterrupt(uint8_t *string, int len)
+{
+ int copiedBytes = 0;
+
+ for(copiedBytes = 0; copiedBytes < len; copiedBytes++)
+ {
+ uint16_t nextHead = (txBuffHead + 1) % txBuffSize;
+ if(nextHead != txBuffTail)
+ {
+ this->txBuff[txBuffHead] = string[copiedBytes];
+ txBuffHead = nextHead;
+ }
+ else
+ break;
+ }
+
+ fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_SERIAL_EVT_TX_EMPTY);
+
+ //set the TX interrupt
+ attach(this, &MicroBitSerial::dataWritten, Serial::IrqType::TxIrq);
+
+ return copiedBytes;
+}
+
+/**
+ * Locks the mutex so that others can't use this serial instance for reception
+ */
+void MicroBitSerial::lockRx()
+{
+ status |= MICROBIT_SERIAL_RX_IN_USE;
+}
+
+/**
+ * Locks the mutex so that others can't use this serial instance for transmission
+ */
+void MicroBitSerial::lockTx()
+{
+ status |= MICROBIT_SERIAL_TX_IN_USE;
+}
+
+/**
+ * Unlocks the mutex so that others can use this serial instance for reception
+ */
+void MicroBitSerial::unlockRx()
+{
+ status &= ~MICROBIT_SERIAL_RX_IN_USE;
+}
+
+/**
+ * Unlocks the mutex so that others can use this serial instance for transmission
+ */
+void MicroBitSerial::unlockTx()
+{
+ status &= ~MICROBIT_SERIAL_TX_IN_USE;
+}
+
+/**
+ * We do not want to always have our buffers initialised, especially if users to not
+ * use them. We only bring them up on demand.
+ */
+int MicroBitSerial::initialiseRx()
+{
+ if((status & MICROBIT_SERIAL_RX_BUFF_INIT))
+ {
+ //ensure that we receive no interrupts after freeing our buffer
+ detach(Serial::IrqType::RxIrq);
+ free(this->rxBuff);
+ }
+
+ status &= ~MICROBIT_SERIAL_RX_BUFF_INIT;
+
+ if((this->rxBuff = (uint8_t *)malloc(rxBuffSize)) == NULL)
+ return MICROBIT_NO_RESOURCES;
+
+ this->rxBuffHead = 0;
+ this->rxBuffTail = 0;
+
+ //set the receive interrupt
+ status |= MICROBIT_SERIAL_RX_BUFF_INIT;
+ attach(this, &MicroBitSerial::dataReceived, Serial::IrqType::RxIrq);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * We do not want to always have our buffers initialised, especially if users to not
+ * use them. We only bring them up on demand.
+ */
+int MicroBitSerial::initialiseTx()
+{
+ if((status & MICROBIT_SERIAL_TX_BUFF_INIT))
+ {
+ //ensure that we receive no interrupts after freeing our buffer
+ detach(Serial::IrqType::TxIrq);
+ free(this->txBuff);
+ }
+
+ status &= ~MICROBIT_SERIAL_TX_BUFF_INIT;
+
+ if((this->txBuff = (uint8_t *)malloc(txBuffSize)) == NULL)
+ return MICROBIT_NO_RESOURCES;
+
+ this->txBuffHead = 0;
+ this->txBuffTail = 0;
+
+ status |= MICROBIT_SERIAL_TX_BUFF_INIT;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * An internal method that either spin waits if mode is set to SYNC_SPINWAIT
+ * or puts the fiber to sleep if the mode is set to SYNC_SLEEP
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP
+ */
+void MicroBitSerial::send(MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ while(txBufferedSize() > 0);
+
+ if(mode == SYNC_SLEEP)
+ fiber_sleep(0);
+}
+
+/**
+ * Reads a single character from the rxBuff
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - A character is read from the rxBuff if available, if there
+ * are no characters to be read, a value of zero is returned immediately.
+ *
+ * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there
+ * are no characters to be read, this method will spin
+ * (lock up the processor) until a character is available.
+ *
+ * SYNC_SLEEP - A character is read from the rxBuff if available, if there
+ * are no characters to be read, the calling fiber sleeps
+ * until there is a character available.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return a character from the circular buffer, or MICROBIT_NO_DATA is there
+ * are no characters in the buffer.
+ */
+int MicroBitSerial::getChar(MicroBitSerialMode mode)
+{
+ if(mode == ASYNC)
+ {
+ if(!isReadable())
+ return MICROBIT_NO_DATA;
+ }
+
+ if(mode == SYNC_SPINWAIT)
+ while(!isReadable());
+
+ if(mode == SYNC_SLEEP)
+ {
+ if(!isReadable())
+ eventAfter(1, mode);
+ }
+
+ char c = rxBuff[rxBuffTail];
+
+ rxBuffTail = (rxBuffTail + 1) % rxBuffSize;
+
+ return c;
+}
+
+/**
+ * An internal method that copies values from a circular buffer to a linear buffer.
+ *
+ * @param circularBuff a pointer to the source circular buffer
+ *
+ * @param circularBuffSize the size of the circular buffer
+ *
+ * @param linearBuff a pointer to the destination linear buffer
+ *
+ * @param tailPosition the tail position in the circular buffer you want to copy from
+ *
+ * @param headPosition the head position in the circular buffer you want to copy to
+ *
+ * @note this method assumes that the linear buffer has the appropriate amount of
+ * memory to contain the copy operation
+ */
+void MicroBitSerial::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition)
+{
+ int toBuffIndex = 0;
+
+ while(tailPosition != headPosition)
+ {
+ linearBuff[toBuffIndex++] = circularBuff[tailPosition];
+
+ tailPosition = (tailPosition + 1) % circularBuffSize;
+ }
+}
+
+/**
+ * Sends a single character over the serial line.
+ *
+ * @param c the character to send
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - the character is copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - the character is copied into the txBuff and this method
+ * will spin (lock up the processor) until the character has
+ * been sent.
+ *
+ * SYNC_SLEEP - the character is copied into the txBuff and the fiber sleeps
+ * until the character has been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+int MicroBitSerial::sendChar(char c, MicroBitSerialMode mode)
+{
+ if(txInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockTx();
+
+ //lazy initialisation of our tx buffer
+ if(!(status & MICROBIT_SERIAL_TX_BUFF_INIT))
+ {
+ int result = initialiseTx();
+
+ if(result != MICROBIT_OK)
+ return result;
+ }
+
+ uint8_t toTransmit[2] = { c, '\0'};
+
+ int bytesWritten = setTxInterrupt(toTransmit, 1);
+
+ send(mode);
+
+ unlockTx();
+
+ return bytesWritten;
+}
+
+/**
+ * Sends a ManagedString over the serial line.
+ *
+ * @param s the string to send
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - bytes are copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - bytes are copied into the txBuff and this method
+ * will spin (lock up the processor) until all bytes
+ * have been sent.
+ *
+ * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps
+ * until all bytes have been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+int MicroBitSerial::send(ManagedString s, MicroBitSerialMode mode)
+{
+ return send((uint8_t *)s.toCharArray(), s.length(), mode);
+}
+
+/**
+ * Sends a buffer of known length over the serial line.
+ *
+ * @param buffer a pointer to the first character of the buffer
+ *
+ * @param len the number of bytes that are safely available to read.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - bytes are copied into the txBuff and returns immediately.
+ *
+ * SYNC_SPINWAIT - bytes are copied into the txBuff and this method
+ * will spin (lock up the processor) until all bytes
+ * have been sent.
+ *
+ * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps
+ * until all bytes have been sent. This allows other fibers
+ * to continue execution.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the serial instance for transmission.
+ */
+int MicroBitSerial::send(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode)
+{
+ if(txInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockTx();
+
+ //lazy initialisation of our tx buffer
+ if(!(status & MICROBIT_SERIAL_TX_BUFF_INIT))
+ {
+ int result = initialiseTx();
+
+ if(result != MICROBIT_OK)
+ return result;
+ }
+
+ int bytesWritten = setTxInterrupt(buffer, bufferLen);
+
+ send(mode);
+
+ unlockTx();
+
+ return bytesWritten;
+}
+
+/**
+ * Reads a single character from the rxBuff
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - A character is read from the rxBuff if available, if there
+ * are no characters to be read, a value of MICROBIT_NO_DATA is returned immediately.
+ *
+ * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there
+ * are no characters to be read, this method will spin
+ * (lock up the processor) until a character is available.
+ *
+ * SYNC_SLEEP - A character is read from the rxBuff if available, if there
+ * are no characters to be read, the calling fiber sleeps
+ * until there is a character available.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return a character, MICROBIT_SERIAL_IN_USE if another fiber is using the serial instance for reception,
+ * MICROBIT_NO_RESOURCES if buffer allocation did not complete successfully, or MICROBIT_NO_DATA if
+ * the rx buffer is empty and the mode given is ASYNC.
+ */
+int MicroBitSerial::read(MicroBitSerialMode mode)
+{
+ if(rxInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockRx();
+
+ //lazy initialisation of our buffers
+ if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT))
+ {
+ int result = initialiseRx();
+
+ if(result != MICROBIT_OK)
+ return result;
+ }
+
+ char c = (char)getChar(mode);
+
+ unlockRx();
+
+ return c;
+}
+
+/**
+ * Reads multiple characters from the rxBuff and returns them as a ManagedString
+ *
+ * @param size the number of characters to read.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, it will read however
+ * many characters there are available.
+ *
+ * SYNC_SPINWAIT - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, this method will spin
+ * (lock up the processor) until the desired number of characters have been read.
+ *
+ * SYNC_SLEEP - If the desired number of characters are available, this will return
+ * a ManagedString with the expected size. Otherwise, the calling fiber sleeps
+ * until the desired number of characters have been read.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return A ManagedString, or an empty ManagedString if an error was encountered during the read.
+ */
+ManagedString MicroBitSerial::read(int size, MicroBitSerialMode mode)
+{
+ uint8_t buff[size + 1] = { 0 };
+
+ int returnedSize = read((uint8_t *)buff, size, mode);
+
+ if(returnedSize <= 0)
+ return ManagedString();
+
+ return ManagedString((char *)buff, returnedSize);
+}
+
+/**
+ * Reads multiple characters from the rxBuff and fills a user buffer.
+ *
+ * @param buffer a pointer to a user allocated buffer.
+ *
+ * @param bufferLen the amount of data that can be safely stored
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, it will fill the buffer with however
+ * many characters there are available.
+ *
+ * SYNC_SPINWAIT - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, this method will spin (lock up the processor)
+ * and fill the buffer until the desired number of characters have been read.
+ *
+ * SYNC_SLEEP - If the desired number of characters are available, this will fill
+ * the given buffer. Otherwise, the calling fiber sleeps
+ * until the desired number of characters have been read.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return the number of characters read, or MICROBIT_SERIAL_IN_USE if another fiber
+ * is using the instance for receiving.
+ */
+int MicroBitSerial::read(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode)
+{
+ if(rxInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockRx();
+
+ //lazy initialisation of our rx buffer
+ if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT))
+ {
+ int result = initialiseRx();
+
+ if(result != MICROBIT_OK)
+ return result;
+ }
+
+ int bufferIndex = 0;
+
+ int temp = 0;
+
+ if(mode == ASYNC)
+ {
+ while((temp = getChar(mode)) != MICROBIT_NO_DATA && bufferIndex < bufferLen)
+ {
+ buffer[bufferIndex] = (char)temp;
+ bufferIndex++;
+ }
+ }
+
+ if(mode == SYNC_SPINWAIT)
+ {
+ while(bufferIndex < bufferLen)
+ {
+ buffer[bufferIndex] = (char)getChar(mode);
+ bufferIndex++;
+ }
+ }
+
+ if(mode == SYNC_SLEEP)
+ {
+ if(bufferLen > rxBufferedSize())
+ eventAfter(bufferLen - rxBufferedSize(), mode);
+
+ while(bufferIndex < bufferLen)
+ {
+ buffer[bufferIndex] = (char)getChar(mode);
+ bufferIndex++;
+ }
+ }
+
+ unlockRx();
+
+ return bufferIndex;
+}
+
+
+/**
+ * Reads until one of the delimeters matches a character in the rxBuff
+ *
+ * @param delimeters a ManagedString containing a sequence of delimeter characters e.g. ManagedString("\r\n")
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, it will return an Empty ManagedString.
+ *
+ * SYNC_SPINWAIT - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, this method will spin (lock up the processor) until a
+ * received character matches one of the delimeters.
+ *
+ * SYNC_SLEEP - If one of the delimeters matches a character already in the rxBuff
+ * this method will return a ManagedString up to the delimeter.
+ * Otherwise, the calling fiber sleeps until a character matching one
+ * of the delimeters is seen.
+ *
+ * Defaults to SYNC_SLEEP.
+ *
+ * @return A ManagedString containing the characters up to a delimeter, or an Empty ManagedString,
+ * if another fiber is currently using this instance for reception.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+ManagedString MicroBitSerial::readUntil(ManagedString delimeters, MicroBitSerialMode mode)
+{
+
+ if(rxInUse())
+ return ManagedString();
+
+ //lazy initialisation of our rx buffer
+ if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT))
+ {
+ int result = initialiseRx();
+
+ if(result != MICROBIT_OK)
+ return result;
+ }
+
+ lockRx();
+
+ int localTail = rxBuffTail;
+ int preservedTail = rxBuffTail;
+
+ int foundIndex = -1;
+
+ //ASYNC mode just iterates through our stored characters checking for any matches.
+ while(localTail != rxBuffHead && foundIndex == -1)
+ {
+ //we use localTail to prevent modification of the actual tail.
+ char c = rxBuff[localTail];
+
+ for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
+ if(delimeters.charAt(delimeterIterator) == c)
+ foundIndex = localTail;
+
+ localTail = (localTail + 1) % rxBuffSize;
+ }
+
+ //if our mode is SYNC_SPINWAIT and we didn't see any matching characters in our buffer
+ //spin until we find a match!
+ if(mode == SYNC_SPINWAIT)
+ {
+ while(foundIndex == -1)
+ {
+ while(localTail == rxBuffHead);
+
+ char c = rxBuff[localTail];
+
+ for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
+ if(delimeters.charAt(delimeterIterator) == c)
+ foundIndex = localTail;
+
+ localTail = (localTail + 1) % rxBuffSize;
+ }
+ }
+
+ //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a
+ //matching character.
+ if(mode == SYNC_SLEEP && foundIndex == -1)
+ {
+ eventOn(delimeters, mode);
+
+ foundIndex = rxBuffHead - 1;
+
+ this->delimeters = ManagedString();
+ }
+
+ if(foundIndex >= 0)
+ {
+ //calculate our local buffer size
+ int localBuffSize = (preservedTail > foundIndex) ? (rxBuffSize - preservedTail) + foundIndex : foundIndex - preservedTail;
+
+ uint8_t localBuff[localBuffSize + 1] = { 0 };
+
+ circularCopy(rxBuff, rxBuffSize, localBuff, preservedTail, foundIndex);
+
+ //plus one for the character we listened for...
+ rxBuffTail = (rxBuffTail + localBuffSize + 1) % rxBuffSize;
+
+ unlockRx();
+
+ return ManagedString((char *)localBuff, localBuffSize);
+ }
+
+ unlockRx();
+
+ return ManagedString();
+}
+
+/**
+ * A wrapper around the inherited method "baud" so we can trap the baud rate
+ * as it changes and restore it if redirect() is called.
+ *
+ * @param baudrate the new baudrate. See:
+ * - https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/serial_api.c
+ * for permitted baud rates.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if baud rate is less than 0, otherwise MICROBIT_OK.
+ *
+ * @note the underlying implementation chooses the first allowable rate at or above that requested.
+ */
+void MicroBitSerial::baud(int baudrate)
+{
+ if(baudrate < 0)
+ return;
+
+ this->baudrate = baudrate;
+
+ RawSerial::baud(baudrate);
+}
+
+/**
+ * A way of dynamically configuring the serial instance to use pins other than USBTX and USBRX.
+ *
+ * @param tx the new transmission pin.
+ *
+ * @param rx the new reception pin.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently transmitting or receiving, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::redirect(PinName tx, PinName rx)
+{
+ if(txInUse() || rxInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockTx();
+ lockRx();
+
+ if(txBufferedSize() > 0)
+ detach(Serial::IrqType::TxIrq);
+
+ detach(Serial::IrqType::RxIrq);
+
+ serial_free(&_serial);
+ serial_init(&_serial, tx, rx);
+
+ attach(this, &MicroBitSerial::dataReceived, Serial::IrqType::RxIrq);
+
+ if(txBufferedSize() > 0)
+ attach(this, &MicroBitSerial::dataWritten, Serial::IrqType::TxIrq);
+
+ this->baud(this->baudrate);
+
+ unlockRx();
+ unlockTx();
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures an event to be fired after "len" characters.
+ *
+ * @param len the number of characters to wait before triggering the event.
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::eventAfter(int len, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //configure our head match...
+ this->rxBuffHeadMatch = (rxBuffHead + len) % rxBuffSize;
+
+ //block!
+ if(mode == SYNC_SLEEP)
+ fiber_wait_for_event(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_HEAD_MATCH);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Configures an event to be fired on a match with one of the delimeters.
+ *
+ * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
+ *
+ * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
+ * gives a different behaviour:
+ *
+ * ASYNC - Will configure the event and return immediately.
+ *
+ * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
+ *
+ * SYNC_SLEEP - Will configure the event and block the current fiber until the
+ * event is received.
+ *
+ * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
+ *
+ * @note delimeters are matched on a per byte basis.
+ */
+int MicroBitSerial::eventOn(ManagedString delimeters, MicroBitSerialMode mode)
+{
+ if(mode == SYNC_SPINWAIT)
+ return MICROBIT_INVALID_PARAMETER;
+
+ //configure our head match...
+ this->delimeters = delimeters;
+
+ //block!
+ if(mode == SYNC_SLEEP)
+ fiber_wait_for_event(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH);
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Determines whether there is any data waiting in our Rx buffer.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ *
+ * @note We do not wrap the super's readable() method as we don't want to
+ * interfere with communities that use manual calls to serial.readable().
+ */
+int MicroBitSerial::isReadable()
+{
+ return (rxBuffTail != rxBuffHead) ? 1 : 0;
+}
+
+/**
+ * Determines if we have space in our txBuff.
+ *
+ * @return 1 if we have space, 0 if we do not.
+ *
+ * @note We do not wrap the super's writeable() method as we don't want to
+ * interfere with communities that use manual calls to serial.writeable().
+ */
+int MicroBitSerial::isWriteable()
+{
+ return (txBuffHead != (txBuffTail - 1)) ? 1 : 0;
+}
+
+/**
+ * Reconfigures the size of our rxBuff
+ *
+ * @param size the new size for our rxBuff
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for reception, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::setRxBufferSize(uint8_t size)
+{
+ if(rxInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockRx();
+
+ this->rxBuffSize = size;
+
+ int result = initialiseRx();
+
+ unlockRx();
+
+ return result;
+}
+
+/**
+ * Reconfigures the size of our txBuff
+ *
+ * @param size the new size for our txBuff
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for transmission, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::setTxBufferSize(uint8_t size)
+{
+ if(txInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockTx();
+
+ this->txBuffSize = size;
+
+ int result = initialiseTx();
+
+ unlockTx();
+
+ return result;
+}
+
+/**
+ * The size of our rx buffer in bytes.
+ *
+ * @return the current size of rxBuff in bytes
+ */
+int MicroBitSerial::getRxBufferSize()
+{
+ return this->rxBuffSize;
+}
+
+/**
+ * The size of our tx buffer in bytes.
+ *
+ * @return the current size of txBuff in bytes
+ */
+int MicroBitSerial::getTxBufferSize()
+{
+ return this->txBuffSize;
+}
+
+/**
+ * Sets the tail to match the head of our circular buffer for reception,
+ * effectively clearing the reception buffer.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for reception, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::clearRxBuffer()
+{
+ if(rxInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockRx();
+
+ rxBuffTail = rxBuffHead;
+
+ unlockRx();
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Sets the tail to match the head of our circular buffer for transmission,
+ * effectively clearing the transmission buffer.
+ *
+ * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance
+ * for transmission, otherwise MICROBIT_OK.
+ */
+int MicroBitSerial::clearTxBuffer()
+{
+ if(txInUse())
+ return MICROBIT_SERIAL_IN_USE;
+
+ lockTx();
+
+ txBuffTail = txBuffHead;
+
+ unlockTx();
+
+ return MICROBIT_OK;
+}
+
+/**
+ * The number of bytes currently stored in our rx buffer waiting to be digested,
+ * by the user.
+ *
+ * @return The currently buffered number of bytes in our rxBuff.
+ */
+int MicroBitSerial::rxBufferedSize()
+{
+ if(rxBuffTail > rxBuffHead)
+ return (rxBuffSize - rxBuffTail) + rxBuffHead;
+
+ return rxBuffHead - rxBuffTail;
+}
+
+/**
+ * The number of bytes currently stored in our tx buffer waiting to be transmitted
+ * by the hardware.
+ *
+ * @return The currently buffered number of bytes in our txBuff.
+ */
+int MicroBitSerial::txBufferedSize()
+{
+ if(txBuffTail > txBuffHead)
+ return (txBuffSize - txBuffTail) + txBuffHead;
+
+ return txBuffHead - txBuffTail;
+}
+
+/**
+ * Determines if the serial bus is currently in use by another fiber for reception.
+ *
+ * @return The state of our mutex lock for reception.
+ *
+ * @note Only one fiber can call read at a time
+ */
+int MicroBitSerial::rxInUse()
+{
+ return (status & MICROBIT_SERIAL_RX_IN_USE);
+}
+
+/**
+ * Determines if the serial bus is currently in use by another fiber for transmission.
+ *
+ * @return The state of our mutex lock for transmition.
+ *
+ * @note Only one fiber can call send at a time
+ */
+int MicroBitSerial::txInUse()
+{
+ return (status & MICROBIT_SERIAL_TX_IN_USE);
+}
+
+/**
+ * Detaches a previously configured interrupt
+ *
+ * @param interruptType one of Serial::RxIrq or Serial::TxIrq
+ */
+void MicroBitSerial::detach(Serial::IrqType interruptType)
+{
+ //we detach by sending a bad value to attach, for some weird reason...
+ attach((MicroBitSerial *)NULL, &MicroBitSerial::dataReceived, interruptType);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitStorage.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,485 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for the MicroBitStorage class.
+ * This allows reading and writing of FLASH memory.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitStorage.h"
+#include "MicroBitCompat.h"
+
+/**
+ * Default constructor.
+ *
+ * Creates an instance of MicroBitStorage which acts like a KeyValueStore
+ * that allows the retrieval, addition and deletion of KeyValuePairs.
+ */
+MicroBitStorage::MicroBitStorage()
+{
+ //initialise our magic block, if required.
+ size();
+}
+
+/**
+ * Writes the given number of bytes to the address specified.
+ *
+ * @param buffer the data to write.
+ *
+ * @param address the location in memory to write to.
+ *
+ * @param length the number of bytes to write.
+ *
+ * @note currently not implemented.
+ */
+int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
+{
+ (void) buffer;
+ (void) address;
+ (void) length;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Method for erasing a page in flash.
+ *
+ * @param page_address Address of the first word in the page to be erased.
+ */
+void MicroBitStorage::flashPageErase(uint32_t * page_address)
+{
+ // Turn on flash erase enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+
+ // Erase page:
+ NRF_NVMC->ERASEPAGE = (uint32_t)page_address;
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+
+ // Turn off flash erase enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+}
+
+/**
+ * Function for copying words from one location to another.
+ *
+ * @param from the address to copy data from.
+ *
+ * @param to the address to copy the data to.
+ *
+ * @param sizeInWords the number of words to copy
+ */
+void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
+{
+ // Turn on flash write enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
+
+ for(int i = 0; i < sizeInWords; i++)
+ {
+ *(to + i) = *(from + i);
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
+ }
+
+ // Turn off flash write enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
+}
+
+/**
+ * Method for writing a word of data in flash with a value.
+ *
+ * @param address Address of the word to change.
+ *
+ * @param value Value to be written to flash.
+ */
+void MicroBitStorage::flashWordWrite(uint32_t * address, uint32_t value)
+{
+ // Turn on flash write enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+
+ *address = value;
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+
+ // Turn off flash write enable and wait until the NVMC is ready:
+ NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
+
+ while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
+}
+
+/**
+ * Function for populating the scratch page with a KeyValueStore.
+ *
+ * @param store the KeyValueStore struct to write to the scratch page.
+ */
+void MicroBitStorage::scratchKeyValueStore(KeyValueStore store)
+{
+ //calculate our various offsets
+ uint32_t *s = (uint32_t *) &store;
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+
+ uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
+
+ //KeyValueStore is word aligned.
+ int wordsToWrite = sizeof(KeyValueStore) / 4;
+
+ //write the given KeyValueStore
+ for (int i = 0; i < wordsToWrite; i++)
+ {
+ flashWordWrite(scratchPointer, *s);
+ scratchPointer++;
+ s++;
+ }
+}
+
+/**
+ * Function for populating the scratch page with a KeyValuePair.
+ *
+ * @param pair the KeyValuePair struct to write to the scratch page.
+ *
+ * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer
+ * is used to determine the offset into the scratch page, where the KeyValuePair should
+ * be written.
+ */
+void MicroBitStorage::scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPointer)
+{
+ //we can only write using words
+ uint32_t *p = (uint32_t *) &pair;
+
+ //calculate our various offsets
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+ uint32_t pg_num = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
+
+ uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
+ uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
+
+ uint32_t flashPointerOffset = flashPointer - flashBlockPointer;
+
+ scratchPointer += flashPointerOffset;
+
+ //KeyValuePair is word aligned...
+ int wordsToWrite = sizeof(KeyValuePair) / 4;
+
+ //write
+ for (int i = 0; i < wordsToWrite; i++)
+ {
+ flashWordWrite(scratchPointer, *p);
+ scratchPointer++;
+ p++;
+ }
+}
+
+/**
+ * Places a given key, and it's corresponding value into flash at the earliest
+ * available point.
+ *
+ * @param key the unique name that should be used as an identifier for the given data.
+ * The key is presumed to be null terminated.
+ *
+ * @param data a pointer to the beginning of the data to be persisted.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
+ */
+int MicroBitStorage::put(const char *key, uint8_t *data)
+{
+ KeyValuePair pair = KeyValuePair();
+
+ memcpy(pair.key, key, min(sizeof(pair.key), strlen(key)));
+ memcpy(pair.value, data, sizeof(pair.value));
+
+ //calculate our various offsets.
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+ uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
+ uint32_t *flashBlockPointer = flashPointer;
+ uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
+
+ uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
+ uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
+
+ int storeSize = size();
+
+ //our KeyValueStore struct is always at 0
+ flashPointer += kvStoreSize;
+
+ KeyValuePair storedPair = KeyValuePair();
+
+ int found = 0;
+
+ //erase our scratch page
+ flashPageErase(scratchPointer);
+
+ //iterate through key value pairs in flash, writing them to the scratch page.
+ for(int i = 0; i < storeSize; i++)
+ {
+ memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
+
+ //check if the keys match...
+ if(strcmp((char *)storedPair.key, (char *)pair.key) == 0)
+ {
+ found = 1;
+ //scratch our KeyValueStore struct so that it is preserved.
+ scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
+ scratchKeyValuePair(pair, flashPointer);
+ }
+ else
+ {
+ scratchKeyValuePair(storedPair, flashPointer);
+ }
+
+ flashPointer += kvPairSize;
+ }
+
+ if(!found)
+ {
+ //if we haven't got a match for the key, check we can add a new KeyValuePair
+ if(storeSize == (int)((pg_size - kvStoreSize) / MICROBIT_STORAGE_BLOCK_SIZE))
+ return MICROBIT_NO_RESOURCES;
+
+ storeSize += 1;
+
+ //scratch our updated values.
+ scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
+ scratchKeyValuePair(pair, flashPointer);
+ }
+
+ //erase our storage page
+ flashPageErase((uint32_t *)flashBlockPointer);
+
+ //copy from scratch to storage.
+ flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Places a given key, and it's corresponding value into flash at the earliest
+ * available point.
+ *
+ * @param key the unique name that should be used as an identifier for the given data.
+ *
+ * @param data a pointer to the beginning of the data to be persisted.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
+ */
+int MicroBitStorage::put(ManagedString key, uint8_t* data)
+{
+ return put((char *)key.toCharArray(), data);
+}
+
+/**
+ * Retreives a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
+ * NULL if the key was not found in storage.
+ *
+ * @note it is up to the user to free memory after use.
+ */
+KeyValuePair* MicroBitStorage::get(const char* key)
+{
+ //calculate our offsets for our storage page
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+ uint32_t pg_num = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
+
+ uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
+
+ int storeSize = size();
+
+ //we haven't got anything stored, so return...
+ if(storeSize == 0)
+ return NULL;
+
+ //our KeyValueStore struct is always at 0
+ flashBlockPointer += sizeof(KeyValueStore) / 4;
+
+ KeyValuePair *pair = new KeyValuePair();
+
+ int i;
+
+ //iterate through flash until we have a match, or drop out.
+ for(i = 0; i < storeSize; i++)
+ {
+ memcpy(pair, flashBlockPointer, sizeof(KeyValuePair));
+
+ if(strcmp(key,(char *)pair->key) == 0)
+ break;
+
+ flashBlockPointer += sizeof(KeyValuePair) / 4;
+ }
+
+ //clean up
+ if(i == storeSize)
+ {
+ delete pair;
+ return NULL;
+ }
+
+ return pair;
+}
+
+/**
+ * Retreives a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
+ * NULL if the key was not found in storage.
+ *
+ * @note it is up to the user to free memory after use.
+ */
+KeyValuePair* MicroBitStorage::get(ManagedString key)
+{
+ return get((char *)key.toCharArray());
+}
+
+/**
+ * Removes a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
+ * was not found in flash.
+ */
+int MicroBitStorage::remove(const char* key)
+{
+ //calculate our various offsets
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+ uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
+ uint32_t *flashBlockPointer = flashPointer;
+ uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
+
+ uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
+ uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
+
+ int storeSize = size();
+
+ //if we have no data, we have nothing to do.
+ if(storeSize == 0)
+ return MICROBIT_NO_DATA;
+
+ //our KeyValueStore struct is always at 0
+ flashPointer += kvStoreSize;
+ scratchPointer += kvStoreSize;
+
+ KeyValuePair storedPair = KeyValuePair();
+
+ int found = 0;
+
+ //set up our scratch area
+ flashPageErase(scratchPointer);
+
+ //iterate through our flash copy pairs to scratch, unless there is a key patch
+ for(int i = 0; i < storeSize; i++)
+ {
+ memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
+
+ //if we have a match, don't increment our scratchPointer
+ if(strcmp((char *)storedPair.key, (char *)key) == 0)
+ {
+ found = 1;
+ //write our new KeyValueStore data
+ scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize - 1));
+ }
+ else
+ {
+ //otherwise copy the KeyValuePair from our storage page.
+ flashCopy(flashPointer, scratchPointer, sizeof(KeyValuePair) / 4);
+ scratchPointer += sizeof(KeyValuePair) / 4;
+ }
+
+ flashPointer += sizeof(KeyValuePair) / 4;
+ }
+
+ //if we haven't got a match, write our old KeyValueStore struct
+ if(!found)
+ {
+ scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
+ return MICROBIT_NO_DATA;
+ }
+
+ //copy scratch to our storage page
+ flashPageErase((uint32_t *)flashBlockPointer);
+ flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Removes a KeyValuePair identified by a given key.
+ *
+ * @param key the unique name used to identify a KeyValuePair in flash.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
+ * was not found in flash.
+ */
+int MicroBitStorage::remove(ManagedString key)
+{
+ return remove((char *)key.toCharArray());
+}
+
+/**
+ * The size of the flash based KeyValueStore.
+ *
+ * @return the number of entries in the key value store
+ */
+int MicroBitStorage::size()
+{
+ uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
+ uint32_t pg_num = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
+
+ uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
+
+ KeyValueStore store = KeyValueStore();
+
+ //read our data!
+ memcpy(&store, flashBlockPointer, sizeof(KeyValueStore));
+
+ //if we haven't used flash before, we need to configure it
+ if(store.magic != MICROBIT_STORAGE_MAGIC)
+ {
+ store.magic = MICROBIT_STORAGE_MAGIC;
+ store.size = 0;
+
+ //erase the scratch page and write our new KeyValueStore
+ flashPageErase((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)));
+ scratchKeyValueStore(store);
+
+ //erase flash, and copy the scratch page over
+ flashPageErase((uint32_t *)flashBlockPointer);
+ flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, pg_size/4);
+ }
+
+ return store.size;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/drivers/MicroBitThermometer.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,278 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "MicroBitThermometer.h"
+#include "MicroBitSystemTimer.h"
+#include "MicroBitFiber.h"
+
+/*
+ * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
+ * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
+ * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
+ * as a compatability option, but does not support the options used...
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "nrf_soc.h"
+#include "nrf_sdm.h"
+
+/*
+ * Return to our predefined compiler settings.
+ */
+#if !defined(__arm)
+#pragma GCC diagnostic pop
+#endif
+
+/**
+ * Constructor.
+ * Create new MicroBitThermometer that gives an indication of the current temperature.
+ *
+ * @param _storage an instance of MicroBitStorage used to persist temperature offset data
+ *
+ * @param id the unique EventModel id of this component. Defaults to MICROBIT_ID_THERMOMETER.
+ *
+ * @code
+ * MicroBitStorage storage;
+ * MicroBitThermometer thermometer(storage);
+ * @endcode
+ */
+MicroBitThermometer::MicroBitThermometer(MicroBitStorage& _storage, uint16_t id) :
+ storage(&_storage)
+{
+ this->id = id;
+ this->samplePeriod = MICROBIT_THERMOMETER_PERIOD;
+ this->sampleTime = 0;
+ this->offset = 0;
+
+ KeyValuePair *tempCalibration = storage->get(ManagedString("tempCal"));
+
+ if(tempCalibration != NULL)
+ {
+ memcpy(&offset, tempCalibration->value, sizeof(int16_t));
+ delete tempCalibration;
+ }
+}
+
+/**
+ * Constructor.
+ * Create new MicroBitThermometer that gives an indication of the current temperature.
+ *
+ * @param id the unique EventModel id of this component. Defaults to MICROBIT_ID_THERMOMETER.
+ *
+ * @code
+ * MicroBitThermometer thermometer;
+ * @endcode
+ */
+MicroBitThermometer::MicroBitThermometer(uint16_t id) :
+ storage(NULL)
+{
+ this->id = id;
+ this->samplePeriod = MICROBIT_THERMOMETER_PERIOD;
+ this->sampleTime = 0;
+ this->offset = 0;
+}
+
+/**
+ * Gets the current temperature of the microbit.
+ *
+ * @return the current temperature, in degrees celsius.
+ *
+ * @code
+ * thermometer.getTemperature();
+ * @endcode
+ */
+int MicroBitThermometer::getTemperature()
+{
+ updateSample();
+ return temperature - offset;
+}
+
+
+/**
+ * Updates the temperature sample of this instance of MicroBitThermometer
+ * only if isSampleNeeded() indicates that an update is required.
+ *
+ * This call also will add the thermometer to fiber components to receive
+ * periodic callbacks.
+ *
+ * @return MICROBIT_OK on success.
+ */
+int MicroBitThermometer::updateSample()
+{
+ if(!(status & MICROBIT_THERMOMETER_ADDED_TO_IDLE))
+ {
+ // If we're running under a fiber scheduer, register ourselves for a periodic callback to keep our data up to date.
+ // Otherwise, we do just do this on demand, when polled through our read() interface.
+ fiber_add_idle_component(this);
+ status |= MICROBIT_THERMOMETER_ADDED_TO_IDLE;
+ }
+
+ // check if we need to update our sample...
+ if(isSampleNeeded())
+ {
+ int32_t processorTemperature;
+ uint8_t sd_enabled;
+
+ // 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.
+
+ sd_softdevice_is_enabled(&sd_enabled);
+
+ if (sd_enabled)
+ {
+ // 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 = system_timer_current_time() + samplePeriod;
+
+ // Send an event to indicate that we'e updated our temperature.
+ MicroBitEvent e(id, MICROBIT_THERMOMETER_EVT_UPDATE);
+ }
+
+ return MICROBIT_OK;
+};
+
+/**
+ * Indicates if we'd like some processor time to sense the temperature.
+ *
+ * @returns 1 if we'd like some processor time, 0 otherwise.
+ */
+int MicroBitThermometer::isIdleCallbackNeeded()
+{
+ return isSampleNeeded();
+}
+
+/**
+ * Periodic callback from MicroBit idle thread.
+ */
+void MicroBitThermometer::idleTick()
+{
+ updateSample();
+}
+
+/**
+ * Determines if we're due to take another temperature reading
+ *
+ * @return 1 if we're due to take a temperature reading, 0 otherwise.
+ */
+int MicroBitThermometer::isSampleNeeded()
+{
+ return system_timer_current_time() >= sampleTime;
+}
+
+/**
+ * Set the sample rate at which the temperatureis read (in ms).
+ *
+ * The default sample period is 1 second.
+ *
+ * @param period the requested time between samples, in milliseconds.
+ *
+ * @note the temperature is always read in the background, and is only updated
+ * when the processor is idle, or when the temperature is explicitly read.
+ */
+void MicroBitThermometer::setPeriod(int period)
+{
+ updateSample();
+ samplePeriod = period;
+}
+
+/**
+ * Reads the currently configured sample rate of the thermometer.
+ *
+ * @return The time between samples, in milliseconds.
+ */
+int MicroBitThermometer::getPeriod()
+{
+ return samplePeriod;
+}
+
+/**
+ * Set the value that is used to offset the raw silicon temperature.
+ *
+ * @param offset the offset for the silicon temperature
+ *
+ * @return MICROBIT_OK on success
+ */
+int MicroBitThermometer::setOffset(int offset)
+{
+ if(this->storage != NULL)
+ this->storage->put(ManagedString("tempCal"), (uint8_t *)&offset);
+
+ this->offset = offset;
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Retreive the value that is used to offset the raw silicon temperature.
+ *
+ * @return the current offset.
+ */
+int MicroBitThermometer::getOffset()
+{
+ return offset;
+}
+
+/**
+ * This member function fetches the raw silicon temperature, and calculates
+ * the value used to offset the raw silicon temperature based on a given temperature.
+ *
+ * @param calibrationTemp the temperature used to calculate the raw silicon temperature
+ * offset.
+ *
+ * @return MICROBIT_OK on success
+ */
+int MicroBitThermometer::setCalibration(int calibrationTemp)
+{
+ updateSample();
+ return setOffset(temperature - calibrationTemp);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/ManagedString.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,489 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a ManagedString.
+ *
+ * Uses basic reference counting to implement a copy-assignable, immutable string.
+ *
+ * This maps closely to the constructs found in many high level application languages,
+ * such as Touch Develop.
+ *
+ * Written from first principles here, for several reasons:
+ * 1) std::shared_ptr is not yet availiable on the ARMCC compiler
+ *
+ * 2) to reduce memory footprint - we don't need many of the other features in the std library
+ *
+ * 3) it makes an interesting case study for anyone interested in seeing how it works!
+ *
+ * 4) we need explicit reference counting to inter-op with low-level application langauge runtimes.
+ *
+ * 5) the reference counting needs to also work for read-only, flash-resident strings
+ */
+#include <string.h>
+#include <stdlib.h>
+
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "ManagedString.h"
+#include "MicroBitCompat.h"
+
+static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0";
+
+/**
+ * Internal constructor helper.
+ *
+ * Configures this ManagedString to refer to the static EmptyString
+ */
+void ManagedString::initEmpty()
+{
+ ptr = (StringData*)(void*)empty;
+}
+
+/**
+ * Internal constructor helper.
+ *
+ * Creates this ManagedString based on a given null terminated char array.
+ */
+void ManagedString::initString(const char *str)
+{
+ // Initialise this ManagedString as a new string, using the data provided.
+ // We assume the string is sane, and null terminated.
+ int len = strlen(str);
+ ptr = (StringData *) malloc(4+len+1);
+ ptr->init();
+ ptr->len = len;
+ memcpy(ptr->data, str, len+1);
+}
+
+/**
+ * Constructor.
+ * Create a managed string from a specially prepared string literal.
+ *
+ * @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned.
+ *
+ * @code
+ * static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello";
+ * ManagedString s((StringData*)(void*)hello);
+ * @endcode
+ */
+ManagedString::ManagedString(StringData *p)
+{
+ ptr = p;
+ ptr->incr();
+}
+
+/**
+ * Get current ptr, do not decr() it, and set the current instance to empty string.
+ *
+ * This is to be used by specialized runtimes which pass StringData around.
+ */
+StringData* ManagedString::leakData()
+{
+ StringData *res = ptr;
+ initEmpty();
+ return res;
+}
+
+/**
+ * Constructor.
+ *
+ * Create a managed string from a given integer.
+ *
+ * @param value The integer from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s(20);
+ * @endcode
+ */
+ManagedString::ManagedString(const int value)
+{
+ char str[12];
+
+ itoa(value, str);
+ initString(str);
+}
+
+/**
+ * Constructor.
+ * Create a managed string from a given char.
+ *
+ * @param value The character from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s('a');
+ * @endcode
+ */
+ManagedString::ManagedString(const char value)
+{
+ char str[2] = {value, 0};
+ initString(str);
+}
+
+
+/**
+ * Constructor.
+ *
+ * Create a managed string from a pointer to an 8-bit character buffer.
+ *
+ * The buffer is copied to ensure safe memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str The character array on which to base the new ManagedString.
+ *
+ * @code
+ * ManagedString s("abcdefg");
+ * @endcode
+ */
+ManagedString::ManagedString(const char *str)
+{
+ // Sanity check. Return EmptyString for anything distasteful
+ if (str == NULL || *str == 0)
+ {
+ initEmpty();
+ return;
+ }
+
+ initString(str);
+}
+
+/**
+ * Private Constructor.
+ *
+ * Create a managed string based on a concat of two strings.
+ * The buffer is copied to ensure sane memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str1 The first string on which to base the new ManagedString.
+ *
+ * @param str2 The second string on which to base the new ManagedString.
+ */
+ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2)
+{
+ // Calculate length of new string.
+ int len = s1.length() + s2.length();
+
+ // Create a new buffer for holding the new string data.
+ ptr = (StringData*) malloc(4+len+1);
+ ptr->init();
+ ptr->len = len;
+
+ // Enter the data, and terminate the string.
+ memcpy(ptr->data, s1.toCharArray(), s1.length());
+ memcpy(ptr->data + s1.length(), s2.toCharArray(), s2.length());
+ ptr->data[len] = 0;
+}
+
+
+/**
+ * Constructor.
+ * Create a ManagedString from a PacketBuffer. All bytes in the
+ * PacketBuffer are added to the ManagedString.
+ *
+ * @param buffer The PacktBuffer from which to create the ManagedString.
+ *
+ * @code
+ * ManagedString s = radio.datagram.recv();
+ * @endcode
+ */
+ManagedString::ManagedString(PacketBuffer buffer)
+{
+ // Allocate a new buffer ( just in case the data is not NULL terminated).
+ ptr = (StringData*) malloc(4+buffer.length()+1);
+ ptr->init();
+
+ // Store the length of the new string
+ ptr->len = buffer.length();
+ memcpy(ptr->data, buffer.getBytes(), buffer.length());
+ ptr->data[buffer.length()] = 0;
+}
+
+/**
+ * Constructor.
+ * Create a ManagedString from a pointer to an 8-bit character buffer of a given length.
+ *
+ * The buffer is copied to ensure sane memory management (the supplied
+ * character buffer may be declared on the stack for instance).
+ *
+ * @param str The character array on which to base the new ManagedString.
+ *
+ * @param length The length of the character array
+ *
+ * @code
+ * ManagedString s("abcdefg",7);
+ * @endcode
+ */
+ManagedString::ManagedString(const char *str, const int16_t length)
+{
+ // Sanity check. Return EmptyString for anything distasteful
+ if (str == NULL || *str == 0 || (uint16_t)length > strlen(str)) // XXX length should be unsigned on the interface
+ {
+ initEmpty();
+ return;
+ }
+
+
+ // Allocate a new buffer, and create a NULL terminated string.
+ ptr = (StringData*) malloc(4+length+1);
+ ptr->init();
+ // Store the length of the new string
+ ptr->len = length;
+ memcpy(ptr->data, str, length);
+ ptr->data[length] = 0;
+}
+
+/**
+ * Copy constructor.
+ * Makes a new ManagedString identical to the one supplied.
+ *
+ * Shares the character buffer and reference count with the supplied ManagedString.
+ *
+ * @param s The ManagedString to copy.
+ *
+ * @code
+ * ManagedString s("abcdefg");
+ * ManagedString p(s);
+ * @endcode
+ */
+ManagedString::ManagedString(const ManagedString &s)
+{
+ ptr = s.ptr;
+ ptr->incr();
+}
+
+
+/**
+ * Default constructor.
+ *
+ * Create an empty ManagedString.
+ *
+ * @code
+ * ManagedString s();
+ * @endcode
+ */
+ManagedString::ManagedString()
+{
+ initEmpty();
+}
+
+/**
+ * Destructor.
+ *
+ * Free this ManagedString, and decrement the reference count to the
+ * internal character buffer.
+ *
+ * If we're holding the last reference, also free the character buffer.
+ */
+ManagedString::~ManagedString()
+{
+ ptr->decr();
+}
+
+/**
+ * Copy assign operation.
+ *
+ * Called when one ManagedString is assigned the value of another.
+ *
+ * If the ManagedString being assigned is already refering to a character buffer,
+ * decrement the reference count and free up the buffer as necessary.
+ *
+ * Then, update our character buffer to refer to that of the supplied ManagedString,
+ * and increase its reference count.
+ *
+ * @param s The ManagedString to copy.
+ *
+ * @code
+ * ManagedString s("abcd");
+ * ManagedString p("efgh");
+ * p = s // p now points to s, s' ref is incremented
+ * @endcode
+ */
+ManagedString& ManagedString::operator = (const ManagedString& s)
+{
+ if (this->ptr == s.ptr)
+ return *this;
+
+ ptr->decr();
+ ptr = s.ptr;
+ ptr->incr();
+
+ return *this;
+}
+
+/**
+ * Equality operation.
+ *
+ * Called when one ManagedString is tested to be equal to another using the '==' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ * ManagedString p("efgh");
+ *
+ * if(p == s)
+ * display.scroll("We are the same!");
+ * else
+ * display.scroll("We are different!"); //p is not equal to s - this will be called
+ * @endcode
+ */
+bool ManagedString::operator== (const ManagedString& s)
+{
+ return ((length() == s.length()) && (strcmp(toCharArray(),s.toCharArray())==0));
+}
+
+/**
+ * Inequality operation.
+ *
+ * Called when one ManagedString is tested to be less than another using the '<' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is alphabetically less than to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("a");
+ * ManagedString p("b");
+ *
+ * if(s < p)
+ * display.scroll("a is before b!"); //a is before b
+ * else
+ * display.scroll("b is before a!");
+ * @endcode
+ */
+bool ManagedString::operator< (const ManagedString& s)
+{
+ return (strcmp(toCharArray(), s.toCharArray())<0);
+}
+
+/**
+ * Inequality operation.
+ *
+ * Called when one ManagedString is tested to be greater than another using the '>' operator.
+ *
+ * @param s The ManagedString to test ourselves against.
+ *
+ * @return true if this ManagedString is alphabetically greater than to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("a");
+ * ManagedString p("b");
+ *
+ * if(p>a)
+ * display.scroll("b is after a!"); //b is after a
+ * else
+ * display.scroll("a is after b!");
+ * @endcode
+ */
+bool ManagedString::operator> (const ManagedString& s)
+{
+ return (strcmp(toCharArray(), s.toCharArray())>0);
+}
+
+/**
+ * Extracts a ManagedString from this string, at the position provided.
+ *
+ * @param start The index of the first character to extract, indexed from zero.
+ *
+ * @param length The number of characters to extract from the start position
+ *
+ * @return a ManagedString representing the requested substring.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcdefg");
+ *
+ * display.scroll(s.substring(0,2)) // displays "ab"
+ * @endcode
+ */
+ManagedString ManagedString::substring(int16_t start, int16_t length)
+{
+ // If the parameters are illegal, just return a reference to the empty string.
+ if (start >= this->length())
+ return ManagedString(ManagedString::EmptyString);
+
+ // Compute a safe copy length;
+ length = min(this->length()-start, length);
+
+ // Build a ManagedString from this.
+ return ManagedString(toCharArray()+start, length);
+}
+
+/**
+ * Concatenates this string with the one provided.
+ *
+ * @param s The ManagedString to concatenate.
+ *
+ * @return a new ManagedString representing the joined strings.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ * ManagedString p("efgh")
+ *
+ * display.scroll(s + p) // scrolls "abcdefgh"
+ * @endcode
+ */
+ManagedString ManagedString::operator+ (ManagedString& s)
+{
+ // If the other string is empty, nothing to do!
+ if(s.length() == 0)
+ return *this;
+
+ if (length() == 0)
+ return s;
+
+ return ManagedString(*this, s);
+}
+
+
+/**
+ * Provides a character value at a given position in the string, indexed from zero.
+ *
+ * @param index The position of the character to return.
+ *
+ * @return the character at posisiton index, zero if index is invalid.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * ManagedString s("abcd");
+ *
+ * display.scroll(s.charAt(1)) // scrolls "b"
+ * @endcode
+ */
+char ManagedString::charAt(int16_t index)
+{
+ return (index >=0 && index < length()) ? ptr->data[index] : 0;
+}
+
+/**
+ * Empty string constant literal
+ */
+ManagedString ManagedString::EmptyString((StringData*)(void*)empty);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/Matrix4.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,285 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "Matrix4.h"
+#include "mbed.h"
+
+/**
+* Class definition for a simple matrix, optimised for n x 4 or 4 x n matrices.
+*
+* This class is heavily optimised for these commonly used matrices as used in 3D geometry,
+* and is not intended as a general purpose matrix class. For programmers needing more flexible
+* Matrix support, the mbed Matrix and MatrixMath classes from Ernsesto Palacios provide a good basis:
+*
+* https://developer.mbed.org/cookbook/MatrixClass
+* https://developer.mbed.org/users/Yo_Robot/code/MatrixMath/
+*/
+
+/**
+ * Constructor.
+ * Create a matrix of the given size.
+ *
+ * @param rows the number of rows in the matrix to be created.
+ *
+ * @param cols the number of columns in the matrix to be created.
+ *
+ * @code
+ * Matrix4(10, 4); // Creates a Matrix with 10 rows and 4 columns.
+ * @endcode
+ */
+Matrix4::Matrix4(int rows, int cols)
+{
+ this->rows = rows;
+ this->cols = cols;
+
+ int size = rows * cols;
+
+ if (size > 0)
+ data = new float[size];
+ else
+ data = NULL;
+}
+
+/**
+ * Constructor.
+ * Create a matrix that is an identical copy of the given matrix.
+ *
+ * @param matrix The matrix to copy.
+ *
+ * @code
+ * Matrix newMatrix(matrix); .
+ * @endcode
+ */
+Matrix4::Matrix4(const Matrix4 &matrix)
+{
+ this->rows = matrix.rows;
+ this->cols = matrix.cols;
+
+ int size = rows * cols;
+
+ if (size > 0)
+ {
+ data = new float[size];
+ for (int i = 0; i < size; i++)
+ data[i] = matrix.data[i];
+ }
+ else
+ {
+ data = NULL;
+ }
+
+}
+
+/**
+ * Determines the number of columns in this matrix.
+ *
+ * @return The number of columns in the matrix.
+ *
+ * @code
+ * int c = matrix.width();
+ * @endcode
+ */
+int Matrix4::width()
+{
+ return cols;
+}
+
+/**
+ * Determines the number of rows in this matrix.
+ *
+ * @return The number of rows in the matrix.
+ *
+ * @code
+ * int r = matrix.height();
+ * @endcode
+ */
+int Matrix4::height()
+{
+ return rows;
+}
+
+/**
+ * Reads the matrix element at the given position.
+ *
+ * @param row The row of the element to read.
+ *
+ * @param col The column of the element to read.
+ *
+ * @return The value of the matrix element at the given position. 0 is returned if the given index is out of range.
+ *
+ * @code
+ * float v = matrix.get(1,2);
+ * @endcode
+ */
+float Matrix4::get(int row, int col)
+{
+ if (row < 0 || col < 0 || row >= rows || col >= cols)
+ return 0;
+
+ return data[width() * row + col];
+}
+
+/**
+ * Writes the matrix element at the given position.
+ *
+ * @param row The row of the element to write.
+ *
+ * @param col The column of the element to write.
+ *
+ * @param v The new value of the element.
+ *
+ * @code
+ * matrix.set(1,2,42.0);
+ * @endcode
+ */
+void Matrix4::set(int row, int col, float v)
+{
+ if (row < 0 || col < 0 || row >= rows || col >= cols)
+ return;
+
+ data[width() * row + col] = v;
+}
+
+/**
+ * Transposes this matrix.
+ *
+ * @return the resultant matrix.
+ *
+ * @code
+ * matrix.transpose();
+ * @endcode
+ */
+Matrix4 Matrix4::transpose()
+{
+ Matrix4 result = Matrix4(cols, rows);
+
+ for (int i = 0; i < width(); i++)
+ for (int j = 0; j < height(); j++)
+ result.set(i, j, get(j, i));
+
+ return result;
+}
+
+/**
+ * Multiplies this matrix with the given matrix (if possible).
+ *
+ * @param matrix the matrix to multiply this matrix's values against.
+ *
+ * @param transpose Transpose the matrices before multiplication. Defaults to false.
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.multiply(matrixB);
+ * @endcode
+ */
+Matrix4 Matrix4::multiply(Matrix4 &matrix, bool transpose)
+{
+ int w = transpose ? height() : width();
+ int h = transpose ? width() : height();
+
+ if (w != matrix.height())
+ return Matrix4(0, 0);
+
+ Matrix4 result(h, matrix.width());
+
+ for (int r = 0; r < result.height(); r++)
+ {
+ for (int c = 0; c < result.width(); c++)
+ {
+ float v = 0.0;
+
+ for (int i = 0; i < w; i++)
+ v += (transpose ? get(i, r) : get(r, i)) * matrix.get(i, c);
+
+ result.set(r, c, v);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Performs an optimised inversion of a 4x4 matrix.
+ * Only 4x4 matrices are supported by this operation.
+ *
+ * @return the resultant matrix. An empty matrix is returned if the operation canot be completed.
+ *
+ * @code
+ * Matrix result = matrixA.invert();
+ * @endcode
+ */
+Matrix4 Matrix4::invert()
+{
+ // We only support square matrices of size 4...
+ if (width() != height() || width() != 4)
+ return Matrix4(0, 0);
+
+ Matrix4 result(width(), height());
+
+ result.data[0] = data[5] * data[10] * data[15] - data[5] * data[11] * data[14] - data[9] * data[6] * data[15] + data[9] * data[7] * data[14] + data[13] * data[6] * data[11] - data[13] * data[7] * data[10];
+ result.data[1] = -data[1] * data[10] * data[15] + data[1] * data[11] * data[14] + data[9] * data[2] * data[15] - data[9] * data[3] * data[14] - data[13] * data[2] * data[11] + data[13] * data[3] * data[10];
+ result.data[2] = data[1] * data[6] * data[15] - data[1] * data[7] * data[14] - data[5] * data[2] * data[15] + data[5] * data[3] * data[14] + data[13] * data[2] * data[7] - data[13] * data[3] * data[6];
+ result.data[3] = -data[1] * data[6] * data[11] + data[1] * data[7] * data[10] + data[5] * data[2] * data[11] - data[5] * data[3] * data[10] - data[9] * data[2] * data[7] + data[9] * data[3] * data[6];
+ result.data[4] = -data[4] * data[10] * data[15] + data[4] * data[11] * data[14] + data[8] * data[6] * data[15] - data[8] * data[7] * data[14] - data[12] * data[6] * data[11] + data[12] * data[7] * data[10];
+ result.data[5] = data[0] * data[10] * data[15] - data[0] * data[11] * data[14] - data[8] * data[2] * data[15] + data[8] * data[3] * data[14] + data[12] * data[2] * data[11] - data[12] * data[3] * data[10];
+ result.data[6] = -data[0] * data[6] * data[15] + data[0] * data[7] * data[14] + data[4] * data[2] * data[15] - data[4] * data[3] * data[14] - data[12] * data[2] * data[7] + data[12] * data[3] * data[6];
+ result.data[7] = data[0] * data[6] * data[11] - data[0] * data[7] * data[10] - data[4] * data[2] * data[11] + data[4] * data[3] * data[10] + data[8] * data[2] * data[7] - data[8] * data[3] * data[6];
+ result.data[8] = data[4] * data[9] * data[15] - data[4] * data[11] * data[13] - data[8] * data[5] * data[15] + data[8] * data[7] * data[13] + data[12] * data[5] * data[11] - data[12] * data[7] * data[9];
+ result.data[9] = -data[0] * data[9] * data[15] + data[0] * data[11] * data[13] + data[8] * data[1] * data[15] - data[8] * data[3] * data[13] - data[12] * data[1] * data[11] + data[12] * data[3] * data[9];
+ result.data[10] = data[0] * data[5] * data[15] - data[0] * data[7] * data[13] - data[4] * data[1] * data[15] + data[4] * data[3] * data[13] + data[12] * data[1] * data[7] - data[12] * data[3] * data[5];
+ result.data[11] = -data[0] * data[5] * data[11] + data[0] * data[7] * data[9] + data[4] * data[1] * data[11] - data[4] * data[3] * data[9] - data[8] * data[1] * data[7] + data[8] * data[3] * data[5];
+ result.data[12] = -data[4] * data[9] * data[14] + data[4] * data[10] * data[13] + data[8] * data[5] * data[14] - data[8] * data[6] * data[13] - data[12] * data[5] * data[10] + data[12] * data[6] * data[9];
+ result.data[13] = data[0] * data[9] * data[14] - data[0] * data[10] * data[13] - data[8] * data[1] * data[14] + data[8] * data[2] * data[13] + data[12] * data[1] * data[10] - data[12] * data[2] * data[9];
+ result.data[14] = -data[0] * data[5] * data[14] + data[0] * data[6] * data[13] + data[4] * data[1] * data[14] - data[4] * data[2] * data[13] - data[12] * data[1] * data[6] + data[12] * data[2] * data[5];
+ result.data[15] = data[0] * data[5] * data[10] - data[0] * data[6] * data[9] - data[4] * data[1] * data[10] + data[4] * data[2] * data[9] + data[8] * data[1] * data[6] - data[8] * data[2] * data[5];
+
+ float det = data[0] * result.data[0] + data[1] * result.data[4] + data[2] * result.data[8] + data[3] * result.data[12];
+
+ if (det == 0)
+ return Matrix4(0, 0);
+
+ det = 1.0f / det;
+
+ for (int i = 0; i < 16; i++)
+ result.data[i] *= det;
+
+ return result;
+}
+
+/**
+ * Destructor.
+ *
+ * Frees any memory consumed by this Matrix4 instance.
+ */
+Matrix4::~Matrix4()
+{
+ if (data != NULL)
+ {
+ delete data;
+ data = NULL;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/MicroBitEvent.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,97 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MicroBitEvent
+ *
+ * It represents a common event that is generated by the various components on the micro:bit.
+ */
+#include "MicroBitConfig.h"
+#include "MicroBitEvent.h"
+#include "MicroBitSystemTimer.h"
+#include "EventModel.h"
+
+EventModel* EventModel::defaultEventBus = NULL;
+
+/**
+ * Constructor.
+ *
+ * @param src The id of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A.
+ *
+ * @param value A component specific code indicating the cause of the event.
+ *
+ * @param mode Optional definition of how the event should be processed after construction (if at all):
+ * CREATE_ONLY: MicroBitEvent is initialised, and no further processing takes place.
+ * CREATE_AND_FIRE: MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!).
+ *
+ * @code
+ * // Create and launch an event using the default configuration
+ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK);
+ *
+ * // Create an event only, do not fire onto an EventModel.
+ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,CREATE_AND_FIRE);
+ * @endcode
+ */
+MicroBitEvent::MicroBitEvent(uint16_t source, uint16_t value, MicroBitEventLaunchMode mode)
+{
+ this->source = source;
+ this->value = value;
+ this->timestamp = system_timer_current_time();
+
+ if(mode != CREATE_ONLY)
+ this->fire();
+}
+
+/**
+ * Default constructor - initialises all values, and sets timestamp to the current time.
+ */
+MicroBitEvent::MicroBitEvent()
+{
+ this->source = 0;
+ this->value = 0;
+ this->timestamp = system_timer_current_time();
+}
+
+/**
+ * Fires this MicroBitEvent onto the Default EventModel, or a custom one!
+ */
+void MicroBitEvent::fire()
+{
+ if(EventModel::defaultEventBus)
+ EventModel::defaultEventBus->send(*this);
+}
+
+
+/**
+ * Constructor.
+ * Create a new MicroBitEventQueueItem.
+ *
+ * @param evt The event to be queued.
+ */
+MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt)
+{
+ this->evt = evt;
+ this->next = NULL;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/MicroBitImage.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,887 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Class definition for a MicroBitImage.
+ *
+ * An MicroBitImage is a simple bitmap representation of an image.
+ * n.b. This is a mutable, managed type.
+ */
+
+#include "MicroBitConfig.h"
+#include "MicroBitImage.h"
+#include "MicroBitFont.h"
+#include "MicroBitCompat.h"
+#include "ManagedString.h"
+#include "ErrorNo.h"
+
+
+/**
+ * The null image. We actally create a small one byte buffer here, just to keep NULL pointers out of the equation.
+ */
+static const uint16_t empty[] __attribute__ ((aligned (4))) = { 0xffff, 1, 1, 0, };
+MicroBitImage MicroBitImage::EmptyImage((ImageData*)(void*)empty);
+
+/**
+ * Default Constructor.
+ * Creates a new reference to the empty MicroBitImage bitmap
+ *
+ * @code
+ * MicroBitImage i(); //an empty image instance
+ * @endcode
+ */
+MicroBitImage::MicroBitImage()
+{
+ // Create new reference to the EmptyImage and we're done.
+ init_empty();
+}
+
+
+/**
+ * Constructor.
+ * Create a blank bitmap representation of a given size.
+ *
+ * @param x the width of the image.
+ *
+ * @param y the height of the image.
+ *
+ * Bitmap buffer is linear, with 8 bits per pixel, row by row,
+ * top to bottom with no word alignment. Stride is therefore the image width in pixels.
+ * in where w and h are width and height respectively, the layout is therefore:
+ *
+ * |[0,0]...[w,o][1,0]...[w,1] ... [[w,h]
+ *
+ * A copy of the image is made in RAM, as images are mutable.
+ *
+ * TODO: Consider an immutable flavour, which might save us RAM for animation spritesheets...
+ * ...as these could be kept in FLASH.
+ */
+MicroBitImage::MicroBitImage(const int16_t x, const int16_t y)
+{
+ this->init(x,y,NULL);
+}
+
+/**
+ * Copy Constructor.
+ * Add ourselves as a reference to an existing MicroBitImage.
+ *
+ * @param image The MicroBitImage to reference.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n");
+ * MicroBitImage i2(i); //points to i
+ * @endcode
+ */
+MicroBitImage::MicroBitImage(const MicroBitImage &image)
+{
+ ptr = image.ptr;
+ ptr->incr();
+}
+
+/**
+ * Constructor.
+ * Create a blank bitmap representation of a given size.
+ *
+ * @param s A text based representation of the image given whitespace delimited numeric values.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * @endcode
+ */
+MicroBitImage::MicroBitImage(const char *s)
+{
+ int width = 0;
+ int height = 0;
+ int count = 0;
+ int digit = 0;
+
+ char parseBuf[10];
+
+ const char *parseReadPtr;
+ char *parseWritePtr;
+ uint8_t *bitmapPtr;
+
+ if (s == NULL)
+ {
+ init_empty();
+ return;
+ }
+
+ // First pass: Parse the string to determine the geometry of the image.
+ // We do this from first principles to avoid unecessary load of the strtok() libs etc.
+ parseReadPtr = s;
+
+ while (*parseReadPtr)
+ {
+ if (isdigit(*parseReadPtr))
+ {
+ // Ignore numbers.
+ digit = 1;
+ }
+ else if (*parseReadPtr =='\n')
+ {
+ if (digit)
+ {
+ count++;
+ digit = 0;
+ }
+
+ height++;
+
+ width = count > width ? count : width;
+ count = 0;
+ }
+ else
+ {
+ if (digit)
+ {
+ count++;
+ digit = 0;
+ }
+ }
+
+ parseReadPtr++;
+ }
+
+ this->init(width, height, NULL);
+
+ // Second pass: collect the data.
+ parseReadPtr = s;
+ parseWritePtr = parseBuf;
+ bitmapPtr = this->getBitmap();
+
+ while (*parseReadPtr)
+ {
+ if (isdigit(*parseReadPtr))
+ {
+ *parseWritePtr = *parseReadPtr;
+ parseWritePtr++;
+ }
+ else
+ {
+ *parseWritePtr = 0;
+ if (parseWritePtr > parseBuf)
+ {
+ *bitmapPtr = atoi(parseBuf);
+ bitmapPtr++;
+ parseWritePtr = parseBuf;
+ }
+ }
+
+ parseReadPtr++;
+ }
+}
+
+/**
+ * Constructor.
+ * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr().
+ *
+ * @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned.
+ *
+ * @code
+ * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i((ImageData*)(void*)heart);
+ * @endcode
+ */
+MicroBitImage::MicroBitImage(ImageData *p)
+{
+ ptr = p;
+ ptr->incr();
+}
+
+/**
+ * Get current ptr, do not decr() it, and set the current instance to empty image.
+ *
+ * This is to be used by specialized runtimes which pass ImageData around.
+ */
+ImageData *MicroBitImage::leakData()
+{
+ ImageData* res = ptr;
+ init_empty();
+ return res;
+}
+
+
+/**
+ * Constructor.
+ * Create a bitmap representation of a given size, based on a given buffer.
+ *
+ * @param x the width of the image.
+ *
+ * @param y the height of the image.
+ *
+ * @param bitmap a 2D array representing the image.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * @endcode
+ */
+MicroBitImage::MicroBitImage(const int16_t x, const int16_t y, const uint8_t *bitmap)
+{
+ this->init(x,y,bitmap);
+}
+
+/**
+ * Destructor.
+ *
+ * Removes buffer resources held by the instance.
+ */
+MicroBitImage::~MicroBitImage()
+{
+ ptr->decr();
+}
+
+/**
+ * Internal constructor which defaults to the EmptyImage instance variable
+ */
+void MicroBitImage::init_empty()
+{
+ ptr = (ImageData*)(void*)empty;
+}
+
+/**
+ * Internal constructor which provides sanity checking and initialises class properties.
+ *
+ * @param x the width of the image
+ *
+ * @param y the height of the image
+ *
+ * @param bitmap an array of integers that make up an image.
+ */
+void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap)
+{
+ //sanity check size of image - you cannot have a negative sizes
+ if(x < 0 || y < 0)
+ {
+ init_empty();
+ return;
+ }
+
+
+ // Create a copy of the array
+ ptr = (ImageData*)malloc(sizeof(ImageData) + x * y);
+ ptr->init();
+ ptr->width = x;
+ ptr->height = y;
+
+ // create a linear buffer to represent the image. We could use a jagged/2D array here, but experimentation
+ // showed this had a negative effect on memory management (heap fragmentation etc).
+
+ if (bitmap)
+ this->printImage(x,y,bitmap);
+ else
+ this->clear();
+}
+
+/**
+ * Copy assign operation.
+ *
+ * Called when one MicroBitImage is assigned the value of another using the '=' operator.
+ *
+ * Decrement our reference count and free up the buffer as necessary.
+ *
+ * Then, update our buffer to refer to that of the supplied MicroBitImage,
+ * and increase its reference count.
+ *
+ * @param s The MicroBitImage to reference.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * MicroBitImage i1();
+ * i1 = i; // i1 now references i
+ * @endcode
+ */
+MicroBitImage& MicroBitImage::operator = (const MicroBitImage& i)
+{
+ if(ptr == i.ptr)
+ return *this;
+
+ ptr->decr();
+ ptr = i.ptr;
+ ptr->incr();
+
+ return *this;
+}
+
+/**
+ * Equality operation.
+ *
+ * Called when one MicroBitImage is tested to be equal to another using the '==' operator.
+ *
+ * @param i The MicroBitImage to test ourselves against.
+ *
+ * @return true if this MicroBitImage is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * MicroBitImage i();
+ * MicroBitImage i1();
+ *
+ * if(i == i1) //will be true
+ * display.scroll("true");
+ * @endcode
+ */
+bool MicroBitImage::operator== (const MicroBitImage& i)
+{
+ if (ptr == i.ptr)
+ return true;
+ else
+ return (ptr->width == i.ptr->width && ptr->height == i.ptr->height && (memcmp(getBitmap(), i.ptr->data, getSize())==0));
+}
+
+
+/**
+ * Resets all pixels in this image to 0.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.clear();
+ * @endcode
+ */
+void MicroBitImage::clear()
+{
+ memclr(getBitmap(), getSize());
+}
+
+/**
+ * Sets the pixel at the given co-ordinates to a given value.
+ *
+ * @param x The co-ordinate of the pixel to change.
+ *
+ * @param y The co-ordinate of the pixel to change.
+ *
+ * @param value The new value of the pixel (the brightness level 0-255)
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.setPixelValue(0,0,255);
+ * @endcode
+ *
+ * @note all coordinates originate from the top left of an image.
+ */
+int MicroBitImage::setPixelValue(int16_t x , int16_t y, uint8_t value)
+{
+ //sanity check
+ if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ this->getBitmap()[y*getWidth()+x] = value;
+ return MICROBIT_OK;
+}
+
+/**
+ * Retreives the value of a given pixel.
+ *
+ * @param x The x co-ordinate of the pixel to read. Must be within the dimensions of the image.
+ *
+ * @param y The y co-ordinate of the pixel to read. Must be within the dimensions of the image.
+ *
+ * @return The value assigned to the given pixel location (the brightness level 0-255), or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i("0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n1,0,1,0,1\n0,1,0,1,0\n"); // 5x5 image
+ * i.getPixelValue(0,0); //should be 0;
+ * @endcode
+ */
+int MicroBitImage::getPixelValue(int16_t x , int16_t y)
+{
+ //sanity check
+ if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ return this->getBitmap()[y*getWidth()+x];
+}
+
+/**
+ * Replaces the content of this image with that of a given 2D array representing
+ * the image.
+ *
+ * @param x the width of the image. Must be within the dimensions of the image.
+ *
+ * @param y the width of the image. Must be within the dimensions of the image.
+ *
+ * @param bitmap a 2D array representing the image.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i();
+ * i.printImage(0,0,heart);
+ * @endcode
+ *
+ * @note all coordinates originate from the top left of an image.
+ */
+int MicroBitImage::printImage(int16_t width, int16_t height, const uint8_t *bitmap)
+{
+ const uint8_t *pIn;
+ uint8_t *pOut;
+ int pixelsToCopyX, pixelsToCopyY;
+
+ // Sanity check.
+ if (width <= 0 || width <= 0 || bitmap == NULL)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Calcualte sane start pointer.
+ pixelsToCopyX = min(width,this->getWidth());
+ pixelsToCopyY = min(height,this->getHeight());
+
+ pIn = bitmap;
+ pOut = this->getBitmap();
+
+ // Copy the image, stride by stride.
+ for (int i=0; i<pixelsToCopyY; i++)
+ {
+ memcpy(pOut, pIn, pixelsToCopyX);
+ pIn += width;
+ pOut += this->getWidth();
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Pastes a given bitmap at the given co-ordinates.
+ *
+ * Any pixels in the relvant area of this image are replaced.
+ *
+ * @param image The MicroBitImage to paste.
+ *
+ * @param x The leftmost X co-ordinate in this image where the given image should be pasted. Defaults to 0.
+ *
+ * @param y The uppermost Y co-ordinate in this image where the given image should be pasted. Defaults to 0.
+ *
+ * @param alpha set to 1 if transparency clear pixels in given image should be treated as transparent. Set to 0 otherwise. Defaults to 0.
+ *
+ * @return The number of pixels written.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.paste(i, -5, 0); // a small heart
+ * @endcode
+ */
+int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8_t alpha)
+{
+ uint8_t *pIn, *pOut;
+ int cx, cy;
+ int pxWritten = 0;
+
+ // Sanity check.
+ // We permit writes that overlap us, but ones that are clearly out of scope we can filter early.
+ if (x >= getWidth() || y >= getHeight() || x+image.getWidth() <= 0 || y+image.getHeight() <= 0)
+ return 0;
+
+ //Calculate the number of byte we need to copy in each dimension.
+ cx = x < 0 ? min(image.getWidth() + x, getWidth()) : min(image.getWidth(), getWidth() - x);
+ cy = y < 0 ? min(image.getHeight() + y, getHeight()) : min(image.getHeight(), getHeight() - y);
+
+ // Calculate sane start pointer.
+ pIn = image.ptr->data;
+ pIn += (x < 0) ? -x : 0;
+ pIn += (y < 0) ? -image.getWidth()*y : 0;
+
+ pOut = getBitmap();
+ pOut += (x > 0) ? x : 0;
+ pOut += (y > 0) ? getWidth()*y : 0;
+
+ // Copy the image, stride by stride
+ // If we want primitive transparecy, we do this byte by byte.
+ // If we don't, use a more efficient block memory copy instead. Every little helps!
+
+ if (alpha)
+ {
+ for (int i=0; i<cy; i++)
+ {
+ for (int j=0; j<cx; j++)
+ {
+ // Copy this byte if appropriate.
+ if (*(pIn+j) != 0){
+ *(pOut+j) = *(pIn+j);
+ pxWritten++;
+ }
+ }
+
+ pIn += image.getWidth();
+ pOut += getWidth();
+ }
+ }
+ else
+ {
+ for (int i=0; i<cy; i++)
+ {
+ memcpy(pOut, pIn, cx);
+
+ pxWritten += cx;
+ pIn += image.getWidth();
+ pOut += getWidth();
+ }
+ }
+
+ return pxWritten;
+}
+
+/**
+ * Prints a character to the display at the given location
+ *
+ * @param c The character to display.
+ *
+ * @param x The x co-ordinate of on the image to place the top left of the character. Defaults to 0.
+ *
+ * @param y The y co-ordinate of on the image to place the top left of the character. Defaults to 0.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * MicroBitImage i(5,5);
+ * i.print('a');
+ * @endcode
+ */
+int MicroBitImage::print(char c, int16_t x, int16_t y)
+{
+ unsigned char v;
+ int x1, y1;
+
+ MicroBitFont font = MicroBitFont::getSystemFont();
+
+ // Sanity check. Silently ignore anything out of bounds.
+ if (x >= getWidth() || y >= getHeight() || c < MICROBIT_FONT_ASCII_START || c > font.asciiEnd)
+ return MICROBIT_INVALID_PARAMETER;
+
+ // Paste.
+ int offset = (c-MICROBIT_FONT_ASCII_START) * 5;
+
+ for (int row=0; row<MICROBIT_FONT_HEIGHT; row++)
+ {
+ v = (char)*(font.characters + offset);
+
+ offset++;
+
+ // Update our Y co-ord write position
+ y1 = y+row;
+
+ for (int col = 0; col < MICROBIT_FONT_WIDTH; col++)
+ {
+ // Update our X co-ord write position
+ x1 = x+col;
+
+ if (x1 < getWidth() && y1 < getHeight())
+ this->getBitmap()[y1*getWidth()+x1] = (v & (0x10 >> col)) ? 255 : 0;
+ }
+ }
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Shifts the pixels in this Image a given number of pixels to the left.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.shiftLeft(5); // a small heart
+ * @endcode
+ */
+int MicroBitImage::shiftLeft(int16_t n)
+{
+ uint8_t *p = getBitmap();
+ int pixels = getWidth()-n;
+
+ if (n <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ if(n >= getWidth())
+ {
+ clear();
+ return MICROBIT_OK;
+ }
+
+ for (int y = 0; y < getHeight(); y++)
+ {
+ // Copy, and blank fill the rightmost column.
+ memcpy(p, p+n, pixels);
+ memclr(p+pixels, n);
+ p += getWidth();
+ }
+
+ return MICROBIT_OK;
+}
+
+/**
+ * Shifts the pixels in this Image a given number of pixels to the right.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart); // a big heart
+ * i.shiftLeft(5); // a small heart
+ * i.shiftRight(5); // a big heart
+ * @endcode
+ */
+int MicroBitImage::shiftRight(int16_t n)
+{
+ uint8_t *p = getBitmap();
+ int pixels = getWidth()-n;
+
+ if (n <= 0)
+ return MICROBIT_INVALID_PARAMETER;
+
+ if(n >= getWidth())
+ {
+ clear();
+ return MICROBIT_OK;
+ }
+
+ for (int y = 0; y < getHeight(); y++)
+ {
+ // Copy, and blank fill the leftmost column.
+ memmove(p+n, p, pixels);
+ memclr(p, n);
+ p += getWidth();
+ }
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Shifts the pixels in this Image a given number of pixels to upward.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.shiftUp(1);
+ * @endcode
+ */
+int MicroBitImage::shiftUp(int16_t n)
+{
+ uint8_t *pOut, *pIn;
+
+ if (n <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ if(n >= getHeight())
+ {
+ clear();
+ return MICROBIT_OK;
+ }
+
+ pOut = getBitmap();
+ pIn = getBitmap()+getWidth()*n;
+
+ for (int y = 0; y < getHeight(); y++)
+ {
+ // Copy, and blank fill the leftmost column.
+ if (y < getHeight()-n)
+ memcpy(pOut, pIn, getWidth());
+ else
+ memclr(pOut, getWidth());
+
+ pIn += getWidth();
+ pOut += getWidth();
+ }
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Shifts the pixels in this Image a given number of pixels to downward.
+ *
+ * @param n The number of pixels to shift.
+ *
+ * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.shiftDown(1);
+ * @endcode
+ */
+int MicroBitImage::shiftDown(int16_t n)
+{
+ uint8_t *pOut, *pIn;
+
+ if (n <= 0 )
+ return MICROBIT_INVALID_PARAMETER;
+
+ if(n >= getHeight())
+ {
+ clear();
+ return MICROBIT_OK;
+ }
+
+ pOut = getBitmap() + getWidth()*(getHeight()-1);
+ pIn = pOut - getWidth()*n;
+
+ for (int y = 0; y < getHeight(); y++)
+ {
+ // Copy, and blank fill the leftmost column.
+ if (y < getHeight()-n)
+ memcpy(pOut, pIn, getWidth());
+ else
+ memclr(pOut, getWidth());
+
+ pIn -= getWidth();
+ pOut -= getWidth();
+ }
+
+ return MICROBIT_OK;
+}
+
+
+/**
+ * Converts the bitmap to a csv ManagedString.
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * uBit.serial.printString(i.toString()); // "0,1,0,1,0,0,0,0,0,0\n..."
+ * @endcode
+ */
+ManagedString MicroBitImage::toString()
+{
+ //width including commans and \n * height
+ int stringSize = getSize() * 2;
+
+ //plus one for string terminator
+ char parseBuffer[stringSize + 1];
+
+ parseBuffer[stringSize] = '\0';
+
+ uint8_t *bitmapPtr = getBitmap();
+
+ int parseIndex = 0;
+ int widthCount = 0;
+
+ while (parseIndex < stringSize)
+ {
+ if(*bitmapPtr)
+ parseBuffer[parseIndex] = '1';
+ else
+ parseBuffer[parseIndex] = '0';
+
+ parseIndex++;
+
+ if(widthCount == getWidth()-1)
+ {
+ parseBuffer[parseIndex] = '\n';
+ widthCount = 0;
+ }
+ else
+ {
+ parseBuffer[parseIndex] = ',';
+ widthCount++;
+ }
+
+ parseIndex++;
+ bitmapPtr++;
+ }
+
+ return ManagedString(parseBuffer);
+}
+
+/**
+ * Crops the image to the given dimensions.
+ *
+ * @param startx the location to start the crop in the x-axis
+ *
+ * @param starty the location to start the crop in the y-axis
+ *
+ * @param width the width of the desired cropped region
+ *
+ * @param height the height of the desired cropped region
+ *
+ * @code
+ * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart
+ * MicroBitImage i(10,5,heart);
+ * i.crop(0,0,2,2).toString() // "0,1\n1,1\n"
+ * @endcode
+ */
+MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cropHeight)
+{
+ int newWidth = startx + cropWidth;
+ int newHeight = starty + cropHeight;
+
+ if (newWidth >= getWidth() || newWidth <=0)
+ newWidth = getWidth();
+
+ if (newHeight >= getHeight() || newHeight <= 0)
+ newHeight = getHeight();
+
+ //allocate our storage.
+ uint8_t cropped[newWidth * newHeight];
+
+ //calculate the pointer to where we want to begin cropping
+ uint8_t *copyPointer = getBitmap() + (getWidth() * starty) + startx;
+
+ //get a reference to our storage
+ uint8_t *pastePointer = cropped;
+
+ //go through row by row and select our image.
+ for (int i = starty; i < newHeight; i++)
+ {
+ memcpy(pastePointer, copyPointer, newWidth);
+
+ copyPointer += getWidth();
+ pastePointer += newHeight;
+ }
+
+ return MicroBitImage(newWidth, newHeight, cropped);
+}
+
+/**
+ * Check if image is read-only (i.e., residing in flash).
+ */
+bool MicroBitImage::isReadOnly()
+{
+ return ptr->isReadOnly();
+}
+
+/**
+ * Create a copy of the image bitmap. Used particularly, when isReadOnly() is true.
+ *
+ * @return an instance of MicroBitImage which can be modified independently of the current instance
+ */
+MicroBitImage MicroBitImage::clone()
+{
+ return MicroBitImage(getWidth(), getHeight(), getBitmap());
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/PacketBuffer.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,326 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include "MicroBitConfig.h"
+#include "PacketBuffer.h"
+#include "ErrorNo.h"
+
+// Create the EmptyPacket reference.
+PacketBuffer PacketBuffer::EmptyPacket = PacketBuffer(1);
+
+/**
+ * Default Constructor.
+ * Creates an empty Packet Buffer.
+ *
+ * @code
+ * PacketBuffer p();
+ * @endcode
+ */
+PacketBuffer::PacketBuffer()
+{
+ this->init(NULL, 0, 0);
+}
+
+/**
+ * Constructor.
+ * Creates a new PacketBuffer of the given size.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @code
+ * PacketBuffer p(16); // Creates a PacketBuffer 16 bytes long.
+ * @endcode
+ */
+PacketBuffer::PacketBuffer(int length)
+{
+ this->init(NULL, length, 0);
+}
+
+/**
+ * Constructor.
+ * Creates an empty Packet Buffer of the given size,
+ * and fills it with the data provided.
+ *
+ * @param data The data with which to fill the buffer.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @param rssi The radio signal strength at the time this packet was recieved. Defaults to 0.
+ *
+ * @code
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p(buf, 3); // Creates a PacketBuffer 3 bytes long.
+ * @endcode
+ */
+PacketBuffer::PacketBuffer(uint8_t *data, int length, int rssi)
+{
+ this->init(data, length, rssi);
+}
+
+/**
+ * Copy Constructor.
+ * Add ourselves as a reference to an existing PacketBuffer.
+ *
+ * @param buffer The PacketBuffer to reference.
+ *
+ * @code
+ * PacketBuffer p();
+ * PacketBuffer p2(p); // Refers to the same packet as p.
+ * @endcode
+ */
+PacketBuffer::PacketBuffer(const PacketBuffer &buffer)
+{
+ ptr = buffer.ptr;
+ ptr->incr();
+}
+
+/**
+ * Internal constructor-initialiser.
+ *
+ * @param data The data with which to fill the buffer.
+ *
+ * @param length The length of the buffer to create.
+ *
+ * @param rssi The radio signal strength at the time this packet was recieved.
+ */
+void PacketBuffer::init(uint8_t *data, int length, int rssi)
+{
+ if (length < 0)
+ length = 0;
+
+ ptr = (PacketData *) malloc(sizeof(PacketData) + length);
+ ptr->init();
+
+ ptr->length = length;
+ ptr->rssi = rssi;
+
+ // Copy in the data buffer, if provided.
+ if (data)
+ memcpy(ptr->payload, data, length);
+}
+
+/**
+ * Destructor.
+ *
+ * Removes buffer resources held by the instance.
+ */
+PacketBuffer::~PacketBuffer()
+{
+ ptr->decr();
+}
+
+/**
+ * Copy assign operation.
+ *
+ * Called when one PacketBuffer is assigned the value of another using the '=' operator.
+ *
+ * Decrements our reference count and free up the buffer as necessary.
+ *
+ * Then, update our buffer to refer to that of the supplied PacketBuffer,
+ * and increase its reference count.
+ *
+ * @param p The PacketBuffer to reference.
+ *
+ * @code
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p1(16);
+ * PacketBuffer p2(buf, 3);
+ *
+ * p1 = p2;
+ * @endcode
+ */
+PacketBuffer& PacketBuffer::operator = (const PacketBuffer &p)
+{
+ if(ptr == p.ptr)
+ return *this;
+
+ ptr->decr();
+ ptr = p.ptr;
+ ptr->incr();
+
+ return *this;
+}
+
+/**
+ * Array access operation (read).
+ *
+ * Called when a PacketBuffer is dereferenced with a [] operation.
+ *
+ * Transparently map this through to the underlying payload for elegance of programming.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * uint8_t data = p1[0];
+ * @endcode
+ */
+uint8_t PacketBuffer::operator [] (int i) const
+{
+ return ptr->payload[i];
+}
+
+/**
+ * Array access operation (modify).
+ *
+ * Called when a PacketBuffer is dereferenced with a [] operation.
+ *
+ * Transparently map this through to the underlying payload for elegance of programming.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1[0] = 42;
+ * @endcode
+ */
+uint8_t& PacketBuffer::operator [] (int i)
+{
+ return ptr->payload[i];
+}
+
+/**
+ * Equality operation.
+ *
+ * Called when one PacketBuffer is tested to be equal to another using the '==' operator.
+ *
+ * @param p The PacketBuffer to test ourselves against.
+ *
+ * @return true if this PacketBuffer is identical to the one supplied, false otherwise.
+ *
+ * @code
+ * MicroBitDisplay display;
+ * uint8_t buf = {13,5,2};
+ * PacketBuffer p1();
+ * PacketBuffer p2();
+ *
+ * if(p1 == p2) // will be true
+ * display.scroll("same!");
+ * @endcode
+ */
+bool PacketBuffer::operator== (const PacketBuffer& p)
+{
+ if (ptr == p.ptr)
+ return true;
+ else
+ return (ptr->length == p.ptr->length && (memcmp(ptr->payload, p.ptr->payload, ptr->length)==0));
+}
+
+/**
+ * Sets the byte at the given index to value provided.
+ *
+ * @param position The index of the byte to change.
+ *
+ * @param value The new value of the byte (0-255).
+ *
+ * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255.
+ * @endcode
+ */
+int PacketBuffer::setByte(int position, uint8_t value)
+{
+ if (position < ptr->length)
+ {
+ ptr->payload[position] = value;
+ return MICROBIT_OK;
+ }
+ else
+ {
+ return MICROBIT_INVALID_PARAMETER;
+ }
+}
+
+/**
+ * Determines the value of the given byte in the packet.
+ *
+ * @param position The index of the byte to read.
+ *
+ * @return The value of the byte at the given position, or MICROBIT_INVALID_PARAMETER.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setByte(0,255); // Sets the first byte in the buffer to the value 255.
+ * p1.getByte(0); // Returns 255.
+ * @endcode
+ */
+int PacketBuffer::getByte(int position)
+{
+ if (position < ptr->length)
+ return ptr->payload[position];
+ else
+ return MICROBIT_INVALID_PARAMETER;
+}
+
+/**
+ * Provide a pointer to a memory location containing the packet data.
+ *
+ * @return The contents of this packet, as an array of bytes.
+ */
+uint8_t*PacketBuffer::getBytes()
+{
+ return ptr->payload;
+}
+
+/**
+ * Gets number of bytes in this buffer
+ *
+ * @return The size of the buffer in bytes.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.length(); // Returns 16.
+ * @endcode
+ */
+int PacketBuffer::length()
+{
+ return ptr->length;
+}
+
+/**
+ * Retrieves the received signal strength of this packet.
+ *
+ * @return The signal strength of the radio when this packet was received, in -dbM.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.getRSSI(); // Returns the received signal strength.
+ * @endcode
+ */
+int PacketBuffer::getRSSI()
+{
+ return ptr->rssi;
+}
+
+/**
+ * Sets the received signal strength of this packet.
+ *
+ * @code
+ * PacketBuffer p1(16);
+ * p1.setRSSI(37);
+ * @endcode
+ */
+void PacketBuffer::setRSSI(uint8_t rssi)
+{
+ ptr->rssi = rssi;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/types/RefCounted.cpp Thu Apr 07 01:33:22 2016 +0100
@@ -0,0 +1,98 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 British Broadcasting Corporation.
+This software is provided by Lancaster University by arrangement with the BBC.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * Base class for payload for ref-counted objects. Used by ManagedString and MicroBitImage.
+ * There is no constructor, as this struct is typically malloc()ed.
+ */
+#include "mbed.h"
+#include "MicroBitConfig.h"
+#include "RefCounted.h"
+#include "MicroBitDisplay.h"
+
+/**
+ * Initializes for one outstanding reference.
+ */
+void RefCounted::init()
+{
+ // Initialize to one reference (lowest bit set to 1)
+ refCount = 3;
+}
+
+/**
+ * Checks if the object resides in flash memory.
+ *
+ * @param t the object to check.
+ *
+ * @return true if the object resides in flash memory, false otherwise.
+ */
+static inline bool isReadOnlyInline(RefCounted *t)
+{
+ uint32_t refCount = t->refCount;
+
+ if (refCount == 0xffff)
+ return true; // object in flash
+
+ // Do some sanity checking while we're here
+ if (refCount == 1 || // object should have been deleted
+ (refCount & 1) == 0) // refCount doesn't look right
+ microbit_panic(MICROBIT_HEAP_ERROR);
+
+ // Not read only
+ return false;
+}
+
+/**
+ * Checks if the object resides in flash memory.
+ *
+ * @return true if the object resides in flash memory, false otherwise.
+ */
+bool RefCounted::isReadOnly()
+{
+ return isReadOnlyInline(this);
+}
+
+/**
+ * Increment reference count.
+ */
+void RefCounted::incr()
+{
+ if (!isReadOnlyInline(this))
+ refCount += 2;
+}
+
+/**
+ * Decrement reference count.
+ */
+void RefCounted::decr()
+{
+ if (isReadOnlyInline(this))
+ return;
+
+ refCount -= 2;
+ if (refCount == 1) {
+ free(this);
+ }
+}
