Skip to content
Snippets Groups Projects
epicardium.h 45.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • #ifndef _EPICARDIUM_H
    #define _EPICARDIUM_H
    
    #include <stdint.h>
    
    #include <errno.h>
    
    #ifndef __SPHINX_DOC
    
    fleur's avatar
    fleur committed
    /* Some headers are not recognized by hawkmoth for some odd reason */
    
    #include <stddef.h>
    
    fleur's avatar
    fleur committed
    #include <stdbool.h>
    
    #else
    typedef unsigned int size_t;
    
    fleur's avatar
    fleur committed
    typedef _Bool bool;
    
    #endif /* __SPHINX_DOC */
    
    /*
     * These definitions are required for the code-generator.  Please don't touch!
     */
    
    #ifndef API
    
    rahix's avatar
    rahix committed
    #define API(id, def) def
    
    #endif
    
    #ifndef API_ISR
    #define API_ISR(id, isr) void isr(void);
    #endif
    
    /*
     * IDs for all defined API calls.  These IDs should not be needed in application
     * code on any side.
     */
    
    /* clang-format off */
    
    #define API_SYSTEM_EXIT             0x1
    #define API_SYSTEM_EXEC             0x2
    
    #define API_SYSTEM_RESET            0x3
    
    #define API_BATTERY_VOLTAGE         0x4
    
    
    #define API_INTERRUPT_ENABLE        0xA
    #define API_INTERRUPT_DISABLE       0xB
    
    #define API_UART_WRITE_STR         0x10
    #define API_UART_READ_CHAR         0x11
    #define API_UART_READ_STR          0x12
    
    #define API_STREAM_READ            0x1F
    
    
    #define API_DISP_OPEN              0x20
    #define API_DISP_CLOSE             0x21
    #define API_DISP_PRINT             0x22
    #define API_DISP_CLEAR             0x23
    #define API_DISP_UPDATE            0x24
    #define API_DISP_LINE              0x25
    #define API_DISP_RECT              0x26
    #define API_DISP_CIRC              0x27
    #define API_DISP_PIXEL             0x28
    #define API_DISP_FRAMEBUFFER       0x29
    
    #define API_FILE_OPEN              0x40
    #define API_FILE_CLOSE             0x41
    #define API_FILE_READ              0x42
    #define API_FILE_WRITE             0x44
    #define API_FILE_FLUSH             0x45
    #define API_FILE_SEEK              0x46
    #define API_FILE_TELL              0x47
    #define API_FILE_STAT              0x48
    
    #define API_FILE_OPENDIR           0x49
    #define API_FILE_READDIR           0x4a
    #define API_FILE_UNLINK            0x4b
    
    #define API_FILE_RENAME            0x4c
    #define API_FILE_MKDIR             0x4d
    
    
    #define API_RTC_GET_SECONDS        0x50
    #define API_RTC_SCHEDULE_ALARM     0x51
    
    #define API_RTC_SET_MILLISECONDS   0x52
    
    
    #define API_LEDS_SET               0x60
    
    fleur's avatar
    fleur committed
    #define API_LEDS_SET_HSV           0x61
    #define API_LEDS_PREP              0x62
    #define API_LEDS_PREP_HSV          0x63
    #define API_LEDS_UPDATE            0x64
    #define API_LEDS_SET_POWERSAVE     0x65
    #define API_LEDS_SET_ROCKET        0x66
    #define API_LEDS_SET_FLASHLIGHT    0x67
    #define API_LEDS_DIM_TOP           0x68
    #define API_LEDS_DIM_BOTTOM        0x69
    #define API_LEDS_SET_ALL           0x6a
    #define API_LEDS_SET_ALL_HSV       0x6b
    #define API_LEDS_SET_GAMMA_TABLE   0x6c
    
    #define API_LEDS_CLEAR_ALL         0x6d
    
    
    #define API_VIBRA_SET              0x70
    #define API_VIBRA_VIBRATE          0x71
    
    #define API_LIGHT_SENSOR_RUN       0x80
    #define API_LIGHT_SENSOR_GET       0x81
    #define API_LIGHT_SENSOR_STOP      0x82
    
    
    #define API_BUTTONS_READ           0x90
    
    
    #define API_GPIO_SET_PIN_MODE      0xA0
    #define API_GPIO_GET_PIN_MODE      0xA1
    #define API_GPIO_WRITE_PIN         0xA2
    #define API_GPIO_READ_PIN          0xA3
    
    
    #define API_TRNG_READ              0xB0
    
    
    #define API_PERSONAL_STATE_SET     0xc0
    #define API_PERSONAL_STATE_GET     0xc1
    #define API_PERSONAL_STATE_IS_PERSISTENT 0xc2
    
    
    #define API_BME680_INIT            0xD0
    #define API_BME680_DEINIT          0xD1
    #define API_BME680_GET_DATA        0xD2
    
    
    #define API_BHI160_ENABLE          0xe0
    #define API_BHI160_DISABLE         0xe1
    
    
    /* clang-format on */
    
    typedef uint32_t api_int_id_t;
    
    /**
     * Interrupts
     * ==========
     * Next to API calls, Epicardium API also has an interrupt mechanism to serve
     * the other direction.  These interrupts can be enabled/disabled
     * (masked/unmasked) using :c:func:`epic_interrupt_enable` and
     * :c:func:`epic_interrupt_disable`.
     */
    
    /**
     * Enable/unmask an API interrupt.
     *
     * :param int_id: The interrupt to be enabled
     */
    API(API_INTERRUPT_ENABLE, int epic_interrupt_enable(api_int_id_t int_id));
    
    /**
     * Disable/mask an API interrupt.
     *
     * :param int_id: The interrupt to be disabled
     */
    API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
    
    /**
     * The following interrupts are defined:
     */
    
    /* clang-format off */
    
    /** Reset Handler */
    
    #define EPIC_INT_RESET                  0
    /** ``^C`` interrupt. See :c:func:`epic_isr_ctrl_c` for details.  */
    #define EPIC_INT_CTRL_C                 1
    
    /** UART Receive interrupt.  See :c:func:`epic_isr_uart_rx`. */
    
    #define EPIC_INT_UART_RX                2
    
    /** RTC Alarm interrupt.  See :c:func:`epic_isr_rtc_alarm` */
    #define EPIC_INT_RTC_ALARM              3
    
    /** BHI */
    
    #define EPIC_INT_BHI160_ACCELEROMETER   4
    API_ISR(EPIC_INT_BHI160_ACCELEROMETER, epic_isr_bhi160_accelerometer);
    
    #define EPIC_INT_BHI160_ORIENTATION     5
    API_ISR(EPIC_INT_BHI160_ORIENTATION, epic_isr_bhi160_orientation);
    #define EPIC_INT_BHI160_GYROSCOPE       6
    
    API_ISR(EPIC_INT_BHI160_GYROSCOPE, epic_isr_bhi160_gyroscope);
    
    /* Number of defined interrupts. */
    
    #define EPIC_INT_NUM                    7
    
    /* clang-format on */
    
    
    /*
     * "Reset Handler*.  This isr is implemented by the API caller and is used to
     * reset the core for loading a new payload.
     *
     * Just listed here for completeness.  You don't need to implement this yourself.
     */
    API_ISR(EPIC_INT_RESET, __epic_isr_reset);
    
    /**
     * Core API
     * ========
     * The following functions control execution of code on core 1.
     */
    
    /**
     * Stop execution of the current payload and return to the menu.
     *
     * :param int ret:  Return code.
     * :return: :c:func:`epic_exit` will never return.
     */
    void epic_exit(int ret) __attribute__((noreturn));
    
    /*
     * The actual epic_exit() function is not an API call because it needs special
     * behavior.  The underlying call is __epic_exit() which returns.  After calling
     * this API function, epic_exit() will enter the reset handler.
     */
    API(API_SYSTEM_EXIT, void __epic_exit(int ret));
    
    /**
     * Stop execution of the current payload and immediately start another payload.
     *
     * :param char* name: Name (path) of the new payload to start.  This can either
     *    be:
     *
     *    - A path to an ``.elf`` file (l0dable).
     *    - A path to a ``.py`` file (will be loaded using Pycardium).
     *    - A path to a directory (assumed to be a Python module, execution starts
     *      with ``__init__.py`` in this folder).
     *
     * :return: :c:func:`epic_exec` will only return in case loading went wrong.
     *    The following error codes can be returned:
     *
     *    - ``-ENOENT``: File not found.
     *    - ``-ENOEXEC``: File not a loadable format.
     */
    int epic_exec(char *name);
    
    /*
     * Underlying API call for epic_exec().  The function is not an API call itself
     * because it needs special behavior when loading a new payload.
     */
    API(API_SYSTEM_EXEC, int __epic_exec(char *name));
    
    /**
     * Reset/Restart card10
     */
    API(API_SYSTEM_RESET, void epic_system_reset(void));
    
    
    /**
     * Battery Voltage
     * ===============
     */
    
    /**
     * Read the current battery voltage.
     */
    API(API_BATTERY_VOLTAGE, int epic_read_battery_voltage(float *result));
    
    
    /**
     * UART/Serial Interface
     * =====================
     */
    
    /**
     * Write a string to all connected serial devices.  This includes:
     *
     * - Real UART, whose pins are mapped onto USB-C pins.  Accessible via the HW-debugger.
     * - A CDC-ACM device available via USB.
     * - Maybe, in the future, bluetooth serial?
     *
     * :param str:  String to write.  Does not necessarily have to be NULL-terminated.
     * :param length:  Amount of bytes to print.
     */
    
    API(API_UART_WRITE_STR, void epic_uart_write_str(
    	const char *str,
    	intptr_t length
    ));
    
     * Try reading a single character from any connected serial device.
    
     * If nothing is available, :c:func:`epic_uart_read_char` returns ``(-1)``.
    
     * :return:  The byte or ``(-1)`` if no byte was available.
     */
    API(API_UART_READ_CHAR, int epic_uart_read_char(void));
    
    
    /**
     * Read as many characters as possible from the UART queue.
     *
     * :c:func:`epic_uart_read_str` will not block if no new data is available.  For
     * an example, see :c:func:`epic_isr_uart_rx`.
     *
     * :param char* buf: Buffer to be filled with incoming data.
     * :param size_t cnt: Size of ``buf``.
     * :returns: Number of bytes read.  Can be ``0`` if no data was available.
     *    Might be a negative value if an error occured.
     */
    API(API_UART_READ_STR, int epic_uart_read_str(char *buf, size_t cnt));
    
    
    /**
     * **Interrupt Service Routine**
     *
     * UART receive interrupt.  This interrupt is triggered whenever a new character
     * becomes available on any connected UART device.  This function is weakly
     * aliased to :c:func:`epic_isr_default` by default.
     *
     * **Example**:
     *
     * .. code-block:: cpp
     *
     *    void epic_isr_uart_rx(void)
     *    {
     *            char buffer[33];
     *            int n = epic_uart_read_str(&buffer, sizeof(buffer) - 1);
     *            buffer[n] = '\0';
     *            printf("Got: %s\n", buffer);
     *    }
     *
     *    int main(void)
     *    {
     *            epic_interrupt_enable(EPIC_INT_UART_RX);
    
     *            while (1) {
     *                    __WFI();
     *            }
     *    }
    
    API_ISR(EPIC_INT_UART_RX, epic_isr_uart_rx);
    
    /**
     * **Interrupt Service Routine**
     *
     * A user-defineable ISR which is triggered when a ``^C`` (``0x04``) is received
     * on any serial input device.  This function is weakly aliased to
     * :c:func:`epic_isr_default` by default.
     *
     * To enable this interrupt, you need to enable :c:data:`EPIC_INT_CTRL_C`:
     *
     * .. code-block:: cpp
     *
     *    epic_interrupt_enable(EPIC_INT_CTRL_C);
     */
    API_ISR(EPIC_INT_CTRL_C, epic_isr_ctrl_c);
    
    
    /**
     * Buttons
     * =======
     *
     */
    
    /** Button IDs */
    enum epic_button {
    	/** ``1``, Bottom left button (bit 0). */
    	BUTTON_LEFT_BOTTOM   = 1,
    	/** ``2``, Bottom right button (bit 1). */
    	BUTTON_RIGHT_BOTTOM  = 2,
    	/** ``4``, Top right button (bit 2). */
    	BUTTON_RIGHT_TOP     = 4,
    	/** ``8``, Top left (power) button (bit 3). */
    	BUTTON_LEFT_TOP      = 8,
    	/** ``8``, Top left (power) button (bit 3). */
    	BUTTON_RESET         = 8,
    };
    
    /**
     * Read buttons.
     *
     * :c:func:`epic_buttons_read` will read all buttons specified in ``mask`` and
     * return set bits for each button which was reported as pressed.
     *
     * .. note::
     *
     *    The reset button cannot be unmapped from reset functionality.  So, while
     *    you can read it, it cannot be used for app control.
     *
     * **Example**:
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    uint8_t pressed = epic_buttons_read(BUTTON_LEFT_BOTTOM | BUTTON_RIGHT_BOTTOM);
     *
     *    if (pressed & BUTTON_LEFT_BOTTOM) {
     *            // Bottom left button is pressed
     *    }
     *
     *    if (pressed & BUTTON_RIGHT_BOTTOM) {
     *            // Bottom right button is pressed
     *    }
     *
     * :param uint8_t mask: Mask of buttons to read.  The 4 LSBs correspond to the 4
     *     buttons:
     *
     *     ===== ========= ============ ===========
     *     ``3`` ``2``     ``1``        ``0``
     *     ----- --------- ------------ -----------
     *     Reset Right Top Right Bottom Left Bottom
     *     ===== ========= ============ ===========
     *
     *     Use the values defined in :c:type:`epic_button` for masking, as shown in
     *     the example above.
     * :return: Returns nonzero value if unmasked buttons are pushed.
     */
    API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask));
    
    
    /**
     * Wristband GPIO
     * ==============
     */
    
    /** GPIO pins IDs */
    enum gpio_pin {
        /** ``1``, Wristband connector 1 */
        GPIO_WRISTBAND_1 = 1,
        /** ``2``, Wristband connector 2 */
        GPIO_WRISTBAND_2 = 2,
        /** ``3``, Wristband connector 3 */
        GPIO_WRISTBAND_3 = 3,
        /** ``4``, Wristband connector 4 */
        GPIO_WRISTBAND_4 = 4,
    };
    
    /** GPIO pin modes */
    enum gpio_mode {
        /** Configure the pin as input */
        GPIO_MODE_IN = (1<<0),
        /** Configure the pin as output */
        GPIO_MODE_OUT = (1<<1),
    
        /** Enable the internal pull-up resistor */
        GPIO_PULL_UP = (1<<6),
        /** Enable the internal pull-down resistor */
        GPIO_PULL_DOWN = (1<<7),
    };
    
    /**
     * Set the mode of a card10 GPIO pin.
     *
     * :c:func:`epic_gpio_set_pin_mode` will set the pin specified by ``pin`` to the mode ``mode``.
     * If the specified pin ID is not valid this function will do nothing.
     *
     * **Example:**
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    // Configure wristband pin 1 as output.
     *    if (epic_gpio_set_pin_mode(GPIO_WRISTBAND_1, GPIO_MODE_OUT)) {
     *        // Do your error handling here...
     *    }
     *
     * :param uint8_t pin: ID of the pin to configure. Use on of the IDs defined in :c:type:`gpio_pin`.
     * :param uint8_t mode: Mode to be configured. Use a combination of the :c:type:`gpio_mode` flags.
     * :returns: ``0`` if the mode was set, ``-EINVAL`` if ``pin`` is not valid or the mode could not be set.
     */
    API(API_GPIO_SET_PIN_MODE, int epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode));
    
    /**
     * Get the mode of a card10 GPIO pin.
     *
     * :c:func:`epic_gpio_get_pin_mode` will get the current mode of the GPIO pin specified by ``pin``.
     *
     * **Example:**
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    // Get the mode of wristband pin 1.
     *    int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1);
     *    if (mode < 0) {
     *        // Do your error handling here...
     *    } else {
     *        // Do something with the queried mode information
     *    }
     *
     * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
     * :returns: Configuration byte for the specified pin or ``-EINVAL`` if the pin is not valid.
     */
    API(API_GPIO_GET_PIN_MODE, int epic_gpio_get_pin_mode(uint8_t pin));
    
    /**
     * Write value to a card10 GPIO pin,
     *
     * :c:func:`epic_gpio_write_pin` will set the value of the GPIO pin described by ``pin`` to either on or off depending on ``on``.
     *
     * **Example:**
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    // Get the mode of wristband pin 1.
     *    int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1);
     *    if (mode < 0) {
     *        // Do your error handling here...
     *    } else {
     *        // Do something with the queried mode information
     *    }
     *
     * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
     * :param bool on: Sets the pin to either true (on/high) or false (off/low).
     * :returns: ``0`` on succcess, ``-EINVAL`` if ``pin`` is not valid or is not configured as an output.
     */
    API(API_GPIO_WRITE_PIN, int epic_gpio_write_pin(uint8_t pin, bool on));
    
    /**
     * Read value of a card10 GPIO pin.
     *
     * :c:func:`epic_gpio_read_pin` will get the value of the GPIO pin described by ``pin``.
     *
     * **Example:**
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    // Get the current value of wristband pin 1.
     *    uint32_t value = epic_gpio_read_pin(GPIO_WRISTBAND_1);
     *    if (mode == -EINVAL) {
     *        // Do your error handling here...
     *    } else {
     *        // Do something with the current value
     *    }
     *
     * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`.
     * :returns: ``-EINVAL`` if ``pin`` is not valid, an integer value otherwise.
     */
    API(API_GPIO_READ_PIN, uint32_t epic_gpio_read_pin(uint8_t pin));
    
    
    fleur's avatar
    fleur committed
     * Set one of card10's RGB LEDs to a certain color in RGB format.
    
    fleur's avatar
    fleur committed
     * This function is rather slow when setting multiple LEDs, use
     * :c:func:`leds_set_all` or :c:func:`leds_prep` + :c:func:`leds_update`
     * instead.
    
    fleur's avatar
    fleur committed
     * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
     *    are the 4 "ambient" LEDs.
     * :param uint8_t r:  Red component of the color.
     * :param uint8_t g:  Green component of the color.
     * :param uint8_t b:  Blue component of the color.
    
    API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b));
    
    
    fleur's avatar
    fleur committed
    /**
     * Set one of card10's RGB LEDs to a certain color in HSV format.
     *
     * This function is rather slow when setting multiple LEDs, use
     * :c:func:`leds_set_all_hsv` or :c:func:`leds_prep_hsv` + :c:func:`leds_update`
     * instead.
     *
     * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14 are the 4 "ambient" LEDs.
     * :param float h:  Hue component of the color. (0 <= h < 360)
     * :param float s:  Saturation component of the color. (0 <= s <= 1)
     * :param float v:  Value/Brightness component of the color. (0 <= v <= 0)
     */
    API(API_LEDS_SET_HSV, void epic_leds_set_hsv(int led, float h, float s, float v));
    
    /**
     * Set multiple of card10's RGB LEDs to a certain color in RGB format.
     *
     * The first ``len`` leds are set, the remaining ones are not modified.
     *
     * :param uint8_t[len][r,g,b] pattern:  Array with RGB Values with 0 <= len <=
     *    15. 0-10 are the LEDs on the top and 11-14 are the 4 "ambient" LEDs.
     * :param uint8_t len: Length of 1st dimension of ``pattern``, see above.
     */
    API(API_LEDS_SET_ALL, void epic_leds_set_all(uint8_t *pattern, uint8_t len));
    
    /**
     * Set multiple of card10's RGB LEDs to a certain color in HSV format.
     *
     * The first ``len`` led are set, the remaining ones are not modified.
     *
     * :param uint8_t[len][h,s,v] pattern:  Array of format with HSV Values with 0
     *    <= len <= 15.  0-10 are the LEDs on the top and 11-14 are the 4 "ambient"
     *    LEDs. (0 <= h < 360, 0 <= s <= 1, 0 <= v <= 1)
     * :param uint8_t len: Length of 1st dimension of ``pattern``, see above.
     */
    API(API_LEDS_SET_ALL_HSV, void epic_leds_set_all_hsv(float *pattern, uint8_t len));
    
    /**
     * Prepare one of card10's RGB LEDs to be set to a certain color in RGB format.
     *
     * Use :c:func:`leds_update` to apply changes.
     *
     * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
     *    are the 4 "ambient" LEDs.
     * :param uint8_t r:  Red component of the color.
     * :param uint8_t g:  Green component of the color.
     * :param uint8_t b:  Blue component of the color.
     */
    API(API_LEDS_PREP, void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b));
    
    /**
     * Prepare one of card10's RGB LEDs to be set to a certain color in HSV format.
     *
     * Use :c:func:`leds_update` to apply changes.
     *
     * :param int led:  Which LED to set.  0-10 are the LEDs on the top and 11-14
     *    are the 4 "ambient" LEDs.
     * :param uint8_t h:  Hue component of the color. (float, 0 <= h < 360)
     * :param uint8_t s:  Saturation component of the color. (float, 0 <= s <= 1)
     * :param uint8_t v:  Value/Brightness component of the color. (float, 0 <= v <= 0)
     */
    API(API_LEDS_PREP_HSV, void epic_leds_prep_hsv(int led, float h, float s, float v));
    
    /**
     * Set global brightness for top RGB LEDs.
     *
     * Aside from PWM, the RGB LEDs' overall brightness can be controlled with a
     * current limiter independently to achieve a higher resolution at low
     * brightness which can be set with this function.
     *
     * :param uint8_t value:  Global brightness of top LEDs. (1 <= value <= 8, default = 1)
     */
    API(API_LEDS_DIM_BOTTOM, void epic_leds_dim_bottom(uint8_t value));
    
    /**
     * Set global brightness for bottom RGB LEDs.
     *
     * Aside from PWM, the RGB LEDs' overall brightness can be controlled with a
     * current limiter independently to achieve a higher resolution at low
     * brightness which can be set with this function.
     *
     * :param uint8_t value:  Global brightness of bottom LEDs. (1 <= value <= 8, default = 8)
     */
    API(API_LEDS_DIM_TOP, void epic_leds_dim_top(uint8_t value));
    
    /**
     * Enables or disables powersave mode.
     *
     * Even when set to zero, the RGB LEDs still individually consume ~1mA.
     * Powersave intelligently switches the supply power in groups. This introduces
     * delays in the magnitude of ~10µs, so it can be disabled for high speed
     * applications such as POV.
     *
     * :param bool eco:  Activates powersave if true, disables it when false. (default = True)
     */
    API(API_LEDS_SET_POWERSAVE, void epic_leds_set_powersave(bool eco));
    
    /**
     * Updates the RGB LEDs with changes that have been set with :c:func:`leds_prep`
     * or :c:func:`leds_prep_hsv`.
     *
     * The LEDs can be only updated in bulk, so using this approach instead of
     * :c:func:`leds_set` or :c:func:`leds_set_hsv` significantly reduces the load
     * on the corresponding hardware bus.
     */
    API(API_LEDS_UPDATE, void epic_leds_update(void));
    
    /**
     * Set the brightness of one of the rocket LEDs.
     *
     * :param int led:  Which LED to set.
     *
     *    +-------+--------+----------+
     *    |   ID  | Color  | Location |
     *    +=======+========+==========+
     *    | ``0`` | Blue   | Left     |
     *    +-------+--------+----------+
     *    | ``1`` | Yellow | Top      |
     *    +-------+--------+----------+
     *    | ``2`` | Green  | Right    |
     *    +-------+--------+----------+
    
     * :param uint8_t value:  Brightness of LED (value between 0 and 31).
    
    fleur's avatar
    fleur committed
     */
    API(API_LEDS_SET_ROCKET, void epic_leds_set_rocket(int led, uint8_t value));
    
    /**
     * Turn on the bright side LED which can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist.
     *
     *:param bool power:  Side LED on if true.
     */
    API(API_LEDS_SET_FLASHLIGHT, void epic_set_flashlight(bool power));
    
    /**
     * Set gamma lookup table for individual rgb channels.
     *
     * Since the RGB LEDs' subcolor LEDs have different peak brightness and the
     * linear scaling introduced by PWM is not desireable for color accurate work,
     * custom lookup tables for each individual color channel can be loaded into the
     * Epicardium's memory with this function.
     *
     * :param uint8_t rgb_channel:  Color whose gamma table is to be updated, 0->Red, 1->Green, 2->Blue.
     * :param uint8_t[256] gamma_table: Gamma lookup table. (default = 4th order power function rounded up)
     */
    API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table(
    	uint8_t rgb_channel,
    	uint8_t *gamma_table
    ));
    
    
    /**
     * Set all LEDs to a certain RGB color.
     *
     * :param uint8_t r: Value for the red color channel.
     * :param uint8_t g: Value for the green color channel.
     * :param uint8_t b: Value for the blue color channel.
     */
    API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b));
    
    
    /**
     * BME680
     * ======
     */
    
    /**
     * BME680 Sensor Data
     */
    struct bme680_sensor_data {
    	/** Temperature in degree celsius */
    	float temperature;
    	/** Humidity in % relative humidity */
    	float humidity;
    	/** Pressure in hPa */
    	float pressure;
    	/** Gas resistance in Ohms */
    	float gas_resistance;
    };
    
    /**
     * Initialize the BM680 sensor.
     *
     * :return: 0 on success or ``-Exxx`` on error.  The following
     *     errors might occur:
     *
     *     - ``-EFAULT``:  On NULL-pointer.
     *     - ``-EINVAL``:  Invalid configuration.
     *     - ``-EIO``:  Communication with the device failed.
     *     - ``-ENODEV``:  Device was not found.
     */
    API(API_BME680_INIT, int epic_bme680_init());
    
    /**
     * De-Initialize the BM680 sensor.
     *
     * :return: 0 on success or ``-Exxx`` on error.  The following
     *     errors might occur:
     *
     *     - ``-EFAULT``:  On NULL-pointer.
     *     - ``-EINVAL``:  Invalid configuration.
     *     - ``-EIO``:  Communication with the device failed.
     *     - ``-ENODEV``:  Device was not found.
     */
    API(API_BME680_DEINIT, int epic_bme680_deinit());
    
    /**
     * Get the current BME680 data.
     *
     * :param data: Where to store the environmental data.
     * :return: 0 on success or ``-Exxx`` on error.  The following
     *     errors might occur:
     *
     *     - ``-EFAULT``:  On NULL-pointer.
     *     - ``-EINVAL``:  Sensor not initialized.
     *     - ``-EIO``:  Communication with the device failed.
     *     - ``-ENODEV``:  Device was not found.
     */
    API(API_BME680_GET_DATA,
    	int epic_bme680_read_sensors(struct bme680_sensor_data *data));
    
    
    /**
     * Personal State
     * ==============
     * Card10 can display your personal state.
     *
     * If a personal state is set the top-left LED on the bottom side of the
     * harmonics board is directly controlled by epicardium and it can't be
     * controlled by pycardium.
     *
     * To re-enable pycardium control the personal state has to be cleared. To do
     * that simply set it to ``STATE_NONE``.
     *
     * The personal state can be set to be persistent which means it won't get reset
     * on pycardium application change/restart.
     */
    
    /** Possible personal states. */
    enum personal_state {
        /** ``0``, No personal state - LED is under regular application control. */
        STATE_NONE = 0,
        /** ``1``, "no contact, please!" - I am overloaded. Please leave me be - red led, continuously on. */
        STATE_NO_CONTACT = 1,
        /** ``2``, "chaos" - Adventure time - blue led, short blink, long blink. */
        STATE_CHAOS = 2,
        /** ``3``, "communication" - want to learn something or have a nice conversation - green led, long blinks. */
        STATE_COMMUNICATION = 3,
        /** ``4``, "camp" - I am focussed on self-, camp-, or community maintenance - yellow led, fade on and off. */
        STATE_CAMP = 4,
    };
    
    /**
     * Set the users personal state.
     *
     * Using :c:func:`epic_personal_state_set` an application can set the users personal state.
     *
     * :param uint8_t state: The users personal state. Must be one of :c:type:`personal_state`.
     * :param bool persistent: Indicates whether the configured personal state will remain set and active on pycardium application restart/change.
     * :returns: ``0`` on success, ``-EINVAL`` if an invalid state was requested.
     */
    API(API_PERSONAL_STATE_SET, int epic_personal_state_set(uint8_t state,
                                                            bool persistent));
    
    /**
     * Get the users personal state.
     *
     * Using :c:func:`epic_personal_state_get` an application can get the currently set personal state of the user.
     *
     * :returns: A value with exactly one value of :c:type:`personal_state` set.
     */
    API(API_PERSONAL_STATE_GET, int epic_personal_state_get());
    
    /**
     * Get whether the users personal state is persistent.
     *
     * Using :c:func:`epic_personal_state_is_persistent` an app can find out whether the users personal state is persistent or transient.
     *
     * :returns: ``1`` if the state is persistent, ``0`` otherwise.
     */
    API(API_PERSONAL_STATE_IS_PERSISTENT, int epic_personal_state_is_persistent());
    
    
    /**
     * Sensor Data Streams
     * ===================
     * A few of card10's sensors can do continuous measurements.  To allow
     * performant access to their data, the following function is made for generic
     * access to streams.
     */
    
    /**
     * Read sensor data into a buffer.  ``epic_stream_read()`` will read as many
    
     * sensor samples into the provided buffer as possible and return the number of
     * samples written.  If no samples are available, ``epic_stream_read()`` will
     * return ``0`` immediately.
     *
     * ``epic_stream_read()`` expects the provided buffer to have a size which is a
     * multiple of the sample size for the given stream.  For the sample-format and
     * size, please consult the sensors documentation.
     *
     * Before reading the internal sensor sample queue, ``epic_stream_read()`` will
     * call a sensor specific *poll* function to allow the sensor driver to fetch
     * new samples from its hardware.  This should, however, never take a long
     * amount of time.
    
     *
     * :param int sd: Sensor Descriptor.  You get sensor descriptors as return
     *    values when activating the respective sensors.
     * :param void* buf: Buffer where sensor data should be read into.
     * :param size_t count: How many bytes to read at max.  Note that fewer bytes
     *    might be read.  In most cases, this should be ``sizeof(buf)``.
     * :return: Number of data packets read (**not** number of bytes) or a negative
     *    error value.  Possible errors:
     *
     *    - ``-ENODEV``: Sensor is not currently available.
     *    - ``-EBADF``: The given sensor descriptor is unknown.
    
     *    - ``-EINVAL``:  ``count`` is not a multiple of the sensor's sample size.
    
     *    - ``-EBUSY``: The descriptor table lock could not be acquired.
    
     *
     * **Example**:
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    struct foo_measurement sensor_data[16];
     *    int foo_sd, n;
     *
     *    foo_sd = epic_foo_sensor_enable(9001);
     *
     *    while (1) {
     *            n = epic_stream_read(
     *                    foo_sd,
     *                    &sensor_data,
     *                    sizeof(sensor_data)
     *            );
     *
     *            // Print out the measured sensor samples
     *            for (int i = 0; i < n; i++) {
     *                    printf("Measured: %?\n", sensor_data[i]);
     *            }
     *    }
     */
    API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count));
    
    
    /**
     * BHI160 Sensor Fusion
     * ====================
     * card10 has a BHI160 onboard which is used as an IMU.  BHI160 exposes a few
     * different sensors which can be accessed using Epicardium API.
     *
     * **Example**:
     *
     * .. code-block:: cpp
     *
     *    #include "epicardium.h"
     *
     *    // Configure a sensor & enable it
     *    struct bhi160_sensor_config cfg = {0};
     *    cfg.sample_buffer_len = 40;
     *    cfg.sample_rate = 4;   // Hz
     *    cfg.dynamic_range = 2; // g
     *
     *    int sd = epic_bhi160_enable_sensor(BHI160_ACCELEROMETER, &cfg);
     *
     *    // Read sensor data
     *    while (1) {
     *            struct bhi160_data_vector buf[10];
     *
     *            int n = epic_stream_read(sd, buf, sizeof(buf));
     *
     *            for (int i = 0; i < n; i++) {
     *                    printf("X: %6d Y: %6d Z: %6d\n",
     *                           buf[i].x,
     *                           buf[i].y,
     *                           buf[i].z);
     *            }
     *    }
     *
     *    // Disable the sensor
     *    epic_bhi160_disable_sensor(BHI160_ACCELEROMETER);
     */
    
    /**
     * BHI160 Sensor Types
     * -------------------
     */
    
    /**
     * BHI160 virtual sensor type.
     */
    enum bhi160_sensor_type {
    	/**
    	 * Accelerometer
    	 *
    	 * - Data type: :c:type:`bhi160_data_vector`
    	 * - Dynamic range: g's (1x Earth Gravity, ~9.81m*s^-2)
    	 */
    	BHI160_ACCELEROMETER               = 0,
    	/** Magnetometer (**Unimplemented**) */
    	BHI160_MAGNETOMETER                = 1,
    
    	/** Orientation */
    
    	BHI160_ORIENTATION                 = 2,
    
    	/** Gyroscope */
    
    	BHI160_GYROSCOPE                   = 3,
    	/** Gravity (**Unimplemented**) */
    	BHI160_GRAVITY                     = 4,
    	/** Linear acceleration (**Unimplemented**) */
    	BHI160_LINEAR_ACCELERATION         = 5,
    	/** Rotation vector (**Unimplemented**) */
    	BHI160_ROTATION_VECTOR             = 6,
    	/** Uncalibrated magnetometer (**Unimplemented**) */
    	BHI160_UNCALIBRATED_MAGNETOMETER   = 7,
    	/** Game rotation vector (whatever that is supposed to be) */
    	BHI160_GAME_ROTATION_VECTOR        = 8,
    	/** Uncalibrated gyroscrope (**Unimplemented**) */
    	BHI160_UNCALIBRATED_GYROSCOPE      = 9,
    	/** Geomagnetic rotation vector (**Unimplemented**) */
    	BHI160_GEOMAGNETIC_ROTATION_VECTOR = 10,
    };
    
    
    enum bhi160_data_type {
    	BHI160_DATA_TYPE_VECTOR
    };
    
    
    /**
     * BHI160 Sensor Data Types
     * ------------------------
     */
    
    /**
     * Vector Data.  The scaling of these values is dependent on the chosen dynamic
     * range.  See the individual sensor's documentation for details.
     */
    struct bhi160_data_vector {
    
    	enum bhi160_data_type data_type;
    
    
    	/** X */
    	int16_t x;
    	/** Y */
    	int16_t y;
    	/** Z */
    	int16_t z;
    };
    
    /**
     * BHI160 API
     * ----------
     */
    
    /**
     * Configuration for a BHI160 sensor.
     *
     * This struct is used when enabling a sensor using
     * :c:func:`epic_bhi160_enable_sensor`.
     */
    struct bhi160_sensor_config {
    	/**
    	 * Number of samples Epicardium should keep for this sensor.  Do not set
    	 * this number too high as the sample buffer will eat RAM.
    	 */
    	size_t sample_buffer_len;
    	/**
    	 * Sample rate for the sensor in Hz.  Maximum data rate is limited
    	 * to 200 Hz for all sensors though some might be limited at a lower
    	 * rate.
    	 */
    	uint16_t sample_rate;
    	/**
    	 * Dynamic range.  Interpretation of this value depends on
    	 * the sensor type.  Please refer to the specific sensor in
    	 * :c:type:`bhi160_sensor_type` for details.
    	 */
    	uint16_t dynamic_range;