diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index bbd166c3..65a67360 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -6,9 +6,16 @@ #include "c_string.h" #include "user_interface.h" #include "driver/uart.h" +#include "osapi.h" #define CANARY_VALUE 0x32383132 +#define FADE_IN 1 +#define FADE_OUT 0 +#define SHIFT_LOGICAL 0 +#define SHIFT_CIRCULAR 1 + + typedef struct { int canary; int size; @@ -151,20 +158,96 @@ static int ws2812_buffer_fill(lua_State* L) { static int ws2812_buffer_fade(lua_State* L) { ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int fade = luaL_checkinteger(L, 2); + unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); uint8_t * p = &buffer->values[0]; + int val = 0; int i; for(i = 0; i < buffer->size * buffer->colorsPerLed; i++) { - *p++ /= fade; + if (direction == FADE_OUT) + { + *p++ /= fade; + } + else + { + // as fade in can result in value overflow, an int is used to perform the check afterwards + val = *p * fade; + if (val > 255) val = 255; + *p++ = val; + } } return 0; } + +static int ws2812_buffer_shift(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); + const int shiftValue = luaL_checkinteger(L, 2); + const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL ); + + luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); + luaL_argcheck(L, shiftValue > 0-buffer->size && shiftValue < buffer->size, 2, "shifting more elements than buffer size"); + + int shift = shiftValue >= 0 ? shiftValue : -shiftValue; + + // check if we want to shift at all + if (shift == 0) + { + return 0; + } + + uint8_t * tmp_pixels = luaM_malloc(L, buffer->colorsPerLed * sizeof(uint8_t) * shift); + int i,j; + size_t shift_len, remaining_len; + // calculate length of shift section and remaining section + shift_len = shift*buffer->colorsPerLed; + remaining_len = (buffer->size-shift)*buffer->colorsPerLed; + + if (shiftValue > 0) + { + // Store the values which are moved out of the array (last n pixels) + c_memcpy(tmp_pixels, &buffer->values[(buffer->size-shift)*buffer->colorsPerLed], shift_len); + // Move pixels to end + os_memmove(&buffer->values[shift*buffer->colorsPerLed], &buffer->values[0], remaining_len); + // Fill beginning with temp data + if (shift_type == SHIFT_LOGICAL) + { + c_memset(&buffer->values[0], 0, shift_len); + } + else + { + c_memcpy(&buffer->values[0], tmp_pixels, shift_len); + } + } + else + { + // Store the values which are moved out of the array (last n pixels) + c_memcpy(tmp_pixels, &buffer->values[0], shift_len); + // Move pixels to end + os_memmove(&buffer->values[0], &buffer->values[shift*buffer->colorsPerLed], remaining_len); + // Fill beginning with temp data + if (shift_type == SHIFT_LOGICAL) + { + c_memset(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], 0, shift_len); + } + else + { + c_memcpy(&buffer->values[(buffer->size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); + } + } + // Free memory + luaM_free(L, tmp_pixels); + + return 0; +} + + + static int ws2812_buffer_get(lua_State* L) { ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); const int led = luaL_checkinteger(L, 2) - 1; @@ -252,12 +335,13 @@ static int ws2812_buffer_write(lua_State* L) { static const LUA_REG_TYPE ws2812_buffer_map[] = { - { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, - { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, - { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, - { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, - { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, - { LSTRKEY( "write" ), LFUNCVAL( ws2812_buffer_write )}, + { LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )}, + { LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )}, + { LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )}, + { LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )}, + { LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )}, + { LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )}, + { LSTRKEY( "write" ), LFUNCVAL( ws2812_buffer_write )}, { LSTRKEY( "__index" ), LROVAL ( ws2812_buffer_map )}, { LNILKEY, LNILVAL} }; @@ -267,6 +351,10 @@ static const LUA_REG_TYPE ws2812_map[] = { LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )}, { LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )}, { LSTRKEY( "init" ), LFUNCVAL( ws2812_init )}, + { LSTRKEY( "FADE_IN" ), LNUMVAL( FADE_IN ) }, + { LSTRKEY( "FADE_OUT" ),LNUMVAL( FADE_OUT ) }, + { LSTRKEY( "SHIFT_LOGICAL" ),LNUMVAL( SHIFT_LOGICAL ) }, + { LSTRKEY( "SHIFT_CIRCULAR" ),LNUMVAL( SHIFT_CIRCULAR ) }, { LNILKEY, LNILVAL} }; diff --git a/docs/en/modules/ws2812.md b/docs/en/modules/ws2812.md index 408bc505..0c7ce3da 100644 --- a/docs/en/modules/ws2812.md +++ b/docs/en/modules/ws2812.md @@ -135,13 +135,14 @@ The number of given bytes must match the number of bytesPerLed of the buffer buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip ``` ## ws2812.buffer:fade() -Divide each byte of each led by the given value. Useful for a fading effect +Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect. #### Syntax -`buffer:fade(value)` +`buffer:fade(value [, direction])` #### Parameters - - `value` value by which divide each byte + - `value` value by which to divide or multiply each byte + - `direction` ws2812.FADE\_IN or ws2812.FADE\_OUT. Defaults to ws2812.FADE\_OUT #### Returns `nil` @@ -149,7 +150,26 @@ Divide each byte of each led by the given value. Useful for a fading effect #### Example ```lua buffer:fade(2) +buffer:fade(2, ws2812.FADE_IN) ``` +## ws2812.buffer:shift() +Shift the content of the buffer in positive or negative direction. This allows simple animation effects. + +#### Syntax +`buffer:shift(value [, mode])` + +#### Parameters + - `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards. + - `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL. + +#### Returns +`nil` + +#### Example +```lua +buffer:shift(3) +``` + ## ws2812.buffer:write() Output the buffer to the led strip