The micro:bit is a microcomputer programmable in C/C++, TypeScript and Python. This article discusses hard- and software capabilities and limitations of the micro:bit. Software developers not yet familiar with the device will get a first orientation on how to get started with each programming language, together with Dos and Don’ts I learnt from first-hand experience.
Introducing the micro:bit
Occasionally a developer of mobile apps or desktop software will start a project that involves microcomputers. Such devices are small and can easily be attached to other objects. There are hundreds of different project ideas that could be implemented with such devices. Often, you’ll solder other hardware sensors and actors to the microcomputer, like potentiometers, LEDs, switches or speakers. For such projects, connecting a full-blown computer, laptop or even a Raspberry Pi would be overkill, due to their size and power requirements. You may have heard of microcomputers, like Arduino UNO. The micro:bit is a more recent development, created by BBC in 2015. It is ideally suited for electronic crafts projects, because it is cheap (~15€), small (4 x 5 cm) and light-weight (5 grams). It can be powered over USB or 2xAAA batteries, consuming very little power (5-30 mW). Aside from a programmable CPU, it comes with a number of sensors and actors:
- Integrated accelerometer
- Integrated compass
- Bluetooth Low Energy (BLE)
- Integrated 5×5 LED matrix, to show text or sense environment light intensity
- 2 physical programmable push-buttons
- CPU temperature sensor
The details about the micro:bit’s hardware are found here. Watch out, there are two different hardware revisions, 1.3 and 1.5. You can differentiate them by checking whether the printed compass and accelerometer labels on your micro:bit point to two distinct chips (version 1.3) or to a single chip (version 1.5).
Before you buy a micro:bit, there are a few limitations to keep in mind:
- Not all hardware/software features can be used together at the same time. See section Some important caveats below. Also, some features are only available in specific programming languages. Make sure you verify this at the beginning of your project!
- The RAM is very limited. Most of the 16 KB memory are used by your compiled code, the run-time and extension modules. Forget about even simple things such as storing 500 numbers in a list. In general, any embedded developer is faced with this kind of resource shortage.
- Computations are slow! The micro:bit is not suited for performing complex mathematical calculations.
- There is no debugging – you have to use print(). Some exceptions apply, see below.
Developing on the micro:bit
When I started developing for the micro:bit, the first question was: which languages can I use and what libraries already exist to make my life easier? To summarize, you can use the following languages:
- C/C++. Only recommended if you have prior experience in embedded development!
- A simplified dialect of TypeScript using the MakeCode web editor
- A simplified dialect of Python called MicroPython. More precisely: a customized, older version, not the official one from here. There is a web editor and several offline editors I’ll discuss below.
With C/C++ and TypeScript, code is compiled to native code. When using MicroPython, the code editor produces a “hex” file which contains both the MicroPython run-time (as native code), your own (compiled) code and the source code as text. That hex file is then flashed to the device, which works in one of two ways. Either you save the hex file on the micro:bit, which is detected as mass-storage device when connected to your machine via USB. Or the editor flashes it to the device directly. The latter is only possible if you use a web editor in Chrome which supports WebUSB, or when using an offline editor.
In general, I recommend TypeScript or MicroPython. They come with libraries to access nice, high-level functionality, such as:
- Scrolling of long text over the 5×5 LED matrix, or drawing pixel-art images
- A radio module which allows for easy micro:bit-to-micro:bit communication via Bluetooth, see here for technical details
- Measurement of environment light levels via the LED matrix, see https://makecode.microbit.org/projects/light-level-meter
- Playing music or melodies (given that a speaker is connected)
- Servo motor control
I’ll now go into the details of each language.
There are several IDEs to choose from when developing in C/C++:
- ARM mbed, see here
- Arduino IDE, see here
I’m not an embedded developer and have not tried these IDEs myself. However, the provided links should point you in the right direction.
I recommend the web editor MakeCode by Microsoft, which runs entirely in the browser. It offers code auto-complete and shows doc-strings of all library APIs. Although there is a stand-alone Windows app of MakeCode, I found it easier to just use the web editor. Make sure to use Chrome, which offers WebUSB. This will give you the exact same capabilities as the stand-alone MakeCode app: automatic flashing (no need to save a hex file every time you compile), and inspection of the serial console (and visualization of its values). After you have opened or create a project in the editor, click on the cog wheel icon in the top right, then click “Pair device”, and follow the instructions.
Side effects when switching representations
Some APIs, like accessing Bluetooth, are not enabled by default, because not every projects needs them, but their code consumes valuable memory. These APIs are encapsulated in so-called extension modules, which you need to add explicitly. Examples include BLE (Bluetooth) functionality, or working with LED strips (neopixel). Click on “cog wheel (top right) → Extensions” to select and add such an extension module to your project. To remove an extension module again, click on the Explorer box and then click the corresponding trash icon.
Tipps when using MakeCode
- Keep a copy of your TypeScript file in a Git repository, to track changes over time.
- Also, put the project settings file under version control. In the top right of the editor, click on the settings cog wheel → Project settings → Edit Settings as text. Copy the content to a separate file, e.g. project-settings.json, and put it into your Git repo. This file contains which extension modules your project uses, as well as the BLE pairing mode (if BLE is used).
- Make use of the Search… box (top center) to find APIs. The results can be drag-and-dropped into the correct position. This works in both blocks and TypeScript mode.
- You can have several “threads” in parallel, using control.inBackground(func()), see here. However, if any of the functions you schedule with this mechanism is long-lasting, you have to explicitly give control back to the scheduler from time to time. This way, it can switch from one task to another, which avoids task starvation. You have to use basic.pause() for this. You can just call basic.pause(0) to cause round-robin scheduling, but note that the current “thread” will be suspended for at least 4-6 ms (not 0 ms).
- There is also control.waitMicros() to pause execution. However, this only causes the current thread to sleep, but won’t wake up others. Note: threads are called “fibers” in micro:bit speak.
- If you want to use Bluetooth LE, start with this guide. It explains the different versions (and problems) with pairing a third-party device (such as your Android/iOS device) to your micro:bit.
- MakeCode includes a simulator which allows you to mock sensor inputs, like the measured temperature, compass orientation, or simulating button presses. To activate the simulator, press the play button below the micro:bit image, shown in the top left. You can only mock those sensors and actors that you actually access in your code. An example is shown here.
Some important caveats
- Not all extension modules are compatible with each other, i.e., they cannot be used at the same time. For instance:
- radio + Bluetooth, because radio also uses the Bluetooth stack, but runs a proprietary protocol on top of it.
- Bluetooth + neopixel, see here.
- The function func provided to basic.forever(func) is called regularly. But when func terminates, it is not called again immediately. There is a delay of sometimes over 20 ms between the end of func and func being called again. If you run very timing-sensitive code, make sure that func never terminates, by using a while/for-loop within func.
- basic.pause(millis) (which suspends the current thread) may sleep longer than specified. Try control.waitMicros(micros) instead, but note that it may sleep shorter than expected, and does not resume other tasks scheduled in other threads.
- In some cases, the serial output (shown in the “Device console” window) may lag behind by one line. Consequently, you may want to make a consecutive serial.printLine(“”) call to explicitly flush the previous printLine() content.
- Calls to serial.printLine() are quite slow! Call serial.setWriteLinePadding(0) at the beginning of your code to speed up printing by a factor of ~3!
The micro:bit-specific APIs of MicroPython are documented here. You’ll find more technical details about MicroPython here. It explains why Bluetooth is not available (due to memory limits), and that the generated hex file also contains a clear-text form of your source-code. You can verify this by dragging a hex file from the micro:bit mass storage device into the MicroPython web editor, which will reveal the source code.
There are several editors you can use, which have advantages and disadvantages:
|Visualize data from console||Auto-complete||Inspect API documentation||Minify code||Upload multiple modules|
|JetBrains PyCharm plug-in|
|Web editor using Chrome with WebUSB|
A few notes for the editors:
- JetBrains PyCharm plug-in:
- The uflash Python package version requested by the PyCharm plug-in may be outdated. uflash is used to flash your code to the micro:bit and includes the MicroPython run-time, whose version might also be outdated.
- Although uploading multiple modules (last table column) should be supported by the plug-in, it did not actually work in my tests. This bug may be fixed by the time you read this.
- Code minification can be achieved manually, by using the uflash -m command in a terminal, instead of using PyCharm’s built in Run Configurations.
- The inspection of API documentation is very limited. It will only show when you type the open bracket following a function name. You cannot access the API documentation by any other keyboard short cut.
- Code minification needs to be enabled manually in the settings dialog.
All editors offer access to the serial console, also referred to as REPL. The REPL shows printed output, but becomes interactive (s.t. you can evaluate Python code at run-time) in the following scenarios:
- A KeyboardInterrupt error was raised → you can use this to manually set a “break point” that halts all execution and then allows you to inspect the values of different variables
- The end of the program has been reached
- Explicit invoke from your IDE
Note: in case you don’t see any output of your print() statements after you opened the REPL, you may have to press CTRL+D (while the REPL has keyboard focus) to show the output. This soft-resets the micro:bit, restarting your program.
Firmware term confusion
The firmware you can download from here is used to update the DAPLink software. This software runs on a separate chip and provides the USB mass-storage controller, the console / serial device (to read things you print() in your code on your computer) and a HID device that can be used by debuggers like GDB. See here for more details.
This firmware has nothing to do with the software you run on the micro:bit, or the underlying MicroPython run-time version (in case you use MicroPython). The MicroPython run-time is embedded in the code editor you use, which generates the hex files to be flashed to the micro:bit.
The DAPLink firmware only needs to be updated when certain aspects of the USB connection require you to do so. For instance, the WebUSB functionality of the Chrome browser requires a newer DAPLink firmware. WebUSB allows you to flash MicroPython or TypeScript code from the web editor to the device directly.
Measure the speed impact
For timing-critical applications, make sure you measure how long computations (code paths) take. Don’t blindly believe the documentation. For instance, in TypeScript, basic.pause(1) does not take 1 ms, but of sometimes more than 5 ms. You can use control.waitMicros() instead, but note that it may sleep shorter than the duration you specified!
Developing for the micro:bit is easy, thanks to mature web-based IDEs that require no installation, supporting programming languages like TypeScript or MicroPython. Your software development skills will transfer quickly to these modern languages, making development a breeze – compared to how things were done just a decade ago, writing software in C for an Arduino board. If you’re not sure whether to invest, try out the MakeCode editor first, and simulate sensor values and buttons.