From d4b5b0cbaf430d385aec81a0023f1d2e25665bf2 Mon Sep 17 00:00:00 2001 From: Gregor Hartmann Date: Mon, 9 Dec 2019 13:30:09 +0100 Subject: [PATCH] Fixes for `ws2812` and `ws2812_effects` (#2953) * clean effects library * Fix several issues in ws2812 and effects * Implement working way of calling shift from callback --- .gitignore | 1 + app/modules/ws2812.c | 121 ++++++------ app/modules/ws2812.h | 25 ++- app/modules/ws2812_effects.c | 283 ++++++++++++++-------------- lua_tests/mispec.lua | 159 ++++++++++++++++ lua_tests/mispec_ws2812.lua | 153 +++++++++++++++ lua_tests/mispec_ws2812_2.lua | 149 +++++++++++++++ lua_tests/mispec_ws2812_effects.lua | 31 +++ 8 files changed, 723 insertions(+), 199 deletions(-) create mode 100644 lua_tests/mispec.lua create mode 100644 lua_tests/mispec_ws2812.lua create mode 100644 lua_tests/mispec_ws2812_2.lua create mode 100644 lua_tests/mispec_ws2812_effects.lua diff --git a/.gitignore b/.gitignore index 736ae691..62f8d181 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ tools/toolchains/ .project .settings/ .vscode +.vs #ignore temp file for build infos buildinfo.h diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index fa892079..8f9e6c41 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -16,10 +16,6 @@ #define MODE_DUAL 1 - - - - // Init UART1 to be able to stream WS2812 data to GPIO2 pin // If DUAL mode is selected, init UART0 to stream to TXD0 as well // You HAVE to redirect LUA's output somewhere else @@ -168,15 +164,15 @@ static int ws2812_write(lua_State* L) { return 0; } -static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { +static ptrdiff_t posrelat(ptrdiff_t pos, size_t len) { /* relative string position: negative means back from end */ if (pos < 0) pos += (ptrdiff_t)len + 1; - return (pos >= 0) ? pos : 0; + return MIN(MAX(pos, 1), len); } static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed) { // Allocate memory - size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t); + size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds; ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size); // Associate its metatable @@ -245,17 +241,11 @@ static int ws2812_buffer_fill_lua(lua_State* L) { return 0; } -static int ws2812_buffer_fade(lua_State* L) { - ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - const int fade = luaL_checkinteger(L, 2); - unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); - - luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); - +void ws2812_buffer_fade(ws2812_buffer * buffer, int fade, unsigned direction) { uint8_t * p = &buffer->values[0]; int val = 0; int i; - for(i = 0; i < buffer->size * buffer->colorsPerLed; i++) + for (i = 0; i < buffer->size * buffer->colorsPerLed; i++) { if (direction == FADE_OUT) { @@ -269,78 +259,101 @@ static int ws2812_buffer_fade(lua_State* L) { *p++ = val; } } +} +static int ws2812_buffer_fade_lua(lua_State* L) { + ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); + const int fade = luaL_checkinteger(L, 2); + unsigned direction = luaL_optinteger( L, 3, FADE_OUT ); + + luaL_argcheck(L, fade > 0, 2, "fade value should be a strict positive number"); + + ws2812_buffer_fade(buffer, fade, direction); + + return 0; +} + +int ws2812_buffer_shift(lua_State* L, ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end){ + + ws2812_buffer_shift_prepare* prepare = ws2812_buffer_get_shift_prepare(L, buffer, shiftValue, shift_type, pos_start, pos_end); + ws2812_buffer_shift_prepared(prepare); + // Free memory + luaM_free(L, prepare); return 0; } - -int ws2812_buffer_shift(ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end) { +ws2812_buffer_shift_prepare* ws2812_buffer_get_shift_prepare(lua_State* L, ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end){ ptrdiff_t start = posrelat(pos_start, buffer->size); ptrdiff_t end = posrelat(pos_end, buffer->size); - if (start < 1) start = 1; - if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size; start--; int size = end - start; size_t offset = start * buffer->colorsPerLed; - //luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size"); + luaL_argcheck(L, shiftValue >= 0-size && shiftValue <= 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 || size <= 0) - { - return 0; - } - - uint8_t * tmp_pixels = malloc(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 = (size-shift)*buffer->colorsPerLed; - if (shiftValue > 0) + ws2812_buffer_shift_prepare* prepare = luaM_malloc(L, sizeof(ws2812_buffer_shift_prepare) + shift_len); + prepare->offset = offset; + prepare->tmp_pixels = (uint8_t*)(prepare+1); + prepare->shiftValue = shiftValue; + prepare->shift_len = shift_len; + prepare->remaining_len = remaining_len; + prepare->shift_type = shift_type; + prepare->buffer = buffer; + + return prepare; +} + +void ws2812_buffer_shift_prepared(ws2812_buffer_shift_prepare* prepare) { + + // check if we want to shift at all + if (prepare->shift_len == 0 || (prepare->shift_len + prepare->remaining_len) <= 0) + { + return; + } + + if (prepare->shiftValue > 0) { // Store the values which are moved out of the array (last n pixels) - memcpy(tmp_pixels, &buffer->values[offset + (size-shift)*buffer->colorsPerLed], shift_len); + memcpy(prepare->tmp_pixels, &prepare->buffer->values[prepare->offset + prepare->remaining_len], prepare->shift_len); // Move pixels to end - os_memmove(&buffer->values[offset + shift*buffer->colorsPerLed], &buffer->values[offset], remaining_len); + os_memmove(&prepare->buffer->values[prepare->offset + prepare->shift_len], &prepare->buffer->values[prepare->offset], prepare->remaining_len); // Fill beginning with temp data - if (shift_type == SHIFT_LOGICAL) + if (prepare->shift_type == SHIFT_LOGICAL) { - memset(&buffer->values[offset], 0, shift_len); + memset(&prepare->buffer->values[prepare->offset], 0, prepare->shift_len); } else { - memcpy(&buffer->values[offset], tmp_pixels, shift_len); + memcpy(&prepare->buffer->values[prepare->offset], prepare->tmp_pixels, prepare->shift_len); } } else { // Store the values which are moved out of the array (last n pixels) - memcpy(tmp_pixels, &buffer->values[offset], shift_len); + memcpy(prepare->tmp_pixels, &prepare->buffer->values[prepare->offset], prepare->shift_len); // Move pixels to end - os_memmove(&buffer->values[offset], &buffer->values[offset + shift*buffer->colorsPerLed], remaining_len); + os_memmove(&prepare->buffer->values[prepare->offset], &prepare->buffer->values[prepare->offset + prepare->shift_len], prepare->remaining_len); // Fill beginning with temp data - if (shift_type == SHIFT_LOGICAL) + if (prepare->shift_type == SHIFT_LOGICAL) { - memset(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], 0, shift_len); + memset(&prepare->buffer->values[prepare->offset + prepare->remaining_len], 0, prepare->shift_len); } else { - memcpy(&buffer->values[offset + (size-shift)*buffer->colorsPerLed], tmp_pixels, shift_len); + memcpy(&prepare->buffer->values[prepare->offset + prepare->remaining_len], prepare->tmp_pixels, prepare->shift_len); } } - // Free memory - free(tmp_pixels); - - return 0; } - static int ws2812_buffer_shift_lua(lua_State* L) { ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); @@ -351,11 +364,10 @@ static int ws2812_buffer_shift_lua(lua_State* L) { const int pos_end = luaL_optinteger(L, 5, -1); - ws2812_buffer_shift(buffer, shiftValue, shift_type, pos_start, pos_end); + ws2812_buffer_shift(L, buffer, shiftValue, shift_type, pos_start, pos_end); return 0; } - static int ws2812_buffer_dump(lua_State* L) { ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); @@ -366,8 +378,7 @@ static int ws2812_buffer_dump(lua_State* L) { static int ws2812_buffer_replace(lua_State* L) { ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer"); - size_t l = buffer->size; - ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), l); + ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), buffer->size); uint8_t *src; size_t srcLen; @@ -425,8 +436,8 @@ static int ws2812_buffer_mix(lua_State* L) { val += (int32_t)(source[src].values[i] * source[src].factor); } - val += 128; // rounding istead of floor - val >>= 8; + val += 128; // rounding istead of floor + val /= 256; // do not use implemetation dependant right shift if (val < 0) { val = 0; @@ -501,7 +512,7 @@ static int ws2812_buffer_set(lua_State* L) { // Overflow check if( buffer->colorsPerLed*led + len > buffer->colorsPerLed*buffer->size ) { - return luaL_error(L, "string size will exceed strip length"); + return luaL_error(L, "string size will exceed strip length"); } memcpy(&buffer->values[buffer->colorsPerLed*led], buf, len); @@ -531,8 +542,6 @@ static int ws2812_buffer_sub(lua_State* L) { size_t l = lhs->size; ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; if (start <= end) { ws2812_buffer *result = allocate_buffer(L, end - start + 1, lhs->colorsPerLed); memcpy(result->values, lhs->values + lhs->colorsPerLed * (start - 1), lhs->colorsPerLed * (end - start + 1)); @@ -591,10 +600,9 @@ static int ws2812_buffer_tostring(lua_State* L) { return 1; } - LROT_BEGIN(ws2812_buffer) LROT_FUNCENTRY( dump, ws2812_buffer_dump ) - LROT_FUNCENTRY( fade, ws2812_buffer_fade ) + LROT_FUNCENTRY( fade, ws2812_buffer_fade_lua) LROT_FUNCENTRY( fill, ws2812_buffer_fill_lua ) LROT_FUNCENTRY( get, ws2812_buffer_get ) LROT_FUNCENTRY( replace, ws2812_buffer_replace ) @@ -609,8 +617,6 @@ LROT_BEGIN(ws2812_buffer) LROT_FUNCENTRY( __tostring, ws2812_buffer_tostring ) LROT_END( ws2812_buffer, ws2812_buffer, LROT_MASK_INDEX ) - - LROT_BEGIN(ws2812) LROT_FUNCENTRY( init, ws2812_init ) LROT_FUNCENTRY( newBuffer, ws2812_new_buffer ) @@ -623,7 +629,6 @@ LROT_BEGIN(ws2812) LROT_NUMENTRY( SHIFT_CIRCULAR, SHIFT_CIRCULAR ) LROT_END( ws2812, NULL, 0 ) - int luaopen_ws2812(lua_State *L) { // TODO: Make sure that the GPIO system is initialized luaL_rometatable(L, "ws2812.buffer", LROT_TABLEREF(ws2812_buffer)); diff --git a/app/modules/ws2812.h b/app/modules/ws2812.h index 9b31910f..95fd3f02 100644 --- a/app/modules/ws2812.h +++ b/app/modules/ws2812.h @@ -17,6 +17,12 @@ #define SHIFT_LOGICAL 0 #define SHIFT_CIRCULAR 1 +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif typedef struct { int size; @@ -24,9 +30,26 @@ typedef struct { uint8_t values[0]; } ws2812_buffer; +typedef struct { + size_t offset; + uint8_t* tmp_pixels; + int shiftValue; + size_t shift_len; + size_t remaining_len; + unsigned shift_type; + ws2812_buffer* buffer; +} ws2812_buffer_shift_prepare; + void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2); -int ws2812_buffer_shift(ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end); +// To shift the lua_State is needed for error message and memory allocation. +// We also need the shift operation inside a timer callback, where we cannot access the lua_State, +// so This is split up in prepare and the actual call, which can be called multiple times with the same prepare object. +// After being done just luaM_free on the prepare object. +void ws2812_buffer_shift_prepared(ws2812_buffer_shift_prepare* prepare); +ws2812_buffer_shift_prepare* ws2812_buffer_get_shift_prepare(lua_State* L, ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end); + int ws2812_buffer_fill(ws2812_buffer * buffer, int * colors); +void ws2812_buffer_fade(ws2812_buffer * buffer, int fade, unsigned direction); #endif /* APP_MODULES_WS2812_H_ */ diff --git a/app/modules/ws2812_effects.c b/app/modules/ws2812_effects.c index e53676be..7e30dabc 100644 --- a/app/modules/ws2812_effects.c +++ b/app/modules/ws2812_effects.c @@ -38,6 +38,10 @@ #define min3(a,b, c) min((a), min((b), (c))) #define max3(a,b, c) max((a), max((b), (c))) +#define IDX_R 1 +#define IDX_G 0 +#define IDX_B 2 +#define IDX_W 3 typedef struct { @@ -54,6 +58,7 @@ typedef struct { uint8_t effect_type; uint8_t color[4]; int effect_int_param1; + ws2812_buffer_shift_prepare* prepare; } ws2812_effects; @@ -91,9 +96,6 @@ static int ws2812_write(ws2812_buffer* buffer) { size_t length1, length2; const char *buffer1, *buffer2; - buffer1 = 0; - length1 = 0; - buffer1 = buffer->values; length1 = buffer->colorsPerLed*buffer->size; @@ -115,11 +117,11 @@ static int ws2812_set_pixel(int pixel, uint32_t color) { uint8_t w = buffer->colorsPerLed == 4 ? ((color & 0xFF000000) >> 24) : 0; int offset = pixel * buffer->colorsPerLed; - buffer->values[offset] = g; - buffer->values[offset+1] = r; - buffer->values[offset+2] = b; + buffer->values[offset+IDX_R] = r; + buffer->values[offset+IDX_G] = g; + buffer->values[offset+IDX_B] = b; if (buffer->colorsPerLed == 4) { - buffer->values[offset+3] = w; + buffer->values[offset+IDX_W] = w; } return 0; @@ -158,12 +160,14 @@ static int ws2812_effects_init(lua_State *L) { luaL_argcheck(L, buffer != NULL, 1, "no valid buffer provided"); // get rid of old state if (state != NULL) { + if (state->prepare) { + luaM_free(L, state->prepare); + } luaL_unref(L, LUA_REGISTRYINDEX, state->buffer_ref); free((void *) state); } // Allocate memory and set all to zero - size_t size = sizeof(ws2812_effects) + buffer->colorsPerLed*sizeof(uint8_t); - state = (ws2812_effects *) calloc(1,size); + state = (ws2812_effects *) calloc(1,sizeof(ws2812_effects)); // initialize state->speed = SPEED_DEFAULT; state->mode_delay = DELAY_DEFAULT; @@ -203,10 +207,10 @@ static int ws2812_effects_get_speed(lua_State* L) { } static int ws2812_effects_set_speed(lua_State* L) { - uint8_t speed = luaL_checkinteger(L, 1); + int speed = luaL_checkinteger(L, 1); luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG); - luaL_argcheck(L, speed >= 0 && speed <= 255, 1, "should be a 0-255"); - state->speed = speed; + luaL_argcheck(L, speed >= SPEED_MIN && speed <= SPEED_MAX, 1, "should be 0-255"); + state->speed = (uint8_t)speed; state->mode_delay = 10; return 0; } @@ -230,37 +234,35 @@ static int ws2812_effects_set_delay(lua_State* L) { static int ws2812_effects_set_brightness(lua_State* L) { - uint8_t brightness = luaL_checkint(L, 1); + int brightness = luaL_checkint(L, 1); luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG); - luaL_argcheck(L, brightness >= 0 && brightness < 256, 1, "should be a 0-255"); - state->brightness = brightness; + luaL_argcheck(L, brightness >= BRIGHTNESS_MIN && brightness <= BRIGHTNESS_MAX, 1, "should be 0-255"); + state->brightness = (uint8_t) brightness; return 0; } -static int ws2812_effects_fill_buffer(uint32_t color) { +static void ws2812_effects_fill_buffer(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { ws2812_buffer * buffer = state->buffer; - uint8_t g = ((color & 0x00FF0000) >> 16); - uint8_t r = ((color & 0x0000FF00) >> 8); - uint8_t b = (color & 0x000000FF); - uint8_t w = buffer->colorsPerLed == 4 ? ((color & 0xFF000000) >> 24) : 0; + uint8_t bright_g = g * state->brightness / BRIGHTNESS_MAX; + uint8_t bright_r = r * state->brightness / BRIGHTNESS_MAX; + uint8_t bright_b = b * state->brightness / BRIGHTNESS_MAX; + uint8_t bright_w = w * state->brightness / BRIGHTNESS_MAX; // Fill buffer int i; uint8_t * p = &buffer->values[0]; for(i = 0; i < buffer->size; i++) { - *p++ = g * state->brightness / 255; - *p++ = r * state->brightness / 255; - *p++ = b * state->brightness / 255; + *p++ = bright_g; + *p++ = bright_r; + *p++ = bright_b; if (buffer->colorsPerLed == 4) { - *p++ = w * state->brightness / 255; + *p++ = bright_w; } } - - return 0; } @@ -279,9 +281,7 @@ static int ws2812_effects_fill_color() { uint8_t b = state->color[2]; uint8_t w = state->color[3]; - uint32_t color = (w << 24) | (g << 16) | (r << 8) | b; - - ws2812_effects_fill_buffer(color); + ws2812_effects_fill_buffer(r, g, b, w); return 0; } @@ -302,7 +302,7 @@ static int ws2812_effects_mode_blink() { // on ws2812_effects_fill_color(); } - else { + else { // off ws2812_buffer * buffer = state->buffer; memset(&buffer->values[0], 0, buffer->size * buffer->colorsPerLed); @@ -383,9 +383,9 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) { // convert to RGB uint32_t grb = hsv2grb(h, s, v); - *p++ = ((grb & 0x00FF0000) >> 16) * state->brightness / 255; - *p++ = ((grb & 0x0000FF00) >> 8) * state->brightness / 255; - *p++ = (grb & 0x000000FF) * state->brightness / 255; + *p++ = ((grb & 0x00FF0000) >> 16) * state->brightness / BRIGHTNESS_MAX; + *p++ = ((grb & 0x0000FF00) >> 8) * state->brightness / BRIGHTNESS_MAX; + *p++ = (grb & 0x000000FF) * state->brightness / BRIGHTNESS_MAX; for (j = 3; j < buffer->colorsPerLed; j++) { *p++ = 0; @@ -442,9 +442,9 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) { int steps = numPixels - 1; for(i = 0; i < numPixels; i++) { - *p++ = (g1 + ((g2-g1) * i / steps)) * state->brightness / 255; - *p++ = (r1 + ((r2-r1) * i / steps)) * state->brightness / 255; - *p++ = (b1 + ((b2-b1) * i / steps)) * state->brightness / 255; + *p++ = (g1 + ((g2-g1) * i / steps)) * state->brightness / BRIGHTNESS_MAX; + *p++ = (r1 + ((r2-r1) * i / steps)) * state->brightness / BRIGHTNESS_MAX; + *p++ = (b1 + ((b2-b1) * i / steps)) * state->brightness / BRIGHTNESS_MAX; for (j = 3; j < buffer->colorsPerLed; j++) { *p++ = 0; @@ -465,9 +465,9 @@ static int ws2812_effects_mode_random_color() { ws2812_buffer * buffer = state->buffer; uint32_t color = color_wheel(state->mode_color_index); - uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255; - uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255; - uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255; + uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / BRIGHTNESS_MAX; + uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / BRIGHTNESS_MAX; + uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / BRIGHTNESS_MAX; // Fill buffer int i,j; @@ -500,9 +500,9 @@ static int ws2812_effects_mode_rainbow() { int i,j; uint8_t * p = &buffer->values[0]; for(i = 0; i < buffer->size; i++) { - *p++ = g * state->brightness / 255; - *p++ = r * state->brightness / 255; - *p++ = b * state->brightness / 255; + *p++ = g * state->brightness / BRIGHTNESS_MAX; + *p++ = r * state->brightness / BRIGHTNESS_MAX; + *p++ = b * state->brightness / BRIGHTNESS_MAX; for (j = 3; j < buffer->colorsPerLed; j++) { *p++ = 0; @@ -526,9 +526,9 @@ static int ws2812_effects_mode_rainbow_cycle(int repeat_count) { for(i = 0; i < buffer->size; i++) { uint16_t wheel_index = (i * 360 / buffer->size * repeat_count) % 360; uint32_t color = color_wheel(wheel_index); - uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255; - uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255; - uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255; + uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / BRIGHTNESS_MAX; + uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / BRIGHTNESS_MAX; + uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / BRIGHTNESS_MAX; *p++ = g; *p++ = r; *p++ = b; @@ -565,9 +565,9 @@ static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) { if(g1<0) g1=0; if(r1<0) r1=0; if(b1<0) b1=0; - *p++ = g1 * state->brightness / 255; - *p++ = r1 * state->brightness / 255; - *p++ = b1 * state->brightness / 255; + *p++ = g1 * state->brightness / BRIGHTNESS_MAX; + *p++ = r1 * state->brightness / BRIGHTNESS_MAX; + *p++ = b1 * state->brightness / BRIGHTNESS_MAX; for (j = 3; j < buffer->colorsPerLed; j++) { *p++ = 0; } @@ -582,13 +582,13 @@ static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) { static int ws2812_effects_mode_halloween() { ws2812_buffer * buffer = state->buffer; - int g1 = 50 * state->brightness / 255; - int r1 = 255 * state->brightness / 255; - int b1 = 0 * state->brightness / 255; + int g1 = 50 * state->brightness / BRIGHTNESS_MAX; + int r1 = 255 * state->brightness / BRIGHTNESS_MAX; + int b1 = 0 * state->brightness / BRIGHTNESS_MAX; - int g2 = 0 * state->brightness / 255; - int r2 = 255 * state->brightness / 255; - int b2 = 130 * state->brightness / 255; + int g2 = 0 * state->brightness / BRIGHTNESS_MAX; + int r2 = 255 * state->brightness / BRIGHTNESS_MAX; + int b2 = 130 * state->brightness / BRIGHTNESS_MAX; // Fill buffer @@ -612,13 +612,13 @@ static int ws2812_effects_mode_halloween() { static int ws2812_effects_mode_circus_combustus() { ws2812_buffer * buffer = state->buffer; - int g1 = 0 * state->brightness / 255; - int r1 = 255 * state->brightness / 255; - int b1 = 0 * state->brightness / 255; + int g1 = 0 * state->brightness / BRIGHTNESS_MAX; + int r1 = 255 * state->brightness / BRIGHTNESS_MAX; + int b1 = 0 * state->brightness / BRIGHTNESS_MAX; - int g2 = 255 * state->brightness / 255; - int r2 = 255 * state->brightness / 255; - int b2 = 255 * state->brightness / 255; + int g2 = 255 * state->brightness / BRIGHTNESS_MAX; + int r2 = 255 * state->brightness / BRIGHTNESS_MAX; + int b2 = 255 * state->brightness / BRIGHTNESS_MAX; // Fill buffer int i,j; @@ -659,9 +659,7 @@ static int ws2812_effects_mode_larson_scanner() { ws2812_buffer * buffer = state->buffer; int led_index = 0; - for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) { - buffer->values[i] = buffer->values[i] >> 1; - } + ws2812_buffer_fade(buffer, 2, FADE_OUT); uint16_t pos = 0; @@ -694,9 +692,9 @@ static int ws2812_effects_mode_color_wipe() { } else { - uint8_t px_r = state->color[1] * state->brightness / 255; - uint8_t px_g = state->color[0] * state->brightness / 255; - uint8_t px_b = state->color[2] * state->brightness / 255; + uint8_t px_r = state->color[1] * state->brightness / BRIGHTNESS_MAX; + uint8_t px_g = state->color[0] * state->brightness / BRIGHTNESS_MAX; + uint8_t px_b = state->color[2] * state->brightness / BRIGHTNESS_MAX; buffer->values[led_index] = px_g; buffer->values[led_index + 1] = px_r; buffer->values[led_index + 2] = px_b; @@ -769,9 +767,8 @@ static uint32_t ws2812_effects_mode_delay() /** * run loop for the effects. */ -static void ws2812_effects_loop(void *p) +static void ws2812_effects_loop(void* p) { - if (state->effect_type == WS2812_EFFECT_BLINK) { ws2812_effects_mode_blink(); @@ -783,7 +780,7 @@ static void ws2812_effects_loop(void *p) else if (state->effect_type == WS2812_EFFECT_RAINBOW_CYCLE) { // the rainbow cycle effect can be achieved by shifting the buffer - ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + ws2812_buffer_shift_prepared(state->prepare); } else if (state->effect_type == WS2812_EFFECT_FLICKER) { @@ -815,11 +812,11 @@ static void ws2812_effects_loop(void *p) } else if (state->effect_type == WS2812_EFFECT_HALLOWEEN) { - ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + ws2812_buffer_shift_prepared(state->prepare); } else if (state->effect_type == WS2812_EFFECT_CIRCUS_COMBUSTUS) { - ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + ws2812_buffer_shift_prepared(state->prepare); } else if (state->effect_type == WS2812_EFFECT_LARSON_SCANNER) { @@ -827,7 +824,7 @@ static void ws2812_effects_loop(void *p) } else if (state->effect_type == WS2812_EFFECT_CYCLE) { - ws2812_buffer_shift(state->buffer, state->effect_int_param1, SHIFT_CIRCULAR, 1, -1); + ws2812_buffer_shift_prepared(state->prepare); } else if (state->effect_type == WS2812_EFFECT_COLOR_WIPE) { @@ -847,12 +844,21 @@ static void ws2812_effects_loop(void *p) ws2812_write(state->buffer); // set the timer if (state->running == 1 && state->mode_delay >= 10) + if (state->running == 1 && state->mode_delay >= 10) { os_timer_disarm(&(state->os_t)); os_timer_arm(&(state->os_t), state->mode_delay, FALSE); } } +void prepare_shift(lua_State* L, ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end){ + // deinit old effect + if (state->prepare) { + luaM_free(L, state->prepare); + } + + state->prepare = ws2812_buffer_get_shift_prepare(L, buffer, shiftValue, shift_type, pos_start, pos_end); +} /** * Set the active effect mode @@ -887,105 +893,102 @@ static int ws2812_effects_set_mode(lua_State* L) { switch (state->effect_type) { case WS2812_EFFECT_STATIC: - // fill with currently set color - ws2812_effects_fill_color(); - state->mode_delay = 250; - break; + // fill with currently set color + ws2812_effects_fill_color(); + state->mode_delay = 250; + break; case WS2812_EFFECT_BLINK: - ws2812_effects_mode_blink(); - break; + ws2812_effects_mode_blink(); + break; case WS2812_EFFECT_GRADIENT: - if(arg_type == LUA_TSTRING) - { - size_t length1; - const char *buffer1 = lua_tolstring(L, 2, &length1); - - if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0)) + if(arg_type == LUA_TSTRING) { - luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors"); + size_t length1; + const char *buffer1 = lua_tolstring(L, 2, &length1); + + if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0)) + { + luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors"); + } + + ws2812_effects_gradient(buffer1, length1); + ws2812_write(state->buffer); + } + else + { + luaL_argerror(L, 2, "string expected"); } - ws2812_effects_gradient(buffer1, length1); - ws2812_write(state->buffer); - } - else - { - luaL_argerror(L, 2, "string expected"); - } - - break; + break; case WS2812_EFFECT_GRADIENT_RGB: - if(arg_type == LUA_TSTRING) - { - size_t length1; - const char *buffer1 = lua_tolstring(L, 2, &length1); - - if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0)) + if(arg_type == LUA_TSTRING) { - luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors"); + size_t length1; + const char *buffer1 = lua_tolstring(L, 2, &length1); + + if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0)) + { + luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors"); + } + + ws2812_effects_gradient_rgb(buffer1, length1); + ws2812_write(state->buffer); + } + else + { + luaL_argerror(L, 2, "string expected"); } - ws2812_effects_gradient_rgb(buffer1, length1); - ws2812_write(state->buffer); - } - else - { - luaL_argerror(L, 2, "string expected"); - } - - break; + break; case WS2812_EFFECT_RANDOM_COLOR: - ws2812_effects_mode_random_color(); - break; + ws2812_effects_mode_random_color(); + break; case WS2812_EFFECT_RAINBOW: - ws2812_effects_mode_rainbow(); - break; + ws2812_effects_mode_rainbow(); + break; case WS2812_EFFECT_RAINBOW_CYCLE: - ws2812_effects_mode_rainbow_cycle(effect_param != EFFECT_PARAM_INVALID ? effect_param : 1); - break; - // flicker + ws2812_effects_mode_rainbow_cycle(effect_param != EFFECT_PARAM_INVALID ? effect_param : 1); + prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + break; case WS2812_EFFECT_FLICKER: - state->effect_int_param1 = effect_param; - break; + state->effect_int_param1 = effect_param; + break; case WS2812_EFFECT_FIRE_FLICKER: case WS2812_EFFECT_FIRE_FLICKER_SOFT: case WS2812_EFFECT_FIRE_FLICKER_INTENSE: - { state->color[0] = 255-40; state->color[1] = 255; state->color[2] = 40; state->color[3] = 0; - } - break; + break; case WS2812_EFFECT_HALLOWEEN: - ws2812_effects_mode_halloween(); - break; + ws2812_effects_mode_halloween(); + prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + break; case WS2812_EFFECT_CIRCUS_COMBUSTUS: - ws2812_effects_mode_circus_combustus(); - break; + ws2812_effects_mode_circus_combustus(); + prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1); + break; case WS2812_EFFECT_LARSON_SCANNER: - ws2812_effects_mode_larson_scanner(); - break; + ws2812_effects_mode_larson_scanner(); + break; case WS2812_EFFECT_CYCLE: - if (effect_param != EFFECT_PARAM_INVALID) { - state->effect_int_param1 = effect_param; - } - break; + if (effect_param != EFFECT_PARAM_INVALID) { + state->effect_int_param1 = effect_param; + } + prepare_shift(L, state->buffer, state->effect_int_param1, SHIFT_CIRCULAR, 1, -1); + break; case WS2812_EFFECT_COLOR_WIPE: - { - uint32_t black = 0; - ws2812_effects_fill_buffer(black); + // fill buffer with black. r,g,b,w = 0 + ws2812_effects_fill_buffer(0, 0, 0, 0); ws2812_effects_mode_color_wipe(); break; - } case WS2812_EFFECT_RANDOM_DOT: - { // check if more than 1 dot shall be set state->effect_int_param1 = effect_param; - uint32_t black = 0; - ws2812_effects_fill_buffer(black); + // fill buffer with black. r,g,b,w = 0 + ws2812_effects_fill_buffer(0, 0, 0, 0); break; - } } } diff --git a/lua_tests/mispec.lua b/lua_tests/mispec.lua new file mode 100644 index 00000000..9025f5bd --- /dev/null +++ b/lua_tests/mispec.lua @@ -0,0 +1,159 @@ +local moduleName = ... or 'mispec' +local M = {} +_G[moduleName] = M + +-- Helpers: +function ok(expression, desc) + if expression == nil then expression = false end + desc = desc or 'expression is not ok' + if not expression then + error(desc .. '\n' .. debug.traceback()) + end +end + +function ko(expression, desc) + if expression == nil then expression = false end + desc = desc or 'expression is not ko' + if expression then + error(desc .. '\n' .. debug.traceback()) + end +end + +function eq(a, b) + if type(a) ~= type(b) then + error('type ' .. type(a) .. ' is not equal to ' .. type(b) .. '\n' .. debug.traceback()) + end + if type(a) == 'function' then + return string.dump(a) == string.dump(b) + end + if a == b then return true end + if type(a) ~= 'table' then + error(string.format("%q",tostring(a)) .. ' is not equal to ' .. string.format("%q",tostring(b)) .. '\n' .. debug.traceback()) + end + for k,v in pairs(a) do + if b[k] == nil or not eq(v, b[k]) then return false end + end + for k,v in pairs(b) do + if a[k] == nil or not eq(v, a[k]) then return false end + end + return true +end + +function failwith(message, func, ...) + local status, err = pcall(func, ...) + if status then + local messagePart = "" + if message then + messagePart = " containing \"" .. message .. "\"" + end + error("Error expected" .. messagePart .. '\n' .. debug.traceback()) + end + if (message and not string.find(err, message)) then + error("expected errormessage \"" .. err .. "\" to contain \"" .. message .. "\"" .. '\n' .. debug.traceback() ) + end + return true +end + +function fail(func, ...) + return failwith(nil, func, ...) +end + +local function eventuallyImpl(func, retries, delayMs) + local prevEventually = _G.eventually + _G.eventually = function() error("Cannot nest eventually/andThen.") end + local status, err = pcall(func) + _G.eventually = prevEventually + if status then + M.queuedEventuallyCount = M.queuedEventuallyCount - 1 + M.runNextPending() + else + if retries > 0 then + local t = tmr.create() + t:register(delayMs, tmr.ALARM_SINGLE, M.runNextPending) + t:start() + + table.insert(M.pending, 1, function() eventuallyImpl(func, retries - 1, delayMs) end) + else + M.failed = M.failed + 1 + print("\n ! it failed:", err) + + -- remove all pending eventuallies as spec has failed at this point + for i = 1, M.queuedEventuallyCount - 1 do + table.remove(M.pending, 1) + end + M.queuedEventuallyCount = 0 + M.runNextPending() + end + end +end + +function eventually(func, retries, delayMs) + retries = retries or 10 + delayMs = delayMs or 300 + + M.queuedEventuallyCount = M.queuedEventuallyCount + 1 + + table.insert(M.pending, M.queuedEventuallyCount, function() + eventuallyImpl(func, retries, delayMs) + end) +end + +function andThen(func) + eventually(func, 0, 0) +end + +function describe(name, itshoulds) + M.name = name + M.itshoulds = itshoulds +end + +-- Module: +M.runNextPending = function() + local next = table.remove(M.pending, 1) + if next then + node.task.post(next) + next = nil + else + M.succeeded = M.total - M.failed + local elapsedSeconds = (tmr.now() - M.startTime) / 1000 / 1000 + print(string.format( + '\n\nCompleted in %.2f seconds.\nSuccess rate is %.1f%% (%d failed out of %d).', + elapsedSeconds, 100 * M.succeeded / M.total, M.failed, M.total)) + M.pending = nil + M.queuedEventuallyCount = nil + end +end + +M.run = function() + M.pending = {} + M.queuedEventuallyCount = 0 + M.startTime = tmr.now() + M.total = 0 + M.failed = 0 + local it = {} + it.should = function(_, desc, func) + table.insert(M.pending, function() + uart.write(0, '\n * ' .. desc) + M.total = M.total + 1 + if M.pre then M.pre() end + local status, err = pcall(func) + if not status then + print("\n ! it failed:", err) + M.failed = M.failed + 1 + end + if M.post then M.post() end + M.runNextPending() + end) + end + it.initialize = function(_, pre) M.pre = pre end; + it.cleanup = function(_, post) M.post = post end; + M.itshoulds(it) + + print('' .. M.name .. ', it should:') + M.runNextPending() + + M.itshoulds = nil + M.name = nil +end + +print ("loaded mispec") diff --git a/lua_tests/mispec_ws2812.lua b/lua_tests/mispec_ws2812.lua new file mode 100644 index 00000000..9538ca0c --- /dev/null +++ b/lua_tests/mispec_ws2812.lua @@ -0,0 +1,153 @@ +require 'mispec' + +local buffer, buffer1, buffer2 + +local function initBuffer(buffer, ...) + local i,v + for i,v in ipairs({...}) do + buffer:set(i, v, v*2, v*3, v*4) + end + return buffer +end + +local function equalsBuffer(buffer1, buffer2) + return eq(buffer1:dump(), buffer2:dump()) +end + + +describe('WS2812 buffers', function(it) + + it:should('initialize a buffer', function() + buffer = ws2812.newBuffer(9, 3) + ko(buffer == nil) + ok(eq(buffer:size(), 9), "check size") + ok(eq(buffer:dump(), string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)), "initialize with 0") + + failwith("should be a positive integer", ws2812.newBuffer, 9, 0) + failwith("should be a positive integer", ws2812.newBuffer, 9, -1) + failwith("should be a positive integer", ws2812.newBuffer, 0, 3) + failwith("should be a positive integer", ws2812.newBuffer, -1, 3) + end) + + it:should('have correct size', function() + buffer = ws2812.newBuffer(9, 3) + ok(eq(buffer:size(), 9), "check size") + buffer = ws2812.newBuffer(9, 22) + ok(eq(buffer:size(), 9), "check size") + buffer = ws2812.newBuffer(13, 1) + ok(eq(buffer:size(), 13), "check size") + end) + + it:should('fill a buffer with one color', function() + buffer = ws2812.newBuffer(3, 3) + buffer:fill(1,222,55) + ok(eq(buffer:dump(), string.char(1,222,55,1,222,55,1,222,55)), "RGB") + buffer = ws2812.newBuffer(3, 4) + buffer:fill(1,222,55, 77) + ok(eq(buffer:dump(), string.char(1,222,55,77,1,222,55,77,1,222,55,77)), "RGBW") + end) + + it:should('replace correctly', function() + buffer = ws2812.newBuffer(5, 3) + buffer:replace(string.char(3,255,165,33,0,244,12,87,255)) + ok(eq(buffer:dump(), string.char(3,255,165,33,0,244,12,87,255,0,0,0,0,0,0)), "RGBW") + + buffer = ws2812.newBuffer(5, 3) + buffer:replace(string.char(3,255,165,33,0,244,12,87,255), 2) + ok(eq(buffer:dump(), string.char(0,0,0,3,255,165,33,0,244,12,87,255,0,0,0)), "RGBW") + + buffer = ws2812.newBuffer(5, 3) + buffer:replace(string.char(3,255,165,33,0,244,12,87,255), -5) + ok(eq(buffer:dump(), string.char(3,255,165,33,0,244,12,87,255,0,0,0,0,0,0)), "RGBW") + + failwith("Does not fit into destination", function() buffer:replace(string.char(3,255,165,33,0,244,12,87,255), 4) end) + end) + + it:should('replace correctly issue #2921', function() + local buffer = ws2812.newBuffer(5, 3) + buffer:replace(string.char(3,255,165,33,0,244,12,87,255), -7) + ok(eq(buffer:dump(), string.char(3,255,165,33,0,244,12,87,255,0,0,0,0,0,0)), "RGBW") + end) + + it:should('get/set correctly', function() + buffer = ws2812.newBuffer(3, 4) + buffer:fill(1,222,55,13) + ok(eq({buffer:get(2)},{1,222,55,13})) + buffer:set(2, 4,53,99,0) + ok(eq({buffer:get(1)},{1,222,55,13})) + ok(eq({buffer:get(2)},{4,53,99,0})) + ok(eq(buffer:dump(), string.char(1,222,55,13,4,53,99,0,1,222,55,13)), "RGBW") + + failwith("index out of range", function() buffer:get(0) end) + failwith("index out of range", function() buffer:get(4) end) + failwith("index out of range", function() buffer:set(0,1,2,3,4) end) + failwith("index out of range", function() buffer:set(4,1,2,3,4) end) + failwith("number expected, got no value", function() buffer:set(2,1,2,3) end) +-- failwith("extra values given", function() buffer:set(2,1,2,3,4,5) end) + end) + + it:should('fade correctly', function() + buffer = ws2812.newBuffer(1, 3) + buffer:fill(1,222,55) + buffer:fade(2) + ok(buffer:dump() == string.char(0,111,27), "RGB") + buffer:fill(1,222,55) + buffer:fade(3, ws2812.FADE_OUT) + ok(buffer:dump() == string.char(0,222/3,55/3), "RGB") + buffer:fill(1,222,55) + buffer:fade(3, ws2812.FADE_IN) + ok(buffer:dump() == string.char(3,255,165), "RGB") + buffer = ws2812.newBuffer(1, 4) + buffer:fill(1,222,55, 77) + buffer:fade(2, ws2812.FADE_OUT) + ok(eq(buffer:dump(), string.char(0,111,27,38)), "RGBW") + end) + + it:should('mix correctly issue #1736', function() + buffer1 = ws2812.newBuffer(1, 3) + buffer2 = ws2812.newBuffer(1, 3) + buffer1:fill(10,22,54) + buffer2:fill(10,27,55) + buffer1:mix(256/8*7,buffer1,256/8,buffer2) + ok(eq({buffer1:get(1)}, {10,23,54})) + end) + + it:should('mix saturation correctly ', function() + buffer1 = ws2812.newBuffer(1, 3) + buffer2 = ws2812.newBuffer(1, 3) + + buffer1:fill(10,22,54) + buffer2:fill(10,27,55) + buffer1:mix(256/2,buffer1,-256,buffer2) + ok(eq({buffer1:get(1)}, {0,0,0})) + + buffer1:fill(10,22,54) + buffer2:fill(10,27,55) + buffer1:mix(25600,buffer1,256/8,buffer2) + ok(eq({buffer1:get(1)}, {255,255,255})) + + buffer1:fill(10,22,54) + buffer2:fill(10,27,55) + buffer1:mix(-257,buffer1,255,buffer2) + ok(eq({buffer1:get(1)}, {0,5,1})) + end) + + it:should('mix with strings correctly ', function() + buffer1 = ws2812.newBuffer(1, 3) + buffer2 = ws2812.newBuffer(1, 3) + + buffer1:fill(10,22,54) + buffer2:fill(10,27,55) + buffer1:mix(-257,buffer1:dump(),255,buffer2:dump()) + ok(eq({buffer1:get(1)}, {0,5,1})) + end) + + it:should('power', function() + buffer = ws2812.newBuffer(2, 4) + buffer:fill(10,22,54,234) + ok(eq(buffer:power(), 2*(10+22+54+234))) + end) + +end) + +mispec.run() diff --git a/lua_tests/mispec_ws2812_2.lua b/lua_tests/mispec_ws2812_2.lua new file mode 100644 index 00000000..a9b4056b --- /dev/null +++ b/lua_tests/mispec_ws2812_2.lua @@ -0,0 +1,149 @@ +require 'mispec' + +local buffer, buffer1, buffer2 + +local function initBuffer(buffer, ...) + local i,v + for i,v in ipairs({...}) do + buffer:set(i, v, v*2, v*3, v*4) + end + return buffer +end + +local function equalsBuffer(buffer1, buffer2) + return eq(buffer1:dump(), buffer2:dump()) +end + + +describe('WS2812 buffers', function(it) + + it:should('shift LOGICAL', function() + + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(4, 4) + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,0,0,7,8) + buffer1:shift(2) + ok(equalsBuffer(buffer1, buffer2), "shift right") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,9,12,0,0) + buffer1:shift(-2) + ok(equalsBuffer(buffer1, buffer2), "shift left") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,7,0,8,12) + buffer1:shift(1, nil, 2,3) + ok(equalsBuffer(buffer1, buffer2), "shift middle right") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,7,9,0,12) + buffer1:shift(-1, nil, 2,3) + ok(equalsBuffer(buffer1, buffer2), "shift middle left") + + -- bounds checks, handle gracefully as string:sub does + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,8,9,12,0) + buffer1:shift(-1, ws2812.SHIFT_LOGICAL, 0,5) + ok(equalsBuffer(buffer1, buffer2), "shift left out of bound") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,0,7,8,9) + buffer1:shift(1, ws2812.SHIFT_LOGICAL, 0,5) + ok(equalsBuffer(buffer1, buffer2), "shift right out of bound") + + end) + + it:should('shift LOGICAL issue #2946', function() + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(4, 4) + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,0,0,0,0) + buffer1:shift(4) + ok(equalsBuffer(buffer1, buffer2), "shift all right") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,0,0,0,0) + buffer1:shift(-4) + ok(equalsBuffer(buffer1, buffer2), "shift all left") + + failwith("shifting more elements than buffer size", function() buffer1:shift(10) end) + failwith("shifting more elements than buffer size", function() buffer1:shift(-6) end) + end) + + it:should('shift CIRCULAR', function() + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(4, 4) + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,9,12,7,8) + buffer1:shift(2, ws2812.SHIFT_CIRCULAR) + ok(equalsBuffer(buffer1, buffer2), "shift right") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,9,12,7,8) + buffer1:shift(-2, ws2812.SHIFT_CIRCULAR) + ok(equalsBuffer(buffer1, buffer2), "shift left") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,7,9,8,12) + buffer1:shift(1, ws2812.SHIFT_CIRCULAR, 2,3) + ok(equalsBuffer(buffer1, buffer2), "shift middle right") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,7,9,8,12) + buffer1:shift(-1, ws2812.SHIFT_CIRCULAR, 2,3) + ok(equalsBuffer(buffer1, buffer2), "shift middle left") + + -- bounds checks, handle gracefully as string:sub does + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,8,9,12,7) + buffer1:shift(-1, ws2812.SHIFT_CIRCULAR, 0,5) + ok(equalsBuffer(buffer1, buffer2), "shift left out of bound") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,12,7,8,9) + buffer1:shift(1, ws2812.SHIFT_CIRCULAR, 0,5) + ok(equalsBuffer(buffer1, buffer2), "shift right out of bound") + + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,12,7,8,9) + buffer1:shift(1, ws2812.SHIFT_CIRCULAR, -12,12) + ok(equalsBuffer(buffer1, buffer2), "shift right way out of bound") + + end) + + it:should('sub', function() + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(4, 4) + initBuffer(buffer1,7,8,9,12) + buffer1 = buffer1:sub(4,3) + ok(eq(buffer1:size(), 0), "sub empty") + + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(2, 4) + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,9,12) + buffer1 = buffer1:sub(3,4) + ok(equalsBuffer(buffer1, buffer2), "sub") + + buffer1 = ws2812.newBuffer(4, 4) + buffer2 = ws2812.newBuffer(4, 4) + initBuffer(buffer1,7,8,9,12) + initBuffer(buffer2,7,8,9,12) + buffer1 = buffer1:sub(-12,33) + ok(equalsBuffer(buffer1, buffer2), "out of bounds") + end) + + + + +--[[ +ws2812.buffer:__concat() +--]] + +end) + +mispec.run() diff --git a/lua_tests/mispec_ws2812_effects.lua b/lua_tests/mispec_ws2812_effects.lua new file mode 100644 index 00000000..3fc8d95b --- /dev/null +++ b/lua_tests/mispec_ws2812_effects.lua @@ -0,0 +1,31 @@ +require 'mispec' + +local buffer, buffer1, buffer2 + +describe('WS2812_effects', function(it) + + it:should('set_speed', function() + buffer = ws2812.newBuffer(9, 3) + ws2812_effects.init(buffer) + + ws2812_effects.set_speed(0) + ws2812_effects.set_speed(255) + + failwith("should be", ws2812_effects.set_speed, -1) + failwith("should be", ws2812_effects.set_speed, 256) + end) + + it:should('set_brightness', function() + buffer = ws2812.newBuffer(9, 3) + ws2812_effects.init(buffer) + + ws2812_effects.set_brightness(0) + ws2812_effects.set_brightness(255) + + failwith("should be", ws2812_effects.set_brightness, -1) + failwith("should be", ws2812_effects.set_brightness, 256) + end) + +end) + +mispec.run()