Skip to content

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
void onButtonA(MicroBitEvent)
{
    uBit.display.print("A");
}

int main()
{
    uBit.init();
    uBit.messageBus.listen(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK, onButtonA);

    release_fiber(); // Stops the program from quitting
}

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
void onButtonA(Event e)
{
    if (e.value == DEVICE_BUTTON_EVT_CLICK)
        uBit.display.scroll("CLICK");

    if (e.value == DEVICE_BUTTON_EVT_DOWN) // Doesn't wait for user to release the button!
        uBit.display.scroll("DOWN");
}

int main()
{
    uBit.init();
    uBit.messageBus.listen(DEVICE_ID_BUTTON_A, DEVICE_EVT_ANY, onButtonA);

    release_fiber();
}

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
void onEvent(Event)
{
    uBit.display.scroll("SOMETHING HAPPENED!");
}

int main()
{
    uBit.init();
    uBit.messageBus.listen(DEVICE_ID_ANY, DEVICE_EVT_ANY, onEvent);

    release_fiber();
}

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
bool pressed = false;

void onButtonA(Event)
{
    pressed = true;
}

int main()
{
    uBit.init();
    uBit.messageBus.listen(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK, onButtonA, MESSAGE_BUS_LISTENER_IMMEDIATE);

    while(1)
    {
        if(pressed)
            uBit.display.scroll("Pressed!");
        uBit.sleep(100);
    }
}

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
MyCoolObject::onButtonPressed(Event)
{
    uBit.display.print("A");
}

MyCoolObject::MyCoolObject()
{
    uBit.messageBus.listen(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK, this, &MyCoolObject::onButtonPressed);
}

Again, it is also possible to a threading mode as an optional final parameter:

Example

1
2
3
4
5
6
7
8
9
MyCoolObject::onButtonPressed(Event)
{
    uBit.display.print("A");
}

MyCoolObject::MyCoolObject()
{
    uBit.messageBus.listen(DEVICE_ID_BUTTON_A, DEVICE_BUTTON_EVT_CLICK, this, &MyCoolObject::onButtonPressed, MESSAGE_BUS_LISTENER_IMMEDIATE);
}

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

Event
evt - 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.

bool
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.

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

int
n - 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

int
id - 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.

int
value - 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

int
id - 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.

int
value - 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_t
flags - 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

int
id - The Event ID used to register the listener.

int
value - 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.