Skip to content
Snippets Groups Projects
Commit 75ae3534 authored by rahix's avatar rahix
Browse files

Add pycardium modules

parent bbd7e555
No related branches found
No related tags found
No related merge requests found
---
title: Pycardium Modules Development
---
Pycardium is our micropython port. You can find it [here](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium).
This page gives an overview on adding new C modules to pycardium. These modules are mostly meant to make use of the [Epicardium API](./epicardium_api_development) to enable python scripts to interact with card10.
## Basic Structure
#### Module Source
Modules live in [`pycardium/modules`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/pycardium/modules). To add a new module, create a file in there containing the following example content. This module is named `example` but you should of course replace that with your own name:
```c
#include "py/obj.h"
/* Define a function in this module */
static mp_obj_t mp_example_hello(mp_obj_t number)
{
int num = mp_obj_get_int(number);
printf("Hello from example: %d\n", num);
return mp_const_none;
}
/* Create an object for this function */
static MP_DEFINE_CONST_FUN_OBJ_1(example_hello_obj, mp_example_hello);
/* The globals table for this module */
static const mp_rom_map_elem_t example_module_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example)},
{MP_ROM_QSTR(MP_QSTR_hello), MP_ROM_PTR(&example_hello_obj)},
};
static MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
const mp_obj_module_t example_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t*)&example_module_globals,
};
/* This is a special macro that will make MicroPython aware of this module */
MP_REGISTER_MODULE(MP_QSTR_example, example_module, MODULE_EXAMPLE_ENABLED);
```
#### Build System
Next, you need to add your module to [`pycardium/meson.build`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/meson.build). There is a list of module sources at the very top called `modsrc` where you need to add your c file (`modules/example.c`).
#### QSTR Definitions
If you now run `ninja -C build/`, you will hit a few errors regarding missing QSTR definitions. With the example module, those will look similar to
```text
../pycardium/modules/example.c:15:46: error: 'MP_QSTR_example' undeclared here (not in a function)
15 | {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_example)},
```
To fix those, you need to add all of the missing QSTRs to [`pycardium/modules/qstrdefs.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/modules/qstrdefs.h). Add a section for your module with all the necessary definitions (ideally sorted):
```c
/* example */
Q(example)
Q(hello)
```
Each of these `Q(...)` will expand to a define for `MP_QSTR_...`. So `Q(example)` corresponds to `MP_QSTR_example`.
What are QSTRs? Take a look at the [QSTR](#qstr) section of this page.
#### Enable Module
Last step is to enable your module in the MicroPython config header [`pycardium/mpconfigport.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/mpconfigport.h). Search for the list of existing modules and add yours in there as well. The line should look similar to this:
```c
#define MODULE_EXAMPLE_ENABLED (1)
```
## Wrapping Epicardium API
Most modules will probably make use of the Epicardium API. Doing so does not require any extra work apart from adding
```c
#include "epicardium.h"
static mp_obj_t mp_example_hello(mp_obj_t number)
{
char buf[80];
snprintf(buf, "Hello from example: %d\n", num);
/* Call an Epicardium API function */
epic_uart_write_str(buf, strlen(buf));
return mp_const_none;
}
```
## QSTR
QSTRs are so called "interned strings". This means they are not allocated like normal python objects but instead live in flash and are indexed. This allow MicroPython to very efficiently use them as identifiers. According to them, comparing two QSTR is as fast as comparing integers.
Unfortunately, the way these QSTRs are collected from the source files is quite weird. MicroPython comes with a few python scripts (namely [`makeqstrdefs.py`](https://github.com/micropython/micropython/blob/master/py/makeqstrdefs.py) and [`makeqstrdata.py`](https://github.com/micropython/micropython/blob/master/py/makeqstrdata.py)) that parse the C source files and search for uses of `MP_QSTR_*`. These are then sorted and indexed into a header file called `qstrdefs.collected.h`. This is closely tied in with their Makefiles.
As we use our own build system, we had to somehow wrap this generation to work for us as well. This is done using a few scripts in [`lib/micropython`](https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/lib/micropython).
Currently, our build system does **not** parse the module sources to search for QSTRs. Instead all QSTRs needed by modules need to be defined in the header [`pycardium/modules/qstrdefs.h`](https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/pycardium/modules/qstrdefs.h).
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment