diff --git a/Documentation/conf.py b/Documentation/conf.py index 769dbc5df08d1425dd5c99e3c85f251720442bc2..7dafb1841c521151d8a63dc91f1b892e9b2193e4 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -87,7 +87,7 @@ html_context = { # }}} # -- Options for Auto-Doc ---------------------------------------------------- {{{ -autodoc_mock_imports = ["sys_display", "ucollections", "urandom", "utime"] +autodoc_mock_imports = ["sys_display", "sys_leds", "ucollections", "urandom", "utime"] autodoc_member_order = "bysource" # }}} diff --git a/Documentation/pycardium/leds.rst b/Documentation/pycardium/leds.rst index 44af2a6a93d5926f499de0b2c7e7752e038a2c11..e3cae9ab077af93e7d11bbe39e50d5bea425cfb1 100644 --- a/Documentation/pycardium/leds.rst +++ b/Documentation/pycardium/leds.rst @@ -1,38 +1,8 @@ ``leds`` - LEDs =============== +The ``leds`` module provides functions to interact with card10's RGB LEDs. +This is the 11 LEDs above the display and 4 LEDs on the underside of the +top-board, in the four corners. -.. py:function:: leds.set(led, color) - - Set one of the card10's RGB LEDs to a certain color. - - **Example**: - - .. code-block:: python - - import leds, color - - # Set all of the top LEDs to red - for i in range(11): - leds.set(i, color.RED) - - :param led: Which led to set. 0-10 are the leds on the top - and 11-14 are the 4 "ambient" leds. - :param color: What color to set the led to. Should be a - :py:class:`color.Color` but any list/tuple with 3 elements - will work just as well. - -.. py:data:: leds.BOTTOM_RIGHT - - Index of the LED in the bottom right of card10. - -.. py:data:: leds.BOTTOM_LEFT - - Index of the LED in the bottom left of card10. - -.. py:data:: leds.TOP_RIGHT - - Index of the LED in the top right of card10. - -.. py:data:: leds.TOP_LEFT - - Index of the LED in the top left of card10. +.. automodule:: leds + :members: diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 34af173e945577840886dec0d2b46b9ad72de4a3..2c65e376d4f3741592d34e1e64f57cd3c9714db2 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -5,10 +5,12 @@ #include <errno.h> #ifndef __SPHINX_DOC -/* stddef.h is not recognized by hawkmoth for some odd reason */ +/* Some headers are not recognized by hawkmoth for some odd reason */ #include <stddef.h> +#include <stdbool.h> #else typedef unsigned int size_t; +typedef _Bool bool; #endif /* __SPHINX_DOC */ /* @@ -63,6 +65,18 @@ typedef unsigned int size_t; #define API_RTC_SCHEDULE_ALARM 0x51 #define API_LEDS_SET 0x60 +#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_VIBRA_SET 0x70 #define API_VIBRA_VIBRATE 0x71 @@ -211,22 +225,169 @@ API_ISR(EPIC_INT_CTRL_C, epic_isr_ctrl_c); */ /** - * Set one of card10's RGB LEDs to a certain color. + * Set one of card10's RGB LEDs to a certain color in RGB format. * - * .. warning:: + * 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. * - * This API function is not yet stable and is this not part of the API - * freeze. Any binary using :c:func:`epic_leds_set` might stop working at - * any time. Once this warning is removed, the function can be considered - * stable like the rest of the API. - * - * :param led: Which led to set. 0-10 are the leds on the top and 11-14 are the 4 "ambient" leds. - * :param r: Red component of the color. - * :param g: Green component of the color. - * :param b: Blue component of the color. + * :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)); +/** + * 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 (only two brightness levels are + * supported right now). + */ +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 +)); + /** * Sensor Data Streams * =================== diff --git a/epicardium/modules/leds.c b/epicardium/modules/leds.c index c4a3f977b8886eadcaac06d818541e87468339a1..0fa35c1616e5c40791a2e630847b62448c86b1dc 100644 --- a/epicardium/modules/leds.c +++ b/epicardium/modules/leds.c @@ -1,7 +1,88 @@ #include "leds.h" +#include "pmic.h" +//#include "FreeRTOS.h" +//#include "task.h" + +//TODO: create smth like vTaskDelay(pdMS_TO_TICKS(//put ms here)) for us, remove blocking delay from /lib/leds.c to avoid process blocking void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b) { - leds_set(led, r, g, b); + leds_prep(led, r, g, b); + leds_update_power(); + leds_update(); +} + +void epic_leds_set_hsv(int led, float h, float s, float v) +{ + leds_prep_hsv(led, h, s, v); + leds_update_power(); + leds_update(); +} + +void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b) +{ + leds_prep(led, r, g, b); +} + +void epic_leds_prep_hsv(int led, float h, float s, float v) +{ + leds_prep_hsv(led, h, s, v); +} + +void epic_leds_set_all(uint8_t *pattern_ptr, uint8_t len) +{ + uint8_t(*pattern)[3] = (uint8_t(*)[3])pattern_ptr; + for (int i = 0; i < len; i++) { + leds_prep(i, pattern[i][0], pattern[i][1], pattern[i][2]); + } + leds_update_power(); + leds_update(); +} + +void epic_leds_set_all_hsv(float *pattern_ptr, uint8_t len) +{ + float(*pattern)[3] = (float(*)[3])pattern_ptr; + for (int i = 0; i < len; i++) { + leds_prep_hsv(i, pattern[i][0], pattern[i][1], pattern[i][2]); + } + leds_update_power(); leds_update(); -} \ No newline at end of file +} + +void epic_leds_dim_top(uint8_t value) +{ + leds_set_dim_top(value); + leds_update(); +} + +void epic_leds_dim_bottom(uint8_t value) +{ + leds_set_dim_bottom(value); + leds_update(); +} + +void epic_leds_set_rocket(int led, uint8_t value) +{ + pmic_set_led(led, value); +} + +void epic_set_flashlight(bool power) +{ + leds_flashlight(power); +} + +void epic_leds_update(void) +{ + leds_update_power(); + leds_update(); +} + +void epic_leds_set_powersave(bool eco) +{ + leds_powersave(eco); +} + +void epic_leds_set_gamma_table(uint8_t rgb_channel, uint8_t gamma_table[256]) +{ + leds_set_gamma_table(rgb_channel, gamma_table); +} diff --git a/hw-tests/dual-core/main.c b/hw-tests/dual-core/main.c index ca91f9d63161412f52271c7797e172c5e7be20be..5c27950215bd835ce488ba17225ab309b0f0468c 100644 --- a/hw-tests/dual-core/main.c +++ b/hw-tests/dual-core/main.c @@ -24,31 +24,12 @@ int main(void) Paint_DrawImage(Heart, 0, 0, 160, 80); LCD_Update(); - for (int i = 0; i < 11; i++) { - leds_set_dim(i, 1); - } - - int h = 0; - // Release core1 core1_start((void *)0x10080000); + int h = 0; while (1) { #define NUM 15 - for (int i = 0; i < NUM; i++) { - if (i < 12) { - leds_set_hsv( - i, - (h + 360 / NUM * i) % 360, - 1., - 1. / 8 - ); - } else { - leds_set_hsv( - i, (h + 360 / NUM * i) % 360, 1., 1. - ); - } - } leds_update(); TMR_Delay(MXC_TMR0, MSEC(10), 0); diff --git a/hw-tests/hello-world/main.c b/hw-tests/hello-world/main.c index d5787c094e48b57bc4716852415fc11518a1bc18..b797233be16784476f33da9201ecd1cab2b70a32 100644 --- a/hw-tests/hello-world/main.c +++ b/hw-tests/hello-world/main.c @@ -33,7 +33,7 @@ int main(void) LCD_Update(); for (int i = 0; i < 11; i++) { - leds_set_dim(i, 1); + // leds_set_dim(i, 1); } int __attribute__((unused)) h = 0; diff --git a/lib/card10/leds.c b/lib/card10/leds.c index c847709a8e76ce4e0f5f59c6cad1d2a295994315..3f284db4ab1d1dae0f561510e1671b93b83bcafe 100644 --- a/lib/card10/leds.c +++ b/lib/card10/leds.c @@ -1,10 +1,14 @@ #include "gpio.h" #include "portexpander.h" - +#include "max32665.h" #include <stdint.h> #include <string.h> - +#include <stdbool.h> +#include <stdio.h> #define NUM_LEDS 15 +#define DEFAULT_DIM_TOP 1 +#define DEFAULT_DIM_BOTTOM 8 +#define MAX_DIM 8 static const gpio_cfg_t rgb_dat_pin = { PORT_1, PIN_14, GPIO_FUNC_OUT, GPIO_PAD_NONE @@ -12,7 +16,13 @@ static const gpio_cfg_t rgb_dat_pin = { static const gpio_cfg_t rgb_clk_pin = { PORT_1, PIN_15, GPIO_FUNC_OUT, GPIO_PAD_NONE }; -static uint8_t leds[NUM_LEDS][4]; +static uint8_t leds[NUM_LEDS][3]; +static uint8_t gamma_table[3][256]; +static uint8_t active_groups; +static uint8_t bottom_dim; //index 11-14 +static uint8_t top_dim; //index 0-10 +static bool powersave; +static long powerup_wait_cycles = 500; /***** Functions *****/ // ***************************************************************************** @@ -174,19 +184,45 @@ static void leds_stop(void) shift(0xFF); } -void leds_set_dim(uint8_t led, uint8_t dim) +static uint8_t led_to_dim_value(uint8_t led) +{ + return (led < 11) ? top_dim : bottom_dim; +} + +void leds_set_dim_top(uint8_t value) +{ + top_dim = (value > MAX_DIM) ? MAX_DIM : value; +} + +void leds_set_dim_bottom(uint8_t value) { - leds[led][3] = dim; + bottom_dim = (value > MAX_DIM) ? MAX_DIM : value; } -void leds_set(uint8_t led, uint8_t r, uint8_t g, uint8_t b) +void leds_prep(uint8_t led, uint8_t r, uint8_t g, uint8_t b) { leds[led][0] = r; leds[led][1] = g; leds[led][2] = b; } -void leds_set_hsv(uint8_t led, float h, float s, float v) +#if 0 +//don't use, is buggy +void leds_set_autodim(uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + if(led==NUM_LEDS){ + leds_set(led,r,g,b); + return; + } + leds[led][3] = max(r,max(g,b)); + float gain = (float)255/leds[led][3]; //might cause rounding->overflow errors might debug later idk~ + leds[led][0] = (uint8_t)(r*gain); + leds[led][1] = (uint8_t)(g*gain); + leds[led][2] = (uint8_t)(b*gain); +} +#endif + +void leds_prep_hsv(uint8_t led, float h, float s, float v) { hsv in = { h, s, v }; rgb out = hsv2rgb(in); @@ -195,15 +231,121 @@ void leds_set_hsv(uint8_t led, float h, float s, float v) leds[led][2] = out.b * 255; } +static bool is_led_on(uint8_t led) // scheduled to be on after next update +{ + if (!led_to_dim_value(led)) { + return false; + } + for (int i = 0; i < 3; i++) { + if (leds[led][i] != 0) { + return true; + } + } + return false; +} + +static uint8_t led_to_group(uint8_t led) +{ + if (led == 14) { + return 1; + } else if (led >= 11) { + return 2; + } + return 3; +} + +static uint8_t +check_privilege(void) //returns number of hierarchical groups with power +{ + for (int i = 0; i < NUM_LEDS; i++) { + if (is_led_on(i)) { + return led_to_group(i); + } + } + return 0; +} + +static uint8_t power_pin_conversion(uint8_t group) +{ + if (group == 2) { + return 1; + } + if (group == 1) { + return 2; + } + return 0; +} + +static void power_all(void) +{ + for (int i = 0; i < 3; i++) { + portexpander_prep(i, 0); + } + portexpander_update(); +} + +void leds_update_power(void) +{ + if (!powersave) { + return; + } + uint8_t new_groups = + check_privilege(); //there must be a prettier way to do this but meh + if (new_groups == active_groups) { + return; + } + for (int i = 0; i < 3; i++) { + if (i < new_groups) { + portexpander_prep(power_pin_conversion(i), 0); + } else { + portexpander_prep(power_pin_conversion(i), 1); + } + } + portexpander_update(); + if (active_groups < new_groups) { + for (int i = 0; i < powerup_wait_cycles; i++) { + __NOP(); + } + } + active_groups = new_groups; +} + +void leds_powersave(bool eco) +{ + powersave = eco; + if (!powersave) { + power_all(); + } else { + leds_update_power(); + } +} + void leds_update(void) { leds_start(); for (int i = NUM_LEDS - 1; i >= 0; i--) { - leds_shift(leds[i][0], leds[i][1], leds[i][2], leds[i][3]); + leds_shift( + gamma_table[0][leds[i][0]], + gamma_table[1][leds[i][1]], + gamma_table[2][leds[i][2]], + led_to_dim_value(i) + ); } leds_stop(); } +void leds_flashlight(bool power) +{ + portexpander_set(7, (power) ? 0 : 1); +} + +void leds_set_gamma_table(uint8_t rgb_channel, uint8_t table[256]) +{ + for (int i = 0; i < 256; i++) { + gamma_table[rgb_channel][i] = table[i]; + } +} + void leds_init(void) { GPIO_Config(&rgb_clk_pin); @@ -214,17 +356,19 @@ void leds_init(void) memset(leds, 0, sizeof(leds)); + powersave = TRUE; + top_dim = DEFAULT_DIM_TOP; + bottom_dim = DEFAULT_DIM_BOTTOM; for (int i = 0; i < NUM_LEDS; i++) { - leds[i][3] = 8; + for (int j = 0; j < 3; j++) { + leds[i][j] = 0; + } } - - if (portexpander_detected()) { - // Turn on LEDs - // TODO: only turn on LEDs if value != 0,0,0 && dim > 0 - portexpander_set(0, 0); - portexpander_set(1, 0); - portexpander_set(2, 0); + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 3; j++) { + int k = (i * (1 + i) + 255) >> 8; + gamma_table[j][i] = (k * (k + 1) + 255) >> 8; + } } - leds_update(); } diff --git a/lib/card10/leds.h b/lib/card10/leds.h index 7b4dc91a16d5dc4b8ffa0df5652c0a6b749919e2..8977daa7cc75233ac6d4b5bf9ace7a1f03781811 100644 --- a/lib/card10/leds.h +++ b/lib/card10/leds.h @@ -1,11 +1,17 @@ #ifndef LED_H #include <stdint.h> +#include <stdbool.h> -void leds_set_dim(uint8_t led, uint8_t dim); -void leds_set(uint8_t led, uint8_t r, uint8_t g, uint8_t b); -void leds_set_hsv(uint8_t led, float h, float s, float v); +void leds_set_dim_top(uint8_t value); +void leds_set_dim_bottom(uint8_t value); +void leds_prep(uint8_t led, uint8_t r, uint8_t g, uint8_t b); +void leds_prep_hsv(uint8_t led, float h, float s, float v); +void leds_update_power(void); void leds_update(void); void leds_init(void); +void leds_powersave(bool eco); +void leds_flashlight(bool power); +void leds_set_gamma_table(uint8_t rgb_channel, uint8_t table[256]); #endif diff --git a/lib/card10/portexpander.c b/lib/card10/portexpander.c index 6ae8cd93b3866fa0dd794928875e2fda33d234f6..c6a8c302292828c5796d6b8f95a82fc1a9db8e26 100644 --- a/lib/card10/portexpander.c +++ b/lib/card10/portexpander.c @@ -106,6 +106,24 @@ void portexpander_set(uint8_t pin, uint8_t value) } } +void portexpander_prep(uint8_t pin, uint8_t value) +{ + if (pin < 8) { + if (value) { + output_state |= (1 << pin); + } else { + output_state &= ~(1 << pin); + } + } +} + +void portexpander_update(void) +{ + if (detected) { + portexpander_write(PE_C_OUTPUT_PORT, output_state); + } +} + void portexpander_set_mask(uint8_t mask, uint8_t values) { if (detected) { diff --git a/lib/card10/portexpander.h b/lib/card10/portexpander.h index 24e4e5ffbccfc6e1b47cbf987df6c141931cc484..86614bdd09305ae725fd7d2145c058e184602ed2 100644 --- a/lib/card10/portexpander.h +++ b/lib/card10/portexpander.h @@ -8,6 +8,8 @@ void portexpander_init(void); uint8_t portexpander_get(void); void portexpander_set(uint8_t pin, uint8_t value); void portexpander_set_mask(uint8_t mask, uint8_t values); +void portexpander_prep(uint8_t pin, uint8_t value); +void portexpander_update(void); bool portexpander_detected(void); #endif diff --git a/pycardium/meson.build b/pycardium/meson.build index 810a6845996d5a08ffbcb255440cbf2fa503faa0..0f460274f36f57760330df31f4336357b94e4538 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -4,7 +4,7 @@ modsrc = files( 'modules/fat_file.c', 'modules/fat_reader_import.c', 'modules/interrupt.c', - 'modules/leds.c', + 'modules/sys_leds.c', 'modules/light_sensor.c', 'modules/sys_display.c', 'modules/utime.c', diff --git a/pycardium/modules/leds.c b/pycardium/modules/leds.c deleted file mode 100644 index 7428069cc4c7ae4ac21b81877120e9a168634975..0000000000000000000000000000000000000000 --- a/pycardium/modules/leds.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "epicardium.h" - -#include "py/obj.h" -#include "py/objlist.h" -#include "py/runtime.h" - -static mp_obj_t mp_leds_set(mp_obj_t led_in, mp_obj_t color_in) -{ - int led = mp_obj_get_int(led_in); - - if (mp_obj_get_int(mp_obj_len(color_in)) < 3) { - mp_raise_ValueError("color must have 3 elements"); - } - - uint8_t red = mp_obj_get_int( - mp_obj_subscr(color_in, mp_obj_new_int(0), MP_OBJ_SENTINEL) - ); - uint8_t green = mp_obj_get_int( - mp_obj_subscr(color_in, mp_obj_new_int(1), MP_OBJ_SENTINEL) - ); - uint8_t blue = mp_obj_get_int( - mp_obj_subscr(color_in, mp_obj_new_int(2), MP_OBJ_SENTINEL) - ); - - epic_leds_set(led, red, green, blue); - - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_2(leds_set_obj, mp_leds_set); - -static const mp_rom_map_elem_t leds_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_leds) }, - { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&leds_set_obj) }, - { MP_ROM_QSTR(MP_QSTR_BOTTOM_RIGHT), MP_OBJ_NEW_SMALL_INT(11) }, - { MP_ROM_QSTR(MP_QSTR_BOTTOM_LEFT), MP_OBJ_NEW_SMALL_INT(12) }, - { MP_ROM_QSTR(MP_QSTR_TOP_RIGHT), MP_OBJ_NEW_SMALL_INT(13) }, - { MP_ROM_QSTR(MP_QSTR_TOP_LEFT), MP_OBJ_NEW_SMALL_INT(14) }, -}; -static MP_DEFINE_CONST_DICT(leds_module_globals, leds_module_globals_table); - -const mp_obj_module_t leds_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&leds_module_globals, -}; - -/* Register the module to make it available in Python */ -/* clang-format off */ -MP_REGISTER_MODULE(MP_QSTR_leds, leds_module, MODULE_LEDS_ENABLED); diff --git a/pycardium/modules/py/ledfx.py b/pycardium/modules/py/ledfx.py new file mode 100644 index 0000000000000000000000000000000000000000..2181f19c4749282c0d75deaa19f245b9621bfd2a --- /dev/null +++ b/pycardium/modules/py/ledfx.py @@ -0,0 +1,48 @@ +import leds, utime, math + + +def col_cor(colors, brightness=1, gamma=1): + return [ + [int(255 * brightness * math.pow((y / 255.0), gamma)) for y in x] + for x in colors + ] + + +def halo(colors): + used_leds = len(colors) + colors += [[0, 0, 0]] * (11 - used_leds) + colors += [colors[used_leds - 1]] + [colors[0]] * 2 + [colors[used_leds - 1]] + return colors + + +def kitt( + cycles=100, + delay=80, + power=10, + minimum=0.3, + rgb=[255, 0, 0], + spectrum=[], + halo=False, +): + + kitt_table = [((-math.cos(math.pi * (x / 10.0))) + 1) / 2.0 for x in range(21)] + kitt_table = [math.pow(x, power) * (1 - minimum) + minimum for x in kitt_table] + + for i in range(cycles): + j = i % 20 + if j > 10: + j = 20 - j + if spectrum == []: + used_leds = 11 + output = [[int(x * y) for y in rgb] for x in kitt_table[j : (j + 11)]] + else: + used_leds = len(spectrum) + output = [ + [int(y * kitt_table[j + x]) for y in spectrum[x]] + for x in range(used_leds) + ] + if halo: + halo(output) + leds.set_all(output) + utime.sleep_ms(delay) + leds.clear() diff --git a/pycardium/modules/py/leds.py b/pycardium/modules/py/leds.py new file mode 100644 index 0000000000000000000000000000000000000000..05038e848abaae8f559021bfe74944988b4b0fb5 --- /dev/null +++ b/pycardium/modules/py/leds.py @@ -0,0 +1,225 @@ +import sys_leds +import math + + +def update(): + """ + Updates the RGB LEDs. + + This will apply changes that have been set with :func:`leds.prep` or + :func:`leds.prep_hsv`. The LEDs can be only updated in bulk, so using this + approach instead of :func:`leds.set` or :func:`leds.set_hsv` significantly + reduces the load on the corresponding hardware bus. + """ + sys_leds.update() + + +def clear(): + """ + Turns all LEDs off. + + Does **not** reactivate powersave if it has been deactivated, in which case + ~15mA will be wasted. + """ + values = [[0, 0, 0] for x in range(15)] + sys_leds.set_all(values) + + +def flashlight(on): + """ + Turn on the bright side LED. + + This LED 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 on: Side LED on if true. + """ + sys_leds.set_flashlight(on) + + +def dim_top(value): + """ + Set global brightness for top RGB LEDs. + + :param int value: Brightness. Default = 1, range = 1...8 + """ + sys_leds.dim_top(value) + + +def dim_bottom(value): + """ + Set global brightness for bottom RGB LEDs. + + :param int value: Brightness. Default = 8, range = 1...8 + """ + sys_leds.dim_bottom(value) + + +def rocket(led, value): + """ + Set brightness of one of the rocket LEDs. + + :param int led: Choose your rocket! + + +-------+--------+----------+ + | ID | Color | Location | + +=======+========+==========+ + | ``0`` | Blue | Left | + +-------+--------+----------+ + | ``1`` | Yellow | Top | + +-------+--------+----------+ + | ``2`` | Green | Right | + +-------+--------+----------+ + :param int value: Brightness of LED (only two brightness levels are + supported right now). + """ + sys_leds.set_rocket(led, value) + + +def prep(led, color): + """ + Prepare am RGB LED to be set to an RGB value. + + Changes are applied upon calling :func:`leds.update`. This is faster than + individual :func:`leds.set` or :func:`leds.set_hsv` calls in case of + multiple changes. + + :param int led: Which LED to prepare. 0-10 are the LEDs on the top and + 11-14 are the 4 "ambient" LEDs + :param [r,g,b] color: RGB triplet + """ + sys_leds.prep(led, color) + + +def prep_hsv(led, color): + """ + Prepare an RGB LED to be set to an HSV value. + + Changes are applied upon calling :func:`leds.update`. This is faster than + individual :func:`leds.set` or :func:`leds.set_hsv` calls in case of + multiple changes. + + :param int led: Which LED to prepare. 0-10 are the LEDs on the top and + 11-14 are the 4 "ambient" LEDs + :param [h,s,v] color: HSV triplet + """ + sys_leds.prep_hsv(led, color) + + +def set(led, color): + """ + Set an RGB LED to an RGB value. + + :param int led: Which LED to set. 0-10 are the LEDs on the top and 11-14 + are the 4 "ambient" LEDs + :param [r,g,b] color: RGB triplet + """ + sys_leds.set(led, color) + + +def set_hsv(led, color): + """ + Prepare an RGB LED to be set to an HSV value. + + :param int led: Which LED to set. 0-10 are the LEDs on the top and 11-14 + are the 4 "ambient" LEDs + :param [h,s,v] color: HSV triplet + """ + sys_leds.set_hsv(led, color) + + +def set_all(colors): + """ + Set multiple RGB LEDs to RGB values. + + Filling starts at LED0 ascending. + + **Example**: + + .. code-block:: python + + import leds, color + + # 1st red, 2nd green & 3rd blue: + leds.set_all([color.RED, color.GREEN, color.BLUE]) + + :param colors: List of RGB triplets + """ + sys_leds.set_all(colors) + + +def set_all_hsv(colors): + """ + Set multiple RGB LEDs to HSV values. + + Filling starts at LED0 ascending. + + :param colors: List of HSV triplets + """ + sys_leds.set_all_hsv(colors) + + +def gay(value=0.5): + """ + Gamma Adjust Yassistant. Prints a rainbow. + + Recommended calibration prodecure: + + .. code-block:: python + + import leds + + leds.gay(1) + # adjust gain for uniform brightness + leds.gamma_rgb(channel=..., gain=...) + + leds.gay(0.5) + # adjust power~4 for uniform brightness + leds.gamma_rgb(channel=..., power=...) + + :param value: Brightness. Default = 0.5 + """ + values = [[((x * 360.0) / 11), 1.0, value] for x in range(11)] + sys_leds.set_all_hsv(values) + + +def powersave(eco=True): + """ + Enable or disable 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 ~10us, so it can be disabled for high + speed applications such as POV. + + :param bool eco: Activates powersave if ``True``, disables it when ``False``. + """ + sys_leds.set_powersave(eco) + + +def gamma(power=4.0): + """ + Applies same power function gamma correction to all RGB channels. + + :param float power: Exponent of power function. + """ + table = [int(math.ceil(math.pow((x / 255.0), power) * 255)) for x in range(256)] + for i in range(3): + sys_leds.set_gamma(i, table) + sys_leds.update() + + +def gamma_rgb(channel, power=4.0, gain=1.0): + """ + Applies power function gamma correction with optional amplification to a single RGB channel. + + :param int channel: RGB channel to be adjusted. 0->Red, 1->Green, 2->Blue. + :param float power: Exponent of power function. + :param float gain: Amplification of channel. Values above 1.0 might cause + overflow. + """ + table = [ + int(math.ceil(math.pow((x / 255.0), power) * gain * 255)) for x in range(256) + ] + sys_leds.set_gamma(channel, table) + sys_leds.update() diff --git a/pycardium/modules/py/meson.build b/pycardium/modules/py/meson.build index eab92f959051c62bfc4ab065d3385e721d6711f2..7b1b9768a3dfc8736d590098e8876a6779039029 100644 --- a/pycardium/modules/py/meson.build +++ b/pycardium/modules/py/meson.build @@ -2,6 +2,9 @@ python_modules = files( 'color.py', 'htmlcolor.py', 'display.py', + 'leds.py', + 'pride.py', + 'ledfx.py', ) frozen_modules = mpy_cross.process(python_modules) diff --git a/pycardium/modules/py/pride.py b/pycardium/modules/py/pride.py new file mode 100644 index 0000000000000000000000000000000000000000..d45d675dcdcbb5ef7fec1d1e3e442f1e408315f7 --- /dev/null +++ b/pycardium/modules/py/pride.py @@ -0,0 +1,130 @@ +flags = {} +flags["rainbow"] = [ + [255, 0, 24], + [255, 165, 44], + [255, 255, 65], + [0, 128, 24], + [0, 0, 249], + [134, 0, 125], +] +flags["trans"] = [ + [85, 205, 252], + [247, 168, 184], + [255, 255, 255], + [247, 168, 184], + [85, 205, 252], +] +flags["bi"] = [[214, 2, 112], [155, 79, 150], [0, 56, 168]] +flags["ace"] = [[1, 1, 1], [164, 164, 164], [255, 255, 255], [150, 0, 150]] +flags["greyace"] = [ + [150, 0, 150], + [164, 164, 164], + [255, 255, 255], + [164, 164, 164], + [150, 0, 150], +] +flags["aro"] = [ + [61, 165, 66], + [167, 211, 121], + [255, 255, 255], + [169, 169, 169], + [1, 1, 1], +] +flags["greyaro"] = [ + [61, 165, 66], + [164, 164, 164], + [255, 255, 255], + [164, 164, 164], + [61, 165, 66], +] +flags["pan"] = [[255, 27, 141], [255, 218, 0], [27, 179, 255]] +flags["inter"] = [[255, 218, 0], [122, 0, 172]] +flags["genderqueer"] = [[201, 138, 255], [255, 255, 255], [80, 150, 85]] +flags["lesbian"] = [ + [139, 60, 105], + [171, 99, 143], + [187, 127, 179], + [255, 255, 255], + [228, 172, 207], + [214, 113, 113], + [134, 70, 70], +] +flags["nonbinary"] = [[255, 244, 51], [255, 255, 255], [155, 89, 208], [0, 0, 0]] +flags["genderfluid"] = [ + [254, 117, 161], + [255, 255, 255], + [189, 22, 213], + [0, 0, 0], + [50, 61, 187], +] +flags["agender"] = [ + [0, 0, 0], + [150, 150, 150], + [255, 255, 255], + [182, 245, 131], + [255, 255, 255], + [150, 150, 150], + [0, 0, 0], +] +flags["poly"] = [ + [0, 0, 255], + [0, 0, 255], + [0, 0, 255], + [255, 0, 0], + [255, 0, 0], + [255, 255, 0], + [255, 0, 0], + [255, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], +] +import leds, display, math, utime, ledfx + + +def expand(colors, cutoff=12): + output = [] + if len(colors) != 7 or cutoff > 14: + leds_per_color = int(cutoff / len(colors)) + for i in colors: + output += [i] * leds_per_color + else: + for j, i in enumerate(colors): + output += [i] * (1 + (j % 2 == 1)) + return output + + +def show_leds(flag="rainbow", brightness=0.5, gamma=1, cutoff=12, halo=True): + colors = ledfx.col_cor(flags[flag], brightness, gamma) + output = expand(colors, cutoff)[::-1][0:11] + if halo: + output = ledfx.halo(output) + leds.clear() + leds.set_all(output) + + +def show_display(flag="rainbow", brightness=1, gamma=1): + colors = ledfx.col_cor(flags[flag], brightness, gamma) + colors = expand(colors, 160) + with display.open() as disp: + disp.clear() + for line, color in enumerate(colors): + disp.line(line, 0, line, 80, col=color) + disp.update() + disp.close() + + +def get(flag="rainbow"): + return flags[flag] + + +def demo(s_delay=2): + for i in flags: + print(i) + show_leds(flag=i) + show_display(flag=i) + utime.sleep_ms(s_delay * 1000) + leds.clear() + with display.open() as disp: + disp.clear().update() + disp.close() diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index dedc57c05513debe59a7441493dcf7d86ec0060d..1c3990607704dae3ea7ff4cb7d60b8d669b1fa42 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -5,7 +5,20 @@ #endif /* leds */ -Q(leds) +Q(sys_leds) +Q(update) +Q(set) +Q(set_hsv) +Q(prep) +Q(prep_hsv) +Q(set_all) +Q(set_all_hsv) +Q(set_flashlight) +Q(set_rocket) +Q(set_powersave) +Q(set_gamma) +Q(dim_top) +Q(dim_bottom) Q(BOTTOM_LEFT) Q(BOTTOM_RIGHT) Q(TOP_LEFT) diff --git a/pycardium/modules/sys_leds.c b/pycardium/modules/sys_leds.c new file mode 100644 index 0000000000000000000000000000000000000000..55c99ba38d964463b62b33390834022db52c0c58 --- /dev/null +++ b/pycardium/modules/sys_leds.c @@ -0,0 +1,250 @@ +#include "py/obj.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include <stdio.h> + +#include "epicardium.h" + +static mp_obj_t mp_leds_set(mp_obj_t led_in, mp_obj_t color_in) +{ + int led = mp_obj_get_int(led_in); + + if (mp_obj_get_int(mp_obj_len(color_in)) < 3) { + mp_raise_ValueError("color must have 3 elements"); + } + + uint8_t red = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + uint8_t green = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + uint8_t blue = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + + epic_leds_set(led, red, green, blue); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_set_obj, mp_leds_set); + +static mp_obj_t mp_leds_set_hsv(mp_obj_t led_in, mp_obj_t color_in) +{ + int led = mp_obj_get_int(led_in); + + if (mp_obj_get_int(mp_obj_len(color_in)) < 3) { + mp_raise_ValueError("color must have 3 elements"); + } + + float h = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + float s = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + float v = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + + epic_leds_set_hsv(led, h, s, v); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_set_hsv_obj, mp_leds_set_hsv); + +static mp_obj_t mp_leds_prep(mp_obj_t led_in, mp_obj_t color_in) +{ + int led = mp_obj_get_int(led_in); + + if (mp_obj_get_int(mp_obj_len(color_in)) < 3) { + mp_raise_ValueError("color must have 3 elements"); + } + + uint8_t red = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + uint8_t green = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + uint8_t blue = mp_obj_get_int( + mp_obj_subscr(color_in, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + + epic_leds_prep(led, red, green, blue); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_prep_obj, mp_leds_prep); + +static mp_obj_t mp_leds_prep_hsv(mp_obj_t led_in, mp_obj_t color_in) +{ + int led = mp_obj_get_int(led_in); + + if (mp_obj_get_int(mp_obj_len(color_in)) < 3) { + mp_raise_ValueError("color must have 3 elements"); + } + + float h = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + float s = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + float v = mp_obj_get_float( + mp_obj_subscr(color_in, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + + epic_leds_prep_hsv(led, h, s, v); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_prep_hsv_obj, mp_leds_prep_hsv); + +static mp_obj_t mp_leds_set_all(mp_obj_t color_in) +{ + uint8_t len = mp_obj_get_int(mp_obj_len(color_in)); + uint8_t pattern[len][3]; + for (int i = 0; i < len; i++) { + mp_obj_t color = mp_obj_subscr( + color_in, mp_obj_new_int(i), MP_OBJ_SENTINEL + ); + pattern[i][0] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + pattern[i][1] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + pattern[i][2] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + } + epic_leds_set_all((uint8_t *)pattern, len); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(leds_set_all_obj, mp_leds_set_all); + +static mp_obj_t mp_leds_set_all_hsv(mp_obj_t color_in) +{ + uint8_t len = mp_obj_get_int(mp_obj_len(color_in)); + float pattern[len][3]; + for (int i = 0; i < len; i++) { + mp_obj_t color = mp_obj_subscr( + color_in, mp_obj_new_int(i), MP_OBJ_SENTINEL + ); + pattern[i][0] = mp_obj_get_float(mp_obj_subscr( + color, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + pattern[i][1] = mp_obj_get_float(mp_obj_subscr( + color, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + pattern[i][2] = mp_obj_get_float(mp_obj_subscr( + color, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + } + epic_leds_set_all_hsv((float *)pattern, len); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(leds_set_all_hsv_obj, mp_leds_set_all_hsv); + +static mp_obj_t mp_leds_set_flashlight(mp_obj_t on_in) +{ + int on = mp_obj_get_int(on_in); + epic_set_flashlight(on); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1( + leds_set_flashlight_obj, mp_leds_set_flashlight +); + +static mp_obj_t mp_leds_set_rocket(mp_obj_t led_in, mp_obj_t value_in) +{ + int led = mp_obj_get_int(led_in); + int value = mp_obj_get_int(value_in); + epic_leds_set_rocket(led, value); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_set_rocket_obj, mp_leds_set_rocket); + +static mp_obj_t mp_leds_dim_top(mp_obj_t dim_in) +{ + int dim = mp_obj_get_int(dim_in); + epic_leds_dim_top(dim); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(leds_dim_top_obj, mp_leds_dim_top); + +static mp_obj_t mp_leds_dim_bottom(mp_obj_t dim_in) +{ + int dim = mp_obj_get_int(dim_in); + epic_leds_dim_bottom(dim); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(leds_dim_bottom_obj, mp_leds_dim_bottom); + +static mp_obj_t mp_leds_update() +{ + epic_leds_update(); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(leds_update_obj, mp_leds_update); + +static mp_obj_t mp_leds_set_powersave(mp_obj_t eco_in) +{ + int eco = mp_obj_get_int(eco_in); + epic_leds_set_powersave(eco); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(leds_set_powersave_obj, mp_leds_set_powersave); + +static mp_obj_t +mp_leds_set_gamma(mp_obj_t rgb_channel_in, mp_obj_t gamma_table_in) +{ + int rgb_channel = mp_obj_get_int(rgb_channel_in); + if (mp_obj_get_int(mp_obj_len(gamma_table_in)) != 256) { + mp_raise_ValueError("table must have 256 elements"); + } + uint8_t gamma_table[256]; + for (int i = 0; i < 256; i++) { + gamma_table[i] = mp_obj_get_int(mp_obj_subscr( + gamma_table_in, mp_obj_new_int(i), MP_OBJ_SENTINEL) + ); + } + epic_leds_set_gamma_table(rgb_channel, gamma_table); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(leds_set_gamma_obj, mp_leds_set_gamma); + +static const mp_rom_map_elem_t leds_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_leds) }, + { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&leds_set_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_hsv), MP_ROM_PTR(&leds_set_hsv_obj) }, + { MP_ROM_QSTR(MP_QSTR_prep), MP_ROM_PTR(&leds_prep_obj) }, + { MP_ROM_QSTR(MP_QSTR_prep_hsv), MP_ROM_PTR(&leds_prep_hsv_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_all), MP_ROM_PTR(&leds_set_all_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_all_hsv), MP_ROM_PTR(&leds_set_all_hsv_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_rocket), MP_ROM_PTR(&leds_set_rocket_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_flashlight), + MP_ROM_PTR(&leds_set_flashlight_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&leds_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_powersave), + MP_ROM_PTR(&leds_set_powersave_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_gamma), MP_ROM_PTR(&leds_set_gamma_obj) }, + { MP_ROM_QSTR(MP_QSTR_dim_top), MP_ROM_PTR(&leds_dim_top_obj) }, + { MP_ROM_QSTR(MP_QSTR_dim_bottom), MP_ROM_PTR(&leds_dim_bottom_obj) }, + { MP_ROM_QSTR(MP_QSTR_BOTTOM_RIGHT), MP_OBJ_NEW_SMALL_INT(11) }, + { MP_ROM_QSTR(MP_QSTR_BOTTOM_LEFT), MP_OBJ_NEW_SMALL_INT(12) }, + { MP_ROM_QSTR(MP_QSTR_TOP_RIGHT), MP_OBJ_NEW_SMALL_INT(13) }, + { MP_ROM_QSTR(MP_QSTR_TOP_LEFT), MP_OBJ_NEW_SMALL_INT(14) }, +}; +static MP_DEFINE_CONST_DICT(leds_module_globals, leds_module_globals_table); + +const mp_obj_module_t leds_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&leds_module_globals, +}; + +/* Register the module to make it available in Python */ +/* clang-format off */ +MP_REGISTER_MODULE(MP_QSTR_sys_leds, leds_module, MODULE_LEDS_ENABLED);