Foregoing the uBit object¶
Recommended only for experienced users
You should only concern yourself with this page if you know what you're doing.
We purposefully use uBit as our exposed API. We can guarantee that programs built with the uBit object will work in the future, but we cannot guarantee the same for individualised components!
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.
- Firstly, create a program that does not create or initialise a
uBit
object. - Include
MicroBit.h
(or if you prefer, just the header files of the components you want to use). IncludingMicroBit.h
is however, simpler. - Instead, create C++ object instances of the classes that you want to use as global variables in your program. Create as many components as you need. You are free to use any of the constructors in this documentation.
- Call functions on those instances to elicit the behaviour you need, using the name of your object instances instead of
uBit.*
For example, if you wanted to create a program that just used the LED matrix display driver, you might write a program like this:
Example
1 2 3 4 5 6 7 8 9 |
|
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 'Component Constructor' section of each component's API documentation for details and examples.
Example
1 2 3 4 5 6 7 8 9 10 11 |
|
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.
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
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:
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
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:
1 2 3 4 5 6 |
|