Advanced Features of the Runtime

Under the surface, the micro:bit runtime is a highly configurable, modular and component based piece of software.

The uBit object is provided as a collection of the commonly used components, all gathered together in one place to make it easier for novice users to access the functionality of the device. However, there is no obligation to use the uBit abstraction. More advanced users may prefer to create and use only the parts of the runtime they need.

This provides more control and often frees up more memory resource for the application program - but does so at the expense of the user taking more responsibility and additional complexity in their programs.

Using Components Directly

Taking advantage of the modular structure of the micro:bit runtime is fairly straightforward.

For example, if you wanted to create a program that just used the LED matrix display driver, you might write a program like this:

#include "MicroBit.h"

MicroBitDisplay display;

int main()
{
    while(1)
        display.scroll(":)");
}

If you need other components, add them to your program in the same way.

If a component has a dependency on another component (e.g. in the example below, the accelerometer is dependent on an I2C bus), then this will be requested as a mandatory parameter in the constructor.

See the 'Constructor' section of the each components' API documentation for details and examples.

#include "MicroBit.h"

MicroBitI2C i2c = MicroBitI2C(I2C_SDA0, I2C_SCL0);
MicroBitAccelerometer accelerometer = MicroBitAccelerometer(i2c);
MicroBitDisplay display;

int main()
{
    while(1)
        display.scroll(accelerometer.getX());
}

Warning

micro:bit runtime components should always be brought up as global variables. They should not be created as local variables - either in your main method or anywhere else. The reason for this is the the runtime is a multi-threaded environment, and any variables created in stack memory (like local variables) may be paged out by the scheduler, and result in instability if they utilise interrupts or are accessed by other threads. So... don't do it!

System Components

There are also system components that provide background services. Without the uBit object, these will not be created by default. Examples include the fiber scheduler, message bus and heap allocator.

You are not required to initialise these components, but you should do so if you want to benefit from the functionality they provide. The following section describe how to do this.

Initialising the Message Bus

The MicroBitMessageBus allows events to be created and delivered to applications. So if a MicroBitMessageBus is not created, then all events in the micro:bit runtime will be quietly ignored.

To enable this functionality, simply create an instance of the MicroBitMessageBus class. From that point onward in your program, you can raise and listen for events as described in the MicroBitMessageBus documentation.

#include "MicroBit.h"

MicroBitMessageBus bus;
MicroBitButton buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
MicroBitDisplay display;

void onPressed(MicroBitEvent e)
{
    display.print("S");    
}

int main()
{
    bus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onPressed);

    while(1)
    {
    }
}

Warning

Running a MessageBus without the Fiber Scheduler will result in all event handlers being registered as MESSAGE_BUS_LISTENER_IMMEDIATE (see MicroBitMessageBus for details). This means that your event handler will be executed in the context of the code that raised the event. This may include interrupt context, which may not be safe for all operations. It is recommend that you always run the MessageBus with the Fiber Scheduler in order to allow the event to be decoupled from interrupt context.

Initialising the Fiber Scheduler

Often when using asynchronous events, it is also useful to run the fiber scheduler. Without a scheduler in operation, all event handlers (such as the one above) will be executed with the threading mode MESSAGE_BUS_LISTENER_IMMEDIATE, as described on the MicroBitMessageBus documentation.

Also, it is not really possible to transparently enter a power efficient sleep - as illustrated in the busy loop in the above example.

Initialising the fiber scheduler is simple, and is demonstrated in the following example.

From the moment the fiber scheduler is initialised, it is then possible to block the processor in a power efficient way and to operate threaded event handlers:

#include "MicroBit.h"

MicroBitMessageBus bus;
MicroBitButton buttonA(MICROBIT_PIN_BUTTON_A, MICROBIT_ID_BUTTON_A);
MicroBitDisplay display;

void onPressed(MicroBitEvent e)
{
    display.print("S");    
}

int main()
{
    scheduler_init(bus);

    bus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_BUTTON_EVT_CLICK, onPressed);

    while(1)
        fiber_sleep(1000);
}

Note

