uBit.messageBus¶
Overview¶
uBit.messageBus
gives access to the event system of the micro:bit that can notify user code when specific things happen. As it is an event system, you only need to subscribe to events that you are interested in.
About events¶
Events allow for very efficient programs. Instead of a polling approach, where you would consistently check if a value has changed, or that something has happened, events allow you to react to something happening, saving you precious resources. It also makes for much cleaner, and componentised code.
Events on the micro:bit are sent through the Message Bus, which also provides utilties for the receving of events.
Various components on the micro:bit will send events through the message bus. For example, uBit.accelerometer
can raise events to indicate that the micro:bit has be been shaken, or is in freefall. uBit.button
can send events on a range of button up, down, click and hold events.
Programmers are also free (in fact, encouraged!) to send their own events whenever they feel it would be useful.
Note
See the page on the Event
type for more information.
Using the message bus¶
Registering an event handler¶
uBit.messageBus
records which events your program is interested in, and delivers any Event
to your program as they occur through a defined event handler. An event handler is simply a function that can be called automatically when one of your subscribed events occurs.
This is achieved through the listen
function. This lets you attach a callback to a function when a specified event (or events) occur.
You can also control the queuing and threading model used for your callback function on a per-event-handler basis.
This may sound complex at first, but it is actually very simple. For example, to find out when button A is clicked, write some code like this:
Example
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Now, whenever the DEVICE_BUTTON_EVT_CLICK
event is raised in DEVICE_ID_BUTTON_A
, your code inside function onButtonA
will be automatically run.
To be clearer, this just means that onButtonA
will be run when the left button (button A) has been clicked (pushed down and then released.)
Tip
You can think of the first argument of messageBus.listen
as the channel on which the second parameter, the event, is sent on.
You can call listen
as many times as you want to attach handlers to each of the events that are useful for your program.
Wildcard events¶
You may want to capture all events generated by some component. For example, you might want to know when any changes in a button have happened.
In this case, there is a special event value called DEVICE_EVT_ANY
. If you call listen
with this value, then ALL events from the given source component will be delivered to your function.
You can find out which ones by looking at the Event
delivered to your function - it contains the source and value variable of the event.
For example, you could write a program like this to determine what happened:
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
If you want to wildcard any possible event from any component, there is also the DEVICE_ID_ANY
channel.
The following code would attach the onEvent
function to receive all the events from the whole runtime:
Example
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Warning
Use this sparingly and only when absolutely necessary; this could be quite a lot of events! It's best to be specific with which device you're wanting to receive events from.
Removing event handlers¶
Event handlers can be dynamically removed from the message bus as well as added. To do this, use ignore
. This takes precisely the same parameters as
listen
, except that the threading mode argument is never used. This will only remove the handler that you specify.
For example, to remove an event handler for a click on button A:
uBit.messageBus.ignore(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onButtonA);
Defining a Threading Mode¶
Whenever you register a listener, you may choose the threading mode used with that handler. Every event handler can have its own threading mode, that defines when your handler will be executed, and how it will react to receiving multiple events.
There are four permissible modes for event handlers. These are:
Threading mode | Brief Description |
---|---|
MESSAGE_BUS_LISTENER_IMMEDIATE | Handler is called directly from the code raising the event. Event handler is not permitted to block. |
MESSAGE_BUS_LISTENER_DROP_IF_BUSY | Handler is executed through its own fiber. If another event arrives whilst the previous event is still being processed, the new event will be silently dropped. |
MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY | Handler is executed though its own fiber. If another event arrives, it is queued, and the event handler will immediately be called again once processing is complete. (default) |
MESSAGE_BUS_LISTENER_REENTRANT | Every event is executed in its own fiber. if another event arrives, it is handled concurrently in its own fiber. |
These various modes provide great flexibility in how the runtime can be used to support higher level languages and applications. For example, MESSAGE_BUS_LISTENER_IMMEDIATE
is ideal for very simple, lightweight handlers, as this will provide very timely response to events with a low processing overhead. However, it is easy to cause side effects on other parts of the code if it does not return promptly.
MESSAGE_BUS_LISTENER_DROP_IF_BUSY
provide semantics identical to the Scratch programming language, and can be used to build easy to understand, asynchronous environments.
MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY
provides similar semantics, but with tolerance to avoiding loss of high frequency events.
MESSAGE_BUS_LISTENER_REENTRANT
provides guaranteed causal ordering and improved concurrency, but at the cost of additional complexity and RAM.
You can define the threading mode you want to use on a per event handler basis as an optional final parameter to the listen function - in this case, we choose immediate mode:
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Handlers as member functions¶
It is also possible to write event handlers as C++ member functions. You can use a variation of the listen
function to register your member function event handler.
This takes the same form as the examples above, but with an additional parameter to specify the object to call the method on. You are also required to specify your event handler using legal C++ syntax.
For example, you can write code like this to register an event handler in your own class:
Example
1 2 3 4 5 6 7 8 9 |
|
Again, it is also possible to a threading mode as an optional final parameter:
Example
1 2 3 4 5 6 7 8 9 |
|
Warning
Make sure to only have one instance of MicroBit
(uBit
) in your program.
Reserved source IDs¶
The following table shows the event source IDs that have been reserved by the micro:bit V2 Runtime. Take heed of this when defining your own event sources.
Warning
The maximum value you can assign as a source is 65535.
Reserved IDs
Device ID | Value |
---|---|
DEVICE_ID_ANY |
0 |
DEVICE_ID_BUTTON_A |
1 |
DEVICE_ID_BUTTON_B |
2 |
DEVICE_ID_BUTTON_AB |
3 |
DEVICE_ID_BUTTON_RESET |
4 |
DEVICE_ID_ACCELEROMETER |
5 |
DEVICE_ID_COMPASS |
6 |
DEVICE_ID_DISPLAY |
7 |
DEVICE_ID_THERMOMETER |
8 |
DEVICE_ID_RADIO |
9 |
DEVICE_ID_RADIO_DATA_READY |
10 |
DEVICE_ID_MULTIBUTTON_ATTACH |
11 |
DEVICE_ID_SERIAL |
12 |
DEVICE_ID_GESTURE |
13 |
DEVICE_ID_SYSTEM_TIMER |
14 |
DEVICE_ID_SCHEDULER |
15 |
DEVICE_ID_COMPONENT |
16 |
DEVICE_ID_LIGHT_SENSOR |
17 |
DEVICE_ID_TOUCH_SENSOR |
18 |
DEVICE_ID_SYSTEM_DAC |
19 |
DEVICE_ID_SYSTEM_MICROPHONE |
20 |
DEVICE_ID_SYSTEM_LEVEL_DETECTOR |
21 |
DEVICE_ID_SYSTEM_LEVEL_DETECTOR_SPL |
22 |
DEVICE_ID_MSC |
23 |
DEVICE_ID_SPI |
24 |
DEVICE_ID_DISTANCE |
25 |
DEVICE_ID_GYROSCOPE |
26 |
DEVICE_ID_HUMIDITY |
27 |
DEVICE_ID_PRESSURE |
28 |
DEVICE_ID_SINGLE_WIRE_SERIAL |
29 |
DEVICE_ID_JACDAC |
30 |
DEVICE_ID_JACDAC_PHYS |
31 |
DEVICE_ID_JACDAC_CONTROL_SERVICE |
32 |
DEVICE_ID_JACDAC_CONFIGURATION_SERVICE |
33 |
DEVICE_ID_SYSTEM_ADC |
34 |
DEVICE_ID_PULSE_IN |
35 |
DEVICE_ID_IO_P(X) |
100-227 |
DEVICE_ID_MESSAGE_BUS_LISTENER |
1021 |
DEVICE_ID_NOTIFY_ONE |
1022 |
DEVICE_ID_NOTIFY |
1023 |
DEVICE_ID_BUTTON_UP |
2000 |
DEVICE_ID_BUTTON_DOWN |
2001 |
DEVICE_ID_BUTTON_LEFT |
2002 |
DEVICE_ID_BUTTON_RIGHT |
2003 |
DEVICE_ID_JD_DYNAMIC_ID |
3000-4000 |
Message Bus Info¶
Message Bus ID¶
Constant | Value |
---|---|
DEVICE_ID_MESSAGE_BUS_LISTENER | 1021 |
The message bus will send a DEVICE_ID_MESSAGE_BUS_LISTENER
event whenever a new listener is added to the message bus.
This event allows other parts of the system to detect when interactions are taking place with a component. This is primarily used as a power management mechanism - allowing on demand activation of hardware when necessary.
Message Bus Events¶
Constant | Value |
---|---|
Message Bus ID of listener | 1-65535 |
API¶
send¶
int send( Event evt)
Description
Queues the given event to be sent to all registered recipients.
Parameters
Eventevt - The event to send.
Example
MessageBus bus;
// Creates and sends the Event using bus.
Event evt(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK);
// Creates the Event, but delays the sending of that event.
Event evt1(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK, CREATE_ONLY);
bus.send(evt1);
// This has the same effect!
evt1.fire()
process¶
int process( Event & evt)
Description
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.
Parameters
Event &evt - The event to send.
Returns
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 Event .
int process( Event & evt, bool urgent)
Description
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.
Parameters
Event &evt - The event to send.boolurgent - 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.
Returns
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 Event .
elementAt¶
Listener elementAt( int n)
Description
Returns the Listener with the given position in our list.
Parameters
intn - The position in the list to return.
Returns
the Listener at postion n in the list, or NULL if the position is invalid.
add¶
int add( Listener * newListener)
Description
Add the given Listener to the list of event handlers, unconditionally.
Parameters
Listener *newListener
Returns
DEVICE_OK if the listener is valid, DEVICE_INVALID_PARAMETER otherwise.
remove¶
int remove( Listener * newListener)
Description
Remove the given Listener from the list of event handlers.
Parameters
Listener *newListener
Returns
DEVICE_OK if the listener is valid, DEVICE_INVALID_PARAMETER otherwise.
send¶
int send( Event )
Description
Queues the given event to be sent to all registered recipients. The method of delivery will vary depending on the underlying implementation.
Parameters
Event
Returns
This default implementation simply returns DEVICE_NOT_SUPPORTED.
add¶
int add( Listener * )
Description
Add the given Listener to the list of event handlers, unconditionally.
Parameters
Listener *
Returns
This default implementation simply returns DEVICE_NOT_SUPPORTED.
remove¶
int remove( Listener * )
Description
Remove the given Listener from the list of event handlers.
Parameters
Listener *
Returns
This default implementation simply returns DEVICE_NOT_SUPPORTED.
elementAt¶
Listener elementAt( int )
Description
Returns the Listener at the given position in the list.
Parameters
int
Returns
This default implementation simply returns NULL.
setListenerDeletionCallback¶
int setListenerDeletionCallback( void(*)( Listener *) listener_deletion_callback)
Description
Sets a pointer to a handler that is invoked when any listener is deleted.
Parameters
void(*)( Listener *)listener_deletion_callback
Returns
DEVICE_OK on success.
listen¶
int listen( int id, int value, void(*)( Event ) handler)
Description
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.
Parameters
intid - The source of messages to listen for. Events sent from any other IDs will be filtered. Use DEVICE_ID_ANY to receive events from all components.intvalue - The value of messages to listen for. Events with any other values will be filtered. Use DEVICE_EVT_ANY to receive events of any value.void(*)( Event )handler - The function to call when an event is received.
Returns
DEVICE_OK on success, or any valid error code defined in "ErrNo.h". The default implementation simply returns DEVICE_NOT_SUPPORTED.
Example
void onButtonBClicked(Event)
{
//do something
}
// call onButtonBClicked when ever a click event from buttonB is detected.
uBit.messageBus.listen(DEVICE_ID_BUTTON_B, DEVICE_BUTTON_EVT_CLICK, onButtonBClick);
int listen( int id, int value, void(*)( Event ) handler, uint16_t flags)
Description
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.
Parameters
intid - The source of messages to listen for. Events sent from any other IDs will be filtered. Use DEVICE_ID_ANY to receive events from all components.intvalue - The value of messages to listen for. Events with any other values will be filtered. Use DEVICE_EVT_ANY to receive events of any value.void(*)( Event )handler - The function to call when an event is received.uint16_tflags - User specified, implementation specific flags, that allow behaviour of this events listener to be tuned.
Returns
DEVICE_OK on success, or any valid error code defined in "ErrNo.h". The default implementation simply returns DEVICE_NOT_SUPPORTED.
Example
void onButtonBClicked(Event)
{
//do something
}
// call onButtonBClicked when ever a click event from buttonB is detected.
uBit.messageBus.listen(DEVICE_ID_BUTTON_B, DEVICE_BUTTON_EVT_CLICK, onButtonBClick);
ignore¶
int ignore( int id, int value, void(*)( Event ) handler)
Description
Unregister a listener function. Listeners are identified by the Event ID, Event value and handler registered using listen() .
Parameters
intid - The Event ID used to register the listener.intvalue - The Event value used to register the listener.void(*)( Event )handler - The function used to register the listener.
Returns
DEVICE_OK on success or DEVICE_INVALID_PARAMETER if the handler given is NULL.
Example
void onButtonBClick(Event)
{
//do something
}
uBit.messageBus.listen(DEVICE_ID_BUTTON_B, DEVICE_BUTTON_EVT_CLICK, onButtonBClick);
// the previously created listener is now ignored.
uBit.messageBus.ignore(DEVICE_ID_BUTTON_B, DEVICE_BUTTON_EVT_CLICK, onButtonBClick);
Component Constructor¶
Advanced users only
Do not use this unless you really know what you're doing. It's usually best to use uBit
.
MessageBus()
Description
Default constructor.
Adds itself as a fiber component, and also configures itself to be the default EventModel if defaultEventBus is NULL.