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 1048 additions and 221 deletions
Mutex
=====
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
dead-locks.
Design
------
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`.
Definitions
-----------
.. c:autodoc:: epicardium/os/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
any.
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
sure".
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;
mutex_lock(&some_mutex);
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;
out_unlock:
mutex_unlock(&some_mutex);
return ret;
}
Sensor Streams
==============
Sensor drivers can make their data available to core 1 in a stream-like format.
This allows batch-reading many samples and shoud reduce pressure on the
Epicardium API this way. Sensor streams are read on core 1 using
:c:func:`epic_stream_read`.
This page intends to document how to add this stream interface to a sensor driver.
It also serves as a reference of existing streams. For that, take a look at the
definitions in the :c:type:`stream_descriptor` enum.
Adding a new Stream
-------------------
The list of possible sensor streams must be known at compile time. Each stream
gets a unique ID in the :c:type:`stream_descriptor` enum. Please do not assign
IDs manually but instead let the enum assign sequencial IDs. :c:macro:`SD_MAX`
must always be the highest stream ID. Additionally, please document what this
stream is for using a doc-comment so it shows up on this page.
When a sensor driver enables data collection, it should also register its
respective stream. This is done using a :c:type:`stream_info` object. Pass
this object to :c:func:`stream_register` to make your stream available. Your
driver must guarantee the :c:member:`stream_info.queue` handle to be valid until
deregistration using :c:func:`stream_deregister`.
Definitions
-----------
.. c:autodoc:: epicardium/modules/stream.h
Hawkmoth - Sphinx Autodoc for C
===============================
Hawkmoth is a minimalistic Sphinx_ `C Domain`_ autodoc directive extension to
incorporate formatted C source code comments written in reStructuredText_ into
Sphinx based documentation. It uses Clang Python Bindings for parsing, and
generates C Domain directives for C API documentation, and more. In short,
Hawkmoth is Sphinx Autodoc for C.
Hawkmoth aims to be a compelling alternative for documenting C projects using
Sphinx, mainly through its simplicity of design, implementation and use.
.. _Sphinx: http://www.sphinx-doc.org
.. _C Domain: http://www.sphinx-doc.org/en/stable/domains.html
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
Example
-------
Given C source code with rather familiar looking documentation comments::
/**
* Get foo out of bar.
*/
void foobar();
and a directive in the Sphinx project::
.. c:autodoc:: filename.c
you can incorporate code documentation into Sphinx. It's as simple as that.
You can document functions, their parameters and return values, structs, unions,
their members, macros, function-like macros, enums, enumeration constants,
typedefs, variables, as well as have generic documentation comments not attached
to any symbols.
Documentation
-------------
Documentation on how to install and configure Hawkmoth, and write documentation
comments, with examples, is available in the ``doc`` directory in the source
tree, obviously in Sphinx format and using the directive extension. Pre-built
documentation `showcasing what Hawkmoth can do`_ is available at `Read the
Docs`_.
.. _showcasing what Hawkmoth can do: https://hawkmoth.readthedocs.io/en/latest/examples.html
.. _Read the Docs: https://hawkmoth.readthedocs.io/
Installation
------------
You can install Hawkmoth from PyPI_ with::
pip install hawkmoth
You'll additionally need to install Clang and Python 3 bindings for it through
your distro's package manager; they are not available via PyPI. For further
details, see the documentation.
Alternatively, installation packages are available for:
* `Arch Linux`_
In Sphinx ``conf.py``, add ``hawkmoth`` to ``extensions``, and point
``cautodoc_root`` at the source tree. See the extension documentation for
details.
.. _PyPI: https://pypi.org/project/hawkmoth/
.. _Arch Linux: https://aur.archlinux.org/packages/?K=hawkmoth
Development and Contributing
----------------------------
Hawkmoth source code is available on GitHub_. The development version can be
checked out via ``git`` using this command::
git clone https://github.com/jnikula/hawkmoth.git
Please file bugs and feature requests as GitHub issues. Contributions are
welcome both as emailed patches to the mailing list and as pull requests.
.. _GitHub: https://github.com/jnikula/hawkmoth
Dependencies
------------
- Python 3.4
- Sphinx 3
- Clang 6.0
- Python 3 Bindings for Clang 6.0
- sphinx-testing 1.0.0 (for development)
These are the versions Hawkmoth is currently being developed and tested
against. Other versions might work, but no guarantees.
License
-------
Hawkmoth is free software, released under the `2-Clause BSD License`_.
.. _2-Clause BSD License: https://opensource.org/licenses/BSD-2-Clause
Contact
-------
IRC channel ``#hawkmoth`` on freenode_.
Mailing list hawkmoth@freelists.org. Subscription information at the `list home
page`_.
.. _freenode: https://freenode.net/
.. _list home page: https://www.freelists.org/list/hawkmoth
Hawkmoth - Sphinx Autodoc for C
===============================
Hawkmoth is a minimalistic Sphinx_ `C Domain`_ autodoc directive extension to
incorporate formatted C source code comments written in reStructuredText_ into
Sphinx based documentation. It uses Clang Python Bindings for parsing, and
generates C Domain directives for C API documentation, and more. In short,
Hawkmoth is Sphinx Autodoc for C.
Hawkmoth aims to be a compelling alternative for documenting C projects using
Sphinx, mainly through its simplicity of design, implementation and use.
.. _Sphinx: http://www.sphinx-doc.org
.. _C Domain: http://www.sphinx-doc.org/en/stable/domains.html
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
Example
-------
Given C source code with rather familiar looking documentation comments::
/**
* Get foo out of bar.
*/
void foobar();
and a directive in the Sphinx project::
.. c:autodoc:: filename.c
you can incorporate code documentation into Sphinx. It's as simple as that.
You can document functions, their parameters and return values, structs, unions,
their members, macros, function-like macros, enums, enumeration constants,
typedefs, variables, as well as have generic documentation comments not attached
to any symbols.
Documentation
-------------
Documentation on how to configure Hawkmoth and write documentation comments,
with examples, is available in the ``doc`` directory in the source tree,
obviously in Sphinx format and using the directive extension. Pre-built
documentation `showcasing what Hawkmoth can do`_ is available at `Read the
Docs`_.
.. _showcasing what Hawkmoth can do: https://hawkmoth.readthedocs.io/en/latest/examples.html
.. _Read the Docs: https://hawkmoth.readthedocs.io/
Installation
------------
You can install Hawkmoth from PyPI_ with::
pip install hawkmoth
You'll additionally need to install Clang and Python 3 bindings for it through
your distro's package manager; they are not available via PyPI. You may also
need to set ``LD_LIBRARY_PATH`` so that the Clang library can be found. For
example::
export LD_LIBRARY_PATH=$(llvm-config --libdir)
Alternatively, installation packages are available for:
* `Arch Linux`_
In Sphinx ``conf.py``, add ``hawkmoth`` to ``extensions``, and point
``cautodoc_root`` at the source tree. See the extension documentation for
details.
.. _PyPI: https://pypi.org/project/hawkmoth/
.. _Arch Linux: https://aur.archlinux.org/packages/?K=hawkmoth
Development and Contributing
----------------------------
Hawkmoth source code is available on GitHub_. The development version can be
checked out via ``git`` using this command::
git clone https://github.com/jnikula/hawkmoth.git
Please file bugs and feature requests as GitHub issues. Contributions are
welcome both as emailed patches to the mailing list and as pull requests.
.. _GitHub: https://github.com/jnikula/hawkmoth
Dependencies
------------
- Python 3.4
- Sphinx 1.8
- Clang 6.0
- Python 3 Bindings for Clang 6.0
- sphinx-testing 1.0.0 (for development)
These are the versions Hawkmoth is currently being developed and tested
against. Other versions might work, but no guarantees.
License
-------
Hawkmoth is free software, released under the `2-Clause BSD License`_.
.. _2-Clause BSD License: https://opensource.org/licenses/BSD-2-Clause
Contact
-------
IRC channel ``#hawkmoth`` on freenode_.
Mailing list hawkmoth@freelists.org. Subscription information at the `list home
page`_.
.. _freenode: https://freenode.net/
.. _list home page: https://www.freelists.org/list/hawkmoth
0.4
0.7.0
......@@ -52,7 +52,10 @@ class CAutoDocDirective(Directive):
env = self.state.document.settings.env
for (severity, filename, lineno, msg) in errors:
toprint = '{}:{}: {}'.format(filename, lineno, msg)
if filename:
toprint = '{}:{}: {}'.format(filename, lineno, msg)
else:
toprint = '{}'.format(msg)
if severity.value <= env.app.verbosity:
self.logger.log(self._log_lvl[severity], toprint,
......@@ -109,7 +112,7 @@ class CAutoDocDirective(Directive):
return node.children
def setup(app):
app.require_sphinx('1.8')
app.require_sphinx('3.0')
app.add_config_value('cautodoc_root', app.confdir, 'env')
app.add_config_value('cautodoc_compat', None, 'env')
app.add_config_value('cautodoc_clang', None, 'env')
......
......@@ -39,7 +39,10 @@ def main():
print(doc)
for (severity, filename, lineno, msg) in errors:
print('{}: {}:{}: {}'.format(severity.name,
filename, lineno, msg), file=sys.stderr)
if filename:
print('{}: {}:{}: {}'.format(severity.name,
filename, lineno, msg), file=sys.stderr)
else:
print('{}: {}'.format(severity.name, msg), file=sys.stderr)
main()
# Copyright (c) 2016-2017 Jani Nikula <jani@nikula.org>
# Copyright (c) 2018-2019 Bruno Santos <brunomanuelsantos@tecnico.ulisboa.pt>
# Copyright (c) 2018-2020 Bruno Santos <brunomanuelsantos@tecnico.ulisboa.pt>
# Licensed under the terms of BSD 2-Clause, see LICENSE for details.
"""
Documentation comment extractor
......@@ -34,7 +34,7 @@ Otherwise, documentation comments are passed through verbatim.
"""
import enum
import itertools
import re
import sys
from clang.cindex import CursorKind, TypeKind
......@@ -75,18 +75,22 @@ def comment_extract(tu):
current_comment = token
continue
# Store off the token's cursor for a slight performance improvement
# instead of accessing the `cursor` property multiple times.
token_cursor = token.cursor
# cursors that are 1) never documented themselves, and 2) allowed
# between comment and the actual cursor being documented
if (token.cursor.kind == CursorKind.INVALID_FILE or
token.cursor.kind == CursorKind.TYPE_REF or
token.cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE or
token.cursor.kind == CursorKind.MACRO_INSTANTIATION):
if (token_cursor.kind == CursorKind.INVALID_FILE or
token_cursor.kind == CursorKind.TYPE_REF or
token_cursor.kind == CursorKind.PREPROCESSING_DIRECTIVE or
token_cursor.kind == CursorKind.MACRO_INSTANTIATION):
continue
if cursor is not None and token.cursor == cursor:
if cursor is not None and token_cursor == cursor:
continue
cursor = token.cursor
cursor = token_cursor
# Note: current_comment may be None
if current_comment != None and docstr.is_doc(current_comment.spelling):
......@@ -125,16 +129,18 @@ def _get_macro_args(cursor):
if cursor.kind != CursorKind.MACRO_DEFINITION:
return None
tokens = cursor.get_tokens()
# Use the first two tokens to make sure this starts with 'IDENTIFIER('
x = [token for token in itertools.islice(cursor.get_tokens(), 2)]
if (len(x) != 2 or x[0].spelling != cursor.spelling or
x[1].spelling != '(' or x[0].extent.end != x[1].extent.start):
one = next(tokens)
two = next(tokens, None)
if two is None or one.extent.end != two.extent.start or two.spelling != '(':
return None
# Naïve parsing of macro arguments
# FIXME: This doesn't handle GCC named vararg extension FOO(vararg...)
args = []
for token in itertools.islice(cursor.get_tokens(), 2, None):
for token in tokens:
if token.spelling == ')':
return args
elif token.spelling == ',':
......@@ -161,8 +167,26 @@ def _recursive_parse(comments, cursor, nest, compat):
return _result(comment, cursor=cursor, fmt=fmt,
nest=nest, name=name, args=args, compat=compat)
elif cursor.kind == CursorKind.VAR_DECL:
fmt = docstr.Type.VAR
elif cursor.kind in [CursorKind.VAR_DECL, CursorKind.FIELD_DECL]:
if cursor.kind == CursorKind.VAR_DECL:
fmt = docstr.Type.VAR
else:
fmt = docstr.Type.MEMBER
# If this is an array, the dimensions should be applied to the name, not
# the type.
dims = ttype.rsplit(' ', 1)[-1]
if dims.startswith('[') and dims.endswith(']'):
ttype = ttype.rsplit(' ', 1)[0]
name = name + dims
# If this is a function pointer, or an array of function pointers, the
# name should be within the parenthesis as in (*name) or (*name[N]).
fptr_type = re.sub(r'\((\*+)(\[[^]]*\])?\)', r'(\1{}\2)'.format(name),
ttype, count=1)
if fptr_type != ttype:
name = fptr_type
ttype = ''
return _result(comment, cursor=cursor, fmt=fmt,
nest=nest, name=name, ttype=ttype, compat=compat)
......@@ -188,9 +212,15 @@ def _recursive_parse(comments, cursor, nest, compat):
# FIXME: Handle anonymous enumerators.
fmt = docstr.Type.TYPE
fmts = {CursorKind.STRUCT_DECL: docstr.Type.STRUCT,
CursorKind.UNION_DECL: docstr.Type.UNION,
CursorKind.ENUM_DECL: docstr.Type.ENUM}
fmt = fmts[cursor.kind]
# name may be empty for typedefs
result = _result(comment, cursor=cursor, fmt=fmt,
nest=nest, name=ttype, compat=compat)
nest=nest, name=name if name else ttype, compat=compat)
nest += 1
for c in cursor.get_children():
......@@ -205,12 +235,6 @@ def _recursive_parse(comments, cursor, nest, compat):
return _result(comment, cursor=cursor, fmt=fmt,
nest=nest, name=name, compat=compat)
elif cursor.kind == CursorKind.FIELD_DECL:
fmt = docstr.Type.MEMBER
return _result(comment, cursor=cursor, fmt=fmt,
nest=nest, name=name, ttype=ttype, compat=compat)
elif cursor.kind == CursorKind.FUNCTION_DECL:
# FIXME: check args against comment
# FIXME: children may contain extra stuff if the return type is a
......@@ -261,7 +285,8 @@ def clang_diagnostics(errors, diagnostics):
4: ErrorLevel.ERROR}
for diag in diagnostics:
errors.extend([(sev[diag.severity], diag.location.file.name,
filename = diag.location.file.name if diag.location.file else None
errors.extend([(sev[diag.severity], filename,
diag.location.line, diag.spelling)])
# return a list of (comment, metadata) tuples
......
# Copyright (c) 2016-2017 Jani Nikula <jani@nikula.org>
# Copyright (c) 2018-2019 Bruno Santos <brunomanuelsantos@tecnico.ulisboa.pt>
# Copyright (c) 2016-2020 Jani Nikula <jani@nikula.org>
# Copyright (c) 2018-2020 Bruno Santos <brunomanuelsantos@tecnico.ulisboa.pt>
# Licensed under the terms of BSD 2-Clause, see LICENSE for details.
"""
Documentation strings manipulation library
......@@ -17,6 +17,9 @@ class Type(Enum):
TEXT = auto()
VAR = auto()
TYPE = auto()
STRUCT = auto()
UNION = auto()
ENUM = auto()
ENUM_VAL = auto()
MEMBER = auto()
MACRO = auto()
......@@ -31,10 +34,13 @@ _doc_fmt = {
Type.TEXT: (0, '\n{text}\n'),
Type.VAR: (1, '\n.. c:var:: {ttype} {name}\n\n{text}\n'),
Type.TYPE: (1, '\n.. c:type:: {name}\n\n{text}\n'),
Type.ENUM_VAL: (1, '\n.. c:macro:: {name}\n\n{text}\n'),
Type.STRUCT: (1, '\n.. c:struct:: {name}\n\n{text}\n'),
Type.UNION: (1, '\n.. c:union:: {name}\n\n{text}\n'),
Type.ENUM: (1, '\n.. c:enum:: {name}\n\n{text}\n'),
Type.ENUM_VAL: (1, '\n.. c:enumerator:: {name}\n\n{text}\n'),
Type.MEMBER: (1, '\n.. c:member:: {ttype} {name}\n\n{text}\n'),
Type.MACRO: (1, '\n.. c:macro:: {name}\n\n{text}\n'),
Type.MACRO_FUNC: (1, '\n.. c:function:: {name}({args})\n\n{text}\n'),
Type.MACRO_FUNC: (1, '\n.. c:macro:: {name}({args})\n\n{text}\n'),
Type.FUNC: (1, '\n.. c:function:: {ttype} {name}({args})\n\n{text}\n')
}
......
# Copyright (c) 2021, Jani Nikula <jani@nikula.org>
# Licensed under the terms of BSD 2-Clause, see LICENSE for details.
"""
Read the Docs Helpers
=====================
Helpers for setting up and using Hawkmoth on Read the Docs.
"""
import os
import subprocess
def clang_setup(force=False):
"""Try to find and configure libclang location on RTD."""
if 'READTHEDOCS' in os.environ or force:
try:
result = subprocess.run(['llvm-config', '--libdir'],
check=True, capture_output=True, encoding='utf-8')
libdir = result.stdout.strip()
# For some reason there is no plain libclang.so symlink on RTD.
from clang.cindex import Config
Config.set_library_file(os.path.join(libdir, 'libclang.so.1'))
except Exception as e:
print(e)
.. _how_to_build:
How To Build
============
If you just want to write MicroPython code for card10, you probably **won't**
need to build the firmware yourself. This page is for people who want to work
on the underlying firmware itself.
need to build the firmware yourself. This page is for people **who want to work
on the underlying firmware itself**.
Dependencies
------------
* **gcc**, **binutils** & **newlib** for ``arm-none-eabi``: The packages have
slightly different names on different distros.
- Ubuntu: ``gcc-arm-none-eabi``, ``binutils-arm-none-eabi``, ``libnewlib-arm-none-eabi``
- Arch: ``arm-none-eabi-gcc``, ``arm-none-eabi-binutils``, ``arm-none-eabi-newlib``
- Ubuntu / Debian:
.. code-block:: shell-session
apt install gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi
- Arch:
.. code-block:: shell-session
pacman -S arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib
- Fedora
.. code-block:: shell-session
dnf install arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib
- macOS (Note: The card10 firmware team used Linux so far. macOS recommendations here are experimental.)
You can use `Homebrew`_ to install the required tools. The version of the
ARM crosscompiler tool chain is quite important; with the wrong version,
e.g. strip and/or ld might throw strange errors.
.. code-block:: shell-session
brew tap px4/px4
brew install px4/px4/gcc-arm-none-eabi-63
brew install coreutils
.. _Homebrew: https://brew.sh/
- Alternative: Download `ARM's GNU toolchain`_. **TODO**
* **python3**: For meson and various scripts needed for building.
* **meson** (>0.43.0): Unfortunately most distros only have very old versions
* **meson** (>0.43.0) & **ninja**: Unfortunately most distros only have very old versions
of meson in their repositories. Instead, you'll probably save yourself a lot
of headaches by installing meson from ``pip3 install --user meson``.
* **python3-crc16**: Install with ``pip3 install --user crc16``.
of headaches by installing meson from *pip*.
- Ubuntu / Debian:
.. code-block:: shell-session
apt install ninja-build
pip3 install --user meson
- Arch (has latest *meson* in the repos):
.. code-block:: shell-session
pacman -S meson
- macOS
.. code-block:: shell-session
brew install ninja
pip3 install --user meson # see https://mesonbuild.com/Getting-meson.html - you will have to add ~/.local/bin to your PATH.
* One of two CRC packages is required. Pick one:
- Ubuntu / Debian / macOS
.. code-block:: shell-session
pip3 install --user crc16
or
.. code-block:: shell-session
apt install python3-crcmod
or
.. code-block:: shell-session
pip3 install --user crcmod
- Arch
.. code-block:: shell-session
pacman -S python-crc16
* **python3-pillow**: Python Image Library
.. code-block:: shell-session
pip3 install --user pillow
- Arch
.. code-block:: shell-session
pacman -S python-pillow
.. _ARM's GNU toolchain: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
Building
--------
Build using the following two commands:
Cloning
-------
Clone the ``master`` branch of the firmware repository:
.. code-block:: shell-session
$ git clone https://git.card10.badge.events.ccc.de/card10/firmware.git
Build Configuration
-------------------
Initialize the build-system using
.. code-block:: shell-session
$ ./bootstrap.sh
$ ninja -C build/
``bootstrap.sh`` initializes git submodules and runs *meson*. Afterwards you
can build with *ninja*.
Additional arguments to ``bootstrap.sh`` will be passed to *meson*. You can
use this to for example, to enable one or more of the following optional
firmware features:
.. note::
- ``-Ddebug_prints=true``: Print more verbose debugging log messages
- ``-Dble_trace=true``: Enable BLE tracing. This will output lots of status
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.
.. warning::
Our build-system contains a few workarounds around short-comings in meson.
These workarounds might break on some setups which we did not yet test. If
......@@ -40,13 +144,70 @@ can build with *ninja*.
.. _issue tracker: https://git.card10.badge.events.ccc.de/card10/firmware/issues
If ninja succeeds, the binaries are in ``build/``. They are available in two
formats: As an ``.elf`` which can be flashed using a debugger and as a
``.bin`` which can be loaded using the provided bootloader. Here is a list of
the binaries:
Building
--------
Build using *ninja*:
.. code-block:: shell-session
$ ninja -C build/
If ninja succeeds, the resulting binaries are in ``build/``. They are
available in two formats: As an ``.elf`` which can be flashed using a debugger
and as a ``.bin`` which can be loaded using the provided bootloader. Here is a
list of the binaries:
- ``build/bootloader/bootloader.elf``: Our bootloader. It should already be on
your card10. The bootloader can only be flashed using a debugger.
- ``build/pycardium/pycardium_epicardium.bin``: The entire firmware in one ``.bin``.
- ``build/epicardium/epicardium.elf``: The core 0 part of the firmware, called Epicardium.
- ``build/pycardium/pycardium.elf``: Our MicroPython port, the core 1 part of the firmware.
In order to do a rebuild you can issue a clean command to ninja via
.. code-block:: shell-session
$ ninja -C build/ -t clean
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)
and the bootloader does not finish updating, the file might be too large.
~700kB is the normal size, but problems were reported where the file size
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).
Docker
======
Alternatively, clone the ``master`` branch of the firmware repository and enter it:
.. code-block:: shell-session
$ git clone https://git.card10.badge.events.ccc.de/card10/firmware.git
$ cd firmware
Afterwards, build a docker-container which will build the firmware via:
.. code-block:: shell-session
$ docker build -f docker/Dockerfile_fwbuild -t card10-firmware-builder .
Now, you can start the container with the firmware directory mounted, which will build the
firmware into the firmware/build directory:
.. code-block:: shell-session
$ docker run -v $(pwd):/firmware card10-firmware-builder
......@@ -5,35 +5,18 @@ 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
Bootloader
Jul 12 2019
USB activated.
Waiting.
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.
Afterwards **eject** the flash device and reboot card10. You should now see
your new firmware boot up!
If you do not have a debugger, you have to update the firmware using our
bootloader by going into :ref:`usb_file_transfer`.
.. warning::
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.
**You must EJECT the flash device!** ``umount`` & ``sync`` is **not**
enough and will result in the bootloader not loading the new binary.
The bootloader will then display ``Writing.`` in red while it is actually
writing the file to external flash. Please wait until it displays ``Ready.``
again before resetting card10 by pressing the power button again.
Flash Using Debugger
--------------------
......
......@@ -21,18 +21,43 @@ Last but not least, if you want to start hacking the lower-level firmware, the
:caption: Pycardium
pycardium/overview
pycardium/stdlib
pycardium/bhi160
pycardium/ble_hid
pycardium/bme680
pycardium/max30001
pycardium/max86150
pycardium/buttons
pycardium/color
pycardium/config
pycardium/display
pycardium/gpio
pycardium/leds
pycardium/light-sensor
pycardium/os
pycardium/personal_state
pycardium/png
pycardium/power
pycardium/pride
pycardium/simple_menu
pycardium/utime
pycardium/vibra
pycardium/ws2812
.. toctree::
:maxdepth: 1
:caption: Firmware
overview
card10-cfg
usb-file-transfer
how-to-build
how-to-flash
debugger
pycardium-guide
memorymap
epicardium/mutex
epicardium/sensor-streams
.. toctree::
:maxdepth: 1
......@@ -41,5 +66,22 @@ Last but not least, if you want to start hacking the lower-level firmware, the
epicardium/overview
epicardium/api
epicardium-guide
epicardium/internal
.. toctree::
:maxdepth: 1
:caption: Bluetooth
bluetooth/overview
bluetooth/ess
bluetooth/file-transfer
bluetooth/card10
bluetooth/ecg
bluetooth/nimble
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
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 | |
|------------|-----------------------|
......@@ -14,33 +14,75 @@ 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
Bluetooth.
+--------------------+-------------------------------+----------+-------------------------------------------+
| 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 |
+--------------------+-------------------------------+----------+-------------------------------------------+
.. todo::
.. _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
The following tasks have not yet been implemented/are currently in the works:
.. _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
- **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
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.
\*cardium
---------
.. warning::
.. _l0dables:
We have not yet been able to determine if card10 will have had the abilities
described in this section. And if yes, how they will have been implemented.
l0dables
--------
Next to Pycardium, other bare-metal code can also run on core 1. For example,
a Rustcardium or Cccardium.
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
......@@ -40,13 +40,9 @@ the output of
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.
.. todo::
External flash is not yet accessible by Pycardium (ref `#11`_).
.. _#11: https://git.card10.badge.events.ccc.de/card10/firmware/issues/11
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
-----------------------
......@@ -82,8 +78,15 @@ looks like this:
};
/* 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
......
.. 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).