Skip to content
Snippets Groups Projects
Commit e87cdd45 authored by fleur's avatar fleur Committed by rahix
Browse files

feat: Add proper LED module

The new LED modules allow a much broader range of uses of the RGB LEDs.
It also includes smart powersaving and options to set all LEDs at once.
parent b3979a7d
No related branches found
No related tags found
No related merge requests found
Showing
with 1125 additions and 141 deletions
......@@ -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"
# }}}
......
``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:
......@@ -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
* ===================
......
#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);
}
......@@ -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);
......
......@@ -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;
......
#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();
}
#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
......@@ -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) {
......
......@@ -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
......@@ -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',
......
#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);
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()
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()
......@@ -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)
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()
......@@ -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)
......
#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);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment