not logged in | [Login]

Brain barf

This section to be deleted/reformatted/made politically correct at a later date

  • I will not be teaching C or Python - that is completely off-topic and I ask that questions regarding C or Python questions not specific to micropython be taken elsewhere.
  • Basic knowledge of Make will probably help you out as well

Hello, world!


Creating a pyboard module is a complicated task all by itself and I don't want to try and learn both C -> pyboard at the same time as ST's CAN hardware all while struggling to push my compiled code to the board (dfu-util is giving me trouble at the moment). Because of all this complexity, I will start by simply creating a "Hello, world!" pyboard module within the stmhal implementation. I'm a Saab fan, so my module consists of the 6 different Saabs that I've owned and allows you to create more on-the-fly.


  • Any content within angle brackets (that's the < and >) is a variable of your choosing
  • The path <root>/ refers to the top level directory of micropython. I am referring to the directory with in it.

Getting started

  1. First, start by downloading the source code, reading the README, and familiarizing yourself with how to compile from source.
  2. Next you'll need to create two files - <root>/stmhal/<name>.c and <root>/stmhal/<name>.h. Compiling these into the executable is as simple as adding one line below line 112 of <root>/stmhal/Makefile. The new line should simply read <name>.c \.
  3. From here on out, I will replace <name> with saab or form of that with different capitalization depending on the use. Any time you see saab, you should replace it with the name of your module and whatever capitalization you see fit.

Writing Your First Lines of Code

  1. We start by declaring a base object with which we can build our new type: const mp_obj_type_t pyb_saab_type = {...}; See line 292 of the LED module for reference. This variable contains lots of metadata about our object: its name, a function that can be used for printing our object, a function to create a new instance and a dictionary with the names of each public method. Lots more metadata can be filled in - just take a look at the definition of mp_obj_type_t.

  2. The first line of this declaration is {&mp_type_type }. So, at a minimum, we have const mp_obj_type_t pyb_saab_type = { {&mp_type_type } };. This is essentially inheritance - giving a base type to our class. The output of Python's type function will depend on what we give this first field. I think it's a bit confusing and not very important for continuing on, so just type that line and move forward with life. :)

  3. Next we give our object a name - the name that will be used via Python. Micropython uses Q-Strings throughout, which simply means we can drop the name of our object in <root>/stmhal/qstrdefsport.h in the same manner as numerous LED inputs (line 105). Saab's name reads Q(SAAB). Whatever is in the parentheses is important (case matters). Back in saab.c, we're going to add a line to our original code snippet so that it now reads:

    const mp_obj_type_t pyb_saab_type = {
        {&mp_type_type },
        .name = MP_QSTR_SAAB

    Take special note of the fact that SAAB has the same capitalization in both places (Q(SAAB) and MP_QSTR_SAAB).

    • TODO: Confirm that this can be compiled and run as is. It will make a great starting/base point.
  4. Moving forward, we can create stubs for two common methods: those would be printing an instance of our object and creating a new object. A quick modification to the code is all it takes:

    const mp_obj_type_t pyb_saab_type = {
        {&mp_type_type },
        .name = MP_QSTR_SAAB,
        .print = saab_obj_print,
        .make_new = saab_obj_make_new
    1. print: Simple enough - this prints our object. The two lines of the method are relatively self-explanatory - a typical Pythonic type print statement for a class. We might come back and modify this later to give some specific information for this instance of the SAAB.
      void saab_obj_print (void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
      pyb_saab_obj_t *self = self_in;
      print(env, "");
      The signature for this method is... well... "special" I'd say. I don't understand it in the least - but it's easy enough to copy/paste for your own purposes.
    2. new: For the most basic of classes with no variables, it's quite simple:
      STATIC mp_obj_t saab_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
      pyb_saab_obj_t *self = m_new_obj(pyb_saab_obj_t);
      self->base.type = type_in;
      return self;
      We'll add some parameters to it later, but for now, this will get us started with a dynamically allocated instance of the Saab class.