Offline development in micro:bit: Adding a new module in micropython

I recently experimented with adding a new module into micropyton for micro:bit.

Micro:bit

Micro:bit is a great little device, distributed free to all year 7s  across the UK. Here is a picture of how micro:bits look.

More information about them, and also lots of resources, can be found in the Micro:bit Foundation site: https://microbit.org.

There are several web editors,  PXT and micro python for programming micro:bits, and they are absolutely great. But, it is also possible to do offline development!

So, I decided to try out adding a simple module into micro python.

For that, it is necessary to do the preparations for offline development.

Preparation for offline development

For offline development, you would need to install

  • Git
  • Yotta
into your computer.

Information about installing Git is here: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

For Yotta installation, either of the following pages are helpful - though, University of Lancaster page has a complete section for Mac users:
If you follow the University of Lancaster pages, you will come to a stage where you can flash your micro:bit with a sample code. But, since this blog post is about micropython, it is best to clone the micro python for  micro:bit from
https://github.com/bbcmicrobit/micropython
and follow the instructions here to flash the firmware: 

The easiest way to build micropython is to run “make” and flash the resulting “firmware.hex” file under the “build" directory.

When you try making in the micropython directory, you may encounter the following error:

/../../../arm-none-eabi-gcc/5-2016-q3-update/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld: region RAM overflowed with stack

To fix it, I reduced the size of heap in source/microbit/mprun.c
This was the lazy solution for me - others are discussed in the github issue page. 

I found the REPL (Read-Eval-Print Loop) useful to send to micro:bit commands via serial.
For instance, you can use screen command:

screen  /dev/tty.usbmodemxxyy 115200

It is also possible to use the REPL through mu editor: https://github.com/mu-editor/mu

Adding a module

Now, to add a new module to the source, the following site is useful:

However, this site is too general for micro:bit. A good way is to inspect the example modules in the source/microbit directory.  The modlove.cpp and modantigravity.cpp are good examples. 

The first step would be to create your own .cpp file in a similar fashion to these examples. 
Looking at what is common across the example modules and the link above, it more or less becomes clear what a module source file should contain. 
A shortened version of modlove.cpp looks like this.


extern "C" {

#include "microbit/modmicrobit.h"
#include "py/mphal.h"
#include "microbitimage.h"
#include "microbitdisplay.h"

static const mp_float_t bright[7] = { 
    0.0, 1.0/9, 2.0/9, 4.0/9, 6.0/9, 7.0/9, 1.0,
};

void love(int interval = 25 /* ms */) {
   //code for the love function
}

STATIC mp_obj_t love_badaboom(void) {
    // make
    love();
    // ! war
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(love___init___obj, love_badaboom);
MP_DEFINE_CONST_FUN_OBJ_0(love_badaboom_obj, love_badaboom);

STATIC const mp_map_elem_t love_module_globals_table[] = {
    { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_love) },
    { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&love___init___obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_badaboom), (mp_obj_t)&love_badaboom_obj },
};

STATIC MP_DEFINE_CONST_DICT(love_module_globals, love_module_globals_table);

const mp_obj_module_t love_module = {
    .base = { &mp_type_module },
    .name = MP_QSTR_love,
    .globals = (mp_obj_dict_t*)&love_module_globals,
};

}


The micro python link I shared above is useful to understand the meaning of the STATIC variables, and the sections title Adding a Function, and  Function Arguments will be useful to clarify the syntax. 

For my module I did the following:
  • I paid attention to what #include files I need. I also created a modradio.h to be able to call radio functions in my module. These are originally implemented in modradio.cpp. I placed this new header file under under inc/microbit/.
  • I defined a function like “love” and a separate function like “badaboom", that calls my function. 
  • I  defined the necessary CONST_FUN_OBJ_x statements. In my case, the x=2, because my function had 2 parameters. 
  • I defined the module_globals_table similar to the example. 
  • I included the STATIC MP_DEFINE_CONST_DICT statement - changed according to my module variable names.
  • I included the mp_obj_module_t const variable  and changed again according to  my module variable names. 
Next, I added the following statements to  qstrdefsport.h under inc/microbit/. 

Q(modulename)
Q(functionname)

Note that for modlove.cpp, modulename=love, functionname=badaboom. If you are defining more functions, Q strings for these functions would need to be added too.

Also, it is necessary to make additions to mpconfigport.h under inc/microbit/.  These additions are best put under the comment "extra builtin modules to add to the list of known ones


extern const struct _mp_obj_module_t “modulename”;

Also added to "#define MICROPY_PORT_BUILTIN_MODULES \

{ MP_OBJ_NEW_QSTR(MP_QSTR_), (mp_obj_t)&”modulename” }, \

Note that, for the example modelove.cpp above, modulename = love_module. 

After this, the new module should build. To test, I flashed my micro:bit with the firmware, and started REPL in mu.




Maybe, this is the time where I have to say there are easier ways to import files/modules in micro:bit micro python!  If this was all too much, try https://microbit-playground.co.uk/howto/add-python-module-microbit-micropython



Comments