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
This commit is contained in:
parent
e7be7644c0
commit
d4b5b0cbaf
|
@ -16,6 +16,7 @@ tools/toolchains/
|
|||
.project
|
||||
.settings/
|
||||
.vscode
|
||||
.vs
|
||||
|
||||
#ignore temp file for build infos
|
||||
buildinfo.h
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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")
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
Loading…
Reference in New Issue