Function calls to uBit.sleep() must be replaced with the direct, equivalent calls to the scheduler using fiber_sleep().

Initialising the Heap Allocator

The micro:bit runtime provides an optional, heap memory allocator. This is primarily to permit the use of multiple regions of memory to be used as heap memory space for your variables.

The uBit initialisation function will automatically release any memory unused by the Bluetooth stack for general purpose use in this fashion (this typically provides an additional 1K of SRAM under Bluetooth enabled builds, and another 8K if Bluetooth is disabled).

Should you wish to also reclaim memory in this way, you can do so as follows:

#include "MicroBit.h"

int main()
{
    microbit_create_heap(MICROBIT_SD_GATT_TABLE_START + MICROBIT_SD_GATT_TABLE_SIZE, MICROBIT_SD_LIMIT);
}

Compile Time Configuration Options

In addition to the flexibility to initialise only the components that you need, the runtime also provides a central, compile time configuration file called MicroBitConfig.h.

You can use this to reconfigure the default behaviour of the runtime.

The default settings will provide a stable working environment, but advanced users may want to further tailor the behaviour.

To tailor the behaviour, simply edit the MicroBitConfig.h file to change the settings, and then perform a clean rebuild.

Compile Time Options with MicroBitConfig.h

The following options are configurable at compile time through MicroBitConfig.h:

Configuration option Brief Description
MICROBIT_HEAP_ALLOCATOR Enables or disables the MicroBitHeapAllocator for uBit based builds.
MICROBIT_HEAP_BLOCK_SIZE The Block size used by the heap allocator in bytes.
MICROBIT_NESTED_HEAP_SIZE The proportion of SRAM available on the mbed heap to reserve for the micro:bit heap.
MICROBIT_HEAP_REUSE_SD If set to '1', any unused areas of the Soft Device GATT table will be automatically reused as heap memory.
MICROBIT_SD_GATT_TABLE_SIZE The amount of memory (bytes) to dedicate to the SoftDevice GATT table.
SYSTEM_TICK_PERIOD_MS The default scheduling quantum
EVENT_LISTENER_DEFAUT_FLAGS The default threading mode used when a MicroBitMessageBus listener is created.
MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH Maximum event queue depth. If a queue exceeds this depth, further events will be dropped.
MICROBIT_SYSTEM_COMPONENTS The maximum size of the interrupt callback list.
MICROBIT_IDLE_COMPONENTS The maximum size of the idle callback list.
MICROBIT_BLE_ENABLED Enable/Disable Bluetooth during normal operation. If disabled, no Bluetooth communication is possible, but radio functionality is made possible, and an additional 8K of SRAM is released
MICROBIT_BLE_PAIRING_MODE Enable/Disable Bluetooth pairing mode with A+B / reset at power up
MICROBIT_BLE_PRIVATE_ADDRESSES Enable/Disable the use of private resolvable addresses. This is known to be a feature that suffers compatibility issues with many Bluetooth central devices.
MICROBIT_BLE_OPEN Enable/Disable Bluetooth security entirely. Open Bluetooth links are not secure, but are highly useful during the development of Bluetooth services
MICROBIT_BLE_SECURITY_LEVEL Define the default, global Bluetooth security requirements for MicroBit Bluetooth services
MICROBIT_BLE_WHITELIST Enable/Disable the use of Bluetooth whitelisting.
MICROBIT_BLE_ADVERTISING_TIMEOUT Define the period of time for which the Bluetooth stack will advertise (seconds).
MICROBIT_BLE_DEFAULT_TX_POWER Defines default power level of the Bluetooth radio transmitter.
MICROBIT_BLE_DFU_SERVICE Enable/Disable Bluetooth Service: MicroBitDFU. This allows over the air programming during normal operation.
MICROBIT_BLE_EVENT_SERVICE Enable/Disable Bluetooth Service: MicroBitEventService. This allows routing of events from the micro:bit message bus over Bluetooth.
MICROBIT_BLE_DEVICE_INFORMATION_SERVICE Enable/Disable Bluetooth Service: MicroBitDeviceInformationService. This enables the standard Bluetooth device information service.
MICROBIT_BLE_EDDYSTONE_URL Enable/Disable Eddystone URL support. Enabling this enables you to broadcast a physical web url from the microbit
USE_ACCEL_LSB Enable 10 bit sampling on the accelerometer. By default, a more efficient 8 bit sampling if used.
MICROBIT_DISPLAY_TYPE Selects the default matrix configuration for the display driver.
MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS Selects the minimum permissible brightness level for the device.
MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS Selects the maximum permissible brightness level for the device.
MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS Selects the default brightness level for the device.
MICROBIT_DEFAULT_SCROLL_SPEED Selects the time taken to scroll a single pixel, in milliseconds.
MICROBIT_DEFAULT_SCROLL_STRIDE Selects the number of pixels a scroll will move in each quantum.
MICROBIT_DEFAULT_PRINT_SPEED Selects the time each character will be shown on the display during print operations, in milliseconds.
MICROBIT_DEFAULT_SERIAL_MODE Configures the default serial mode used by serial read and send calls.
MICROBIT_DEFAULT_PULLMODE Define the default mode in which the digital input pins are configured. Valid options are PullDown, PullUp and PullNone.
MICROBIT_PANIC_HEAP_FULL Enable this to invoke a panic on out of memory conditions.
MICROBIT_DBG Enable this to route debug messages through the USB serial interface.
MICROBIT_HEAP_DBG Enable this to receive detailed diagnostic messages from the heap allocator via the USB serial interface.
MICROBIT_DAL_VERSION Define the default version information of the runtime.

