Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Show changes
Showing
with 2010 additions and 0 deletions
Memory Map
==========
Flash::
| Address | Usage |
|------------|--------------------|
|0x100F FFFF | |
| | Core 1: 512 kB |
|0x1008 0000 | |
|------------|--------------------|
|0x1007 FFFF | |
| | Core 0: 448 kB |
|0x1001 0000 | |
|------------|--------------------|
|0x1000 FFFF | |
| | Bootloader: 64 kB |
|0x1000 0000 | |
|------------|--------------------|
Ram::
| Address | Usage |
|------------|-----------------------|
|0x2008 BFFF | |
| | API Parameters: 48 kB |
|0x2008 0000 | |
|------------|-----------------------|
|0x2007 FFFF | |
| | Core 1: 256 kB |
|0x2004 0000 | |
|------------|-----------------------|
|0x2003 FFFF | |
| | Core 0: 256 kB |
|0x2000 0000 | |
|------------|-----------------------|
External Flash::
| Address | Usage |
|------------|-----------------------|
|0x0080 0000 | |
| | FAT file system: 8 MB |
|0x0000 0000 | |
|------------|-----------------------|
.. _firmware_overview:
Overview
========
To make the most of card10's dual-core processor, its firmware will have been
divided into two parts: The "main" firmware running on core 0 which will have
been called *Epicardium* and the "user-code" running on core 1. In most cases
this will have been *Pycardium*, our MicroPython port.
.. image:: ./static/overview.svg
Epicardium
----------
Epicardium is based on `FreeRTOS <https://www.freertos.org/>`_. There are a
number of tasks that will have been keeping card10 running. These are:
+--------------------+-------------------------------+----------+-------------------------------------------+
| Name | ID Global | Priority | Description |
+====================+===============================+==========+===========================================+
| `vPmicTask`_ | ``pmic_task_id`` (static) | +4 | Power Management (and Reset Button) |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vLifecycleTask`_ | ``lifecycle_task`` (static) | +3 | Control of the payload running on core 1. |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vBleTask`_ | ``ble_task_id`` (static) | +3 | Bluetooth Low Energy Stack |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vSerialTask`_ | ``serial_task_id`` | +3 | Serial Output via UART/CDC-ACM/BLE |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vApiDispatcher`_ | ``dispatcher_task_id`` | +2 | Epicardium API dispatcher |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vInterruptsTask`_ | ``interrupts_task`` (static) | +2 | Interrupt dispatcher worker |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vMAX30001Task`_ | ``max30001_task_id`` (static) | +1 | `MAX30001`_ ECG driver |
+--------------------+-------------------------------+----------+-------------------------------------------+
| `vBhi160Task`_ | ``bhi160_task_id`` (static) | +1 | `BHI160`_ sensor fusion driver |
+--------------------+-------------------------------+----------+-------------------------------------------+
.. _vPmicTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/pmic.c#L281
.. _vLifecycleTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/lifecycle.c#L361
.. _vBleTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/ble/ble.c#L237
.. _vSerialTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/serial.c#L289
.. _vApiDispatcher: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/dispatcher.c#L25
.. _vInterruptsTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/interrupts.c#L119
.. _vLedTask: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/personal_state.c#L58
.. _vMAX30001Task: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/max30001.c#L378
.. _vBhi160Task: https://git.card10.badge.events.ccc.de/card10/firmware/blob/master/epicardium/modules/bhi.c#L419
.. _MAX30001: https://www.maximintegrated.com/en/products/analog/data-converters/analog-front-end-ics/MAX30001.html
.. _BHI160: https://www.bosch-sensortec.com/bst/products/all_products/bhi160
Epicardium API
--------------
Epicardium exposes lots of functionality via the *Epicardium API*. The
technical details of this API can be found in this :ref:`overview
<epicardium_api_overview>`. If you are interested in adding new API calls,
you should probably read the :ref:`epicardium_api_guide` guide.
Pycardium
---------
Pycardium is our MicroPython fork. Its purpose is to make it as easy as
possible to interact with card10. If you are interested in working on
Pycardium, take a look at the :ref:`pycardium_guide` guide.
.. _l0dables:
l0dables
--------
Next to Pycardium, other bare-metal code can also run on core 1. For example,
a `Rustcardium`_ or C-cardium. These l0dables must be compiled using our special
linker script and should link against the api-caller library so they can
interface with the :ref:`epicardium_api`.
Note: this feature is disabled by default and has to be enabled in :ref:`card10_cfg`. A :ref:`card10_cfg` file dropped into the :ref:`usb_file_transfer` of the badge containing ``execute_elf = true`` is enough.
l0dables are currently built within the source tree of the main repository. See |l0dables_blinky|_ for an example of a hello-world-like program. Within those programs, you can access the :ref:`epicardium_api` to control the hardware and behaviour of the badge.
Once you have a built ELF file, you can drop it into the FAT filesystem of the flash (eg. via :ref:`usb_file_transfer`) and it will be available from the menu program of the badge.
.. _Rustcardium: https://git.card10.badge.events.ccc.de/astro/rust-card10
.. |l0dables_blinky| replace:: ``l0dables/blinky``
.. _l0dables_blinky: https://git.card10.badge.events.ccc.de/card10/firmware/-/tree/master/l0dables
Program Flow Diagram
--------------------
The following diagram is a rough overview of the program flow in this fimware:
.. image:: ./static/firmware-flow.svg
.. _pycardium_guide:
Pycardium Module Development
============================
This is a step-by-step guide on augmenting Pycardium with new modules. These
module are either written in Python or in C:
* **Python Modules**: Python modules live in ``pycardium/modules/py`` and are
pre-compiled and packed into Pycardium at build-time. Python modules are
meant to be used for creating abstractions and helpers based on lower level
functionality. The rationale is that it is easier to write pythonic modules
in python instead of having to wrap all that in C code. Though care has to
be taken as these modules can eat quite a lot of space.
* **C Modules**: C modules allow MicroPython code to interface with the rest of
the system or allow a much faster implementation of performance-critical
code. For interfacing with card10, these modules are mostly meant to make use
of the :ref:`Epicardium API <epicardium_api_overview>`.
Python Modules
--------------
Adding a new Python module
~~~~~~~~~~~~~~~~~~~~~~~~~~
Add a file in ``pycardium/modules/py``. The name of the file dictates the name
of the module. Next, add you file to the list in
``pycardium/modules/py/meson.build`` to make the build-system aware of it.
Rebuild, and you should see your module pop up in the list shown by
.. code-block:: python
help("modules")
Size Consideration
~~~~~~~~~~~~~~~~~~
Python modules can get quite big. If you want to know exact sizes, take a look at
the output of
.. code-block:: shell-session
ls -lh build/pycardium/*@@frozen.c@*/
If your module contains some kind of big lookup-table or data-block, consider
pushing that into external flash. You can then read the data using standard
Python ``open()`` and if you need to decode it, use the ``ustruct`` or ``ujson``
module.
Creating a new C module
-----------------------
Module Source
~~~~~~~~~~~~~
Create a new file in ``pycardium/modules``. The basic structure of a C module
looks like this:
.. code-block:: 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\r\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 */
/* clang-format off */
MP_REGISTER_MODULE(MP_QSTR_example, example_module, MODULE_EXAMPLE_ENABLED);
.. note::
Please keep the ``/* clang-format off */``. It ensures that *clang-format*
won't touch the following line. This is necessary because MicroPython's
codegen scripts can'd deal with newlines in the ``MP_REGISTER_MODULE`` macro.
Build Integration
~~~~~~~~~~~~~~~~~
Next, you need to add your module to ``pycardium/meson.build``. There is a list
of module sources at the very top called ``modsrc`` where you need to add your
file (e.g. ``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 above, they will look similar to
this:
.. code-block:: 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 these errors, you need to add all QSTRs your module needs to
``pycardium/modules/qstrdefs.h``. Add a section for your module where you
define all QSTRs you need:
.. code-block:: cpp
/* example */
Q(example)
Q(hello)
Each ``Q(...)`` will define into a corresponding ``MP_QSTR_...``. So
``Q(example)`` corresponds to ``MP_QSTR_example``.
Enable Module
~~~~~~~~~~~~~
The last step is to actually enable inclusion of your module into the firmware.
Do this by adding a define in ``pycardium/mpconfigport.h``:
.. code-block:: cpp
#define MODULE_EXAMPLE_ENABLED (1)
The name of the define is the one from the last line in the module source above.
Wrapping Epicardium API
-----------------------
Most modules will probably make use of the :ref:`Epicardium API
<epicardium_api_overview>`. Doing so does not require any extra work, you can
just call the API from your module code. You should check the input that your
module got from MicroPython before sending data off to Epicardium. For
example, raise a ``ValueError`` if an integer is too big to fit into the type
specified by the API. You should also gracefully handle errors returned by API
calls. As most API calls use *errno* codes, you can just wrap them in an
``OSError``:
.. code-block:: cpp
int ret = epic_bma_get_accel(&values);
if (ret < 0) {
mp_raise_OSError(-ret);
}
QSTRs
-----
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`_
and `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`_.
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``.
.. _makeqstrdefs.py: https://github.com/micropython/micropython/blob/master/py/makeqstrdefs.py
.. _makeqstrdata.py: https://github.com/micropython/micropython/blob/master/py/makeqstrdata.py
.. _lib/micropython: https://git.card10.badge.events.ccc.de/card10/firmware/tree/master/lib/micropython
Functions for MicroPython
-------------------------
As shown in the example above, you can create functions that can be called from
MicroPython code. These functions always have one of the following signatures.
To create a MicroPython object for a function, you need the macro call shown
after each signature. Please place these calls directly after the function
body as shown above.
.. code-block:: cpp
/* Function with 0 arguments */
mp_obj_t mp_example_fun0(void);
static MP_DEFINE_CONST_FUN_OBJ_0(example_fun0_obj, mp_example_fun0);
/* Function with 1 argument */
mp_obj_t mp_example_fun1(mp_obj_t arg0_in);
static MP_DEFINE_CONST_FUN_OBJ_1(example_fun1_obj, mp_example_fun0);
/* Function with 2 arguments */
mp_obj_t mp_example_fun2(mp_obj_t arg0_in, mp_obj_t arg1_in);
static MP_DEFINE_CONST_FUN_OBJ_2(example_fun2_obj, mp_example_fun0);
/* Function with 3 arguments */
mp_obj_t mp_example_fun3(mp_obj_t arg0_in, mp_obj_t arg1_in, mp_obj_t arg2_in);
static MP_DEFINE_CONST_FUN_OBJ_3(example_fun3_obj, mp_example_fun0);
/* Function with 4 or more arguments */
mp_obj_t mp_example_fun4(size_t n_args, mp_obj_t *args);
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(example_fun4_obj, 4, 4, mp_example_fun4);
For functions with 4 or more arguments, you need to use the
``MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN`` macro and instead of directly accessing
the arguments, you get an array. The macro gets two numbers (they are the same
in the example above): The minimum and maximum number of arguments.
MicroPython Objects
-------------------
**TL;DR**: Look at |obj.h|_. It contains most functions needed to create,
access, and modify MicroPython objects.
.. |obj.h| replace:: ``lib/micropython/micropython/py/obj.h``
.. _obj.h: https://github.com/micropython/micropython/blob/master/py/obj.h
For C modules to interface with MicroPython, you need to be able to interface
with MicroPython objects. The generic type of an object is ``mp_obj_t``. As
you can see in the example above, this is also what a function gets as
arguments and returns back to MicroPython. You can cast ``mp_obj_t`` to
concrete object types if you made sure it is actually the correct type.
Booleans
~~~~~~~~
``True`` and ``False`` are *const* objects, called ``mp_const_true`` and
``mp_const_false`` respectively. A usual equality check can be used:
.. code-block:: cpp
#include "py/obj.h"
#include "py/runtime.h"
if (bool_obj == mp_const_true) {
/* is a boolean true */
} else if (bool_obj == mp_const_false) {
/* is a boolean false */
} else {
mp_raise_TypeError("arg 0 is not a boolean");
}
Integers
~~~~~~~~
As long as your integers stay within **31**-bit limits, integers are stored very
efficiently and can be accessed and created like this:
.. code-block:: cpp
#include "py/obj.h"
/* Create a new integer which is < 2^31 */
mp_obj_t int_obj = MP_OBJ_NEW_SMALL_INT(0xc0ffee);
/* Check if an integer is small and if so, extract it */
if (mp_obj_is_small_int(int_obj)) {
int int_value = MP_OBJ_SMALL_INT_VALUE(int_obj);
}
For bigger integers or if you are uncertain about the limits, use the following
functions:
.. code-block:: cpp
#include "py/obj.h"
/* Create new integer objects in various sizes and signedness */
mp_obj_t int0_obj = mp_obj_new_int((mp_int_t)value);
mp_obj_t int1_obj = mp_obj_new_int_from_uint((mp_uint_t)value);
mp_obj_t int2_obj = mp_obj_new_int_from_ll((long long)value);
mp_obj_t int3_obj = mp_obj_new_int_from_ull((unsigned long long)value);
/* Check if a value is an integer */
if (mp_obj_is_integer(int_obj)) {
/* Get an integer */
int int0 = mp_obj_get_int(int_obj);
int int0 = mp_obj_get_int_truncated(int_obj);
}
int value;
if (!mp_obj_get_int_maybe(int_obj, &value)) {
/* Not an integer! */
}
To get really big integers you have to use
.. code-block:: cpp
#include "py/obj.h"
#include "py/objint.h"
long long value;
mp_obj_int_to_bytes_impl(int_obj, 8, (byte*)&value);
Strings
~~~~~~~
As in CPython, MicroPython also has multiple string representations: There is
``str``, ``bytes``, and ``bytearray`` but also the above mentions ``QSTR``\ s.
Ideally, code should work with as many of these as possible and to ensure this,
please use these generic functions:
.. code-block:: cpp
#include "py/obj.h"
/* Create a new string object */
char buf[] = "Hello MicroPython!";
mp_obj_t str_obj = mp_obj_new_str(buf, sizeof(buf));
/* Check if an object is a string */
if (mp_obj_is_str(str_obj)) {
/* Either str or QSTR */
}
if (mp_obj_is_str_or_bytes) {
/* Either str, QSTR, or bytes */
}
/*
* Get a char array from a string object.
* CAUTION! This string is not necessarily null terminated!
*/
char *str_data;
size_t str_len;
str_data = mp_obj_str_get_data(str_obj, &str_len);
Lists & Tuples
~~~~~~~~~~~~~~
While lists and tuples can be accessed using type specific functions, you
should refrain from doing so: Use generic indexing functions instead, as they
also allow types derived from lists or tuples or types with custom indexing
implementations (i.e. duck-typing).
.. code-block:: cpp
#include "py/obj.h"
#include "py/runtime.h"
/* Get the length of a list-like object */
mp_int_t len = mp_obj_get_int(mp_obj_len(list_obj));
/* Get element at a specific index */
mp_obj_t elem5 = mp_obj_subscr(
list_obj,
MP_OBJ_NEW_SMALL_INT(5),
MP_OBJ_SENTINEL
);
To create a list or tuple:
.. code-block:: cpp
mp_obj_t values[] = {
MP_OBJ_NEW_SMALL_INT(0xde),
MP_OBJ_NEW_SMALL_INT(0xad),
MP_OBJ_NEW_SMALL_INT(0xbe),
MP_OBJ_NEW_SMALL_INT(0xef),
};
/* Create a tuple */
mp_obj_t tuple = mp_obj_new_tuple(4, values);
/* Create a list */
mp_obj_t list = mp_obj_new_list(4, values);
.. py:module:: bhi160
``bhi160`` - Sensor Fusion
==========================
.. versionadded:: 1.4
Supports the BHI160 sensor on the card10 for accelerometer, gyroscope,
magnetometer and orientation.
The coordinate system of the BHI160 sensor data looks like this:
.. image:: ../static/bhi160-coordinates.png
* The **accelerometer** axes are just the ones shown in the picture.
* The **gyroscope**'s angular velocities rotate counter clockwise around
their respective axis.
* The **orientation** sensor uses the following mapping:
+---------------------+----------------------+-------------------+
| X | Y | Z |
+=====================+======================+===================+
| Azimuth (0° - 360°) | Pitch (-180° - 180°) | Roll (-90° - 90°) |
+---------------------+----------------------+-------------------+
**Example**:
.. code-block:: python
import bhi160
import time
bhi = bhi160.BHI160Orientation()
while True:
samples = bhi.read()
if len(samples) == 0:
time.sleep(0.25)
continue
# print the latest sample
print(samples[-1])
time.sleep(0.25)
Orientation
-----------
.. autoclass:: bhi160.BHI160Orientation
:members:
:inherited-members:
Accelerometer
-------------
.. autoclass:: bhi160.BHI160Accelerometer
:members:
:inherited-members:
Gyroscope
---------
.. autoclass:: bhi160.BHI160Gyroscope
:members:
:inherited-members:
Magnetometer
------------
.. autoclass:: bhi160.BHI160Magnetometer
:members:
:inherited-members:
``ble_hid`` - BLE HID
=====================
The ``ble_hid`` module provides access to the BLE Human Interface Device functionality.
.. note::
Make sure to enable the BLE HID functionality in :ref:`card10_cfg` and reboot your card10
if you want to use BLE HID.
Also make sure that the ``adafruit_hid`` folder from the card10 release archive is placed
on the file system of your card10.
.. warning::
At least Ubuntu Linux will keep auto connecting to BLE HID devices once they are
paired to the host computer. If you want to connect your card10 to a phone again,
you might have to temporarily turn off Bluetooth on your computer.
An example application can be found in the preload directory (named ``HID Demo``). It provides
examples how to use the card10 as keyboard, mouse or volume control.
Please refer to the Adafruit CircuitPython HID library for examples how to use the HID service.
The card10 implements the same HID descriptors as the Adafruit CircuitPython BLE library and
should be compatible with all uses of the Adafruit CircuitPython HID library.
**Example emulating a keyboard**:
Adapted from https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/CPB_Keybutton_BLE/cpb_keybutton_ble.py
A more complete version of this example can be found in the HID Demo app on your card10.
.. code-block:: python
import ble_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
k = Keyboard(ble_hid.devices)
kl = KeyboardLayoutUS(k)
k.send(Keycode.BACKSPACE)
# use keyboard_layout for words
kl.write("Demo with a long text to show how fast a card10 can type!")
# add shift modifier
k.send(Keycode.SHIFT, Keycode.F19)
**Example emulating a mouse**:
.. code-block:: python
import ble_hid
import bhi160
import buttons
from adafruit_hid.mouse import Mouse
m = Mouse(ble_hid.devices)
def send_report(samples):
if len(samples) > 0:
x = -int(samples[0].z)
y = -int(samples[0].y)
m.move(x, y)
sensor = bhi160.BHI160Orientation(sample_rate=10, callback=send_report)
b_old = buttons.read()
while True:
b_new = buttons.read()
if not b_old == b_new:
print(b_new)
b_old = b_new
if b_new == buttons.TOP_RIGHT:
m.click(Mouse.MIDDLE_BUTTON)
elif b_new == buttons.BOTTOM_RIGHT:
m.click(Mouse.RIGHT_BUTTON)
elif b_new == buttons.BOTTOM_LEFT:
m.click(Mouse.LEFT_BUTTON)
.. note::
Make sure to catch ``OSError`` exceptions in real applications. The exception will be thrown if
there is connection to the host (or if it is lost) and you want to send an event.
.. automodule:: ble_hid
:members:
.. py:module:: bme680
``bme680`` - Environmental Sensor
=================================
Allows access to environmental data of card10's surroundings.
If ``bsec_enable`` is set in :ref:`card10_cfg`, the proprietary Bosch BSEC
library will be activated which offers the following extra functionality:
- Manual temperature offset compensation
The ``bsec_offset`` configuration allows to configure a static temperature
offset. Please use a reference thermometer to determine the offset of your
card10. If no offset is provided a default offset for a card10 which is
connected to USB, has BLE active and is without a case is used.
- A fixed measurement interval of 3 seconds
This helps to stabilize the temperature of the card10.
- An indoor air quality (IAQ) and equivalent CO2 estimation algorithm
Please refer to the :ref:`bsec_api` API documentation to get more information
about how to interpret these estimates.
.. note::
Please keep in mind that the BME680 can not directly measure CO2. It measures
Volatile Organic Compounds (VOCs). The BSEC library uses this measurement
to compute an Indoor Air Quality (IAQ) indication. It also assumes that all VOCs
in the air are from human breath and computes an equivalent CO2 (eCO2)
value from this. Please be aware of these facts when judging the accuracy
of the IAQ and eCO2 values. Some more information can be found in the
:ref:`bsec_api` API documentation.
.. warning::
For the BSEC library to properly work the card10 should be kept running
for at least 10 hours at least once. This is needed as the BSEC library
periodically writes calibration information about the sensor to the
card10's file system.
Please make sure to observe the IAQ accuracy field. It will tell you if the
IAQ and eCO2 measurements are deemed "accurate" by the BSEC library. Your
application should either inform the user about the current accuracy (e.g.
by color coding) or simply not show any values if the accuracy is below 2.
.. note::
See also the BLE :ref:`ESS`.
**Example**:
.. code-block:: python
import bme680, time
with bme680.Bme680() as environment:
while True:
d = environment.get_data()
print("Temperature: {:10.2f} °C".format(d.temperature))
print("Humidity: {:10.2f} % r.h.".format(d.humidity))
print("Pressure: {:10.2f} hPa".format(d.pressure))
print("Gas Resistance: {:10.2f} Ω".format(d.resistance))
time.sleep(1)
You can use the return type of :py:meth:`~bme680.Bme680.get_data` to decide
if you want to use/display the additonal fields returned if BSEC is enabled.
.. code-block:: python
if isinstance(d, bme680.BSECData):
print("Air quality: {:7d}".format(d.iaq))
Sensor Class
------------
.. autoclass:: bme680.Bme680
:members:
Deprecated Interface
--------------------
The following functions should no longer be used directly. The only exist for
compatibility as they were the old BME680 interface in previous firmware
versions.
.. py:function:: init()
Initialize the sensor.
Before being able to read data from the sensor, you have to call
:py:func:`bme680.init`.
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:function:: get_data()
Perform a single measurement of environmental data.
:return: Tuple containing ``temperature`` (°C), ``humidity`` (% r.h.),
``pressure`` (hPa) and ``gas resistance`` (Ohm).
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:function:: deinit()
Deinitialize the sensor.
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:module:: buttons
``buttons`` - Push Buttons
==========================
The :py:mod:`buttons` module allows you to use card10's push buttons as input
in your scripts.
**Example**:
.. code-block:: python
import buttons
print("Press bottom left or right button:")
while True:
pressed = buttons.read(
buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
)
if pressed != 0:
break
if pressed & buttons.BOTTOM_LEFT != 0:
print("Left button pressed!")
if pressed & buttons.BOTTOM_RIGHT != 0:
print("Right button pressed!")
.. py:function:: read(mask)
Read button status.
:param int mask: Mask of buttons to check. Create the mask by ORing
:py:data:`buttons.BOTTOM_LEFT`, :py:data:`buttons.BOTTOM_RIGHT`,
:py:data:`buttons.TOP_RIGHT`, and :py:data:`buttons.TOP_LEFT` (=
:py:data:`buttons.RESET`).
:returns: An integer with the bits for pressed buttons set. Use the same
costants as for the mask to check which buttons were pressed.
.. py:data:: BOTTOM_LEFT
Bottom left button.
.. py:data:: BOTTOM_RIGHT
Bottom right button.
.. py:data:: TOP_RIGHT
Top right button.
.. py:data:: TOP_LEFT
Top left button (Reset button).
.. py:data:: RESET
Top left button (Reset button).
This diff is collapsed.
``config`` - Configuration
==========================
The ``config`` module provides functions to interact with card10's
configuration file (:ref:`card10_cfg`).
.. automodule:: config
:members:
``display`` - Display
=====================
The display module let's you draw on the card10's display.
Pixels are addressed from top left to bottom right with a range of x: 0 to 159 and y: 0 to 79.
.. code-block:: text
0,0
+---------------------+
| |
| |
| |
| |
+---------------------+
159,79
Drawing operations are clipped, pixels outside of the screen will be ignored.
.. automodule:: display
:members:
.. py:module:: gpio
``gpio`` - GPIO Pins
==========================
The :py:mod:`gpio` module allows you to use card10's GPIO pins as input and
output in your scripts.
**Example**:
.. code-block:: python
import gpio
gpio.set_mode(gpio.WRISTBAND_1, gpio.mode.OUTPUT)
gpio.write(gpio.WRISTBAND_1, True)
gpio.set_mode(gpio.WRISTBAND_2, gpio.mode.INPUT | gpio.mode.PULL_UP)
state = gpio.read(gpio.WRISTBAND_2)
print("State of Wristband pin 2:", state)
.. py:function:: set_mode(pin, mode)
Configure GPIO pin state.
:param int pin: ID of the pin to be configured.
:param int mode: An integer with the bits for the wanted mode set. Create your
integer by ORing :py:data:`gpio.mode.OUTPUT`, :py:data:`gpio.mode.INPUT`,
:py:data:`gpio.mode.ADC`, :py:data:`gpio.mode.PULL_UP`,
:py:data:`gpio.mode.PULL_DOWN`.
.. note:: On WRISTBAND_3, there is no ADC functionality available
.. py:function:: get_mode(pin)
Get GPIO pin state.
:param int pin: ID of the pin of to get the mode of.
:returns: An integer with the configure mode bits set.
.. py:function:: write(pin, value)
Write a value to a GPIO pin.
:param int pin: ID of the pin of to get the mode of.
:param bool value: New pin value.
.. py:function:: read(pin)
Read GPIO pin value.
:param int pin: ID of the pin of to get the mode of.
:returns: Current value of the GPIO pin.
If the pin is configured as ADC, the value returned
will be between 0 and 1000, representing voltages from
0V to 3.3V (:py:data:`gpio.ADC` is only available in 1.9+).
.. py:data:: WRISTBAND_1
Pin ID for Wristband GPIO 1.
.. py:data:: WRISTBAND_2
Pin ID for Wristband GPIO 2.
.. py:data:: WRISTBAND_3
Pin ID for Wristband GPIO 3.
.. py:data:: WRISTBAND_4
Pin ID for Wristband GPIO 4.
.. py:module:: gpio.mode
.. py:data:: OUTPUT
Configures a pin as output.
.. py:data:: INPUT
Configures a pin as input.
.. py:data:: ADC
Configure pin as ADC input.
.. versionadded: 1.9
.. py:data:: PULL_UP
Enables the internal pull-up resistor of a pin.
.. py:data:: PULL_DOWN
Enables the internal pull-down resistor of a pin.
``leds`` - LEDs
===============
The ``leds`` module provides functions to interact with card10's RGB LEDs.
This is the 11 LEDs above the display and 4 LEDs on the underside of the
top-board, in the four corners.
.. automodule:: leds
:members:
``ledfx`` - LED Effects
=======================
.. automodule:: ledfx
:members:
.. py:module:: light_sensor
``light_sensor`` - Ambient Brightness
=====================================
On the harmonic board, there is an IR-LED which can be used in reverse as a
crude brightness sensor. Values returned are in no particular unit but seem to
be fairly stable.
.. py:function:: start()
Turn on the ADC and start reading brightness values. (In past this function must be
called before any measurements can be taken.)
.. py:function:: get_reading()
Get an ambient brightness reading. The returned value is in no particular
unit, though it seems to be fairly stable. The value could be between 0 and 400. Common values:
- ~8: Very dark are
- ~17: Typical hackerspace brightness
- >200: Direct sunlight
:returns: A brightness reading in no particular unit
.. py:function:: stop()
Stop the ADC.
.. py:function:: read()
Direct readout of the light-sensor.
Use this function for low latency readout. The time between polls can have
an effect on the values measures. If you do not need low latency, prefer
:py:func:`light_sensor.get_reading`.
.. versionadded:: 1.8
``max30001`` - MAX30001
=======================
.. automodule:: max30001
:members:
``max86150`` - MAX86150
=======================
.. automodule:: max86150
:members:
.. py:module:: os
``os`` - OS Functions
=====================
The ``os`` module allows access to a few core functionalities of Epicardium and
functions found in CPythons ``os`` module.
CPython-Like
------------
.. py:function:: listdir(dir)
List contents of a directory.
:param str dir: Path to the directory to list.
:returns: A list of entities (files or subdirectories) in the directory
``dir``.
.. py:function:: mkdir(path)
Create a directory named *path*.
:param str path: Path to the directory to create. Only the last component
of this path will be created.
.. py:function:: rename(src, dst)
Rename the file or directory *src* to *dst*. If *dst* exists, the operation
will fail.
:param str src: Path to source file to rename.
:param str dst: Destination path to rename to. Must not exist before
calling :py:func:`os.rename`.
.. py:function:: unlink(path)
Unlink (remove) a file.
:param str path: The file to remove.
.. py:function:: urandom(n)
Return ``n`` random bytes.
.. versionadded:: 1.3
:param int n: Number of random bytes to retrieve.
:returns: ``bytes()`` object with ``n`` random bytes.
Card10-Specific
---------------
.. py:function:: exit(ret = None)
Exit from the current app and return to the menu.
:param int ret: Optional return code, same semantics as Posix (``0`` means
success).
:return: This function will never return.
.. py:function:: exec(name)
Try executing a new app, stopping the currently running one.
``name`` is the path to either a l0dable (ending in ``.elf``) or a python
script (ending in ``.py``). If the path does not lead to an executable file,
``os.exec()`` will raise an exception.
:param str name: Path to new app/script/l0dable.
:return: This function never returns. It can, however raise an exception.
.. py:function:: read_battery()
Read the current battery voltage in V. Please keep in mind that battery
voltage behaves exponentially when interpreting this value.
.. warning::
Card10 will hard-shutdown once the voltage drops below 3.4 V
.. py:function:: reset()
Reboot card10.
.. warning::
Please only call this function if absolutely necessary. In most cases
you'll want to just :py:func:`os.exit` instead.
.. py:function:: usbconfig(config_type)
Change active USB configuration. By default, card10 boots with
:py:data:`os.USB_SERIAL` active.
This will deactivate the currently active USB configuration. This means
that, if you activate :py:data:`os.USB_FLASH` while :py:data:`os.USB_SERIAL`
was active, the USB serial will be disconnected.
:param config_type: Selects which config to activate. Possible
values are :py:data:`os.USB_SERIAL`, :py:data:`os.USB_FLASH`,
or :py:data:`os.USB_NONE`.
.. versionadded:: 1.11
.. py:data:: USB_NONE
No USB device active.
.. py:data:: USB_SERIAL
CDC-ACM serial device active.
.. py:data:: USB_FLASH
Mass-Storage device active.
.. py:function:: fs_is_attached()
Check whether the filesystem is currently attached to card10 (or whether a connected
USB host is currently holding control over it and possibly writing to it).
:returns:
- ``True`` if the filesystem is attached to card10 and an app can read and
write files.
- ``False`` if the filesystem is not available to card10 because a USB
host is currently controlling it.
.. versionadded: 1.18
.. _pycardium_overview:
Overview
========
Pycardium is what we call our MicroPython port for card10. For the most part,
it will behave like a normal MicroPython, enhanced by a few modules to
interface with card10's hardware. We have also included modules from Python's
standard library so you have lots of familiar functionality at hand to start
hacking!
.. note::
Right now, MicroPython is only available via the serial console. card10
makes its console available as a CDC ACM device via USB or, if you have a
debugger, on the debugging serial port.
This means, you can only interact with Python through the REPL console for
now. However, this will soon change, once `#10`_ and `#11`_ are implemented.
.. _#10: https://git.card10.badge.events.ccc.de/card10/firmware/issues/10
.. _#11: https://git.card10.badge.events.ccc.de/card10/firmware/issues/11
In other words: It is currently not possible to execute Python code
from .py files on the filesystem. You have to test new modules using the
serial console.
Serial Console
--------------
If you have a debugger, you can just use the debuggers serial console.
Otherwise, card10 will open a serial usb-device on its usb-port. On most Linux
systems this device will be called ``/dev/ttyACM0`` or ``/dev/ttyACM1``.
Choose a terminal-emulator of your liking and open the above mentioned device.
Baud-rate is 115200. Some options are:
* **screen**: ``sudo screen /dev/ttyACM0 115200``
* **picocom**: ``sudo picocom -b 115200 /dev/ttyACM0``
After connecting, reboot reset the card10 via the power button (left upper
corner) and you should see the output of **menu.py** script (it's located in
*preload/menu.py*). You can press CTRL-C to interrupt the script and jump into
the MicroPython prompt.
To switch on the blue fairy dust you must import the led python module::
import leds
and power it on::
leds.set_rocket(0, 31)
.. note::
If you're using iOS/Mac then you can connect to your serial console using:
.. code-block:: shell-session
screen /dev/tty.usbmodem* 115200
You can now see in your console what buttons you have pressed and your
console outputs/logs. With ``CTRL+C`` you exit the console.
REPL modes
^^^^^^^^^^
MicroPython supports a different REPL modes over the serial console. The modes
can be changed on every new line.
Normal mode
"""""""""""
This is the mode you will first see. You can switch to it by pressing CTRL-B.
If you are in a other mode you can return to this mode by pressing CTRL-B too.
Paste mode
""""""""""
You can enter the paste mode by pressing CTRL-E and you can simple copy 'n'
paste your source code into the console and it will be interpreted and executed
line by line. Every new line will be reply by the prompt with **===**.
RAW mode
""""""""
The RAW mode to be intendend for the usage with tools. By pressing CTRL-A you
will enter the RAW REPL mode. The type in code will not printed. By pressing
CTRL-D the whole entered code will be evaluated and executed. The board will
reply with **OK** and print after that the output (print commands) of the code
or give you tracebacks if an error occured.
You can use **pycard10** (tools/pycard10.py) to execute python files from your
PC directly on the card10.
.. py:module:: personal_state
``personal_state`` - Personal State
===================================
The :py:mod:`personal_state` module allows you to set and get the card10 users
`personal state`_ from your script. The personal state is displayed on the
top-left LED on the bottom of the harmonics board. While the personal state is
set the LED can't be controlled by the :py:mod:`leds` module.
.. _personal state: https://card10.badge.events.ccc.de/ps/
**Example**:
.. code-block:: python
import personal_state
# Enable the "camp" state only while the app is running.
personal_state.set(personal_state.CAMP, False)
# Enable the "chaos" state and keep it after the app exits.
personal_state.set(personal_state.CHAOS, True)
# Query the currently configured state and if it's persistent.
state, persistent = personal_state.get()
# Clear the currently configured state
personal_state.clear()
.. py:function:: set(state, persistent)
Set the users personal state.
:param int state: ID of the personal state to set. Must be one of
:py:data:`personal_state.NO_CONTACT`, :py:data:`personal_state.CHAOS`,
:py:data:`personal_state.COMMUNICATION`, :py:data:`personal_state.CAMP`.
:param int persistent: Controls whether the personal state is persistent. A
persistent state is not reset when the pycardium application is changed
or restarted. In persistent mode the personal state LED is not
controllable by the pycardium application.
.. py:function:: clear()
Clears a previously set personal state.
If no personal state was set this function does nothing. It does not matter
if a set state is marked as persistent or not.
.. py:function:: get()
Get the users personal state.
:returns: A tuple containing the currently set state and a boolean
indicating if it's persistent or not.
.. py:data:: NO_STATE
State ID reported when no personal state is set.
.. py:data:: NO_CONTACT
State ID for the "No Contact" personal state.
.. py:data:: CHAOS
State ID for the "Chaos" personal state.
.. py:data:: COMMUNICATION
State ID for the "Communicatoin" personal state.
.. py:data:: CAMP
State ID for the "Camp" personal state.
``png`` - PNG Decoder
=====================
The ``png`` module provides functions to decode PNG files into raw pixel data
which can be displayed using the card10's display or its LEDs.
.. automodule:: png
:members:
.. py:module:: power
``power`` - PMIC power module handling
======================================
.. versionadded:: 1.4
The :py:mod:`power` module allows you to read the card10's power status
in your scripts.
**Example**:
.. code-block:: python
import power
print(power.read_battery_voltage())
.. py:function:: read_battery_voltage()
Read the battery voltage in V. Please keep in mind that battery
voltage behaves exponentially when interpreting this value.
.. warning::
Card10 will hard-shutdown once the voltage drops below 3.4 V
.. versionadded:: 1.4
.. py:function:: read_battery_current()
Read the battery-side current flow in A.
.. versionadded:: 1.4
.. py:function:: read_chargein_voltage()
Read the charge voltage in V.
.. versionadded:: 1.4
.. py:function:: read_chargein_current()
Read the charge current in A.
.. versionadded:: 1.4
.. py:function:: read_system_voltage()
Read the system-side voltate in V.
.. versionadded:: 1.4
.. py:function:: read_thermistor_voltage()
Read the thermistor voltage in V.
There is a resistor network from GND over a thermistor
(10K at room temperature) over 10K to the Thermistor Bias voltage.
This reads the voltage between thermistor and resistor.
.. versionadded:: 1.4