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:
Gregor Hartmann 2019-12-09 13:30:09 +01:00 committed by Marcel Stör
parent e7be7644c0
commit d4b5b0cbaf
8 changed files with 723 additions and 199 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ tools/toolchains/
.project
.settings/
.vscode
.vs
#ignore temp file for build infos
buildinfo.h

View File

@ -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));

View File

@ -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_ */

View File

@ -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;
}
}
}

159
lua_tests/mispec.lua Normal file
View File

@ -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")

153
lua_tests/mispec_ws2812.lua Normal file
View File

@ -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()

View File

@ -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()

View File

@ -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()