There are also some constants that define the geometry of the micro:bit memory

Warning

Only change these if you really know what you are doing!

Configuration option Brief Description
MICROBIT_SRAM_BASE The start address of usable RAM memory.
MICROBIT_SRAM_END The end address of usable RAM memory.
MICROBIT_SD_LIMIT The end address of RAM memory reserved for Soft Device (Nordic's Bluetooth stack).
MICROBIT_SD_GATT_TABLE_START The start address of the Bluetooth GATT table in RAM.
CORTEX_M0_STACK_BASE The memory address of the top of the system stack.
MICROBIT_STACK_SIZE Amount of memory reserved for the stack (in bytes).
MICROBIT_HEAP_END The end address of the mbed heap space

Compile Time Options with Yotta

Rather than edit the MicroBitConfig.h file to change the default behaviour of the runtime, if you are using yotta, you can also provide a config.json in your project.

Here's a config.json, using all available configuration options, that matches the default values specified in MicroBitConfig.h:

{
    "microbit-dal":{
        "bluetooth":{
            "enabled": 1,
            "pairing_mode": 1,
            "private_addressing": 0,
            "open": 0,
            "whitelist": 1,
            "advertising_timeout": 0,
            "tx_power": 0,
            "dfu_service": 1,
            "event_service": 1,
            "device_info_service": 1,
            "eddystone_url": 0
        },
        "reuse_sd": 1,
        "default_pullmode":"PullDown",
        "gatt_table_size": "0x300",
        "heap_allocator": 1,
        "nested_heap_proportion": 0.75,
        "system_tick_period": 6,
        "system_components": 10,
        "idle_components": 6,
        "use_accel_lsb": 0,
        "min_display_brightness": 1,
        "max_display_brightness": 255,
        "display_scroll_speed": 120,
        "display_scroll_stride": -1,
        "display_print_speed": 400,
        "panic_on_heap_full": 1,
        "debug": 0,
        "heap_debug": 0,
        "stack_size":2048,
        "sram_base":"0x20000008",
        "sram_end":"0x20004000",
        "sd_limit":"0x20002000",
        "gatt_table_start":"0x20001900"
    }
}

It should be noted that all of the above options are optional, and will revert to their default values if not specified. This means that we can also provide a subset of these options, to configure specific parts of the runtime:

{
    "microbit-dal":{
        "bluetooth":{
            "open": 1
        },
        "debug":1
    }
}

Additionally, the options provided through config.json intuitively map onto the #defines listed in MicroBitConfig.h

An example of config.json in operation is available at the microbit-samples GitHub repository.