Commits (249)
......@@ -4,7 +4,7 @@ image: "derq3k/card10-build-env:20190806-195837Z-f95b541-dirty"
stage: build
- ./bootstrap.sh
- ./bootstrap.sh --werror
- ninja -C build/
- arm-none-eabi-size build/bootloader/bootloader.elf build/epicardium/epicardium.elf build/pycardium/pycardium.elf
......@@ -14,7 +14,7 @@ build:
stage: build
- ./bootstrap.sh
- ./bootstrap.sh --werror
- ninja -C build/
- arm-none-eabi-size build/bootloader/bootloader.elf build/epicardium/epicardium.elf build/pycardium/pycardium.elf
......@@ -4,13 +4,148 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
## [v1.13] - 2019-12-09 - [Mushroom]
[Mushroom]: https://card10.badge.events.ccc.de/release/card10-v1.13-Mushroom.zip
### Added
- `micropython.mem_use()` function.
- ECG plotter tool (for desktop machines) which can plot ECG logs taken with card10.
- The `input()` Python function.
- Enabled the MicroPython `framebuf` module for a Pycardium-only framebuffer
- Added the `utime.ticks_us()` and `utime.ticks_ms()` functions for very
accurate timing of MicroPython code.
- Added an option to use the right buttons for scrolling and the left one for
selecting. This will be made configurable in a future release.
- Made timezone configurable with a new `timezone` option in `card10.cfg`.
- Added a setting-menu to the ECG App.
### Changed
- Changed default timezone to CET.
- Made a few library functions callable without any parameters so they are
easier to use.
- Refactored the `card10.cfg` config parser.
### Fixed
- Fixed the Pycardium delay implementation in preparation for features like
button-interrupts. Should also be more accurate now.
- Fixed the filter which is used by the ECG app.
- Fixed the display staying off while printing the sleep-messages.
- Improved the USB-Storage mode in the menu app.
- Fixed GPIO module not properly configuring a pin if both IN and ADC are given.
- Added missing documentation for `os.mkdir()` and `os.rename()`.
- Fixed all `-Wextra` warnings, including a few bugs. Warnings exist for a reason!
### Removed
- Removed unnecessary out-of-bounds checks in display module. Drawing outside
the display is now perfectly fine and the pixels will silently be ignored.
## [v1.12] - 2019-10-19 - [Leek]
[Leek]: https://card10.badge.events.ccc.de/release/card10-v1.12-Leek.zip
### Added
- **USB Storage mode**! You can now select 'USB Storage' in the menu and
access card10's filesystem via USB. No more rebooting into bootloader!
- LED feedback on boot. If your display is broken, you can still see it doing
something now.
- `./tools/pycard10.py --set-time` to set card10's system time from your host.
- 4 new functions in `utime` modules:
* `set_time_ms()`
* `set_unix_time_ms()`
* `unix_time()`
* `unix_time_ms()`
### Changed
- Updated BLE stack
- Refactored gfx API for drawing images (internal).
- Draw partially clipped primitives in all cases (Fixes menu scrolling
- Fatal errors are now handled in a central 'panic' module.
### Fixed
- Make BLE interrupts higher priority than anything else to hopefully increase
- Turn off BLE encryption after closing a connection.
- Fixed mainline bootloader being broken.
- Fixed menu entries being ordered by path instead of name.
- Fixed menu crashing without a message.
- Fixed QSTR build-system.
## [v1.11] - 2019-09-24 - [Karotte]
[Karotte]: https://card10.badge.events.ccc.de/release/card10-v1.11-Karotte.zip
### Added
- **Support for sleep-mode instead of full power-off. This means the RTC now
retains its state!**
- For debugger users: A GDB macro `task_backtrace` which allows to view
backtraces of tasks which are currently swapped out. Use like
(gdb) task_backtrace serial_task_id
(gdb) task_backtrace dispatcher_task_id
(gdb) task_backtrace ble_task_id
- BHI160 magnetometer sensor
- ESB API in Pycardium.
- Monotonic clock API
- New FOSS font ...
### Changed
- `Display.print()` uses a transparent background when printing with `bg == fg`.
- Try different crc16 module during build because different environments might
have different ones installed.
- Improved ECG app, it can now blink on pulse and more!
- Improved BHI160 and BME680 apps.
### Fixed
- Fixed a regression which made it impossible to turn off the flashlight.
- Fixed CRT for l0dables not allowing to overwrite interrupt handlers.
- Fixed ECG App not closing the sensor on `KeyboardInterrupt`.
- Fixed a bug which made the power-button unresponsive when pressed during boot
(Interrupts were getting ignored).
- Fixed `simple_menu.Menu.exit()` not actually working.
- Added a few missing locks in `leds` module.
- Added a workaround for BHI160 axis mapping not being applied in some cases.
- Added a critical-section in BLE stack initialization to prevent weird lock-ups.
- Fixed vibra module crashing when calling `vibra.vibrate()` while already running.
- Fixed sensor-sample overflow leading to I2C bus lockup.
## [v1.10] - 2019-09-05 21:42 - [JerusalemArtichoke]
[JerusalemArtichoke]: https://card10.badge.events.ccc.de/release/card10-v1.10-JerusalemArtichoke.zip
### Added
- **ws2812**: Connect Neopixels to the wristband GPIOs and make your card10
even more colorful!
- DigiClk is now in the default prelude!
- High-pass filter and pulse detection in default ECG app.
- Actually added `uuid` module - it was not built into the firmware before,
by accident.
- `leds.get_rgb()`: Get the current color of an LED.
- `leds.get_rocket()`: Get the current brightness of one of the rockets.
- `micropython.mem_use()` function.
- The analog-clock can now also set the time using the buttons.
### Changed
- **Pycardium**: Switched from `long-long` to `mpz` integer representation.
This should resolve any issues with large numbers which had popped up so far.
- Refactored BME680 sensor interface.
- Made OpenOCD scripts work with more debuggers out of the box.
- Internal changes in preparation for button-interrupts.
### Fixed
- Backlight and Vibration motor were not reset when switching apps.
- Mismatch in default settings of the *Card10 Nickname* app.
- Fixed the PMIC ADC muxer not being properly reset to neutral after a
- Fixed wrong timezone offset calculation in `utime.time_ms()`.
- Fixed bug where `\` characters were not parsed as path separators.
- Fixed the alignment request check in our ELF l0der.
- Fixed a buffer-overflow in the config-parser.
## [v1.9] - 2019-08-28 23:23 - [IcebergLettuce]
......@@ -214,7 +349,11 @@ fbf7c8c0 fix(menu.py) Refactored menu.py based on !138
## [v1.0] - 2019-08-21 00:50
Initial release.
[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.9...master
[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.13...master
[v1.13]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.12...v1.13
[v1.12]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.11...v1.12
[v1.11]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.10...v1.11
[v1.10]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.9...v1.10
[v1.9]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.8...v1.9
[v1.8]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.7...v1.8
[v1.7]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...v1.7
......@@ -28,7 +28,7 @@ The current draft uses following service specification:
- Rockets characteristic:
UUID: ``42230210-2342-2342-2342-234223422342``
write no response
read and write no response
- Background LED Bottom Left characteristic:
......@@ -104,6 +104,9 @@ Rockets characteristic
The Rockets characteristic makes it possible to address every three rockets.
Just write there three byte array, one for evey rocket.
On read you get the current value of all three rockets.
Range is between 0 and 31 (``0x1f``) if send higher value it will set to max of 31.
......@@ -113,8 +116,8 @@ Dataformat:
Rocket0 Rocket1 Rocket2
======= ======= =======
- Enable only Rocket0: ``0xff0000``
- Enable all rockets with 50% brightness: ``0x7f7f7f``
- Enable only Rocket0: ``0x1f0000``
- Enable all rockets with 50% brightness: ``0x0f0f0f``
Background LED <Position> characteristic
On the card10 the ARM Cordio-B50 stack is used, which is in a very early experimental state and has some incompatibilities with some smartphones.
Therefore some alternative stacks are evaluated, which meight be used as a replacement in the long term.
Here a stack called NimBLE is presented, which claims to be feature complete. Originally it has been developed for Mynewt, an open source embedded operating system by Apache (https://mynewt.apache.org/).
There is a working port for the ESP32 espressif ESP-IDF framework.
Like Epicardium, ESP-IDF is based on FreeRTOS. Therefore it can be used for evaluation purposes.
Getting NimBLE run on the ESP32
Install required packages:
.. code-block:: shell-session
sudo apt install git virtualenv python2.7 cmake
.. code-block:: shell-session
sudo pacman -S git python2 python2-virtualenv cmake
Download and extract xtensa ESP32 compiler:
.. code-block:: shell-session
wget https://dl.espressif.com/dl/xtensa-esp32-elf-gcc8_2_0-esp32-2018r1-linux-amd64.tar.xz
tar -xf xtensa-esp32-elf-gcc8_2_0-esp32-2018r1-linux-amd64.tar.xz
Clone esp-idf:
.. code-block:: shell-session
git clone https://github.com/espressif/esp-idf.git
Add xtensa and ESP-IDF path to $PATH:
bash shell:
.. code-block:: shell-session
export IDF_PATH=$PWD/esp-idf
export PATH=${PATH}:$PWD/xtensa-esp32-elf/bin:$PWD/esp-idf/tools
fish shell:
.. code-block:: shell-session
set -gx IDF_PATH $PWD/esp-idf
set -gx PATH $PWD/xtensa-esp32-elf/bin/ $PWD/esp-idf/tools $PATH
Create a python2.7 virtualenv:
.. code-block:: shell-session
cd esp-idf
virtualenv -p /usr/bin/python2.7 venv
Enter the virtualenv:
bash shell:
.. code-block:: shell-session
. venv/bin/activate
fish shell:
.. code-block:: shell-session
. venv/bin/activate.fish
Init git submodules and install all required Python packages:
.. code-block:: shell-session
git submodule update --init --recursive
pip install -r requirements.txt
Now you are ready to build!
The following steps assume that your ESP32 is connected via USB and
is accessible via /dev/ttyUSB0. This meight be different on your system.
There are a few NimbLE examples which can be used for playing around:
Build a BLE server example (host mode):
.. code-block:: shell-session
cd examples/bluetooth/nimble/bleprph
idf.py -p /dev/ttyUSB0 flash monitor
This will build and flash the example to the ESP32 and instantly listens on /dev/ttyUSB0 serial port.
After the flashing process the ESP32 will anounce itself as **nimble-bleprph** device via BLE.
Build a BLE client example (central mode):
.. code-block:: shell-session
cd examples/bluetooth/nimble/blecent
idf.py -p /dev/ttyUSB0 flash monitor
This will build and flash the example to the ESP32 and instantly listens on /dev/ttyUSB0 serial port.
After the flashing process the ESP32 creates a GATT client and performs passive scan, it then connects to peripheral device if the device advertises connectability and the device advertises support for the Alert Notification service (0x1811) as primary service UUID.
.. _card10_cfg:
Certain high-level settings can be configured using a filed named ``card10.cfg``. It is accessed from the :ref:`usb_file_transfer` of the bootloader. Once you are in this mode and have mounted the badge's flash device, you can either create or update a file named ``card10.cfg``.
The file is in the well-known INI-style format, with one setting per line. For instance, if there were an option called ``answer_to_life``, you could set it by writing the following line in the ``card10.cfg`` file:
.. code-block:: text
answer_to_life = 42
Don't forget to unmount the filesystem before rebooting your badge after changing any setting.
Syntax and Types
Lines that start with a ``#`` character are ignored.
Any other line will have the overall syntax of ``option_name = option_value``, with spaces around the ``=`` character optional.
Option names are internal to card10 and described below. Each option has a defined type.
========= ===========
Type name Description
========= ===========
Boolean A true/false value. ``1`` or ``true`` is true, ``0`` or ``false`` is false. Example: ``foo = true``.
String An unquoted string value of maximum 20 bytes. Values longer than 20 bytes are trimmed. Example: ``foo = bar``.
Integer A signed 32-bit integer in base 10. Example: ``foo = 42`` or ``bar = -1337``.
Float A single-precision (32-bit) floating-point number in base 10. Example: ``foo = 13.37``.
========= ===========
Supported options
=============== ========== ===========
Option name Type Description
=============== ========== ===========
``execute_elf`` Boolean Allow running of binary :ref:`l0dables`. These files can be nefarious, so this option is off by default.
--------------- ---------- -----------
``timezone`` String Timezone for card10; must be of format ``[+-]HHMM``. Examples: ``+0800``, ``-0200``
=============== ========== ===========
......@@ -3,6 +3,8 @@ import subprocess
import sys
import time
import sphinx.util.logging
from docutils import nodes
from docutils.parsers import rst
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
......@@ -47,6 +49,21 @@ todo_include_todos = True
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["output", "Thumbs.db", ".DS_Store", "hawkmoth"]
# -- Extensions -------------------------------------------------------------- {{{
class ColorExample(rst.Directive):
has_content = False
required_arguments = 1
optional_arguments = 0
option_spec = {}
def run(self):
color = self.arguments[0]
html_text = '<div style="width: 30px;height: 30px;background: {};border: black 1px solid;border-radius: 15px;"></div>'
return [nodes.raw("", html_text.format(color), format="html")]
# }}}
# -- Options for HTML output ------------------------------------------------- {{{
# The Read the Docs theme is available from
......@@ -90,6 +107,8 @@ html_context = {
autodoc_mock_imports = [
......@@ -123,3 +142,4 @@ except ImportError as e:
# -- Sphinx Setup ------------------------------------------------------------
def setup(app):
app.add_config_value("has_hawkmoth", has_hawkmoth, "")
app.add_directive("color-example", ColorExample)
......@@ -115,6 +115,12 @@ command to soft-reset card10.
.. note::
You will also find the following self-describing gdb files in the firmware
root directory, which do not require additional arguments:
``flash-all.gdb, flash-bootloader.gdb,
flash-both.gdb, flash-epicardium.gdb, flash-pycardium.gdb``
.. warning::
If you are used to use ``mon reset halt``, be aware that the card10 prototypes
do not connect the reset line to the debugger. OpenOCD is configured to only do
Ontop of FreeRTOS, we have our own mutex implementation. **Never use the
FreeRTOS mutexes directly! Always use this abstraction layer instead**. This
mutex implementation tries to make reasoning about program flow and locking
behavior easier. And most importantly tries to help with debugging possible
There are a few guiding design principles:
- Mutexes can only be used from tasks, **never** from interrupts!
- Timers can use mutexes, but only with :c:func:`mutex_trylock`, **never** with
:c:func:`mutex_lock` (Because they are not allowed to block).
- Locking can *never* fail (if it does, we consider this a fatal error ⇒ panic).
- No recursive locking.
- An unlock can only occur from the task which previously acquired the mutex.
- An unlock is only allowed if the mutex was previously acquired.
For a more elaborate explanation of the rationale behind these rules take a
look at the :ref:`mutex-design-reasons`.
.. c:autodoc:: epicardium/modules/mutex.h
.. _mutex-design-reasons:
Reasons for this Design
Locking can *never* fail
This might seem like a bold claim at first but in the end, it is just a matter
of definition and shifting responsibilities. Instead of requiring all code to
be robust against a locking attempt failing, we require all code to properly
lock and unlock their mutexes and thus never producing a situation where
locking would fail.
Because all code using any of the mutexes is contained in the Epicardium
code-base, we can - *hopefully* - audit it properly behaving ahead of time and
thus don't need to add code to ensure correctness at runtime. This makes
downstream code easier to read and easier to reason about.
History of this project has shown that most code does not properly deal with
locking failures anyway: There was code simply skipping the mutexed action on
failure, code blocking a module entirely until reboot, and worst of all: Code
exposing the locking failure to 'user-space' (Pycardium) instead of retrying.
This has lead to spurious errors where technically there would not need to be
Only from tasks
Locking a mutex from an ISR, a FreeRTOS software timer or any other context
which does not allow blocking is complicated to do right. The biggest
difficulty is that a task might be holding the mutex during execution of such a
context and there is no way to wait for it to release the mutex. This requires
careful design of the program flow to choose an alternative option in such a
case. A common approach is to 'outsource' the relevant parts of the code into
an 'IRQ worker' which is essentially just a task waiting for the IRQ to wake it
up and then attempts to lock the mutex.
If you absolutely do need it (and for legacy reasons), software timers *can*
lock a mutex using :c:func:`mutex_trylock` (which never blocks). I strongly
recommend **not** doing that, though. As shown above, you will have to deal
with the case of the mutex being held by another task and it is very well
possible that your timer will get starved of the mutex because the scheduler
has no knowledge of its intentions. In most cases, it is a better idea to use
a task and attempt locking using :c:func:`mutex_lock`.
.. todo::
We might introduce a generic IRQ worker queue system at some point.
No recursive locking
Recursive locking refers to the ability to 'reacquire' a mutex already held by
the current task, deeper down in the call-chain. Only the outermost unlock
will actually release the mutex. This feature is sometimes implemented to
allow more elegant abstractions where downstream code does not need to know
about the mutexes upstream code uses and can still also create a larger region
where the same mutex is held.
But exactly by hiding the locking done by a function, these abstractions make
it hard to trace locking chains and in some cases even make it impossible to
create provably correct behavior. As an alternative, I would suggest using
different mutexes for the different levels of abstraction. This also helps
keeping each mutex separated and 'local' to its purpose.
Only unlock from the acquiring task
Because of the above mentioned mutex locking semantics, there should never be a
need to force-unlock a forgein mutex. Even in cases of failures, all code
should still properly release all mutexes it holds. One notable exceptions is
``panic()``\s which will abort all ongoing operations anyway.
Only unlock once after acquisition
Justified with an argument of robustness, sometimes the :c:func:`mutex_unlock`
call is written in a way that allows unlocking an already unlocked mutex. But
robustness of downstream code will not really be improved by the upstream API
dealing with arguably invalid usage. For example, this could encourage
practices like unlocking everything again at the end of a function "just to be
Instead, code should be written in a way where the lock/unlock pair is
immediately recognizable as belonging together and is thus easily auditable to
have correct locking behavior. A common pattern to help with readability in
this regard is the *Single Function Exit* which looks like this:
.. code-block:: cpp
int function()
int ret;
ret = foo();
if (ret) {
/* Return with an error code */
ret = -ENODEV;
goto out_unlock;
ret = bar();
if (ret) {
/* Return the return value from foo */
goto out_unlock;
ret = 0;
return ret;
......@@ -107,7 +107,6 @@ firmware features:
info related to BLE.
- ``-Ddebug_core1=true``: Enable the core 1 SWD lines which are exposed on the
SAO connector. Only use this if you have a debugger which is modified for core 1.
- ``-Djailbreak_card10=true``: Enable execution of .elf l0dables on core 1.
.. warning::
......@@ -144,6 +143,15 @@ In order to do a rebuild you can issue a clean command to ninja via
Otherwise, rerunning ``./bootstrap.sh`` will also clean the build-directory.
.. note::
**macOS**: If ``strip`` fails to work on the freshly compiled ``mpy-cross``:
"strip: object: (...)/lib/micropython/micropython/mpy-cross/mpy-cross
malformed object (unknown load command 9)", you a likely not using the
`strip` that matches to your ``clang``. Do ``which strip && which clang``,
and if the paths don't match, clean up your PATHs, or as a quick hack,
create a symlink for strip.
.. note::
If you try to flash pycardium_epicardium.bin (renamed to card10.bin)
......@@ -152,4 +160,4 @@ Otherwise, rerunning ``./bootstrap.sh`` will also clean the build-directory.
was >1MB. This was caused by the ``tr`` tool in the build process
(it's supposed to create a large file with 0xff in it) - this requires the
LC_ALL environment variable to be not set, or set to "C"
(but not UTF8 or similar).
\ No newline at end of file
(but not UTF8 or similar).
......@@ -5,27 +5,14 @@ method of flashing:
Flash Without Debugger
If you do not have a debugger, you have to update the firmware using our
bootloader. To do so, you need to reboot card10 while keeping the buttom on
the bottom right pressed. Rebooting is done by either short pressing the power
button (top left) while you have a working firmware, or turning the card10 off
completely (by pressing the power button for 8 seconds) and then starting it again.
.. image:: static/bootloader-buttons.png
If you did everything correctly, the bootloader will display:
.. code-block:: text
Jul 28 2019
USB activated.
If you do not have a debugger, you have to update the firmware using our
bootloader by going into :ref:`usb_file_transfer`.
On your host, you should now see an 8MB flash-device appear. You can now drop
the firmware's ``.bin`` (from ``build/pycardium/pycardium_epicardium.bin`` in
most cases) into this flash-storage. You **must** call the file ``card10.bin``
for the bootloader to use it.
After you get your badge into :ref:`usb_file_transfer`, you can drop the firmware's
``.bin`` (from ``build/pycardium/pycardium_epicardium.bin`` in most cases) into
this flash-storage. You **must** call the file ``card10.bin`` for the
bootloader to use it.
The bootloader will then display ``Writing.`` in red while it is actually
writing the file to external flash. Please wait until it displays ``Ready.``
......@@ -38,17 +38,21 @@ Last but not least, if you want to start hacking the lower-level firmware, the
.. toctree::
:maxdepth: 1
:caption: Firmware
.. toctree::
......@@ -65,6 +69,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the
Indices and tables
......@@ -14,24 +14,38 @@ 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:
* **Dispatcher**: The dispatcher task handles API calls from core 1.
* **PMIC**: The power manager task checks the battery level and other interesting
statistics that can be gathered from our power manager IC (MAX77650).
* **Serial**: Handles serial communication via *UART*, *CDC ACM* and possibly
* **BHI160**: Housekeeping task for interaction with the `BHI160`_.
| 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 |
| `vLedTask`_ | -/- | +1 | LED Animations |
| `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
.. _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
.. todo::
The following tasks have not yet been implemented/are currently in the works:
- **Bluetooth**: The bluetooth stack (`#23`_)
- **Payload Controller**: Control what is running on core 1
.. _#23: https://git.card10.badge.events.ccc.de/card10/firmware/issues/23
Epicardium API
Epicardium exposes lots of functionality via the *Epicardium API*. The
......@@ -41,23 +55,28 @@ you should probably read the :ref:`epicardium_api_guide` guide.
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:
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
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 at build time.
To do this, run ``bootstrap.sh`` with the option ``-Djailbreak_card10=true``
and rebuild the firmware as described in :ref:`how_to_build`.
.. todo::
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.
Provide more details how this works
.. _Rustcardium: https://git.card10.badge.events.ccc.de/astro/rust-card10
Program Flow Diagram
......@@ -2,9 +2,18 @@
``bhi160`` - Sensor Fusion
.. versionadded:: 1.4
Supports the BHI160 sensor on the card10 for accelerometer, gyroscope...
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
All angles and angular velocities (like gyroscope, orientation) rotate counter
clockwise around their respective axis.
......@@ -12,91 +21,39 @@ Supports the BHI160 sensor on the card10 for accelerometer, gyroscope...
import bhi160
import utime
bhi = bhi160.BHI160Orientation()
while True:
samples = bhi.read()
.. class:: bhi160.BHI160Orientation(sample_rate,dynamic_range,callback,sample_buffer_len)
Orientation of the BHI160
sample_rate: int, optional
Sample rate (default is 4)
dynamic_range: int, optional
Dynamic range (default is 2)
callback: callable, optional
Call this callback when enough data is collected (default is None)
.. todo:: The callback functionality is untested, so do not be confused if it does not work.
sample_buffer_len: int, optional
Length of sample buffer (default is 200)
.. py:method:: read():
Read sensor values
:returns: Collected sensor values as list
.. py:method:: close():
Close the connection to the sensor
.. class:: bhi160.BHI160Accelerometer
Accelerometer of the BHI160
sample_rate: int, optional
Sample rate (default is 4)
dynamic_range: int, optional
Dynamic range (default is 2)
callback: callable, optional
Call this callback when enough data is collected (default is None)
.. todo:: The callback functionality is untested, so do not be confused if it does not work.
sample_buffer_len: int, optional
Length of sample buffer (default is 200)
.. py:method:: read():
Read sensor values
:returns: Collected sensor values as list
.. py:method:: close():
Close the connection to the sensor
.. class:: bhi160.BHI160Gyroscope
Gyroscope of the BHI160
sample_rate: int, optional
Sample rate (default is 4)
dynamic_range: int, optional
Dynamic range (default is 2)
callback: callable, optional
Call this callback when enough data is collected (default is None)
.. todo:: The callback functionality is untested, so do not be confused if it does not work.
sample_buffer_len: int, optional
Length of sample buffer (default is 200)
.. py:method:: read():
Read sensor values
:returns: Collected sensor values as list
.. py:method:: close():
Close the connection to the sensor
if len(samples) == 0:
# print the latest sample
.. autoclass:: bhi160.BHI160Orientation
.. autoclass:: bhi160.BHI160Accelerometer
.. autoclass:: bhi160.BHI160Gyroscope
.. autoclass:: bhi160.BHI160Magnetometer
......@@ -2,6 +2,7 @@
``bme680`` - Environmental Sensor
Allows access to environmental data of card10's surroundings.
......@@ -9,17 +10,28 @@
import bme680, utime
with bme680.Bme680() as environment:
while True:
d = environment.get_data()
while True:
temperature, humidity, pressure, resistance = bme680.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))
print("Temperature: {:10.2f} °C".format(temperature))
print("Humidity: {:10.2f} % r.h.".format(humidity))
print("Pressure: {:10.2f} hPa".format(pressure))
print("Gas Resistance: {:10.2f} Ω".format(resistance))
Sensor Class
.. autoclass:: bme680.Bme680
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
.. py:function:: init()
......@@ -29,6 +41,8 @@
.. versionadded:: 1.4
.. deprecated:: 1.10
Use the :py:class:`bme680.Bme680` class instead.
.. py:function:: get_data()
......@@ -38,9 +52,13 @@
``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.
......@@ -14,23 +14,65 @@ The color module also contains a few constanst for commonly used colors:
Camp Colors
.. py:data:: CHAOSBLUE
.. color-example:: #0076BA
.. py:data:: CHAOSBLUE_DARK
.. color-example:: #005383
.. py:data:: COMMYELLOW
.. color-example:: #FFC600
.. py:data:: COMMYELLOW_DARK
.. color-example:: #D39A00
.. py:data:: CAMPGREEN
.. color-example:: #99BA00
.. py:data:: CAMPGREEN_DARK
.. color-example:: #6F8700
.. py:data:: BLACK
.. color-example:: #000
.. py:data:: WHITE
.. color-example:: #fff
.. py:data:: RED
.. color-example:: #f00
.. py:data:: GREEN
.. color-example:: #0f0
.. py:data:: YELLOW
.. color-example:: #ff0
.. py:data:: BLUE
.. color-example:: #00f
.. py:data:: MAGENTA
.. color-example:: #f0f
.. py:data:: CYAN
.. color-example:: #0ff
.. py:module:: htmlcolor
``htmlcolor`` - Color Constants
......@@ -39,142 +81,561 @@ The ``htmlcolor`` module contains even more color constants. Note
that loading the ``htmlcolor`` module will require ~12K of RAM.
.. py:data:: ALICEBLUE
.. color-example:: aliceblue
.. py:data:: ANTIQUEWHITE
.. color-example:: antiquewhite
.. py:data:: AQUA
.. color-example:: aqua
.. py:data:: AQUAMARINE
.. color-example:: aquamarine
.. py:data:: AZURE
.. color-example:: azure
.. py:data:: BEIGE
.. color-example:: beige
.. py:data:: BISQUE
.. color-example:: bisque
.. py:data:: BLACK
.. color-example:: black
.. color-example:: blanchedalmond
.. py:data:: BLUE
.. color-example:: blue
.. py:data:: BLUEVIOLET
.. color-example:: blueviolet
.. py:data:: BROWN
.. color-example:: brown
.. py:data:: BURLYWOOD
.. color-example:: burlywood
.. py:data:: CADETBLUE
.. color-example:: cadetblue
.. py:data:: CHARTREUSE
.. color-example:: chartreuse
.. py:data:: CHOCOLATE
.. color-example:: chocolate
.. py:data:: CORAL
.. color-example:: coral
.. color-example:: cornflowerblue
.. py:data:: CORNSILK
.. color-example:: cornsilk
.. py:data:: CRIMSON
.. color-example:: crimson
.. py:data:: CYAN
.. color-example:: cyan
.. py:data:: DARKBLUE
.. color-example:: darkblue
.. py:data:: DARKCYAN
.. color-example:: darkcyan
.. py:data:: DARKGOLDENROD
.. color-example:: darkgoldenrod
.. py:data:: DARKGRAY
.. color-example:: darkgray
.. py:data:: DARKGREEN
.. color-example:: darkgreen
.. py:data:: DARKKHAKI
.. color-example:: darkkhaki
.. py:data:: DARKMAGENTA
.. color-example:: darkmagenta
.. color-example:: darkolivegreen
.. py:data:: DARKORANGE
.. color-example:: darkorange
.. py:data:: DARKORCHID
.. color-example:: darkorchid
.. py:data:: DARKRED
.. color-example:: darkred
.. py:data:: DARKSALMON
.. color-example:: darksalmon
.. py:data:: DARKSEAGREEN
.. color-example:: darkseagreen
.. py:data:: DARKSLATEBLUE
.. color-example:: darkslateblue
.. py:data:: DARKSLATEGRAY
.. color-example:: darkslategray
.. py:data:: DARKTURQUOISE
.. color-example:: darkturquoise
.. py:data:: DARKVIOLET
.. color-example:: darkviolet
.. py:data:: DEEPPINK
.. color-example:: deeppink
.. py:data:: DEEPSKYBLUE
.. color-example:: deepskyblue
.. py:data:: DIMGRAY
.. color-example:: dimgray
.. py:data:: DODGERBLUE
.. color-example:: dodgerblue
.. py:data:: FIREBRICK
.. color-example:: firebrick
.. py:data:: FLORALWHITE
.. color-example:: floralwhite
.. py:data:: FORESTGREEN
.. color-example:: forestgreen
.. py:data:: FUCHSIA
.. color-example:: fuchsia
.. py:data:: GAINSBORO
.. color-example:: gainsboro
.. py:data:: GHOSTWHITE
.. color-example:: ghostwhite
.. py:data:: GOLD
.. color-example:: gold
.. py:data:: GOLDENROD
.. color-example:: goldenrod
.. py:data:: GRAY
.. color-example:: gray
.. py:data:: GREEN
.. color-example:: green
.. py:data:: GREENYELLOW
.. color-example:: greenyellow
.. py:data:: HONEYDEW
.. color-example:: honeydew
.. py:data:: HOTPINK
.. color-example:: hotpink
.. py:data:: INDIANRED
.. color-example:: indianred
.. py:data:: INDIGO
.. color-example:: indigo
.. py:data:: IVORY
.. color-example:: ivory
.. py:data:: KHAKI
.. color-example:: khaki
.. py:data:: LAVENDER
.. color-example:: lavender
.. py:data:: LAVENDERBLUSH
.. color-example:: lavenderblush
.. py:data:: LAWNGREEN
.. color-example:: lawngreen
.. py:data:: LEMONCHIFFON
.. color-example:: lemonchiffon
.. py:data:: LIGHTBLUE
.. color-example:: lightblue
.. py:data:: LIGHTCORAL
.. color-example:: lightcoral
.. py:data:: LIGHTCYAN
.. color-example:: lightcyan
.. color-example:: lightgoldenrodyellow
.. py:data:: LIGHTGRAY
.. color-example:: lightgray
.. py:data:: LIGHTGREEN
.. color-example:: lightgreen
.. py:data:: LIGHTPINK
.. color-example:: lightpink
.. py:data:: LIGHTSALMON
.. color-example:: lightsalmon
.. py:data:: LIGHTSEAGREEN
.. color-example:: lightseagreen
.. py:data:: LIGHTSKYBLUE
.. color-example:: lightskyblue
.. color-example:: lightslategray
.. color-example:: lightsteelblue
.. py:data:: LIGHTYELLOW
.. color-example:: lightyellow
.. py:data:: LIME
.. color-example:: lime
.. py:data:: LIMEGREEN
.. color-example:: limegreen
.. py:data:: LINEN
.. color-example:: linen
.. py:data:: MAGENTA
.. color-example:: magenta
.. py:data:: MAROON
.. color-example:: maroon
.. color-example:: mediumaquamarine
.. py:data:: MEDIUMBLUE
.. color-example:: mediumblue
.. py:data:: MEDIUMORCHID
.. color-example:: mediumorchid
.. py:data:: MEDIUMPURPLE
.. color-example:: mediumpurple
.. color-example:: mediumseagreen
.. color-example:: mediumslateblue
.. color-example:: mediumspringgreen
.. color-example:: mediumturquoise
.. color-example:: mediumvioletred
.. py:data:: MIDNIGHTBLUE
.. color-example:: midnightblue
.. py:data:: MINTCREAM
.. color-example:: mintcream
.. py:data:: MISTYROSE
.. color-example:: mistyrose
.. py:data:: MOCCASIN
.. color-example:: moccasin
.. py:data:: NAVAJOWHITE
.. color-example:: navajowhite
.. py:data:: NAVY
.. color-example:: navy
.. py:data:: OLDLACE
.. color-example:: oldlace
.. py:data:: OLIVE
.. color-example:: olive
.. py:data:: OLIVEDRAB
.. color-example:: olivedrab
.. py:data:: ORANGE
.. color-example:: orange
.. py:data:: ORANGERED
.. color-example:: orangered
.. py:data:: ORCHID
.. color-example:: orchid
.. py:data:: PALEGOLDENROD
.. color-example:: palegoldenrod
.. py:data:: PALEGREEN
.. color-example:: palegreen
.. py:data:: PALETURQUOISE
.. color-example:: paleturquoise
.. py:data:: PALEVIOLETRED
.. color-example:: palevioletred
.. py:data:: PAPAYAWHIP
.. color-example:: papayawhip
.. py:data:: PEACHPUFF
.. color-example:: peachpuff
.. py:data:: PERU
.. color-example:: peru
.. py:data:: PINK
.. color-example:: pink
.. py:data:: PLUM
.. color-example:: plum
.. py:data:: POWDERBLUE
.. color-example:: powderblue
.. py:data:: PURPLE
.. color-example:: purple
.. py:data:: RED
.. color-example:: red
.. py:data:: ROSYBROWN
.. color-example:: rosybrown
.. py:data:: ROYALBLUE
.. color-example:: royalblue
.. py:data:: SADDLEBROWN
.. color-example:: saddlebrown
.. py:data:: SALMON
.. color-example:: salmon
.. py:data:: SANDYBROWN
.. color-example:: sandybrown
.. py:data:: SEAGREEN
.. color-example:: seagreen
.. py:data:: SEASHELL
.. color-example:: seashell
.. py:data:: SIENNA
.. color-example:: sienna
.. py:data:: SILVER
.. color-example:: silver
.. py:data:: SKYBLUE
.. color-example:: skyblue
.. py:data:: SLATEBLUE
.. color-example:: slateblue
.. py:data:: SLATEGRAY
.. color-example:: slategray
.. py:data:: SNOW