diff --git a/Documentation/conf.py b/Documentation/conf.py
index 10886a9fb03ead2822cafc09b08c717c2cc23b84..a09ee94ed0c473d97e1e1f216f6338df00294105 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -117,6 +117,7 @@ html_context = {
 autodoc_mock_imports = [
     "buttons",
     "interrupt",
+    "sys_ble_hid",
     "sys_bme680",
     "sys_bhi160",
     "sys_display",
diff --git a/Documentation/index.rst b/Documentation/index.rst
index 1090ad48cb9d2fce0e4cc5ed58512cd4b133c737..2816dcd17ed6f00868c18b0b8b6ef83c21c9cf9f 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -23,6 +23,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the
    pycardium/overview
    pycardium/stdlib
    pycardium/bhi160
+   pycardium/ble_hid
    pycardium/bme680
    pycardium/max30001
    pycardium/max86150
diff --git a/Documentation/pycardium/ble_hid.rst b/Documentation/pycardium/ble_hid.rst
new file mode 100644
index 0000000000000000000000000000000000000000..221d5a9c87b484618f46bd24d871de9f55eb1c4e
--- /dev/null
+++ b/Documentation/pycardium/ble_hid.rst
@@ -0,0 +1,87 @@
+``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 ``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)
+
+
+
+
+.. automodule:: ble_hid
+   :members:
diff --git a/pycardium/modules/py/ble_hid.py b/pycardium/modules/py/ble_hid.py
index 3a1bcda03674cd61908fd0cccc66264da6987520..1b9a5b7b09aba972d519f925f3a21dc0229c9371 100644
--- a/pycardium/modules/py/ble_hid.py
+++ b/pycardium/modules/py/ble_hid.py
@@ -3,23 +3,83 @@ import time
 
 
 class Report:
-    def __init__(self, report_id, usage_page, usage):
+    """
+    The Report class provides an interface to the Adafruit CircuitPython HID library
+    (https://github.com/adafruit/Adafruit_CircuitPython_HID/).
+
+    ``ble_hid.devices`` exposes a list of reports for use with the CircuitPython HID
+    classes. You usually do not have to interact with a report yourself but you can make
+    use of its ``send_report`` method to send raw HID reports to the host.
+
+    **Example using Adafruit CircuitPython HID library**:
+
+    .. code-block:: python
+
+        import ble_hid
+        from adafruit_hid.mouse import Mouse
+
+        m = Mouse(ble_hid.devices)
+        m.click(Mouse.MIDDLE_BUTTON)
+
+    **Example using raw non blocking access**:
+    .. code-block:: python
+
+        import ble_hid
+        report = ble_hid.Report(report_id=3, blocking=False) # Consumer Control
+        report.send_report(b'\\xe9\\x00') # 0x00E9: Volume Increase
+        report.send_report(b'\\x00\\x00') # 0x0000: Release button
+
+
+    .. versionadded:: 1.17
+    """
+
+    def __init__(self, report_id, blocking=False, usage_page=None, usage=None):
+        """
+        Initializes a report.
+
+        All parameters are available as properties of the resulting object and can be modified
+        during runtime.
+
+        :param report_id: The id of the report. Currently supported: 1: Keyboard, 2: Mouse, 3: Consumer control
+        :param blocking: If True :py:func`send_report()` will try sending the report until
+                         there is space in the queue (unless the host is not connected).
+        :param usage_page: Used by Adafruit CircuitPython HID library to identify report
+        :param usage: Used by Adafruit CircuitPython HID library to identify report
+        """
+
         self.report_id = report_id
         self.usage_page = usage_page
         self.usage = usage
+        self.blocking = True
 
     def send_report(self, data):
-        while True:
-            try:
-                sys_ble_hid.send_report(self.report_id, data)
-                break
-            except MemoryError:
-                time.sleep(0.1)
+        """
+        Tries to send a report to the host.
+
+        :param data: The data to be sent. Must not exceed the configured length of the report.
+        :rtype: bool
+        :returns: `True` if the report was sent, `False` if the report was queued for sending.
+        :raises OSError: if there is no connection to a host
+        :raises MemoryError: if there was no space in the queue (only raised if ``blocking`` was set to `False`)
+        """
+
+        if self.blocking:
+            # Loop until we are able to place the report in the queue
+            # Forward all other exceptions to the caller
+            while True:
+                try:
+                    return sys_ble_hid.send_report(self.report_id, data)
+                    break
+                except MemoryError:
+                    time.sleep(0.1)
+        else:
+            # Forward all exceptions to the caller
+            return sys_ble_hid.send_report(self.report_id, data)
 
 
 # Reports as defined in the HID report map in epicardium/ble/hid.c
 devices = [
-    Report(report_id=1, usage_page=0x01, usage=0x06),
-    Report(report_id=2, usage_page=0x01, usage=0x02),
-    Report(report_id=3, usage_page=0x0C, usage=0x01),
+    Report(report_id=1, blocking=True, usage_page=0x01, usage=0x06),
+    Report(report_id=2, blocking=True, usage_page=0x01, usage=0x02),
+    Report(report_id=3, blocking=True, usage_page=0x0C, usage=0x01),
 ]
diff --git a/pycardium/modules/sys_ble_hid.c b/pycardium/modules/sys_ble_hid.c
index f7cf77f4824cdde40a53c16743d88bfed9cacae3..7bc725babaf50cf47a53337fe6ca4e25b6521dea 100644
--- a/pycardium/modules/sys_ble_hid.c
+++ b/pycardium/modules/sys_ble_hid.c
@@ -21,7 +21,7 @@ static mp_obj_t mp_sys_ble_hid_send_report(mp_obj_t report_id, mp_obj_t data)
 		mp_raise_OSError(-ret);
 	}
 
-	return mp_obj_new_int(ret);
+	return ret == 0 ? mp_const_true : mp_const_false;
 }
 static MP_DEFINE_CONST_FUN_OBJ_2(
 	sys_ble_hid_send_report_obj, mp_sys_ble_hid_send_report