diff --git a/README.md b/README.md index 2769a4e..3db4804 100644 --- a/README.md +++ b/README.md @@ -48,19 +48,6 @@ Then you should **try** to build using the following command: yt build ``` -For GCC, you will have to swap the CortexContextSwitch.s file with the file CortexContextSwitch.s.gcc which will be located in `/yotta_modules/microbit-dal/source`. For an example of how to do this, look below: - -``` -#first rename the existing one -mv yotta_modules/microbit-dal/source/CortexContextSwitch.s yotta_modules/microbit-dal/source/CortexContextSwitch.s.armcc - -#then rename gcc version so that it is included by cmake -mv yotta_modules/microbit-dal/source/CortexContextSwitch.s.gcc yotta_modules/microbit-dal/source/CortexContextSwitch.s -``` - -This is a temporary measure until Yotta supports assembly preprocessing for armcc. - - **NOTE: To build the final hex files for the micro:bit, you will need to install the srec which can be installed via brew (`brew install srecord`), or you can install it manually from [here](http://srecord.sourceforge.net/).** diff --git a/inc/MemberFunctionCallback.h b/inc/MemberFunctionCallback.h new file mode 100644 index 0000000..308c383 --- /dev/null +++ b/inc/MemberFunctionCallback.h @@ -0,0 +1,77 @@ +#ifndef MEMBER_FUNCTION_CALLBACK_H +#define MEMBER_FUNCTION_CALLBACK_H + +#include "mbed.h" +#include "MicroBitEvent.h" + +/** + * Class definition for a MemberFunctionCallback. + * + * C++ member functions (also known as methods) have a more complex + * representation than normal C functions. This allows a referene 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 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 MemberFunctionCallback(T* object, void (T::*method)(MicroBitEvent e)); + + /** + * 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 representation of a pointer to a C++ member function (method). + * @param object The object the callback method should be invooked on. + * @param method The method to invoke. + */ +template +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; +} + +/** + * Template to create static methods capable of invoking a C++ member function (method) + * based on the given paramters. + */ +template +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 + + diff --git a/inc/MicroBit.h b/inc/MicroBit.h index a1845e5..cdd23d7 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -2,31 +2,34 @@ #define MICROBIT_H #include "mbed.h" -#include "MicroBitConfig.h" -#include "MicroBitPanic.h" - -#include "ErrorNo.h" +#include "MicroBitConfig.h" #include "MicroBitHeapAllocator.h" +#include "MicroBitPanic.h" +#include "ErrorNo.h" #include "MicroBitCompat.h" -#include "MicroBitFiber.h" +#include "MicroBitComponent.h" #include "ManagedType.h" #include "ManagedString.h" +#include "MicroBitImage.h" #include "MicroBitFont.h" -#include "MicroBitImage.h" #include "MicroBitEvent.h" -#include "MicroBitMessageBus.h" #include "DynamicPwm.h" -#include "MicroBitComponent.h" #include "MicroBitI2C.h" -#include "MicroBitSerial.h" +#include "MESEvents.h" + #include "MicroBitButton.h" -#include "MicroBitMultiButton.h" -#include "MicroBitDisplay.h" #include "MicroBitPin.h" -#include "MicroBitIO.h" #include "MicroBitCompass.h" #include "MicroBitAccelerometer.h" +#include "MicroBitMultiButton.h" + +#include "MicroBitSerial.h" +#include "MicroBitIO.h" +#include "MicroBitDisplay.h" + +#include "MicroBitFiber.h" +#include "MicroBitMessageBus.h" #include "ble/BLE.h" #include "ble/services/DeviceInformationService.h" @@ -44,38 +47,6 @@ // Random number generator #define NRF51822_RNG_ADDRESS 0x4000D000 -#define MICROBIT_IO_PINS 20 - -// 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_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 // mBed pin assignments of core components. #define MICROBIT_PIN_SDA P0_30 diff --git a/inc/MicroBitAccelerometer.h b/inc/MicroBitAccelerometer.h index a6008bf..b9cbc4f 100644 --- a/inc/MicroBitAccelerometer.h +++ b/inc/MicroBitAccelerometer.h @@ -2,6 +2,7 @@ #define MICROBIT_ACCELEROMETER_H #include "mbed.h" +#include "MicroBitComponent.h" /** * Relevant pin assignments diff --git a/inc/MicroBitButton.h b/inc/MicroBitButton.h index 32eb20e..231cadd 100644 --- a/inc/MicroBitButton.h +++ b/inc/MicroBitButton.h @@ -2,6 +2,8 @@ #define MICROBIT_BUTTON_H #include "mbed.h" +#include "MicroBitComponent.h" +#include "MicroBitEvent.h" //TODO: When platform is built for MB2 - pins will be defined by default, these will change... #define MICROBIT_PIN_BUTTON_A P0_17 diff --git a/inc/MicroBitCompass.h b/inc/MicroBitCompass.h index 6b24ad2..3863f71 100644 --- a/inc/MicroBitCompass.h +++ b/inc/MicroBitCompass.h @@ -2,6 +2,7 @@ #define MICROBIT_COMPASS_H #include "mbed.h" +#include "MicroBitComponent.h" /** * Relevant pin assignments diff --git a/inc/MicroBitComponent.h b/inc/MicroBitComponent.h index 992e38c..7779069 100644 --- a/inc/MicroBitComponent.h +++ b/inc/MicroBitComponent.h @@ -8,6 +8,41 @@ * If it's in the systemTick queue, you should override systemTick and implement the required functionality. * Similarly if the component is in the idleTick queue, the idleTick member function should be overridden. */ + +// 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_ALERT 27 // Alert channel, used for general purpose condition synchronisation and alerting. + class MicroBitComponent { protected: diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index 27a5c42..03f4e1f 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -77,6 +77,19 @@ #define FIBER_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_NONBLOCKING + +#ifndef MESSAGE_BUS_LISTENER_DEFAULT_FLAGS +#define MESSAGE_BUS_LISTENER_DEFAULT_FLAGS MESSAGE_BUS_LISTENER_REENTRANT +#endif // // Core micro:bit services diff --git a/inc/MicroBitDFUService.h b/inc/MicroBitDFUService.h index 99c24ab..eaa5850 100644 --- a/inc/MicroBitDFUService.h +++ b/inc/MicroBitDFUService.h @@ -1,8 +1,8 @@ #ifndef MICROBIT_DFU_SERVICE_H #define MICROBIT_DFU_SERVICE_H -#include "MicroBit.h" - +#include "mbed.h" +#include "ble/BLE.h" // MicroBit ControlPoint OpCodes // Requests transfer to the Nordic DFU bootloader. diff --git a/inc/MicroBitDisplay.h b/inc/MicroBitDisplay.h index 7ab3ceb..c3b5fcd 100644 --- a/inc/MicroBitDisplay.h +++ b/inc/MicroBitDisplay.h @@ -63,7 +63,10 @@ #define MICROBIT_DISPLAY_ANIMATE_DEFAULT_POS -255 #include "mbed.h" -#include "MicroBit.h" +#include "ManagedString.h" +#include "MicroBitComponent.h" +#include "MicroBitImage.h" +#include "MicroBitFont.h" enum AnimationMode { ANIMATION_MODE_NONE, @@ -102,6 +105,7 @@ class MicroBitDisplay : public MicroBitComponent uint8_t mode; uint8_t greyscaleBitMsk; uint8_t timingCount; + uint16_t nonce; Timeout renderTimer; MicroBitFont font; @@ -217,7 +221,7 @@ class MicroBitDisplay : public MicroBitComponent * Broadcasts an event onto the shared MessageBus * @param eventCode The ID of the event that has occurred. */ - void sendEvent(uint16_t eventcode); + void sendAnimationCompleteEvent(); public: diff --git a/inc/MicroBitEvent.h b/inc/MicroBitEvent.h index 6537e3f..425848b 100644 --- a/inc/MicroBitEvent.h +++ b/inc/MicroBitEvent.h @@ -1,7 +1,16 @@ #ifndef MICROBIT_EVENT_H #define MICROBIT_EVENT_H -#include "MicroBit.h" +#include "mbed.h" + +enum MicroBitEventLaunchMode +{ + CREATE_ONLY, + CREATE_AND_QUEUE, + CREATE_AND_FIRE +}; + +#define MICROBIT_EVENT_DEFAULT_LAUNCH_MODE CREATE_AND_QUEUE /** * Class definition for a MicrobitEvent @@ -22,15 +31,24 @@ class MicroBitEvent * Constructor. * @param src ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A. * @param value Component specific code indicating the cause of the event. - * @param fire whether the event should be fire immediately upon construction + * @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_QUEUE: MicroBitEvent is initialised, and queued on the MicroBitMessageBus. + * CREATE_AND_FIRE: MicroBitEvent is initialised, and its event handlers are immediately fired (not suitable for use in interrupts!). * - * Example: + * Example: Create and launch an event using the default configuration * @code - * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,true); // auto fire + * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK); + * @endcode + * + * Example: Create and launch an event and process all registered event handlers immediately. + * @code + * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,CREATE_AND_FIRE); * @endcode */ - - MicroBitEvent(uint16_t source, uint16_t value, bool fire = true); + + 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. @@ -38,12 +56,32 @@ class MicroBitEvent MicroBitEvent(); /** - * Fires the represented event onto the message bus. + * Fires the represented event onto the message bus using the default configuration. */ void fire(); + + /** + * Fires the represented event onto the message bus. + * @param mode Configuration of how the event is processed. + */ + void fire(MicroBitEventLaunchMode mode); }; +/** + * Enclosing class to hold a chain of events. + */ +struct MicroBitEventQueueItem +{ + MicroBitEvent evt; + MicroBitEventQueueItem *next; + /** + * Constructor. + * Creates a new MicroBitEventQueueItem. + * @param evt The event that is to be queued. + */ + MicroBitEventQueueItem(MicroBitEvent evt); +}; #endif diff --git a/inc/MicroBitEventService.h b/inc/MicroBitEventService.h index 649ff3e..1c751ca 100644 --- a/inc/MicroBitEventService.h +++ b/inc/MicroBitEventService.h @@ -1,7 +1,7 @@ #ifndef MICROBIT_EVENT_SERVICE_H #define MICROBIT_EVENT_SERVICE_H -#include "MicroBit.h" +#include "MicroBitEvent.h" // UUIDs for our service and characteristics extern const uint8_t MicroBitEventServiceUUID[]; diff --git a/inc/MicroBitFiber.h b/inc/MicroBitFiber.h index 218b501..8e31f99 100644 --- a/inc/MicroBitFiber.h +++ b/inc/MicroBitFiber.h @@ -12,7 +12,7 @@ #include "mbed.h" #include "MicroBitConfig.h" -#include "MicroBitMessageBus.h" +#include "MicroBitEvent.h" // 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 diff --git a/inc/MicroBitI2C.h b/inc/MicroBitI2C.h index edcfd65..84303fb 100644 --- a/inc/MicroBitI2C.h +++ b/inc/MicroBitI2C.h @@ -1,8 +1,6 @@ -#include "MicroBit.h" #ifndef MICROBIT_I2C_H #define MICROBIT_I2C_H -#include "MicroBitComponent.h" #include "mbed.h" #define MICROBIT_I2C_MAX_RETRIES 9 diff --git a/inc/MicroBitIO.h b/inc/MicroBitIO.h index 5080d21..ac3cc4a 100644 --- a/inc/MicroBitIO.h +++ b/inc/MicroBitIO.h @@ -2,6 +2,7 @@ #define MICROBIT_IO_H #include "mbed.h" +#include "MicroBitComponent.h" #include "MicroBitPin.h" /** @@ -37,15 +38,13 @@ class MicroBitIO * Constructor. * Create a representation of all given I/O pins on the edge connector */ - MicroBitIO(int MICROBIT_ID_IO_P0, int MICROBIT_ID_IO_P1, int MICROBIT_ID_IO_P2, - int MICROBIT_ID_IO_P3, int MICROBIT_ID_IO_P4, int MICROBIT_ID_IO_P5, - int MICROBIT_ID_IO_P6, int MICROBIT_ID_IO_P7, int MICROBIT_ID_IO_P8, - int MICROBIT_ID_IO_P9, int MICROBIT_ID_IO_P10,int MICROBIT_ID_IO_P11, - int MICROBIT_ID_IO_P12,int MICROBIT_ID_IO_P13,int MICROBIT_ID_IO_P14, - int MICROBIT_ID_IO_P15,int MICROBIT_ID_IO_P16,int MICROBIT_ID_IO_P19, - int MICROBIT_ID_IO_P20); - - + 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 diff --git a/inc/MicroBitListener.h b/inc/MicroBitListener.h new file mode 100644 index 0000000..176dd0c --- /dev/null +++ b/inc/MicroBitListener.h @@ -0,0 +1,97 @@ +#ifndef MICROBIT_LISTENER_H +#define MICROBIT_LISTENER_H + +#include "mbed.h" +#include "MicroBitEvent.h" +#include "MemberFunctionCallback.h" + +// MessageBusListener 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 + +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 ID you would like to listen to from that component + * @param handler A function pointer to call when the event is detected. + */ + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); + + /** + * Alternative constructor where we register a value to be passed to the + * callback. + */ + MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent, void *), void* arg, uint16_t flags = MESSAGE_BUS_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 ID you would like to listen to from that component. + * @param object The C++ object on which to call the event handler. + * @param object The method within the C++ object to call. + */ + template + MicroBitListener(uint16_t id, uint16_t value, T* object, void (T::*method)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_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 ID you would like to listen to from that component. + * @param object The C++ object on which to call the event handler. + * @param object The method within the C++ object to call. + */ + +template +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 + + diff --git a/inc/MicroBitMatrixMaps.h b/inc/MicroBitMatrixMaps.h index 639ed20..df01ca9 100644 --- a/inc/MicroBitMatrixMaps.h +++ b/inc/MicroBitMatrixMaps.h @@ -8,6 +8,8 @@ #ifndef MICROBIT_MATRIX_MAPS_H #define MICROBIT_MATRIX_MAPS_H +#include "mbed.h" +#include "MicroBitDisplay.h" /** * 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. diff --git a/inc/MicroBitMessageBus.h b/inc/MicroBitMessageBus.h index 2106c7a..fdace6a 100644 --- a/inc/MicroBitMessageBus.h +++ b/inc/MicroBitMessageBus.h @@ -4,52 +4,13 @@ #include "mbed.h" #include "MicroBitComponent.h" #include "MicroBitEvent.h" +#include "MicroBitListener.h" // Enumeration of core components. #define MICROBIT_CONTROL_BUS_ID 0 #define MICROBIT_ID_ANY 0 #define MICROBIT_EVT_ANY 0 -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. - void* cb; // Callback function associated with this listener. Either (*cb)(MicroBitEvent) or (*cb)(MicroBitEvent, void*) depending on whether cb_arg is NULL. - void* cb_arg; // Argument to be passed to the caller. This is assumed to be a pointer, so passing in NULL means that the function doesn't take an argument. - MicroBitEvent evt; - - 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 ID you would like to listen to from that component - * @param handler A function pointer to call when the event is detected. - */ - MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)); - - /** - * Alternative constructor where we register a value to be passed to the - * callback. If arg == NULL, the function takes no extra arguemnt. - * Otherwise, the function is understood to take an extra argument. - */ - MicroBitListener(uint16_t id, uint16_t value, void *handler, void* arg); -}; - -struct MicroBitEventQueueItem -{ - MicroBitEvent evt; - MicroBitEventQueueItem *next; - - /** - * Constructor. - * Creates a new MicroBitEventQueueItem. - * @param evt The event that is to be queued. - */ - MicroBitEventQueueItem(MicroBitEvent evt); -}; - /** * Class definition for the MicroBitMessageBus. * @@ -99,10 +60,10 @@ class MicroBitMessageBus : public MicroBitComponent * 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. * - * IT IS RECOMMENDED THAT ALL EXTERNAL CODE USE THE send() FUNCTIONS INSTEAD OF THIS FUNCTION. + * IT IS RECOMMENDED THAT ALL EXTERNAL CODE USE THE send() FUNCTIONS INSTEAD OF THIS FUNCTION, + * or the constructors provided by MicroBitEvent. * * @param evt The event to send. - * @param c The cache entry to reduce lookups for commonly used channels. */ void process(MicroBitEvent evt); @@ -126,24 +87,142 @@ class MicroBitMessageBus : public MicroBitComponent * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected. * @endcode */ - void listen(int id, int value, void (*handler)(MicroBitEvent)); + void listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); /** * Register a listener function. * - * Same as above, except the listener function is passed an extra argument in addition to the - * MicroBitEvent, when called. + * @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. + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected. + * @endcode */ - void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); + void listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); + + /** + * 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. + * + * 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); + * @endcode + */ + template + void listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags = MESSAGE_BUS_LISTENER_DEFAULT_FLAGS); + + + /** + * 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. + * + * + * Example: + * @code + * void onButtonBClick() + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ + void ignore(int id, int value, void (*handler)(MicroBitEvent)); + + /** + * 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. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ + void ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg); + + /** + * 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. + * + * + * Example: + * @code + * + * void SomeClass::onButtonBClick() + * { + * //do something + * } + * + * SomeClass s = new SomeClass(); + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, s, &SomeClass::onButtonBClick); + * @endcode + */ + template + void ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)); + + /** + * Returns a 'nonce' for use with the NONCE_ID channel of the message bus. + */ + uint16_t nonce(); private: - + + /** + * Add the given MicroBitListener to the list of event handlers, unconditionally. + * @param listener The MicroBitListener to validate. + * @return 1 if the listener is valid, 0 otherwise. + */ + int add(MicroBitListener *newListener); + int remove(MicroBitListener *newListener); + 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. - int seq; // Sequence number. Used to invalidate cache entries. + uint16_t nonce_val; // The last nonce issued. - void listen(int id, int value, void* handler, void* arg); void queueEvent(MicroBitEvent &evt); MicroBitEventQueueItem* dequeueEvent(); @@ -151,6 +230,61 @@ class MicroBitMessageBus : public MicroBitComponent virtual int isIdleCallbackNeeded(); }; +/** + * A registrationt function to allow C++ member funcitons (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 hander The method to call when an event is received. + */ +template +void MicroBitMessageBus::listen(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent), uint16_t flags) +{ + if (object == NULL || handler == NULL) + return; + + MicroBitListener *newListener = new MicroBitListener(id, value, object, handler, flags); + + if(!add(newListener)) + delete newListener; +} + +/** + * 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. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +template +void MicroBitMessageBus::ignore(uint16_t id, uint16_t value, T* object, void (T::*handler)(MicroBitEvent)) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, object, handler); + remove(&listener); +} + + #endif diff --git a/inc/MicroBitPin.h b/inc/MicroBitPin.h index edaf844..e4ba2fd 100644 --- a/inc/MicroBitPin.h +++ b/inc/MicroBitPin.h @@ -1,8 +1,8 @@ #ifndef MICROBIT_PIN_H #define MICROBIT_PIN_H -#include "MicroBitComponent.h" #include "mbed.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 diff --git a/inc/MicroBitSerial.h b/inc/MicroBitSerial.h index 2fd34de..ab8ba6e 100644 --- a/inc/MicroBitSerial.h +++ b/inc/MicroBitSerial.h @@ -1,9 +1,9 @@ -#include "MicroBit.h" #ifndef MICROBIT_SERIAL_H #define MICROBIT_SERIAL_H -#include "MicroBitComponent.h" #include "mbed.h" +#include "ManagedString.h" +#include "MicroBitImage.h" #define MICROBIT_SERIAL_DEFAULT_BAUD_RATE 115200 #define MICROBIT_SERIAL_BUFFER_SIZE 20 diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 8cd1472..6f3cc60 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,62 +1,64 @@ -# 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.11) - -enable_language(ASM) - -set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES - "MicroBitSuperMain.cpp" - "MicroBitI2C.cpp" - "MicroBitMultiButton.cpp" - "MicroBitFont.cpp" - "MicroBit.cpp" - "MicroBitButton.cpp" - "MicroBitMessageBus.cpp" - "MicroBitCompass.cpp" - "MicroBitEvent.cpp" - "MicroBitFiber.cpp" - "ManagedString.cpp" - "MicroBitAccelerometer.cpp" - "MicroBitIO.cpp" - "MicroBitCompat.cpp" - "MicroBitImage.cpp" - "MicroBitDisplay.cpp" - "DynamicPwm.cpp" - "MicroBitPin.cpp" - "MicroBitSerial.cpp" - "MicroBitHeapAllocator.cpp" - "ble-services/MicroBitDFUService.cpp" - "ble-services/MicroBitEventService.cpp" - "ble-services/MicroBitLEDService.cpp" - "ble-services/MicroBitAccelerometerService.cpp" -) - -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 -) +# 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.11) + +enable_language(ASM) + +set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES + "MicroBitSuperMain.cpp" + "MicroBitI2C.cpp" + "MicroBitMultiButton.cpp" + "MicroBitFont.cpp" + "MicroBit.cpp" + "MicroBitButton.cpp" + "MicroBitMessageBus.cpp" + "MicroBitCompass.cpp" + "MicroBitEvent.cpp" + "MicroBitFiber.cpp" + "ManagedString.cpp" + "MicroBitAccelerometer.cpp" + "MicroBitIO.cpp" + "MicroBitCompat.cpp" + "MicroBitImage.cpp" + "MicroBitDisplay.cpp" + "DynamicPwm.cpp" + "MicroBitPin.cpp" + "MicroBitSerial.cpp" + "MicroBitHeapAllocator.cpp" + "MicroBitListener.cpp" + "MemberFunctionCallback.cpp" + "ble-services/MicroBitDFUService.cpp" + "ble-services/MicroBitEventService.cpp" + "ble-services/MicroBitLEDService.cpp" + "ble-services/MicroBitAccelerometerService.cpp" +) + +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 +) diff --git a/source/DynamicPwm.cpp b/source/DynamicPwm.cpp index 16d6459..8f82258 100644 --- a/source/DynamicPwm.cpp +++ b/source/DynamicPwm.cpp @@ -1,6 +1,4 @@ -#include "MicroBitHeapAllocator.h" -#include "DynamicPwm.h" - +#include "MicroBit.h" DynamicPwm* DynamicPwm::pwms[NO_PWMS] = { NULL }; diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index a751fb6..aa5d315 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -1,9 +1,7 @@ #include #include #include "mbed.h" -#include "MicroBitHeapAllocator.h" -#include "MicroBitCompat.h" -#include "ManagedString.h" +#include "MicroBit.h" /** diff --git a/source/MemberFunctionCallback.cpp b/source/MemberFunctionCallback.cpp new file mode 100644 index 0000000..f67f626 --- /dev/null +++ b/source/MemberFunctionCallback.cpp @@ -0,0 +1,31 @@ +/** + * Class definition for a MemberFunctionCallback. + * + * C++ member functions (also known as methods) have a more complex + * representation than normal C functions. This allows a referene 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 "MicroBit.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); +} + +/** + * 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)); +} + diff --git a/source/MicroBitDisplay.cpp b/source/MicroBitDisplay.cpp index 3020ddb..5c71143 100644 --- a/source/MicroBitDisplay.cpp +++ b/source/MicroBitDisplay.cpp @@ -3,11 +3,10 @@ * * A MicroBitDisplay represents the LED matrix array on the MicroBit device. */ +#include "mbed.h" #include "MicroBit.h" #include "MicroBitMatrixMaps.h" -#include #include "nrf_gpio.h" -#include "mbed.h" const float timings[MICROBIT_DISPLAY_GREYSCALE_BIT_DEPTH] = {0.000010, 0.000047, 0.000094, 0.000187, 0.000375, 0.000750, 0.001500, 0.003000}; @@ -234,7 +233,7 @@ MicroBitDisplay::animationUpdate() if(animationMode == ANIMATION_MODE_PRINT_CHARACTER) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } } } @@ -243,9 +242,13 @@ MicroBitDisplay::animationUpdate() * Broadcasts an event onto the shared MessageBus * @param eventCode The ID of the event that has occurred. */ -void MicroBitDisplay::sendEvent(uint16_t eventCode) +void MicroBitDisplay::sendAnimationCompleteEvent() { - MicroBitEvent evt(id,eventCode); + // Signal that we've completed an animation. + MicroBitEvent evt1(id,MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + + // Wake up any fibers that were blocked on the animation (if any). + MicroBitEvent evt2(MICROBIT_ID_ALERT, nonce); } /** @@ -266,7 +269,7 @@ void MicroBitDisplay::updateScrollText() if (scrollingChar > scrollingText.length()) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); return; } scrollingChar++; @@ -284,7 +287,8 @@ void MicroBitDisplay::updatePrintText() if (printingChar > printingText.length()) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + + this->sendAnimationCompleteEvent(); return; } @@ -302,7 +306,8 @@ void MicroBitDisplay::updateScrollImage() if ((image.paste(scrollingImage, scrollingImagePosition, 0, 0) == 0) && scrollingImageRendered) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); + return; } @@ -321,7 +326,7 @@ void MicroBitDisplay::updateAnimateImage() if (scrollingImagePosition <= -scrollingImage.getWidth() + (MICROBIT_DISPLAY_WIDTH + scrollingImageStride) && scrollingImageRendered) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); return; } @@ -349,7 +354,7 @@ void MicroBitDisplay::resetAnimation(uint16_t delay) if (animationMode != ANIMATION_MODE_NONE) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } // Clear the display and setup the animation timers. @@ -406,7 +411,8 @@ void MicroBitDisplay::print(char c, int delay) animationMode = ANIMATION_MODE_PRINT_CHARACTER; // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -432,7 +438,8 @@ void MicroBitDisplay::print(ManagedString s, int delay) this->printAsync(s, delay); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -459,7 +466,8 @@ void MicroBitDisplay::print(MicroBitImage i, int x, int y, int alpha, int delay) animationMode = ANIMATION_MODE_PRINT_CHARACTER; // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -542,7 +550,8 @@ void MicroBitDisplay::scroll(ManagedString s, int delay) this->scrollAsync(s, delay); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -569,7 +578,8 @@ void MicroBitDisplay::scroll(MicroBitImage image, int delay, int stride) this->scrollAsync(image, delay, stride); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } /** @@ -603,7 +613,7 @@ void MicroBitDisplay::animateAsync(MicroBitImage image, int delay, int stride, i if (animationMode != ANIMATION_MODE_NONE) { animationMode = ANIMATION_MODE_NONE; - this->sendEvent(MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + this->sendAnimationCompleteEvent(); } this->animationDelay = delay; @@ -646,7 +656,8 @@ void MicroBitDisplay::animate(MicroBitImage image, int delay, int stride, int st this->animateAsync(image, delay, stride, startingPosition); // Wait for completion. - fiber_wait_for_event(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_ANIMATION_COMPLETE); + nonce = uBit.MessageBus.nonce(); + fiber_wait_for_event(MICROBIT_ID_ALERT, nonce); } diff --git a/source/MicroBitEvent.cpp b/source/MicroBitEvent.cpp index ea6fb14..330048a 100644 --- a/source/MicroBitEvent.cpp +++ b/source/MicroBitEvent.cpp @@ -17,14 +17,14 @@ * MicrobitEvent evt(id,MICROBIT_BUTTON_EVT_CLICK,true); // auto fire * @endcode */ -MicroBitEvent::MicroBitEvent(uint16_t source, uint16_t value, bool fire) +MicroBitEvent::MicroBitEvent(uint16_t source, uint16_t value, MicroBitEventLaunchMode mode) { this->source = source; this->value = value; this->timestamp = ticks; - if(fire) - this->fire(); + if(mode != CREATE_ONLY) + this->fire(mode); } /** @@ -37,10 +37,35 @@ MicroBitEvent::MicroBitEvent() this->timestamp = ticks; } +/** + * Fires the represented event onto the message bus. + */ +void MicroBitEvent::fire(MicroBitEventLaunchMode mode) +{ + if (mode == CREATE_AND_QUEUE) + uBit.MessageBus.send(*this); + + else if (mode == CREATE_AND_FIRE) + uBit.MessageBus.process(*this); +} + /** * Fires the represented event onto the message bus. */ void MicroBitEvent::fire() { - uBit.MessageBus.send(*this); + fire(MICROBIT_EVENT_DEFAULT_LAUNCH_MODE); } + + +/** + * Constructor. + * Create a new MicroBitEventQueueItem. + * @param evt The event to be queued. + */ +MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt) +{ + this->evt = evt; + this->next = NULL; +} + diff --git a/source/MicroBitFiber.cpp b/source/MicroBitFiber.cpp index 79dbd62..636f485 100644 --- a/source/MicroBitFiber.cpp +++ b/source/MicroBitFiber.cpp @@ -212,8 +212,8 @@ void scheduler_event(MicroBitEvent evt) t = f->next; // extract the event data this fiber is blocked on. - uint16_t id = f->context & 0xFF; - uint16_t value = (f->context & 0xFF00) >> 16; + uint16_t id = f->context & 0xFFFF; + uint16_t value = (f->context & 0xFFFF0000) >> 16; if ((id == MICROBIT_ID_ANY || id == evt.source) && (value == MICROBIT_EVT_ANY || value == evt.value)) { diff --git a/source/MicroBitI2C.cpp b/source/MicroBitI2C.cpp index a9e025f..67aa723 100644 --- a/source/MicroBitI2C.cpp +++ b/source/MicroBitI2C.cpp @@ -1,5 +1,5 @@ -#include "MicroBit.h" #include "mbed.h" +#include "MicroBit.h" #include "twi_master.h" #include "nrf_delay.h" diff --git a/source/MicroBitIO.cpp b/source/MicroBitIO.cpp index fad8a30..e314c9c 100644 --- a/source/MicroBitIO.cpp +++ b/source/MicroBitIO.cpp @@ -4,37 +4,37 @@ * Represents a single IO pin on the edge connector. */ -#include "MicroBitIO.h" +#include "MicroBit.h" /** * Constructor. * Create a representation of all given I/O pins on the edge connector */ -MicroBitIO::MicroBitIO(int MICROBIT_ID_IO_P0, int MICROBIT_ID_IO_P1, int MICROBIT_ID_IO_P2, - int MICROBIT_ID_IO_P3, int MICROBIT_ID_IO_P4, int MICROBIT_ID_IO_P5, - int MICROBIT_ID_IO_P6, int MICROBIT_ID_IO_P7, int MICROBIT_ID_IO_P8, - int MICROBIT_ID_IO_P9, int MICROBIT_ID_IO_P10,int MICROBIT_ID_IO_P11, - int MICROBIT_ID_IO_P12,int MICROBIT_ID_IO_P13,int MICROBIT_ID_IO_P14, - int MICROBIT_ID_IO_P15,int MICROBIT_ID_IO_P16,int MICROBIT_ID_IO_P19, - int MICROBIT_ID_IO_P20) : - P0 (MICROBIT_ID_IO_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH) - P1 (MICROBIT_ID_IO_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH) - P2 (MICROBIT_ID_IO_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH) - P3 (MICROBIT_ID_IO_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_AD), //COL1 (ANALOG/DIGITAL) - P4 (MICROBIT_ID_IO_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_DIGITAL), //BTN_A - P5 (MICROBIT_ID_IO_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL) - P6 (MICROBIT_ID_IO_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_DIGITAL), //ROW2 - P7 (MICROBIT_ID_IO_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_DIGITAL), //ROW1 - P8 (MICROBIT_ID_IO_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_DIGITAL), //PIN 18 - P9 (MICROBIT_ID_IO_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_DIGITAL), //ROW3 - P10(MICROBIT_ID_IO_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_AD), //COL3 (ANALOG/DIGITAL) - P11(MICROBIT_ID_IO_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_DIGITAL), //BTN_B - P12(MICROBIT_ID_IO_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_DIGITAL), //PIN 20 - P13(MICROBIT_ID_IO_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_DIGITAL), //SCK - P14(MICROBIT_ID_IO_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_DIGITAL), //MISO - P15(MICROBIT_ID_IO_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_DIGITAL), //MOSI - P16(MICROBIT_ID_IO_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL), //PIN 16 - P19(MICROBIT_ID_IO_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_DIGITAL), //SCL - P20(MICROBIT_ID_IO_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_DIGITAL) //SDA +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_DIGITAL), //BTN_A + P5 (ID_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL) + 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 { } diff --git a/source/MicroBitListener.cpp b/source/MicroBitListener.cpp new file mode 100644 index 0000000..09f1498 --- /dev/null +++ b/source/MicroBitListener.cpp @@ -0,0 +1,73 @@ +/** + * Class definition for a MicroBitListener. + * + * MicroBitListener holds all the information related to a single event handler required + * to match and fire event handlers to incoming events. + */ + +#include "mbed.h" +#include "MicroBit.h" + +/** + * Constructor. + * Create a new Message Bus Listener. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component + * @param handler A function pointer to call when the event is detected. + */ +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; +} + +/** + * Constructor. + * Create a new parameterised Message Bus Listener. + * @param id The ID of the component you want to listen to. + * @param value The event ID you would like to listen to from that component. + * @param handler A function pointer to call when the event is detected. + * @param arg An additional argument to pass to the event handler function. + */ +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; +} + +/** + * 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) +{ + MicroBitEventQueueItem *q = new MicroBitEventQueueItem(e); + MicroBitEventQueueItem *p = evt_queue; + + if (evt_queue == NULL) + evt_queue = q; + else + { + while (p->next != NULL) + p = p->next; + + p->next = q; + } +} diff --git a/source/MicroBitMessageBus.cpp b/source/MicroBitMessageBus.cpp index d48fa5f..3d7b999 100644 --- a/source/MicroBitMessageBus.cpp +++ b/source/MicroBitMessageBus.cpp @@ -6,45 +6,6 @@ #include "MicroBit.h" -/** - * Constructor. - * Create a new MicroBitEventQueueItem. - * @param evt The event to be queued. - */ -MicroBitEventQueueItem::MicroBitEventQueueItem(MicroBitEvent evt) -{ - this->evt = evt; - this->next = NULL; -} -/** - * Constructor. - * Create a new Message Bus Listener. - * @param id The ID of the component you want to listen to. - * @param value The event ID you would like to listen to from that component - * @param handler A function pointer to call when the event is detected. - */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void (*handler)(MicroBitEvent)) -{ - this->id = id; - this->value = value; - this->cb = (void*) handler; - this->cb_arg = NULL; - this->next = NULL; -} - -/** - * A low-level, internal version of the constructor, where the handler's type is determined - * by the value of arg. (See MicroBitMessageBus.h). - */ -MicroBitListener::MicroBitListener(uint16_t id, uint16_t value, void* handler, void* arg) -{ - this->id = id; - this->value = value; - this->cb = handler; - this->cb_arg = arg; - this->next = NULL; -} - /** * Constructor. * Create a new Message Bus. @@ -54,6 +15,18 @@ MicroBitMessageBus::MicroBitMessageBus() this->listeners = NULL; this->evt_queue_head = NULL; this->evt_queue_tail = NULL; + this->nonce_val = 0; +} + +/** + * Returns a 'nonce' for use with the NONCE_ID channel of the message bus. + */ +uint16_t MicroBitMessageBus::nonce() +{ + // In the global scheme of things, a terrible nonce generator. + // However, for our purposes, this is simple and adequate for local use. + // This would be a bad idea if our events were networked though - can you think why? + return nonce_val++; } /** @@ -65,10 +38,61 @@ MicroBitMessageBus::MicroBitMessageBus() void async_callback(void *param) { MicroBitListener *listener = (MicroBitListener *)param; - if (listener->cb_arg != NULL) - ((void (*)(MicroBitEvent, void*))listener->cb)(listener->evt, listener->cb_arg); - else - ((void (*)(MicroBitEvent))listener->cb)(listener->evt); + + // 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 te 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; + } + else + break; + } + + // The fiber of exiting... clear our state. + listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY; } @@ -165,8 +189,9 @@ int MicroBitMessageBus::isIdleCallbackNeeded() * * Example: * @code - * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,false); + * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,CREATE_ONLY); * evt.fire(); + * * //OR YOU CAN DO THIS... * MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN); * @endcode @@ -192,33 +217,27 @@ void MicroBitMessageBus::process(MicroBitEvent evt) { MicroBitListener *l; - // Find the start of the sublist where we'll send this event. l = listeners; - while (l != NULL && l->id != evt.source) - l = l->next; - - // Now, send the event to all listeners registered for this event. - while (l != NULL && l->id == evt.source) - { - if(l->value == MICROBIT_EVT_ANY || l->value == evt.value) - { + while (l != NULL) + { + if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY)) + { l->evt = evt; - invoke(async_callback, (void *)l); + + // OK, if this handler has regisitered itself as non-blocking, we just execute it directly... + // This is normally only done for trusted system components. + // Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber + // should the event handler attempt a blocking operation, but doesn't have the overhead + // of creating a fiber needlessly. (cool huh?) + if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING) + async_callback(l); + else + invoke(async_callback, l); } l = l->next; } - // Next, send to any listeners registered for ALL event sources. - l = listeners; - while (l != NULL && l->id == MICROBIT_ID_ANY) - { - l->evt = evt; - invoke(async_callback, (void *)l); - - l = l->next; - } - // Finally, forward the event to any other internal subsystems that may be interested. // We *could* do this through the message bus of course, but this saves additional RAM, // and procssor time (as we know these are non-blocking calls). @@ -243,8 +262,6 @@ void MicroBitMessageBus::process(MicroBitEvent evt) * * @param hander The function to call when an event is received. * - * TODO: We currently don't support C++ member functions as callbacks, which we should. - * * Example: * @code * void onButtonBClick(MicroBitEvent evt) @@ -255,75 +272,210 @@ void MicroBitMessageBus::process(MicroBitEvent evt) * @endcode */ -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) { - this->listen(id, value, (void*) handler, arg); -} - -void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent)) { - this->listen(id, value, (void*) handler, NULL); -} - -void MicroBitMessageBus::listen(int id, int value, void* handler, void* arg) +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags) { - //handler can't be NULL! if (handler == NULL) return; + MicroBitListener *newListener = new MicroBitListener(id, value, handler, flags); + + if(!add(newListener)) + delete newListener; +} + +void MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags) +{ + if (handler == NULL) + return; + + MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg, flags); + + if(!add(newListener)) + delete newListener; +} + +/** + * 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. + * + * + * Example: + * @code + * void onButtonBClick() + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent)) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, handler); + remove(&listener); +} + +/** + * 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. + * + * + * Example: + * @code + * void onButtonBClick(void *arg) + * { + * //do something + * } + * + * uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); + * @endcode + */ +void MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg) +{ + if (handler == NULL) + return; + + MicroBitListener listener(id, value, handler, arg); + remove(&listener); +} + + +/** + * Add the given MicroBitListener to the list of event handlers, unconditionally. + * @param listener The MicroBitListener to validate. + * @return 1 if the listener is valid, 0 otherwise. + */ +int MicroBitMessageBus::add(MicroBitListener *newListener) +{ MicroBitListener *l, *p; + int methodCallback; + + //handler can't be NULL! + if (newListener == NULL) + return 0; + 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. - while (l != NULL) - { - if (l->id == id && l->value == value && l->cb == handler) - return; - l = l->next; - } + // 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); - MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg); + if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb)) + return 0; - //if listeners is null - we can automatically add this listener to the list at the beginning... + 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; - return; + return 1; } - // Maintain an ordered list of listeners. - // Chain is held stictly in increasing order of ID (first level), then value code (second level). + // 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. + // Adding a listener is a rare occurance, so we just walk the list... + p = listeners; l = listeners; - while (l != NULL && l->id < id) + while (l != NULL && l->id < newListener->id) { p = l; l = l->next; } - while (l != NULL && l->id == id && l->value < value) + while (l != NULL && l->id == newListener->id && l->value < newListener->value) { p = l; l = l->next; } //add at front of list - if (p == listeners && (id < p->id || (p->id == id && p->value > value))) + 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; } + + return 1; +} + +/** + * Remove a MicroBitListener from the list that matches the given listener. + * @param listener The MicroBitListener to validate. + * @return The number of listeners removed from the list. + */ +int MicroBitMessageBus::remove(MicroBitListener *listener) +{ + MicroBitListener *l, *p; + int removed = 0; + + //handler can't be NULL! + if (listener == NULL) + return 0; + + l = listeners; + p = NULL; + + // Walk this list of event handlers. Delete any that match the given listener. + while (l != NULL) + { + if (l->id == listener->id && l->value == listener->value && ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))) + { + if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) || + ((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb))) + { + // Found a match. Remove from the list. + if (p == NULL) + listeners = l->next; + else + p->next = l->next; + + // delete the listener. + MicroBitListener *t = l; + l = l->next; + + delete t; + removed++; + + continue; + } + } + + p = l; + l = l->next; + } + + return removed; } diff --git a/source/MicroBitSerial.cpp b/source/MicroBitSerial.cpp index 499a515..9d54c51 100644 --- a/source/MicroBitSerial.cpp +++ b/source/MicroBitSerial.cpp @@ -1,5 +1,5 @@ -#include "MicroBit.h" #include "mbed.h" +#include "MicroBit.h" /** * Constructor. diff --git a/source/ble-services/MicroBitDFUService.cpp b/source/ble-services/MicroBitDFUService.cpp index 729f671..702249f 100644 --- a/source/ble-services/MicroBitDFUService.cpp +++ b/source/ble-services/MicroBitDFUService.cpp @@ -234,7 +234,7 @@ void MicroBitDFUService::releaseFlashCode() { flashCode = NRF_FICR->DEVICEID[0]; - ble.updateCharacteristicValue(microBitDFUServiceFlashCodeCharacteristicHandle, (uint8_t *)&flashCode, sizeof(uint32_t)); + ble.gattServer().notify(microBitDFUServiceFlashCodeCharacteristicHandle,(uint8_t *)&flashCode, sizeof(uint32_t)); } /** diff --git a/source/ble-services/MicroBitEventService.cpp b/source/ble-services/MicroBitEventService.cpp index 121ae1f..0963168 100644 --- a/source/ble-services/MicroBitEventService.cpp +++ b/source/ble-services/MicroBitEventService.cpp @@ -5,9 +5,7 @@ #include "MicroBit.h" #include "ble/UUID.h" - #include "ExternalEvents.h" -#include "MicroBitEventService.h" /** * Constructor.