|
RGB Sunglasses Animation Extension API fw-v3.8.1-1-geacf2f0
rgbx ABI + C++ wrapper for sandboxed .llext animations
|
SPDX-License-Identifier: MIT Copyright (c) 2026 Stuart Alldritt. More...
#include <stddef.h>#include <stdint.h>Go to the source code of this file.
Data Structures | |
| union | rgbx_param_default |
| Default value for one parameter; which member is read is keyed by the parameter's rgbx_param_type. More... | |
| struct | rgbx_param_desc |
One user-tunable parameter, surfaced as a BLE characteristic named name on the extension's auto-generated GATT service. More... | |
| struct | rgbx_manifest |
| Extension self-description. More... | |
| struct | rgbx_inputs |
| Per-tick input snapshot. More... | |
Macros | |
| #define | RGBX_ABI_VERSION 1u |
| ABI version this header describes. | |
| #define | RGBX_MAX_PARAMS 16u |
| Fixed capacity of the per-extension parameter block. | |
| #define | RGBX_MAX_STRING_PARAMS 4u |
| Maximum number of RGBX_PARAM_STRING parameters per extension. | |
| #define | RGBX_PARAM_STRING_MAX 32u |
| Fixed storage size of one string parameter value, INCLUDING the NUL terminator (so the longest usable string is one byte shorter). | |
| #define | RGBX_AUDIO_NUM_BANDS 4u |
| Number of coarse audio bands (energy + beat flag each). | |
| #define | RGBX_AUDIO_NUM_DISPLAY_BUCKETS 20u |
| Number of fine-grained audio display buckets (bar-graph style). | |
| #define | RGBX_PARAM(name_, type_, default_u32_) |
| Initializer for one scalar (UINT32/COLOR/BOOL) rgbx_param_desc entry. | |
| #define | RGBX_PARAM_STR(name_, default_str_) |
| Initializer for one RGBX_PARAM_STRING rgbx_param_desc entry. | |
| #define | RGBX_PARAM_F32(name_, default_f32_) |
| Initializer for one RGBX_PARAM_FLOAT rgbx_param_desc entry. | |
| #define | RGBX_PIXEL_INDEX(w, x, y) |
Byte offset of pixel (x, y) in rgbx_framebuffer for a display w pixels wide. | |
Enumerations | |
| enum | rgbx_param_type { RGBX_PARAM_UINT32 = 0 , RGBX_PARAM_COLOR = 1 , RGBX_PARAM_BOOL = 2 , RGBX_PARAM_STRING = 3 , RGBX_PARAM_FLOAT = 4 } |
| Parameter types, mapped by the host onto the same BLE characteristic presentation formats (CPF) the built-in animations use. More... | |
Required exports | |
Every extension must define all five of the following symbols and mark each with EXPORT_SYMBOL(<name>) (from <zephyr/llext/symbol.h>) so the host can resolve them with llext_find_sym(). The declarations below let the C++ wrapper (and extension code itself) reference them type-safely. | |
| #define | RGBX_SYM_MANIFEST "rgbx_manifest" |
| llext symbol name of rgbx_manifest. | |
| #define | RGBX_SYM_INPUTS "rgbx_inputs" |
| llext symbol name of rgbx_inputs. | |
| #define | RGBX_SYM_FRAMEBUFFER "rgbx_framebuffer" |
| llext symbol name of rgbx_framebuffer. | |
| #define | RGBX_SYM_INIT "rgbx_init" |
| llext symbol name of rgbx_init(). | |
| #define | RGBX_SYM_TICK "rgbx_tick" |
| llext symbol name of rgbx_tick(). | |
| const struct rgbx_manifest | rgbx_manifest |
| The extension's manifest (const data). | |
| struct rgbx_inputs | rgbx_inputs |
| The input block the host writes before each tick. | |
| uint8_t | rgbx_framebuffer [] |
| The scratch framebuffer the extension renders into, sized exactly width * height * 3 bytes (see RGBX_PIXEL_INDEX for layout). | |
| void | rgbx_init (void) |
| Called once, on the sandboxed thread, after every (re)load and before the first tick. | |
| void | rgbx_tick (void) |
| Called once per frame, on the sandboxed thread. | |
Optional exports | |
Optional capabilities are negotiated by SYMBOL PRESENCE, not by the ABI version: the host resolves each of these with a nullptr-tolerant llext_find_sym() and applies a documented default when the symbol is absent. This keeps RGBX_ABI_VERSION reserved for breaking layout changes — an extension built before an optional export existed keeps loading and running unchanged (decided on issue #121; a version bump would have rejected every already-provisioned .llext for a purely advisory signal). An optional export that IS present is still bounds-checked like the required ones; a present-but-invalid symbol rejects the extension. | |
| #define | RGBX_SYM_GOOD_MOMENT "rgbx_good_moment" |
| llext symbol name of rgbx_good_moment. | |
| uint8_t | rgbx_good_moment |
| OPTIONAL. | |
SPDX-License-Identifier: MIT Copyright (c) 2026 Stuart Alldritt.
The RGB Sunglasses animation-extension ABI (flat C, version 1).
This header is the wire contract between the firmware's extension host (fw/src/extensions/) and a loadable animation extension (.llext file built against the LLEXT EDK). It is shipped inside the EDK, so keep it:
Execution model (issue #85): the extension runs entirely on a sandboxed user-mode thread and owns all the memory it touches. The kernel-side host writes rgbx_inputs before each tick and reads rgbx_framebuffer after it; the extension never calls into the firmware. There are deliberately no function imports in ABI v1 — an extension that only touches its own exported globals needs no syscalls and no grants beyond its own domain.
Lifecycle: extensions are discovered and validated at boot, but only the ACTIVE extension is loaded into memory. Activation loads the ELF and runs rgbx_init() on the sandbox thread (a failure there is reported asynchronously — the animation's BLE "Is Active" characteristic notifies false); switching away unloads it. Globals therefore reset to their initial values on every activation.
| #define RGBX_ABI_VERSION 1u |
ABI version this header describes.
The extension stamps this into its manifest; the host refuses to run any other value.
| #define RGBX_AUDIO_NUM_BANDS 4u |
Number of coarse audio bands (energy + beat flag each).
Matches the firmware's beat-detector band count.
| #define RGBX_PARAM | ( | name_, | |
| type_, | |||
| default_u32_ ) |
Initializer for one scalar (UINT32/COLOR/BOOL) rgbx_param_desc entry.
Usable from both C and C++ manifests.
| name_ | Display name (CUD string); must be a string literal. |
| type_ | One of RGBX_PARAM_UINT32, RGBX_PARAM_COLOR or RGBX_PARAM_BOOL. Use RGBX_PARAM_STR() for strings. |
| default_u32_ | Initial value, before any BLE write arrives. For RGBX_PARAM_COLOR it is 0x00RRGGBB; for RGBX_PARAM_BOOL, 0 or 1. |
| #define RGBX_PARAM_F32 | ( | name_, | |
| default_f32_ ) |
Initializer for one RGBX_PARAM_FLOAT rgbx_param_desc entry.
Kept separate from RGBX_PARAM() because that macro hardcodes the .u32 union member; a float default must initialize .f32 so the compiler stores the IEEE-754 bit pattern rather than an integer conversion.
| name_ | Display name (CUD string); must be a string literal. |
| default_f32_ | Initial value, before any BLE write arrives. Must be a finite float constant (the host rejects writes of NaN/Inf, so a non-finite default would be unwritable back once changed). |
| #define RGBX_PARAM_STR | ( | name_, | |
| default_str_ ) |
Initializer for one RGBX_PARAM_STRING rgbx_param_desc entry.
| name_ | Display name (CUD string); must be a string literal. |
| default_str_ | Initial value; must be a string literal at most RGBX_PARAM_STRING_MAX-1 bytes long. |
| #define RGBX_PIXEL_INDEX | ( | w, | |
| x, | |||
| y ) |
Byte offset of pixel (x, y) in rgbx_framebuffer for a display w pixels wide.
Layout is row-major, 3 bytes per pixel: R, G, B.
Performs no bounds checking — the caller is responsible for keeping x and y inside the manifest's width/height. (The C++ wrapper's rgbx::Animation::setPixel() does that check for you.)
| w | Display width in pixels (rgbx_manifest::width). |
| x | Column, 0 .. w-1. |
| y | Row, 0 .. height-1. |
| enum rgbx_param_type |
Parameter types, mapped by the host onto the same BLE characteristic presentation formats (CPF) the built-in animations use.
| Enumerator | |
|---|---|
| RGBX_PARAM_UINT32 | plain unsigned integer (4-byte BLE value) |
| RGBX_PARAM_COLOR | 0x00RRGGBB, high byte ignored (color picker in the companion app) |
| RGBX_PARAM_BOOL | 0 or 1 (toggle in the companion app; 1-byte BLE value) |
| RGBX_PARAM_STRING | UTF-8 string, at most RGBX_PARAM_STRING_MAX-1 bytes (text field in the companion app) |
| RGBX_PARAM_FLOAT | IEEE-754 float32 (numeric field in the companion app). The value RIDES IN THE SAME uint32_t params[] slot as the scalar types, as its raw bit pattern — read it with rgbx::Animation::paramF32() (or an equivalent memcpy bit-cast), never an integer conversion. Requires firmware/SDK >= the release that introduced it: older hosts reject the whole manifest with "bad param type". Adding this enumerator changes no struct layout, so the ABI version stays 1 (same policy as the optional-exports note below: version bumps are reserved for breaking layout changes). |
| void rgbx_init | ( | void | ) |
Called once, on the sandboxed thread, after every (re)load and before the first tick.
Globals are freshly reinitialized at this point (the extension is unloaded whenever it is not the active animation).
| void rgbx_tick | ( | void | ) |
Called once per frame, on the sandboxed thread.
Renders one frame into rgbx_framebuffer using the current rgbx_inputs. Must return well within the host's tick deadline or the extension is aborted and unloaded.
|
extern |
The scratch framebuffer the extension renders into, sized exactly width * height * 3 bytes (see RGBX_PIXEL_INDEX for layout).
The host copies it to the real display after each successful tick.
|
extern |
OPTIONAL.
If exported, the extension sets it during rgbx_tick(): nonzero means the frame just rendered ended at a natural switch boundary (end of a scroll/clip/cycle), so shuffle mode (issue #121) may switch to another animation without visual jarring. Absent symbol = every frame is a good moment (the same default built-in animations get). Must be a writable global (extension .data/.bss, not .rodata).
|
extern |
The input block the host writes before each tick.
Must be a writable global (i.e. live in the extension's .data/.bss, not .rodata).