LED strip refactor (#3158)
`ws2812` buffer extracted to new `pixbuf` module. * The new pixbuf module has more functionality than the `ws2812`-specific buffer it replaces. * This is work in progress towards https://github.com/nodemcu/nodemcu-firmware/issues/2916 * The LED driver modules `ws2812`, `ws2801`, `apa102`, and `tm1829` have sprouted `pixbuf` support. * `NTest` tests for `pixbuf` now exist. While here, document the ws2812 UART-based overlapping with mainline execution. Fixes https://github.com/nodemcu/nodemcu-firmware/issues/3140 Co-authored-by: Gregor Hartmann <HHHartmann@users.noreply.github.com>
This commit is contained in:
parent
9a26e0a94b
commit
85df6b588d
|
@ -45,6 +45,7 @@
|
|||
//#define LUA_USE_MODULES_PCM
|
||||
//#define LUA_USE_MODULES_PERF
|
||||
//#define LUA_USE_MODULES_PIPE
|
||||
//#define LUA_USE_MODULES_PIXBUF
|
||||
//#define LUA_USE_MODULES_PWM
|
||||
//#define LUA_USE_MODULES_PWM2
|
||||
//#define LUA_USE_MODULES_RFSWITCH
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include "platform.h"
|
||||
#include "user_interface.h"
|
||||
|
||||
#include "pixbuf.h"
|
||||
|
||||
|
||||
#define NOP asm volatile(" nop \n\t")
|
||||
|
||||
|
@ -79,9 +81,26 @@ static int apa102_write(lua_State* L) {
|
|||
MOD_CHECK_ID(gpio, clock_pin);
|
||||
uint32_t alt_clock_pin = pin_num[clock_pin];
|
||||
|
||||
size_t buf_len;
|
||||
const char *buf = luaL_checklstring(L, 3, &buf_len);
|
||||
uint32_t nbr_frames = buf_len / 4;
|
||||
const char *buf;
|
||||
uint32_t nbr_frames;
|
||||
|
||||
switch(lua_type(L, 3)) {
|
||||
case LUA_TSTRING: {
|
||||
size_t buf_len;
|
||||
buf = luaL_checklstring(L, 3, &buf_len);
|
||||
nbr_frames = buf_len / 4;
|
||||
break;
|
||||
}
|
||||
case LUA_TUSERDATA: {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 3);
|
||||
luaL_argcheck(L, buffer->nchan == 4, 3, "Pixbuf not 4-channel");
|
||||
buf = (const char *)buffer->values;
|
||||
nbr_frames = buffer->npix;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return luaL_argerror(L, 3, "String or pixbuf expected");
|
||||
}
|
||||
|
||||
if (nbr_frames > 100000) {
|
||||
return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark.");
|
||||
|
|
|
@ -0,0 +1,723 @@
|
|||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pixbuf.h"
|
||||
#define PIXBUF_METATABLE "pixbuf.buf"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
pixbuf *pixbuf_from_lua_arg(lua_State *L, int arg) {
|
||||
return luaL_checkudata(L, arg, PIXBUF_METATABLE);
|
||||
}
|
||||
|
||||
pixbuf *pixbuf_opt_from_lua_arg(lua_State *L, int arg) {
|
||||
return luaL_testudata(L, arg, PIXBUF_METATABLE);
|
||||
}
|
||||
|
||||
static ssize_t posrelat(ssize_t pos, size_t len) {
|
||||
/* relative string position: negative means back from end */
|
||||
if (pos < 0)
|
||||
pos += (ssize_t)len + 1;
|
||||
return MIN(MAX(pos, 1), len);
|
||||
}
|
||||
|
||||
const size_t pixbuf_channels(pixbuf *p) {
|
||||
return p->nchan;
|
||||
}
|
||||
|
||||
const size_t pixbuf_size(pixbuf *p) {
|
||||
return p->npix * p->nchan;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a pixbuf newuserdata using C arguments.
|
||||
*
|
||||
* Allocates, so may throw! Leaves new buffer at the top of the Lua stack
|
||||
* and returns a C pointer.
|
||||
*/
|
||||
static pixbuf *pixbuf_new(lua_State *L, size_t leds, size_t chans) {
|
||||
// Allocate memory
|
||||
|
||||
// A crude hack of an overflow check, but unlikely to be reached in practice
|
||||
if ((leds > 8192) || (chans > 32)) {
|
||||
luaL_error(L, "pixbuf size limits exeeded");
|
||||
return NULL; // UNREACHED
|
||||
}
|
||||
|
||||
size_t size = sizeof(pixbuf) + leds * chans;
|
||||
|
||||
pixbuf *buffer = (pixbuf*)lua_newuserdata(L, size);
|
||||
|
||||
// Associate its metatable
|
||||
luaL_getmetatable(L, PIXBUF_METATABLE);
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
// Save led strip size
|
||||
*(size_t *)&buffer->npix = leds;
|
||||
*(size_t *)&buffer->nchan = chans;
|
||||
|
||||
memset(buffer->values, 0, leds * chans);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Handle a buffer where we can store led values
|
||||
int pixbuf_new_lua(lua_State *L) {
|
||||
const int leds = luaL_checkint(L, 1);
|
||||
const int chans = luaL_checkint(L, 2);
|
||||
|
||||
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
|
||||
luaL_argcheck(L, chans > 0, 2, "should be a positive integer");
|
||||
|
||||
pixbuf_new(L, leds, chans);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_concat_lua(lua_State *L) {
|
||||
pixbuf *lhs = pixbuf_from_lua_arg(L, 1);
|
||||
pixbuf *rhs = pixbuf_from_lua_arg(L, 2);
|
||||
|
||||
luaL_argcheck(L, lhs->nchan == rhs->nchan, 1,
|
||||
"can only concatenate buffers with same channel count");
|
||||
|
||||
size_t osize = lhs->npix + rhs->npix;
|
||||
if (lhs->npix > osize) {
|
||||
return luaL_error(L, "size sum overflow");
|
||||
}
|
||||
|
||||
pixbuf *buffer = pixbuf_new(L, osize, lhs->nchan);
|
||||
|
||||
memcpy(buffer->values, lhs->values, pixbuf_size(lhs));
|
||||
memcpy(buffer->values + pixbuf_size(lhs), rhs->values, pixbuf_size(rhs));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_channels_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
lua_pushinteger(L, buffer->nchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_dump_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
lua_pushlstring(L, (char*)buffer->values, pixbuf_size(buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_eq_lua(lua_State *L) {
|
||||
bool res;
|
||||
|
||||
pixbuf *lhs = pixbuf_from_lua_arg(L, 1);
|
||||
pixbuf *rhs = pixbuf_from_lua_arg(L, 2);
|
||||
|
||||
if (lhs->npix != rhs->npix) {
|
||||
res = false;
|
||||
} else if (lhs->nchan != rhs->nchan) {
|
||||
res = false;
|
||||
} else {
|
||||
res = true;
|
||||
for(size_t i = 0; i < pixbuf_size(lhs); i++) {
|
||||
if(lhs->values[i] != rhs->values[i]) {
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_fade_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
const int fade = luaL_checkinteger(L, 2);
|
||||
unsigned direction = luaL_optinteger( L, 3, PIXBUF_FADE_OUT );
|
||||
|
||||
luaL_argcheck(L, fade > 0, 2, "fade value should be a strictly positive int");
|
||||
|
||||
uint8_t *p = &buffer->values[0];
|
||||
for (size_t i = 0; i < pixbuf_size(buffer); i++)
|
||||
{
|
||||
if (direction == PIXBUF_FADE_OUT)
|
||||
{
|
||||
*p++ /= fade;
|
||||
}
|
||||
else
|
||||
{
|
||||
// as fade in can result in value overflow, an int is used to perform the check afterwards
|
||||
int val = *p * fade;
|
||||
*p++ = MIN(255, val);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Fade an Ixxx-type strip by just manipulating the I bytes */
|
||||
static int pixbuf_fadeI_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
const int fade = luaL_checkinteger(L, 2);
|
||||
unsigned direction = luaL_optinteger( L, 3, PIXBUF_FADE_OUT );
|
||||
|
||||
luaL_argcheck(L, fade > 0, 2, "fade value should be a strictly positive int");
|
||||
|
||||
uint8_t *p = &buffer->values[0];
|
||||
for (size_t i = 0; i < buffer->npix; i++, p+=buffer->nchan) {
|
||||
if (direction == PIXBUF_FADE_OUT) {
|
||||
*p /= fade;
|
||||
} else {
|
||||
int val = *p * fade;
|
||||
*p++ = MIN(255, val);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pixbuf_fill_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
|
||||
if (buffer->npix == 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (lua_gettop(L) != (1 + buffer->nchan)) {
|
||||
return luaL_argerror(L, 1, "need as many values as colors per pixel");
|
||||
}
|
||||
|
||||
/* Fill the first pixel from the Lua stack */
|
||||
for (size_t i = 0; i < buffer->nchan; i++) {
|
||||
buffer->values[i] = luaL_checkinteger(L, 2+i);
|
||||
}
|
||||
|
||||
/* Fill the rest of the pixels from the first */
|
||||
for (size_t i = 1; i < buffer->npix; i++) {
|
||||
memcpy(&buffer->values[i * buffer->nchan], buffer->values, buffer->nchan);
|
||||
}
|
||||
|
||||
out:
|
||||
lua_settop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_get_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
const int led = luaL_checkinteger(L, 2) - 1;
|
||||
size_t channels = buffer->nchan;
|
||||
|
||||
luaL_argcheck(L, led >= 0 && led < buffer->npix, 2, "index out of range");
|
||||
|
||||
uint8_t tmp[channels];
|
||||
memcpy(tmp, &buffer->values[channels*led], channels);
|
||||
|
||||
for (size_t i = 0; i < channels; i++)
|
||||
{
|
||||
lua_pushinteger(L, tmp[i]);
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
/* :map(f, buf1, ilo, ihi, [buf2, ilo2]) */
|
||||
static int pixbuf_map_lua(lua_State *L) {
|
||||
pixbuf *outbuf = pixbuf_from_lua_arg(L, 1);
|
||||
/* f at index 2 */
|
||||
|
||||
pixbuf *buffer1 = pixbuf_opt_from_lua_arg(L, 3);
|
||||
if (!buffer1)
|
||||
buffer1 = outbuf;
|
||||
|
||||
const int ilo = posrelat(luaL_optinteger(L, 4, 1), buffer1->npix) - 1;
|
||||
const int ihi = posrelat(luaL_optinteger(L, 5, buffer1->npix), buffer1->npix) - 1;
|
||||
|
||||
luaL_argcheck(L, ihi > ilo, 3, "Buffer limits out of order");
|
||||
|
||||
size_t npix = ihi - ilo + 1;
|
||||
|
||||
luaL_argcheck(L, npix == outbuf->npix, 1, "Output buffer wrong size");
|
||||
|
||||
pixbuf *buffer2 = pixbuf_opt_from_lua_arg(L, 6);
|
||||
const int ilo2 = buffer2 ? posrelat(luaL_optinteger(L, 7, 1), buffer2->npix) - 1 : 0;
|
||||
|
||||
if (buffer2) {
|
||||
luaL_argcheck(L, ilo2 + npix <= buffer2->npix, 6, "Second buffer too short");
|
||||
}
|
||||
|
||||
for (size_t p = 0; p < npix; p++) {
|
||||
lua_pushvalue(L, 2);
|
||||
for (size_t c = 0; c < buffer1->nchan; c++) {
|
||||
lua_pushinteger(L, buffer1->values[(ilo + p) * buffer1->nchan + c]);
|
||||
}
|
||||
if (buffer2) {
|
||||
for (size_t c = 0; c < buffer2->nchan; c++) {
|
||||
lua_pushinteger(L, buffer2->values[(ilo2 + p) * buffer2->nchan + c]);
|
||||
}
|
||||
}
|
||||
lua_call(L, buffer1->nchan + (buffer2 ? buffer2->nchan : 0), outbuf->nchan);
|
||||
for (size_t c = 0; c < outbuf->nchan; c++) {
|
||||
outbuf->values[(p + 1) * outbuf->nchan - c - 1] = luaL_checkinteger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct mix_source {
|
||||
int factor;
|
||||
const uint8_t *values;
|
||||
};
|
||||
|
||||
static uint32_t pixbuf_mix_clamp(int32_t v) {
|
||||
if (v < 0) { return 0; }
|
||||
if (v > 255) { return 255; }
|
||||
return v;
|
||||
}
|
||||
|
||||
/* This one can sum straightforwardly, channel by channel */
|
||||
static void pixbuf_mix_raw(pixbuf *out, size_t n_src, struct mix_source* src) {
|
||||
size_t cells = pixbuf_size(out);
|
||||
|
||||
for (size_t c = 0; c < cells; c++) {
|
||||
int32_t val = 0;
|
||||
for (size_t s = 0; s < n_src; s++) {
|
||||
val += (int32_t)src[s].values[c] * src[s].factor;
|
||||
}
|
||||
|
||||
val += 128; // rounding instead of floor
|
||||
val /= 256; // do not use implemetation dependant right shift
|
||||
|
||||
out->values[c] = (uint8_t)pixbuf_mix_clamp(val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Mix intensity-mediated three-color pixbufs.
|
||||
*
|
||||
* XXX This is untested in real hardware; do they actually behave like this?
|
||||
*/
|
||||
static void pixbuf_mix_i3(pixbuf *out, size_t ibits, size_t n_src,
|
||||
struct mix_source* src) {
|
||||
for(size_t p = 0; p < out->npix; p++) {
|
||||
int32_t sums[3] = { 0, 0, 0 };
|
||||
|
||||
for (size_t s = 0; s < n_src; s++) {
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
sums[c] += (int32_t)src[s].values[4*p+c+1] // color channel
|
||||
* src[s].values[4*p] // global intensity
|
||||
* src[s].factor; // user factor
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pmaxc = 0;
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
pmaxc = sums[c] > pmaxc ? sums[c] : pmaxc;
|
||||
}
|
||||
|
||||
size_t maxgi;
|
||||
if (pmaxc == 0) {
|
||||
/* Zero value */
|
||||
memset(&out->values[4*p], 0, 4);
|
||||
return;
|
||||
} else if (pmaxc <= (1 << 16)) {
|
||||
/* Minimum global factor */
|
||||
maxgi = 1;
|
||||
} else if (pmaxc >= ((1 << ibits) - 1) << 16) {
|
||||
/* Maximum global factor */
|
||||
maxgi = (1 << ibits) - 1;
|
||||
} else {
|
||||
maxgi = (pmaxc >> 16) + 1;
|
||||
}
|
||||
|
||||
// printf("mixi3: %x %x %x -> %x, %zx\n", sums[0], sums[1], sums[2], pmaxc, maxgi);
|
||||
|
||||
out->values[4*p] = maxgi;
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
out->values[4*p+c+1] = pixbuf_mix_clamp((sums[c] + 256 * maxgi - 127) / (256 * maxgi));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// buffer:mix(factor1, buffer1, ..)
|
||||
// factor is 256 for 100%
|
||||
// uses saturating arithmetic (one buffer at a time)
|
||||
static int pixbuf_mix_core(lua_State *L, size_t ibits) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
pixbuf *src_buffer;
|
||||
|
||||
int pos = 2;
|
||||
size_t n_sources = (lua_gettop(L) - 1) / 2;
|
||||
struct mix_source sources[n_sources];
|
||||
|
||||
if (n_sources == 0) {
|
||||
lua_settop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t src = 0; src < n_sources; src++, pos += 2) {
|
||||
int factor = luaL_checkinteger(L, pos);
|
||||
src_buffer = pixbuf_from_lua_arg(L, pos + 1);
|
||||
|
||||
luaL_argcheck(L, src_buffer->npix == buffer->npix &&
|
||||
src_buffer->nchan == buffer->nchan,
|
||||
pos + 1, "buffer not same size or shape");
|
||||
|
||||
sources[src].factor = factor;
|
||||
sources[src].values = src_buffer->values;
|
||||
}
|
||||
|
||||
if (ibits != 0) {
|
||||
luaL_argcheck(L, src_buffer->nchan == 4, 2, "Requires 4 channel pixbuf");
|
||||
pixbuf_mix_i3(buffer, ibits, n_sources, sources);
|
||||
} else {
|
||||
pixbuf_mix_raw(buffer, n_sources, sources);
|
||||
}
|
||||
|
||||
lua_settop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_mix_lua(lua_State *L) {
|
||||
return pixbuf_mix_core(L, 0);
|
||||
}
|
||||
|
||||
static int pixbuf_mix4I5_lua(lua_State *L) {
|
||||
return pixbuf_mix_core(L, 5);
|
||||
}
|
||||
|
||||
|
||||
// Returns the total of all channels
|
||||
static int pixbuf_power_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
|
||||
int total = 0;
|
||||
size_t p = 0;
|
||||
for (size_t i = 0; i < buffer->npix; i++) {
|
||||
for (size_t j = 0; j < buffer->nchan; j++, p++) {
|
||||
total += buffer->values[p];
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, total);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Returns the total of all channels, intensity-style
|
||||
static int pixbuf_powerI_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
|
||||
int total = 0;
|
||||
size_t p = 0;
|
||||
for (size_t i = 0; i < buffer->npix; i++) {
|
||||
int inten = buffer->values[p++];
|
||||
for (size_t j = 0; j < buffer->nchan - 1; j++, p++) {
|
||||
total += inten * buffer->values[p];
|
||||
}
|
||||
}
|
||||
|
||||
lua_pushinteger(L, total);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_replace_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), buffer->npix);
|
||||
size_t channels = buffer->nchan;
|
||||
|
||||
uint8_t *src;
|
||||
size_t srcLen;
|
||||
|
||||
if (lua_type(L, 2) == LUA_TSTRING) {
|
||||
size_t length;
|
||||
src = (uint8_t *) lua_tolstring(L, 2, &length);
|
||||
srcLen = length / channels;
|
||||
} else {
|
||||
pixbuf *rhs = pixbuf_from_lua_arg(L, 2);
|
||||
luaL_argcheck(L, rhs->nchan == buffer->nchan, 2, "buffers have different channels");
|
||||
src = rhs->values;
|
||||
srcLen = rhs->npix;
|
||||
}
|
||||
|
||||
luaL_argcheck(L, srcLen + start - 1 <= buffer->npix, 2, "does not fit into destination");
|
||||
|
||||
memcpy(buffer->values + (start - 1) * channels, src, srcLen * channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pixbuf_set_lua(lua_State *L) {
|
||||
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
const int led = luaL_checkinteger(L, 2) - 1;
|
||||
const size_t channels = buffer->nchan;
|
||||
|
||||
luaL_argcheck(L, led >= 0 && led < buffer->npix, 2, "index out of range");
|
||||
|
||||
int type = lua_type(L, 3);
|
||||
if(type == LUA_TTABLE)
|
||||
{
|
||||
for (size_t i = 0; i < channels; i++)
|
||||
{
|
||||
lua_rawgeti(L, 3, i+1);
|
||||
buffer->values[channels*led+i] = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else if(type == LUA_TSTRING)
|
||||
{
|
||||
size_t len;
|
||||
const char *buf = lua_tolstring(L, 3, &len);
|
||||
|
||||
// Overflow check
|
||||
if( channels*led + len > channels*buffer->npix ) {
|
||||
return luaL_error(L, "string size will exceed strip length");
|
||||
}
|
||||
if ( len % channels != 0 ) {
|
||||
return luaL_error(L, "string does not contain whole LEDs");
|
||||
}
|
||||
|
||||
memcpy(&buffer->values[channels*led], buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
luaL_argcheck(L, lua_gettop(L) <= 2 + channels, 2 + channels,
|
||||
"extra values given");
|
||||
|
||||
for (size_t i = 0; i < channels; i++)
|
||||
{
|
||||
buffer->values[channels*led+i] = luaL_checkinteger(L, 3+i);
|
||||
}
|
||||
}
|
||||
|
||||
lua_settop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void pixbuf_shift_circular(pixbuf *buffer, struct pixbuf_shift_params *sp) {
|
||||
/* Move a buffer of pixels per iteration; loop repeatedly if needed */
|
||||
uint8_t tmpbuf[32];
|
||||
uint8_t *v = buffer->values;
|
||||
size_t shiftRemaining = sp->shift;
|
||||
size_t cursor = sp->offset;
|
||||
|
||||
do {
|
||||
size_t shiftNow = MIN(shiftRemaining, sizeof tmpbuf);
|
||||
|
||||
if (sp->shiftLeft) {
|
||||
memcpy(tmpbuf, &v[cursor], shiftNow);
|
||||
memmove(&v[cursor], &v[cursor+shiftNow], sp->window - shiftNow);
|
||||
memcpy(&v[cursor+sp->window-shiftNow], tmpbuf, shiftNow);
|
||||
} else {
|
||||
memcpy(tmpbuf, &v[cursor+sp->window-shiftNow], shiftNow);
|
||||
memmove(&v[cursor+shiftNow], &v[cursor], sp->window - shiftNow);
|
||||
memcpy(&v[cursor], tmpbuf, shiftNow);
|
||||
}
|
||||
|
||||
cursor += shiftNow;
|
||||
shiftRemaining -= shiftNow;
|
||||
} while(shiftRemaining > 0);
|
||||
}
|
||||
|
||||
static void pixbuf_shift_logical(pixbuf *buffer, struct pixbuf_shift_params *sp) {
|
||||
/* Logical shifts don't require a temporary buffer, so we just move bytes */
|
||||
uint8_t *v = buffer->values;
|
||||
|
||||
if (sp->shiftLeft) {
|
||||
memmove(&v[sp->offset], &v[sp->offset+sp->shift], sp->window - sp->shift);
|
||||
bzero(&v[sp->offset+sp->window-sp->shift], sp->shift);
|
||||
} else {
|
||||
memmove(&v[sp->offset+sp->shift], &v[sp->offset], sp->window - sp->shift);
|
||||
bzero(&v[sp->offset], sp->shift);
|
||||
}
|
||||
}
|
||||
|
||||
void pixbuf_shift(pixbuf *b, struct pixbuf_shift_params *sp) {
|
||||
#if 0
|
||||
printf("Pixbuf %p shifting %s %s by %zd from %zd with window %zd\n",
|
||||
b,
|
||||
sp->shiftLeft ? "left" : "right",
|
||||
sp->type == PIXBUF_SHIFT_LOGICAL ? "logically" : "circularly",
|
||||
sp->shift, sp->offset, sp->window);
|
||||
#endif
|
||||
|
||||
switch(sp->type) {
|
||||
case PIXBUF_SHIFT_LOGICAL: return pixbuf_shift_logical(b, sp);
|
||||
case PIXBUF_SHIFT_CIRCULAR: return pixbuf_shift_circular(b, sp);
|
||||
}
|
||||
}
|
||||
|
||||
int pixbuf_shift_lua(lua_State *L) {
|
||||
struct pixbuf_shift_params sp;
|
||||
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
const int shift_shift = luaL_checkinteger(L, 2) * buffer->nchan;
|
||||
const unsigned shift_type = luaL_optinteger(L, 3, PIXBUF_SHIFT_LOGICAL);
|
||||
const int pos_start = posrelat(luaL_optinteger(L, 4, 1), buffer->npix);
|
||||
const int pos_end = posrelat(luaL_optinteger(L, 5, -1), buffer->npix);
|
||||
|
||||
if (shift_shift < 0) {
|
||||
sp.shiftLeft = true;
|
||||
sp.shift = -shift_shift;
|
||||
} else {
|
||||
sp.shiftLeft = false;
|
||||
sp.shift = shift_shift;
|
||||
}
|
||||
|
||||
switch(shift_type) {
|
||||
case PIXBUF_SHIFT_LOGICAL:
|
||||
case PIXBUF_SHIFT_CIRCULAR:
|
||||
sp.type = shift_type;
|
||||
break;
|
||||
default:
|
||||
return luaL_argerror(L, 3, "invalid shift type");
|
||||
}
|
||||
|
||||
if (pos_start < 1) {
|
||||
return luaL_argerror(L, 4, "start position must be >= 1");
|
||||
}
|
||||
|
||||
if (pos_end < pos_start) {
|
||||
return luaL_argerror(L, 5, "end position must be >= start");
|
||||
}
|
||||
|
||||
sp.offset = (pos_start - 1) * buffer->nchan;
|
||||
sp.window = (pos_end - pos_start + 1) * buffer->nchan;
|
||||
|
||||
if (sp.shift > pixbuf_size(buffer)) {
|
||||
return luaL_argerror(L, 2, "shifting more elements than buffer size");
|
||||
}
|
||||
|
||||
if (sp.shift > sp.window) {
|
||||
return luaL_argerror(L, 2, "shifting more than sliced window");
|
||||
}
|
||||
|
||||
pixbuf_shift(buffer, &sp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX for backwards-compat with ws2812_effects; deprecated and should be removed */
|
||||
void pixbuf_prepare_shift(pixbuf *buffer, struct pixbuf_shift_params *sp,
|
||||
int shift, enum pixbuf_shift type, int start, int end)
|
||||
{
|
||||
start = posrelat(start, buffer->npix);
|
||||
end = posrelat(end, buffer->npix);
|
||||
|
||||
lua_assert((end > start) && (start > 0) && (end < buffer->npix));
|
||||
|
||||
sp->type = type;
|
||||
sp->offset = (start - 1) * buffer->nchan;
|
||||
sp->window = (end - start + 1) * buffer->nchan;
|
||||
|
||||
if (shift < 0) {
|
||||
sp->shiftLeft = true;
|
||||
sp->shift = -shift * buffer->nchan;
|
||||
} else {
|
||||
sp->shiftLeft = false;
|
||||
sp->shift = shift * buffer->nchan;
|
||||
}
|
||||
}
|
||||
|
||||
static int pixbuf_size_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
lua_pushinteger(L, buffer->npix);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pixbuf_sub_lua(lua_State *L) {
|
||||
pixbuf *lhs = pixbuf_from_lua_arg(L, 1);
|
||||
size_t l = lhs->npix;
|
||||
ssize_t start = posrelat(luaL_checkinteger(L, 2), l);
|
||||
ssize_t end = posrelat(luaL_optinteger(L, 3, -1), l);
|
||||
if (start <= end) {
|
||||
pixbuf *result = pixbuf_new(L, end - start + 1, lhs->nchan);
|
||||
memcpy(result->values, lhs->values + lhs->nchan * (start - 1),
|
||||
lhs->nchan * (end - start + 1));
|
||||
return 1;
|
||||
} else {
|
||||
pixbuf_new(L, 0, lhs->nchan);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int pixbuf_tostring_lua(lua_State *L) {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
|
||||
luaL_Buffer result;
|
||||
luaL_buffinit(L, &result);
|
||||
|
||||
luaL_addchar(&result, '[');
|
||||
int p = 0;
|
||||
for (size_t i = 0; i < buffer->npix; i++) {
|
||||
if (i > 0) {
|
||||
luaL_addchar(&result, ',');
|
||||
}
|
||||
luaL_addchar(&result, '(');
|
||||
for (size_t j = 0; j < buffer->nchan; j++, p++) {
|
||||
if (j > 0) {
|
||||
luaL_addchar(&result, ',');
|
||||
}
|
||||
char numbuf[5];
|
||||
sprintf(numbuf, "%d", buffer->values[p]);
|
||||
luaL_addstring(&result, numbuf);
|
||||
}
|
||||
luaL_addchar(&result, ')');
|
||||
}
|
||||
|
||||
luaL_addchar(&result, ']');
|
||||
luaL_pushresult(&result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LROT_BEGIN(pixbuf_map, NULL, LROT_MASK_INDEX | LROT_MASK_EQ)
|
||||
LROT_TABENTRY ( __index, pixbuf_map )
|
||||
LROT_FUNCENTRY( __eq, pixbuf_eq_lua )
|
||||
|
||||
LROT_FUNCENTRY( __concat, pixbuf_concat_lua )
|
||||
LROT_FUNCENTRY( __tostring, pixbuf_tostring_lua )
|
||||
|
||||
LROT_FUNCENTRY( channels, pixbuf_channels_lua )
|
||||
LROT_FUNCENTRY( dump, pixbuf_dump_lua )
|
||||
LROT_FUNCENTRY( fade, pixbuf_fade_lua )
|
||||
LROT_FUNCENTRY( fadeI, pixbuf_fadeI_lua )
|
||||
LROT_FUNCENTRY( fill, pixbuf_fill_lua )
|
||||
LROT_FUNCENTRY( get, pixbuf_get_lua )
|
||||
LROT_FUNCENTRY( replace, pixbuf_replace_lua )
|
||||
LROT_FUNCENTRY( map, pixbuf_map_lua )
|
||||
LROT_FUNCENTRY( mix, pixbuf_mix_lua )
|
||||
LROT_FUNCENTRY( mix4I5, pixbuf_mix4I5_lua )
|
||||
LROT_FUNCENTRY( power, pixbuf_power_lua )
|
||||
LROT_FUNCENTRY( powerI, pixbuf_powerI_lua )
|
||||
LROT_FUNCENTRY( set, pixbuf_set_lua )
|
||||
LROT_FUNCENTRY( shift, pixbuf_shift_lua )
|
||||
LROT_FUNCENTRY( size, pixbuf_size_lua )
|
||||
LROT_FUNCENTRY( sub, pixbuf_sub_lua )
|
||||
LROT_END(pixbuf_map, NULL, LROT_MASK_INDEX | LROT_MASK_EQ)
|
||||
|
||||
LROT_BEGIN(pixbuf, NULL, 0)
|
||||
LROT_NUMENTRY( FADE_IN, PIXBUF_FADE_IN )
|
||||
LROT_NUMENTRY( FADE_OUT, PIXBUF_FADE_OUT )
|
||||
|
||||
LROT_NUMENTRY( SHIFT_CIRCULAR, PIXBUF_SHIFT_CIRCULAR )
|
||||
LROT_NUMENTRY( SHIFT_LOGICAL, PIXBUF_SHIFT_LOGICAL )
|
||||
|
||||
LROT_FUNCENTRY( newBuffer, pixbuf_new_lua )
|
||||
LROT_END(pixbuf, NULL, 0)
|
||||
|
||||
int luaopen_pixbuf(lua_State *L) {
|
||||
luaL_rometatable(L, PIXBUF_METATABLE, LROT_TABLEREF(pixbuf_map));
|
||||
lua_pushrotable(L, LROT_TABLEREF(pixbuf));
|
||||
return 1;
|
||||
}
|
||||
|
||||
NODEMCU_MODULE(PIXBUF, "pixbuf", pixbuf, luaopen_pixbuf);
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef APP_MODULES_PIXBUF_H_
|
||||
#define APP_MODULES_PIXBUF_H_
|
||||
|
||||
typedef struct pixbuf {
|
||||
const size_t npix;
|
||||
const size_t nchan;
|
||||
|
||||
/* Flexible Array Member; true size is npix * pixbuf_channels_for(type) */
|
||||
uint8_t values[];
|
||||
} pixbuf;
|
||||
|
||||
enum pixbuf_fade {
|
||||
PIXBUF_FADE_IN,
|
||||
PIXBUF_FADE_OUT
|
||||
};
|
||||
|
||||
enum pixbuf_shift {
|
||||
PIXBUF_SHIFT_LOGICAL,
|
||||
PIXBUF_SHIFT_CIRCULAR
|
||||
};
|
||||
|
||||
pixbuf *pixbuf_from_lua_arg(lua_State *, int);
|
||||
const size_t pixbuf_size(pixbuf *);
|
||||
|
||||
// Exported for backwards compat with ws2812 module
|
||||
int pixbuf_new_lua(lua_State *);
|
||||
|
||||
/*
|
||||
* WS2812_EFFECTS does pixbuf manipulation directly in C, which isn't the
|
||||
* intended use case, but for backwards compat, we export just what it needs.
|
||||
* Move this struct to pixbuf.c and mark these exports static instead once
|
||||
* WS2812_EFFECTS is no more.
|
||||
*/
|
||||
struct pixbuf_shift_params {
|
||||
enum pixbuf_shift type;
|
||||
// 0 <= offset <= buffer length
|
||||
size_t offset;
|
||||
// 0 <= window + offset <= buffer length
|
||||
size_t window;
|
||||
// 0 <= shift <= window_size
|
||||
size_t shift;
|
||||
bool shiftLeft;
|
||||
};
|
||||
void pixbuf_shift(pixbuf *, struct pixbuf_shift_params *);
|
||||
void pixbuf_prepare_shift(pixbuf *, struct pixbuf_shift_params *,
|
||||
int val, enum pixbuf_shift, int start, int end);
|
||||
const size_t pixbuf_channels(pixbuf *);
|
||||
/* end WS2812_EFFECTS exports */
|
||||
|
||||
#endif
|
|
@ -5,6 +5,8 @@
|
|||
#include <string.h>
|
||||
#include "user_interface.h"
|
||||
|
||||
#include "pixbuf.h"
|
||||
|
||||
static inline uint32_t _getCycleCount(void) {
|
||||
uint32_t cycles;
|
||||
__asm__ __volatile__("rsr %0,ccount":"=a" (cycles));
|
||||
|
@ -13,8 +15,9 @@ static inline uint32_t _getCycleCount(void) {
|
|||
|
||||
// This algorithm reads the cpu clock cycles to calculate the correct
|
||||
// pulse widths. It works in both 80 and 160 MHz mode.
|
||||
static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, uint32_t length) {
|
||||
uint8_t *p, *end;
|
||||
static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, const uint8_t *pixels, size_t length) {
|
||||
const uint8_t *p, *end;
|
||||
uint8_t phasergb = 0;
|
||||
|
||||
p = pixels;
|
||||
end = p + length;
|
||||
|
@ -28,6 +31,13 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui
|
|||
register int i;
|
||||
|
||||
register uint8_t pixel = *p++;
|
||||
if ((phasergb == 0) && (pixel == 0xFF)) {
|
||||
// clamp initial byte value to avoid constant-current shenanigans. Yuck!
|
||||
pixel = 0xFE;
|
||||
}
|
||||
if (++phasergb == 3) {
|
||||
phasergb = 0;
|
||||
}
|
||||
|
||||
ets_intr_lock();
|
||||
|
||||
|
@ -55,35 +65,27 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui
|
|||
}
|
||||
|
||||
// Lua: tm1829.write(pin, "string")
|
||||
// Byte triples in the string are interpreted as R G B values and sent to the hardware as G R B.
|
||||
// WARNING: this function scrambles the input buffer :
|
||||
// a = string.char(255,0,128)
|
||||
// tm1829.write(3,a)
|
||||
// =a.byte()
|
||||
// (0,255,128)
|
||||
// Byte triples in the string are interpreted as GRB values.
|
||||
static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
|
||||
{
|
||||
const uint8_t pin = luaL_checkinteger(L, 1);
|
||||
const uint8_t *pixels;
|
||||
size_t length;
|
||||
const char *rgb = luaL_checklstring(L, 2, &length);
|
||||
|
||||
// dont modify lua-internal lstring - make a copy instead
|
||||
char *buffer = (char *)malloc(length);
|
||||
|
||||
// Ignore incomplete Byte triples at the end of buffer
|
||||
length -= length % 3;
|
||||
|
||||
// Copy payload and make sure first byte is < 0xFF (triggers
|
||||
// constant current command, instead of PWM duty command)
|
||||
size_t i;
|
||||
for (i = 0; i < length; i += 3) {
|
||||
buffer[i] = rgb[i];
|
||||
buffer[i + 1] = rgb[i + 1];
|
||||
buffer[i + 2] = rgb[i + 2];
|
||||
|
||||
// Check for first byte
|
||||
if (buffer[i] == 0xff)
|
||||
buffer[i] = 0xfe;
|
||||
switch(lua_type(L, 3)) {
|
||||
case LUA_TSTRING: {
|
||||
pixels = luaL_checklstring(L, 2, &length);
|
||||
break;
|
||||
}
|
||||
case LUA_TUSERDATA: {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 2);
|
||||
luaL_argcheck(L, pixbuf_channels(buffer) == 3, 2, "Bad pixbuf format");
|
||||
pixels = buffer->values;
|
||||
length = 3 * buffer->npix;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return luaL_argerror(L, 2, "String or pixbuf expected");
|
||||
}
|
||||
|
||||
// Initialize the output pin and wait a bit
|
||||
|
@ -91,12 +93,10 @@ static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
|
|||
platform_gpio_write(pin, 1);
|
||||
|
||||
// Send the buffer
|
||||
tm1829_write_to_pin(pin_num[pin], (uint8_t*) buffer, length);
|
||||
tm1829_write_to_pin(pin_num[pin], pixels, length);
|
||||
|
||||
os_delay_us(500); // reset time
|
||||
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <string.h>
|
||||
#include "osapi.h"
|
||||
|
||||
#include "pixbuf.h"
|
||||
|
||||
/**
|
||||
* Code is based on https://github.com/CHERTS/esp8266-devkit/blob/master/Espressif/examples/EspLightNode/user/ws2801.c
|
||||
* and provides a similar api as the ws2812 module.
|
||||
|
@ -110,13 +112,28 @@ static int ICACHE_FLASH_ATTR ws2801_init_lua(lua_State* L) {
|
|||
*/
|
||||
static int ICACHE_FLASH_ATTR ws2801_writergb(lua_State* L) {
|
||||
size_t length;
|
||||
const char *buffer = luaL_checklstring(L, 1, &length);
|
||||
const uint8_t *values;
|
||||
|
||||
switch(lua_type(L,1)) {
|
||||
case LUA_TSTRING:
|
||||
values = (const uint8_t*) luaL_checklstring(L, 1, &length);
|
||||
break;
|
||||
case LUA_TUSERDATA: {
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
luaL_argcheck(L, buffer->nchan == 3, 1, "Pixbuf not 3-channel");
|
||||
values = buffer->values;
|
||||
length = pixbuf_size(buffer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return luaL_argerror(L, 1, "pixbuf or string expected");
|
||||
}
|
||||
|
||||
os_delay_us(10);
|
||||
|
||||
ets_intr_lock();
|
||||
|
||||
ws2801_strip(buffer, length);
|
||||
ws2801_strip(values, length);
|
||||
|
||||
ets_intr_unlock();
|
||||
|
||||
|
|
|
@ -9,13 +9,11 @@
|
|||
#include "driver/uart.h"
|
||||
#include "osapi.h"
|
||||
|
||||
#include "ws2812.h"
|
||||
#include "pixbuf.h"
|
||||
|
||||
#define CANARY_VALUE 0x32383132
|
||||
#define MODE_SINGLE 0
|
||||
#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
|
||||
|
@ -125,14 +123,14 @@ static int ws2812_write(lua_State* L) {
|
|||
}
|
||||
else if (type == LUA_TUSERDATA)
|
||||
{
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
|
||||
luaL_argcheck(L, pixbuf_channels(buffer) == 3, 1, "Bad pixbuf format");
|
||||
buffer1 = buffer->values;
|
||||
length1 = buffer->colorsPerLed*buffer->size;
|
||||
length1 = pixbuf_size(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
luaL_argerror(L, 1, "ws2812.buffer or string expected");
|
||||
luaL_argerror(L, 1, "pixbuf or string expected");
|
||||
}
|
||||
|
||||
// Second optionnal parameter
|
||||
|
@ -148,14 +146,14 @@ static int ws2812_write(lua_State* L) {
|
|||
}
|
||||
else if (type == LUA_TUSERDATA)
|
||||
{
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
|
||||
|
||||
pixbuf *buffer = pixbuf_from_lua_arg(L, 2);
|
||||
luaL_argcheck(L, pixbuf_channels(buffer) == 3, 2, "Bad pixbuf format");
|
||||
buffer2 = buffer->values;
|
||||
length2 = buffer->colorsPerLed*buffer->size;
|
||||
length2 = pixbuf_size(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
luaL_argerror(L, 2, "ws2812.buffer or string expected");
|
||||
luaL_argerror(L, 2, "pixbuf or string expected");
|
||||
}
|
||||
|
||||
// Send the buffers
|
||||
|
@ -164,475 +162,20 @@ static int ws2812_write(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
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 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;
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
|
||||
|
||||
// Associate its metatable
|
||||
luaL_getmetatable(L, "ws2812.buffer");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
// Save led strip size
|
||||
buffer->size = leds;
|
||||
buffer->colorsPerLed = colorsPerLed;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
// Handle a buffer where we can store led values
|
||||
static int ws2812_new_buffer(lua_State *L) {
|
||||
const int leds = luaL_checkint(L, 1);
|
||||
const int colorsPerLed = luaL_checkint(L, 2);
|
||||
|
||||
luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
|
||||
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
|
||||
|
||||
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
|
||||
|
||||
memset(buffer->values, 0, colorsPerLed * leds);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int ws2812_buffer_fill(ws2812_buffer * buffer, int * colors) {
|
||||
|
||||
// Grab colors
|
||||
int i, j;
|
||||
|
||||
// Fill buffer
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++)
|
||||
{
|
||||
for (j = 0; j < buffer->colorsPerLed; j++)
|
||||
{
|
||||
*p++ = colors[j];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_fill_lua(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
// Grab colors
|
||||
int i;
|
||||
int * colors = luaM_malloc(L, buffer->colorsPerLed * sizeof(int));
|
||||
|
||||
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||
{
|
||||
colors[i] = luaL_checkinteger(L, 2+i);
|
||||
}
|
||||
|
||||
ws2812_buffer_fill(buffer, colors);
|
||||
|
||||
// Free memory
|
||||
luaM_free(L, colors);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
if (direction == FADE_OUT)
|
||||
{
|
||||
*p++ /= fade;
|
||||
}
|
||||
else
|
||||
{
|
||||
// as fade in can result in value overflow, an int is used to perform the check afterwards
|
||||
val = *p * fade;
|
||||
if (val > 255) val = 255;
|
||||
*p++ = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_freemem(L, prepare, sizeof(ws2812_buffer_shift_prepare) + prepare->shift_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
|
||||
int shift = shiftValue >= 0 ? shiftValue : -shiftValue;
|
||||
|
||||
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;
|
||||
|
||||
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(prepare->tmp_pixels, &prepare->buffer->values[prepare->offset + prepare->remaining_len], prepare->shift_len);
|
||||
// Move pixels to end
|
||||
os_memmove(&prepare->buffer->values[prepare->offset + prepare->shift_len], &prepare->buffer->values[prepare->offset], prepare->remaining_len);
|
||||
// Fill beginning with temp data
|
||||
if (prepare->shift_type == SHIFT_LOGICAL)
|
||||
{
|
||||
memset(&prepare->buffer->values[prepare->offset], 0, prepare->shift_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
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(prepare->tmp_pixels, &prepare->buffer->values[prepare->offset], prepare->shift_len);
|
||||
// Move pixels to end
|
||||
os_memmove(&prepare->buffer->values[prepare->offset], &prepare->buffer->values[prepare->offset + prepare->shift_len], prepare->remaining_len);
|
||||
// Fill beginning with temp data
|
||||
if (prepare->shift_type == SHIFT_LOGICAL)
|
||||
{
|
||||
memset(&prepare->buffer->values[prepare->offset + prepare->remaining_len], 0, prepare->shift_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&prepare->buffer->values[prepare->offset + prepare->remaining_len], prepare->tmp_pixels, prepare->shift_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ws2812_buffer_shift_lua(lua_State* L) {
|
||||
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
const int shiftValue = luaL_checkinteger(L, 2);
|
||||
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
|
||||
|
||||
const int pos_start = luaL_optinteger(L, 4, 1);
|
||||
const int pos_end = luaL_optinteger(L, 5, -1);
|
||||
|
||||
|
||||
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");
|
||||
|
||||
lua_pushlstring(L, buffer->values, buffer->size * buffer->colorsPerLed);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_replace(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
ptrdiff_t start = posrelat(luaL_optinteger(L, 3, 1), buffer->size);
|
||||
|
||||
uint8_t *src;
|
||||
size_t srcLen;
|
||||
|
||||
if (lua_type(L, 2) == LUA_TSTRING) {
|
||||
size_t length;
|
||||
|
||||
src = (uint8_t *) lua_tolstring(L, 2, &length);
|
||||
srcLen = length / buffer->colorsPerLed;
|
||||
} else {
|
||||
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
|
||||
src = rhs->values;
|
||||
srcLen = rhs->size;
|
||||
luaL_argcheck(L, rhs->colorsPerLed == buffer->colorsPerLed, 2, "Buffers have different colors");
|
||||
}
|
||||
|
||||
luaL_argcheck(L, srcLen + start - 1 <= buffer->size, 2, "Does not fit into destination");
|
||||
|
||||
memcpy(buffer->values + (start - 1) * buffer->colorsPerLed, src, srcLen * buffer->colorsPerLed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// buffer:mix(factor1, buffer1, ..)
|
||||
// factor is 256 for 100%
|
||||
// uses saturating arithmetic (one buffer at a time)
|
||||
static int ws2812_buffer_mix(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
int pos = 2;
|
||||
size_t cells = buffer->size * buffer->colorsPerLed;
|
||||
|
||||
int n_sources = (lua_gettop(L) - 1) / 2;
|
||||
|
||||
struct {
|
||||
int factor;
|
||||
const uint8_t *values;
|
||||
} source[n_sources];
|
||||
|
||||
int src;
|
||||
for (src = 0; src < n_sources; src++, pos += 2) {
|
||||
int factor = luaL_checkinteger(L, pos);
|
||||
ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer");
|
||||
|
||||
luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape");
|
||||
|
||||
source[src].factor = factor;
|
||||
source[src].values = src_buffer->values;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < cells; i++) {
|
||||
int32_t val = 0;
|
||||
for (src = 0; src < n_sources; src++) {
|
||||
val += (int32_t)(source[src].values[i] * source[src].factor);
|
||||
}
|
||||
|
||||
val += 128; // rounding istead of floor
|
||||
val /= 256; // do not use implemetation dependant right shift
|
||||
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
} else if (val > 255) {
|
||||
val = 255;
|
||||
}
|
||||
buffer->values[i] = (uint8_t)val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returns the total of all channels
|
||||
static int ws2812_buffer_power(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
size_t cells = buffer->size * buffer->colorsPerLed;
|
||||
|
||||
size_t i;
|
||||
int total = 0;
|
||||
for (i = 0; i < cells; i++) {
|
||||
total += buffer->values[i];
|
||||
}
|
||||
|
||||
lua_pushinteger(L, total);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_get(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
const int led = luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||
{
|
||||
lua_pushinteger(L, buffer->values[buffer->colorsPerLed*led+i]);
|
||||
}
|
||||
|
||||
return buffer->colorsPerLed;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_set(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
const int led = luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
||||
|
||||
int type = lua_type(L, 3);
|
||||
if(type == LUA_TTABLE)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||
{
|
||||
// Get value and push it on stack
|
||||
lua_rawgeti(L, 3, i+1);
|
||||
|
||||
// Convert it as int and store them in buffer
|
||||
buffer->values[buffer->colorsPerLed*led+i] = lua_tointeger(L, -1);
|
||||
}
|
||||
|
||||
// Clean up the stack
|
||||
lua_pop(L, buffer->colorsPerLed);
|
||||
}
|
||||
else if(type == LUA_TSTRING)
|
||||
{
|
||||
size_t len;
|
||||
const char * buf = lua_tolstring(L, 3, &len);
|
||||
|
||||
// Overflow check
|
||||
if( buffer->colorsPerLed*led + len > buffer->colorsPerLed*buffer->size )
|
||||
{
|
||||
return luaL_error(L, "string size will exceed strip length");
|
||||
}
|
||||
|
||||
memcpy(&buffer->values[buffer->colorsPerLed*led], buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||
{
|
||||
buffer->values[buffer->colorsPerLed*led+i] = luaL_checkinteger(L, 3+i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_size(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
lua_pushinteger(L, buffer->size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_sub(lua_State* L) {
|
||||
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
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 <= 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));
|
||||
} else {
|
||||
ws2812_buffer *result = allocate_buffer(L, 0, lhs->colorsPerLed);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_concat(lua_State* L) {
|
||||
ws2812_buffer * lhs = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
ws2812_buffer * rhs = (ws2812_buffer*)luaL_checkudata(L, 2, "ws2812.buffer");
|
||||
|
||||
luaL_argcheck(L, lhs->colorsPerLed == rhs->colorsPerLed, 1, "Can only concatenate buffers with same colors");
|
||||
|
||||
int colorsPerLed = lhs->colorsPerLed;
|
||||
int leds = lhs->size + rhs->size;
|
||||
|
||||
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
|
||||
|
||||
memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size);
|
||||
memcpy(buffer->values + lhs->colorsPerLed * lhs->size, rhs->values, rhs->colorsPerLed * rhs->size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws2812_buffer_tostring(lua_State* L) {
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
|
||||
luaL_Buffer result;
|
||||
luaL_buffinit(L, &result);
|
||||
|
||||
luaL_addchar(&result, '[');
|
||||
int i;
|
||||
int p = 0;
|
||||
for (i = 0; i < buffer->size; i++) {
|
||||
int j;
|
||||
if (i > 0) {
|
||||
luaL_addchar(&result, ',');
|
||||
}
|
||||
luaL_addchar(&result, '(');
|
||||
for (j = 0; j < buffer->colorsPerLed; j++, p++) {
|
||||
if (j > 0) {
|
||||
luaL_addchar(&result, ',');
|
||||
}
|
||||
char numbuf[5];
|
||||
sprintf(numbuf, "%d", buffer->values[p]);
|
||||
luaL_addstring(&result, numbuf);
|
||||
}
|
||||
luaL_addchar(&result, ')');
|
||||
}
|
||||
|
||||
luaL_addchar(&result, ']');
|
||||
luaL_pushresult(&result);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
LROT_BEGIN(ws2812_buffer_map, NULL, LROT_MASK_INDEX)
|
||||
LROT_FUNCENTRY( __concat, ws2812_buffer_concat )
|
||||
LROT_TABENTRY( __index, ws2812_buffer_map )
|
||||
LROT_FUNCENTRY( __tostring, ws2812_buffer_tostring )
|
||||
LROT_FUNCENTRY( dump, ws2812_buffer_dump )
|
||||
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 )
|
||||
LROT_FUNCENTRY( mix, ws2812_buffer_mix )
|
||||
LROT_FUNCENTRY( power, ws2812_buffer_power )
|
||||
LROT_FUNCENTRY( set, ws2812_buffer_set )
|
||||
LROT_FUNCENTRY( shift, ws2812_buffer_shift_lua )
|
||||
LROT_FUNCENTRY( size, ws2812_buffer_size )
|
||||
LROT_FUNCENTRY( sub, ws2812_buffer_sub )
|
||||
LROT_END(ws2812_buffer_map, NULL, LROT_MASK_INDEX)
|
||||
|
||||
LROT_BEGIN(ws2812, NULL, 0)
|
||||
LROT_FUNCENTRY( init, ws2812_init )
|
||||
LROT_FUNCENTRY( newBuffer, ws2812_new_buffer )
|
||||
LROT_FUNCENTRY( newBuffer, pixbuf_new_lua ) // backwards compatibility
|
||||
LROT_FUNCENTRY( write, ws2812_write )
|
||||
LROT_NUMENTRY( FADE_IN, FADE_IN )
|
||||
LROT_NUMENTRY( FADE_OUT, FADE_OUT )
|
||||
LROT_NUMENTRY( FADE_IN, PIXBUF_FADE_IN ) // BC
|
||||
LROT_NUMENTRY( FADE_OUT, PIXBUF_FADE_OUT ) // BC
|
||||
LROT_NUMENTRY( MODE_SINGLE, MODE_SINGLE )
|
||||
LROT_NUMENTRY( MODE_DUAL, MODE_DUAL )
|
||||
LROT_NUMENTRY( SHIFT_LOGICAL, SHIFT_LOGICAL )
|
||||
LROT_NUMENTRY( SHIFT_CIRCULAR, SHIFT_CIRCULAR )
|
||||
LROT_NUMENTRY( SHIFT_LOGICAL, PIXBUF_SHIFT_LOGICAL ) // BC
|
||||
LROT_NUMENTRY( SHIFT_CIRCULAR, PIXBUF_SHIFT_CIRCULAR ) // BC
|
||||
LROT_END(ws2812, NULL, 0)
|
||||
|
||||
int luaopen_ws2812(lua_State *L) {
|
||||
static int luaopen_ws2812(lua_State *L) {
|
||||
// TODO: Make sure that the GPIO system is initialized
|
||||
luaL_rometatable(L, "ws2812.buffer", LROT_TABLEREF(ws2812_buffer_map));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#ifndef APP_MODULES_WS2812_H_
|
||||
#define APP_MODULES_WS2812_H_
|
||||
|
||||
#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lmem.h"
|
||||
#include "platform.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "user_interface.h"
|
||||
#include "driver/uart.h"
|
||||
#include "osapi.h"
|
||||
|
||||
#define FADE_IN 1
|
||||
#define FADE_OUT 0
|
||||
#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;
|
||||
uint8_t colorsPerLed;
|
||||
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);
|
||||
// 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_ */
|
|
@ -10,7 +10,7 @@
|
|||
#include "osapi.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#include "ws2812.h"
|
||||
#include "pixbuf.h"
|
||||
#include "color_utils.h"
|
||||
|
||||
#define CANARY_VALUE 0x32372132
|
||||
|
@ -43,9 +43,8 @@
|
|||
#define IDX_B 2
|
||||
#define IDX_W 3
|
||||
|
||||
|
||||
typedef struct {
|
||||
ws2812_buffer *buffer;
|
||||
pixbuf *buffer;
|
||||
int buffer_ref;
|
||||
uint32_t mode_delay;
|
||||
uint32_t counter_mode_call;
|
||||
|
@ -58,11 +57,10 @@ typedef struct {
|
|||
uint8_t effect_type;
|
||||
uint8_t color[4];
|
||||
int effect_int_param1;
|
||||
ws2812_buffer_shift_prepare* prepare;
|
||||
|
||||
struct pixbuf_shift_params shift;
|
||||
} ws2812_effects;
|
||||
|
||||
|
||||
|
||||
enum ws2812_effects_type {
|
||||
WS2812_EFFECT_STATIC,
|
||||
WS2812_EFFECT_BLINK,
|
||||
|
@ -91,40 +89,33 @@ static ws2812_effects *state;
|
|||
// UTILITY METHODS
|
||||
//-----------------
|
||||
|
||||
// XXX Not exported because this module is its sole non-Lua consumer and we
|
||||
// should be going away soon! Deprecated, 'n all that.
|
||||
extern void ICACHE_RAM_ATTR ws2812_write_data(
|
||||
const uint8_t *pixels, uint32_t length,
|
||||
const uint8_t *pixels2, uint32_t length2);
|
||||
|
||||
static int ws2812_write(ws2812_buffer* buffer) {
|
||||
size_t length1, length2;
|
||||
const char *buffer1, *buffer2;
|
||||
|
||||
buffer1 = buffer->values;
|
||||
length1 = buffer->colorsPerLed*buffer->size;
|
||||
|
||||
buffer2 = 0;
|
||||
length2 = 0;
|
||||
|
||||
// Send the buffers
|
||||
ws2812_write_data(buffer1, length1, buffer2, length2);
|
||||
|
||||
static int ws2812_effects_write(pixbuf* buffer) {
|
||||
ws2812_write_data(buffer->values, pixbuf_size(buffer), 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// :opens_boxes -1
|
||||
static void ws2812_set_pixel(int pixel, uint32_t color) {
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
static int ws2812_set_pixel(int pixel, uint32_t color) {
|
||||
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 w = pixbuf_channels(buffer) == 4 ? ((color & 0xFF000000) >> 24) : 0;
|
||||
|
||||
int offset = pixel * buffer->colorsPerLed;
|
||||
int offset = pixel * pixbuf_channels(buffer);
|
||||
buffer->values[offset+IDX_R] = r;
|
||||
buffer->values[offset+IDX_G] = g;
|
||||
buffer->values[offset+IDX_B] = b;
|
||||
if (buffer->colorsPerLed == 4) {
|
||||
if (pixbuf_channels(buffer) == 4) {
|
||||
buffer->values[offset+IDX_W] = w;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -160,16 +151,14 @@ static int ws2812_effects_init(lua_State *L) {
|
|||
platform_print_deprecation_note("ws2812_effects",
|
||||
"soon; please see https://github.com/nodemcu/nodemcu-firmware/issues/3122");
|
||||
|
||||
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
||||
luaL_argcheck(L, buffer != NULL, 1, "no valid buffer provided");
|
||||
pixbuf * buffer = pixbuf_from_lua_arg(L, 1);
|
||||
|
||||
// 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
|
||||
state = (ws2812_effects *) calloc(1,sizeof(ws2812_effects));
|
||||
// initialize
|
||||
|
@ -245,11 +234,10 @@ static int ws2812_effects_set_brightness(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// :opens_boxes -1
|
||||
static void ws2812_effects_fill_buffer(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
uint8_t bright_g = g * state->brightness / BRIGHTNESS_MAX;
|
||||
uint8_t bright_r = r * state->brightness / BRIGHTNESS_MAX;
|
||||
|
@ -259,11 +247,11 @@ static void ws2812_effects_fill_buffer(uint8_t r, uint8_t g, uint8_t b, uint8_t
|
|||
// Fill buffer
|
||||
int i;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
*p++ = bright_g;
|
||||
*p++ = bright_r;
|
||||
*p++ = bright_b;
|
||||
if (buffer->colorsPerLed == 4) {
|
||||
if (pixbuf_channels(buffer) == 4) {
|
||||
*p++ = bright_w;
|
||||
}
|
||||
}
|
||||
|
@ -308,8 +296,8 @@ static int ws2812_effects_mode_blink() {
|
|||
}
|
||||
else {
|
||||
// off
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
memset(&buffer->values[0], 0, buffer->size * buffer->colorsPerLed);
|
||||
pixbuf * buffer = state->buffer;
|
||||
memset(&buffer->values[0], 0, pixbuf_size(buffer));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -318,10 +306,10 @@ static int ws2812_effects_mode_blink() {
|
|||
|
||||
static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int segments = (length1 / buffer->colorsPerLed) - 1;
|
||||
int segmentSize = buffer->size / segments;
|
||||
int segments = (length1 / pixbuf_channels(buffer)) - 1;
|
||||
int segmentSize = buffer->npix / segments;
|
||||
|
||||
uint8_t g1, r1, b1, g2, r2, b2;
|
||||
int i,j,k;
|
||||
|
@ -330,7 +318,7 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|||
r2 = *gradient_spec++;
|
||||
b2 = *gradient_spec++;
|
||||
// skip non-rgb components
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*gradient_spec++;
|
||||
}
|
||||
|
@ -353,7 +341,7 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|||
g2 = *gradient_spec++;
|
||||
r2 = *gradient_spec++;
|
||||
b2 = *gradient_spec++;
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*gradient_spec++;
|
||||
}
|
||||
|
@ -371,7 +359,7 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|||
int numPixels = segmentSize;
|
||||
// make sure we fill the strip correctly in case of rounding errors
|
||||
if (k == segments - 1) {
|
||||
numPixels = buffer->size - (segmentSize * (segments - 1));
|
||||
numPixels = buffer->npix - (segmentSize * (segments - 1));
|
||||
}
|
||||
|
||||
int steps = numPixels - 1;
|
||||
|
@ -391,7 +379,7 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|||
*p++ = ((grb & 0x0000FF00) >> 8) * state->brightness / BRIGHTNESS_MAX;
|
||||
*p++ = (grb & 0x000000FF) * state->brightness / BRIGHTNESS_MAX;
|
||||
|
||||
for (j = 3; j < buffer->colorsPerLed; j++) {
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
@ -404,10 +392,10 @@ static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|||
|
||||
static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int segments = (length1 / buffer->colorsPerLed) - 1;
|
||||
int segmentSize = buffer->size / segments;
|
||||
int segments = (length1 / pixbuf_channels(buffer)) - 1;
|
||||
int segmentSize = buffer->npix / segments;
|
||||
|
||||
uint8_t g1, r1, b1, g2, r2, b2;
|
||||
int i,j,k;
|
||||
|
@ -416,7 +404,7 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|||
r2 = *buffer1++;
|
||||
b2 = *buffer1++;
|
||||
// skip non-rgb components
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*buffer1++;
|
||||
}
|
||||
|
@ -432,7 +420,7 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|||
r2 = *buffer1++;
|
||||
b2 = *buffer1++;
|
||||
|
||||
for (j = 3; j < buffer->colorsPerLed; j++) {
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++) {
|
||||
*buffer1++;
|
||||
}
|
||||
|
||||
|
@ -440,7 +428,7 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|||
int numPixels = segmentSize;
|
||||
// make sure we fill the strip correctly in case of rounding errors
|
||||
if (k == segments - 1) {
|
||||
numPixels = buffer->size - (segmentSize * (segments - 1));
|
||||
numPixels = buffer->npix - (segmentSize * (segments - 1));
|
||||
}
|
||||
|
||||
int steps = numPixels - 1;
|
||||
|
@ -449,7 +437,7 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|||
*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++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -466,7 +454,7 @@ static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|||
*/
|
||||
static int ws2812_effects_mode_random_color() {
|
||||
state->mode_color_index = get_random_wheel_index(state->mode_color_index);
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
uint32_t color = color_wheel(state->mode_color_index);
|
||||
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / BRIGHTNESS_MAX;
|
||||
|
@ -476,11 +464,11 @@ static int ws2812_effects_mode_random_color() {
|
|||
// Fill buffer
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
*p++ = g;
|
||||
*p++ = r;
|
||||
*p++ = b;
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -493,7 +481,7 @@ static int ws2812_effects_mode_random_color() {
|
|||
*/
|
||||
static int ws2812_effects_mode_rainbow() {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
uint32_t color = color_wheel(state->counter_mode_step);
|
||||
uint8_t r = (color & 0x00FF0000) >> 16;
|
||||
|
@ -503,11 +491,11 @@ static int ws2812_effects_mode_rainbow() {
|
|||
// Fill buffer
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
*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++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -523,12 +511,12 @@ static int ws2812_effects_mode_rainbow() {
|
|||
*/
|
||||
static int ws2812_effects_mode_rainbow_cycle(int repeat_count) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
uint16_t wheel_index = (i * 360 / buffer->size * repeat_count) % 360;
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
uint16_t wheel_index = (i * 360 / buffer->npix * repeat_count) % 360;
|
||||
uint32_t color = color_wheel(wheel_index);
|
||||
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / BRIGHTNESS_MAX;
|
||||
uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / BRIGHTNESS_MAX;
|
||||
|
@ -536,7 +524,7 @@ static int ws2812_effects_mode_rainbow_cycle(int repeat_count) {
|
|||
*p++ = g;
|
||||
*p++ = r;
|
||||
*p++ = b;
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -552,7 +540,7 @@ static int ws2812_effects_mode_rainbow_cycle(int repeat_count) {
|
|||
*/
|
||||
static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
uint8_t p_g = state->color[0];
|
||||
uint8_t p_r = state->color[1];
|
||||
|
@ -561,7 +549,7 @@ static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
|
|||
// Fill buffer
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
int flicker = rand() % (max_flicker > 0 ? max_flicker : 1);
|
||||
int r1 = p_r-flicker;
|
||||
int g1 = p_g-flicker;
|
||||
|
@ -572,7 +560,7 @@ static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
|
|||
*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++) {
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
@ -584,7 +572,7 @@ static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
|
|||
* Halloween effect
|
||||
*/
|
||||
static int ws2812_effects_mode_halloween() {
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int g1 = 50 * state->brightness / BRIGHTNESS_MAX;
|
||||
int r1 = 255 * state->brightness / BRIGHTNESS_MAX;
|
||||
|
@ -598,11 +586,11 @@ static int ws2812_effects_mode_halloween() {
|
|||
// Fill buffer
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
*p++ = (i % 4 < 2) ? g1 : g2;
|
||||
*p++ = (i % 4 < 2) ? r1 : r2;
|
||||
*p++ = (i % 4 < 2) ? b1 : b2;
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -614,7 +602,7 @@ static int ws2812_effects_mode_halloween() {
|
|||
|
||||
|
||||
static int ws2812_effects_mode_circus_combustus() {
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int g1 = 0 * state->brightness / BRIGHTNESS_MAX;
|
||||
int r1 = 255 * state->brightness / BRIGHTNESS_MAX;
|
||||
|
@ -627,7 +615,7 @@ static int ws2812_effects_mode_circus_combustus() {
|
|||
// Fill buffer
|
||||
int i,j;
|
||||
uint8_t * p = &buffer->values[0];
|
||||
for(i = 0; i < buffer->size; i++) {
|
||||
for(i = 0; i < buffer->npix; i++) {
|
||||
if (i % 6 < 2) {
|
||||
*p++ = g1;
|
||||
*p++ = r1;
|
||||
|
@ -643,7 +631,7 @@ static int ws2812_effects_mode_circus_combustus() {
|
|||
*p++ = 0;
|
||||
*p++ = 0;
|
||||
}
|
||||
for (j = 3; j < buffer->colorsPerLed; j++)
|
||||
for (j = 3; j < pixbuf_channels(buffer); j++)
|
||||
{
|
||||
*p++ = 0;
|
||||
}
|
||||
|
@ -660,35 +648,37 @@ static int ws2812_effects_mode_circus_combustus() {
|
|||
*/
|
||||
static int ws2812_effects_mode_larson_scanner() {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
int led_index = 0;
|
||||
|
||||
ws2812_buffer_fade(buffer, 2, FADE_OUT);
|
||||
for(int i=0; i < pixbuf_size(buffer); i++) {
|
||||
buffer->values[i] = buffer->values[i] >> 2;
|
||||
}
|
||||
|
||||
uint16_t pos = 0;
|
||||
|
||||
if(state->counter_mode_step < buffer->size) {
|
||||
if(state->counter_mode_step < buffer->npix) {
|
||||
pos = state->counter_mode_step;
|
||||
} else {
|
||||
pos = (buffer->size * 2) - state->counter_mode_step - 2;
|
||||
pos = (buffer->npix * 2) - state->counter_mode_step - 2;
|
||||
}
|
||||
pos = pos * buffer->colorsPerLed;
|
||||
pos = pos * pixbuf_channels(buffer);
|
||||
buffer->values[pos + 1] = state->color[1];
|
||||
buffer->values[pos] = state->color[0];
|
||||
buffer->values[pos + 2] = state->color[2];
|
||||
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->size * 2) - 2);
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->npix * 2) - 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int ws2812_effects_mode_color_wipe() {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
int led_index = (state->counter_mode_step % buffer->size) * buffer->colorsPerLed;
|
||||
int led_index = (state->counter_mode_step % buffer->npix) * pixbuf_channels(buffer);
|
||||
|
||||
if (state->counter_mode_step >= buffer->size)
|
||||
if (state->counter_mode_step >= buffer->npix)
|
||||
{
|
||||
buffer->values[led_index] = 0;
|
||||
buffer->values[led_index + 1] = 0;
|
||||
|
@ -703,30 +693,30 @@ static int ws2812_effects_mode_color_wipe() {
|
|||
buffer->values[led_index + 1] = px_r;
|
||||
buffer->values[led_index + 2] = px_b;
|
||||
}
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % (buffer->size * 2);
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % (buffer->npix * 2);
|
||||
}
|
||||
|
||||
static int ws2812_effects_mode_random_dot(uint8_t dots) {
|
||||
|
||||
ws2812_buffer * buffer = state->buffer;
|
||||
pixbuf * buffer = state->buffer;
|
||||
|
||||
// fade out
|
||||
for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) {
|
||||
for(int i=0; i < pixbuf_size(buffer); i++) {
|
||||
buffer->values[i] = buffer->values[i] >> 1;
|
||||
}
|
||||
|
||||
for(int i=0; i < dots; i++) {
|
||||
// pick random pixel
|
||||
int led_index = rand() % buffer->size;
|
||||
int led_index = rand() % buffer->npix;
|
||||
|
||||
uint32_t color = (state->color[0] << 16) | (state->color[1] << 8) | state->color[2];
|
||||
if (buffer->colorsPerLed == 4) {
|
||||
if (pixbuf_channels(buffer) == 4) {
|
||||
color = color | (state->color[3] << 24);
|
||||
}
|
||||
ws2812_set_pixel(led_index, color);
|
||||
}
|
||||
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->size * 2) - 2);
|
||||
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->npix * 2) - 2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -767,6 +757,11 @@ static uint32_t ws2812_effects_mode_delay()
|
|||
return delay;
|
||||
}
|
||||
|
||||
static void ws2812_effects_do_shift(void)
|
||||
{
|
||||
pixbuf_shift(state->buffer, &state->shift);
|
||||
ws2812_effects_write(state->buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* run loop for the effects.
|
||||
|
@ -784,7 +779,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_prepared(state->prepare);
|
||||
ws2812_effects_do_shift();
|
||||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_FLICKER)
|
||||
{
|
||||
|
@ -816,11 +811,11 @@ static void ws2812_effects_loop(void* p)
|
|||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_HALLOWEEN)
|
||||
{
|
||||
ws2812_buffer_shift_prepared(state->prepare);
|
||||
ws2812_effects_do_shift();
|
||||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_CIRCUS_COMBUSTUS)
|
||||
{
|
||||
ws2812_buffer_shift_prepared(state->prepare);
|
||||
ws2812_effects_do_shift();
|
||||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_LARSON_SCANNER)
|
||||
{
|
||||
|
@ -828,7 +823,7 @@ static void ws2812_effects_loop(void* p)
|
|||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_CYCLE)
|
||||
{
|
||||
ws2812_buffer_shift_prepared(state->prepare);
|
||||
ws2812_effects_do_shift();
|
||||
}
|
||||
else if (state->effect_type == WS2812_EFFECT_COLOR_WIPE)
|
||||
{
|
||||
|
@ -845,7 +840,7 @@ static void ws2812_effects_loop(void* p)
|
|||
// call count
|
||||
state->counter_mode_call = (state->counter_mode_call + 1) % UINT32_MAX;
|
||||
// write the buffer
|
||||
ws2812_write(state->buffer);
|
||||
ws2812_effects_write(state->buffer);
|
||||
// set the timer
|
||||
if (state->running == 1 && state->mode_delay >= 10)
|
||||
if (state->running == 1 && state->mode_delay >= 10)
|
||||
|
@ -855,15 +850,6 @@ static void ws2812_effects_loop(void* p)
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -910,13 +896,13 @@ static int ws2812_effects_set_mode(lua_State* L) {
|
|||
size_t length1;
|
||||
const char *buffer1 = lua_tolstring(L, 2, &length1);
|
||||
|
||||
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
|
||||
if ((length1 / pixbuf_channels(state->buffer) < 2) || (length1 % pixbuf_channels(state->buffer) != 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);
|
||||
ws2812_effects_write(state->buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -930,13 +916,13 @@ static int ws2812_effects_set_mode(lua_State* L) {
|
|||
size_t length1;
|
||||
const char *buffer1 = lua_tolstring(L, 2, &length1);
|
||||
|
||||
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
|
||||
if ((length1 / pixbuf_channels(state->buffer) < 2) || (length1 % pixbuf_channels(state->buffer) != 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);
|
||||
ws2812_effects_write(state->buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -952,7 +938,7 @@ static int ws2812_effects_set_mode(lua_State* L) {
|
|||
break;
|
||||
case WS2812_EFFECT_RAINBOW_CYCLE:
|
||||
ws2812_effects_mode_rainbow_cycle(effect_param != EFFECT_PARAM_INVALID ? effect_param : 1);
|
||||
prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
||||
pixbuf_prepare_shift(state->buffer, &state->shift, 1, PIXBUF_SHIFT_CIRCULAR, 1, -1);
|
||||
break;
|
||||
case WS2812_EFFECT_FLICKER:
|
||||
state->effect_int_param1 = effect_param;
|
||||
|
@ -967,11 +953,11 @@ static int ws2812_effects_set_mode(lua_State* L) {
|
|||
break;
|
||||
case WS2812_EFFECT_HALLOWEEN:
|
||||
ws2812_effects_mode_halloween();
|
||||
prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
||||
pixbuf_prepare_shift(state->buffer, &state->shift, 1, PIXBUF_SHIFT_CIRCULAR, 1, -1);
|
||||
break;
|
||||
case WS2812_EFFECT_CIRCUS_COMBUSTUS:
|
||||
ws2812_effects_mode_circus_combustus();
|
||||
prepare_shift(L, state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
||||
pixbuf_prepare_shift(state->buffer, &state->shift, 1, PIXBUF_SHIFT_CIRCULAR, 1, -1);
|
||||
break;
|
||||
case WS2812_EFFECT_LARSON_SCANNER:
|
||||
ws2812_effects_mode_larson_scanner();
|
||||
|
@ -980,7 +966,7 @@ static int ws2812_effects_set_mode(lua_State* L) {
|
|||
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);
|
||||
pixbuf_prepare_shift(state->buffer, &state->shift, state->effect_int_param1, PIXBUF_SHIFT_CIRCULAR, 1, -1);
|
||||
break;
|
||||
case WS2812_EFFECT_COLOR_WIPE:
|
||||
// fill buffer with black. r,g,b,w = 0
|
||||
|
|
|
@ -19,7 +19,10 @@ Send ABGR data in 8 bits to a APA102 chain.
|
|||
- `data_pin` any GPIO pin 0, 1, 2, ...
|
||||
- `clock_pin` any GPIO pin 0, 1, 2, ...
|
||||
- `string` payload to be sent to one or more APA102 LEDs.
|
||||
It should be composed from a ABGR quadruplet per element.
|
||||
|
||||
It may be a [pixbuf](pixbuf) with four channels or a string,
|
||||
composed from a ABGR quadruplet per element:
|
||||
|
||||
- `A1` the first pixel's Intensity channel (0-31)
|
||||
- `B1` the first pixel's Blue channel (0-255)<br />
|
||||
- `G1` the first pixel's Green channel (0-255)
|
||||
|
|
|
@ -0,0 +1,342 @@
|
|||
# Pixel Buffer (pixbuf) Module
|
||||
|
||||
| Since | Origin / Contributor | Maintainer | Source |
|
||||
| :----- | :-------------------- | :---------- | :------ |
|
||||
| 2020-?? | [nwf](https://github.com/nwf) | nwf | [pixbuf.c](../../app/modules/pixbuf.c) |
|
||||
|
||||
The pixbuf library offers C-array byte objects and convenient utility functions
|
||||
for maintaining small frame buffers, usually for use with LED arrays, as
|
||||
supported by, e.g., ws2812.
|
||||
|
||||
## pixbuf.newBuffer()
|
||||
Allocate a new memory buffer to store LED values.
|
||||
|
||||
#### Syntax
|
||||
`pixbuf.newBuffer(numberOfLeds, numberOfChannels)`
|
||||
|
||||
#### Parameters
|
||||
- `numberOfLeds` length of the LED strip (in pixels)
|
||||
- `numberOfChannels` the channel count (bytes per pixel)
|
||||
|
||||
#### Returns
|
||||
`pixbuf.buffer` object
|
||||
|
||||
## pixbuf.buffer:get()
|
||||
Return the value at the given position, in native strip color order
|
||||
|
||||
#### Syntax
|
||||
`buffer:get(index)`
|
||||
|
||||
#### Parameters
|
||||
- `index` position in the buffer (1 for first LED)
|
||||
|
||||
#### Returns
|
||||
`(color)`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer = pixbuf.newBuffer(32, 4)
|
||||
print(buffer:get(1))
|
||||
0 0 0 0
|
||||
```
|
||||
|
||||
## pixbuf.buffer:set()
|
||||
Set the value at the given position, in native strip color order
|
||||
|
||||
#### Syntax
|
||||
`buffer:set(index, color)`
|
||||
|
||||
#### Parameters
|
||||
- `index` position in the buffer (1 for the first LED)
|
||||
- `color` payload of the color
|
||||
|
||||
Payload could be:
|
||||
- `number, number, ...`, passing as many colors as required by the array type
|
||||
- `table` should contain one value per color required by the array type
|
||||
- `string` with a natural multiple of the colors required by the array type
|
||||
|
||||
`string` inputs may be used to set multiple consecutive pixels!
|
||||
|
||||
#### Returns
|
||||
The buffer
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer = pixbuf.newBuffer(32, 3)
|
||||
buffer:set(1, 255, 0, 0) -- set the first LED green for a GRB strip
|
||||
```
|
||||
|
||||
```lua
|
||||
buffer = pixbuf.newBuffer(32, 4)
|
||||
buffer:set(1, {255, 0, 0, 255}) -- set the first LED white and red for a RGBW strip
|
||||
```
|
||||
|
||||
```lua
|
||||
-- set the first LED green for a RGB strip and exploit the return value
|
||||
buffer = pixbuf.newBuffer(32, 3):set(1, string.char(0, 255, 0))
|
||||
```
|
||||
|
||||
## pixbuf.buffer:size()
|
||||
Return the size of the buffer in number of LEDs
|
||||
|
||||
#### Syntax
|
||||
`buffer:size()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`int`
|
||||
|
||||
## pixbuf.buffer:channels()
|
||||
Return the buffer's channel count
|
||||
|
||||
#### Syntax
|
||||
`buffer:channels()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`int`
|
||||
|
||||
## pixbuf.buffer:fill()
|
||||
Fill the buffer with the given color.
|
||||
The number of given bytes must match the channel count of the buffer.
|
||||
|
||||
#### Syntax
|
||||
`buffer:fill(color)`
|
||||
|
||||
#### Parameters
|
||||
- `color` bytes for each channel
|
||||
|
||||
#### Returns
|
||||
The buffer
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip
|
||||
```
|
||||
|
||||
## pixbuf.buffer:dump()
|
||||
Returns the contents of the buffer (the pixel values) as a string. This can then be saved to a file or sent over a network and may be fed back to [`pixbuf.buffer:set()`](#pixbufbufferset).
|
||||
|
||||
#### Syntax
|
||||
`buffer:dump()`
|
||||
|
||||
#### Returns
|
||||
A string containing the pixel values.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local s = buffer:dump()
|
||||
```
|
||||
|
||||
## pixbuf.buffer:replace()
|
||||
Inserts a string (or a pixbuf) into another buffer with an offset.
|
||||
The buffer must be of the same type or an error will be thrown.
|
||||
|
||||
#### Syntax
|
||||
`buffer:replace(source[, offset])`
|
||||
|
||||
#### Parameters
|
||||
- `source` the pixel values to be set into the buffer. This is either a string or a pixbuf.
|
||||
- `offset` the offset where the source is to be placed in the buffer. Default is 1. Negative values can be used.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:replace(anotherbuffer:dump()) -- copy one buffer into another via a string
|
||||
buffer:replace(anotherbuffer) -- copy one buffer into another
|
||||
newbuffer = buffer.sub(1) -- make a copy of a buffer into a new buffer
|
||||
```
|
||||
|
||||
## pixbuf.buffer:mix()
|
||||
This is a general method that loads data into a buffer that is a linear combination of data from other buffers. It can be used to copy a buffer or,
|
||||
more usefully, do a cross fade. The pixel values are computed as integers and then range limited to [0, 255]. This means that negative
|
||||
factors work as expected, and that the order of combining buffers does not matter.
|
||||
|
||||
#### Syntax
|
||||
`buffer:mix(factor1, buffer1, ...)`
|
||||
|
||||
#### Parameters
|
||||
- `factor1` This is the factor that the contents of `buffer1` are multiplied by. This factor is scaled by a factor of 256. Thus `factor1` value of 256 is a factor of 1.0.
|
||||
- `buffer1` This is the source buffer. It must be of the same shape as the destination buffer.
|
||||
|
||||
There can be any number of factor/buffer pairs.
|
||||
|
||||
#### Returns
|
||||
The output buffer.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- loads buffer with a crossfade between buffer1 and buffer2
|
||||
buffer:mix(256 - crossmix, buffer1, crossmix, buffer2)
|
||||
|
||||
-- multiplies all values in buffer by 0.75
|
||||
-- This can be used in place of buffer:fade
|
||||
buffer:mix(192, buffer)
|
||||
```
|
||||
|
||||
## pixbuf.buffer:mix4I5()
|
||||
Like [`pixbuf.buffer:mix()`](#pixbufbuffermix) but treats the first channel as
|
||||
a scaling, 5-bit intensity value. The buffers must all have four channels.
|
||||
This is mostly useful for APA102 LEDs.
|
||||
|
||||
## pixbuf.buffer:power()
|
||||
Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each
|
||||
pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The
|
||||
pulse width modulation will cause the *average* current to scale linearly with pixel value.
|
||||
|
||||
#### Syntax
|
||||
`buffer:power()`
|
||||
|
||||
#### Returns
|
||||
An integer which is the sum of all the pixel values.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- Dim the buffer to no more than the PSU can provide
|
||||
local psu_current_ma = 1000
|
||||
local led_current_ma = 20
|
||||
local led_sum = psu_current_ma * 255 / led_current_ma
|
||||
|
||||
local p = buffer:power()
|
||||
if p > led_sum then
|
||||
buffer:mix(256 * led_sum / p, buffer) -- power is now limited
|
||||
end
|
||||
```
|
||||
|
||||
## pixbuf.buffer:powerI()
|
||||
Like [`pixbuf.buffer:power()`](#pixbufbufferpower) but treats the first channel as
|
||||
a scaling intensity value.
|
||||
|
||||
## pixbuf.buffer:fade()
|
||||
Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect.
|
||||
|
||||
#### Syntax
|
||||
`buffer:fade(value [, direction])`
|
||||
|
||||
#### Parameters
|
||||
- `value` value by which to divide or multiply each byte
|
||||
- `direction` pixbuf.FADE\_IN or pixbuf.FADE\_OUT. Defaults to pixbuf.FADE\_OUT
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:fade(2)
|
||||
buffer:fade(2, pixbuf.FADE_IN)
|
||||
```
|
||||
|
||||
## pixbuf.buffer:fadeI()
|
||||
Like [`pixbuf.buffer:fade()`](#pixbufbufferfade) but treats the first channel as
|
||||
a scaling intensity value. This is mostly useful for APA102 LEDs.
|
||||
|
||||
## pixbuf.buffer:shift()
|
||||
Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the
|
||||
standard start and end offset Lua notation. Negative values count backwards from the end of the buffer.
|
||||
|
||||
#### Syntax
|
||||
`buffer:shift(value [, mode[, i[, j]]])`
|
||||
|
||||
#### Parameters
|
||||
- `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards.
|
||||
- `mode` is the shift mode to use. Can be one of `pixbuf.SHIFT_LOGICAL` or `pixbuf.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL.
|
||||
- `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1.
|
||||
- `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:shift(3)
|
||||
```
|
||||
|
||||
## pixbuf.buffer:sub()
|
||||
This implements the extraction function like `string.sub`. The indexes are in leds and all the same rules apply.
|
||||
|
||||
#### Syntax
|
||||
`buffer1:sub(i[, j])`
|
||||
|
||||
#### Parameters
|
||||
- `i` This is the start of the extracted data. Negative values can be used.
|
||||
- `j` this is the end of the extracted data. Negative values can be used. The default is -1.
|
||||
|
||||
#### Returns
|
||||
A buffer containing the extracted piece.
|
||||
|
||||
#### Example
|
||||
```
|
||||
b = buffer:sub(1,10)
|
||||
```
|
||||
|
||||
## pixbuf.buffer:__concat()
|
||||
This implements the `..` operator to concatenate two buffers. They must have the same number of colors per led.
|
||||
|
||||
#### Syntax
|
||||
`buffer1 .. buffer2`
|
||||
|
||||
#### Parameters
|
||||
- `buffer1` this is the start of the resulting buffer
|
||||
- `buffer2` this is the end of the resulting buffer
|
||||
|
||||
#### Returns
|
||||
The concatenated buffer.
|
||||
|
||||
#### Example
|
||||
```
|
||||
ws2812.write(buffer1 .. buffer2)
|
||||
```
|
||||
## pixbuf.buffer:map()
|
||||
Map a function across each pixel of one, or zip a function along two,
|
||||
pixbuf(s), storing into the buffer on which it is called.
|
||||
|
||||
#### Syntax
|
||||
`buffer0:map(f, [buffer1], [start1], [end1], [buffer2, [start2]])`
|
||||
|
||||
#### Parameters
|
||||
- `f` This is the mapping function; it is applied for each pixel to all channels of `buffer1` and
|
||||
all channels of `buffer2`, if given. It must return a value for each channel of the output
|
||||
buffer, `buffer0`.
|
||||
- `buffer1` The first source buffer. Defaults to `buffer0`.
|
||||
- `start1` This is the start of the mapped range of `buffer1`. Negative values can be used and will be interpreted as before the end of `buffer1`. The default is 1.
|
||||
- `end1` this is the end of the mapped range. Negative values can be used. The default is -1 (i.e., the end of `buffer1`).
|
||||
- `buffer2` is a second buffer, for zip operations
|
||||
- `start2` This is the start of the mapped range within `buffer2`. Negative values can be used and will be interpreted as before the end of `buffer2`. The default is 1.
|
||||
|
||||
`buffer0` must have sufficient room to recieve all pixels from `start1` to
|
||||
`end1` (which is true of the defaults, when `buffer1` is `buffer0` and `start1`
|
||||
is 1 and `end1` is -1). `buffer2`, if given, must have sufficient pixels after
|
||||
`start2`.
|
||||
|
||||
#### Returns
|
||||
`buffer0`
|
||||
|
||||
#### Examples
|
||||
|
||||
Change channel order within a single buffer:
|
||||
```Lua
|
||||
buffer:map(function(r,g,b) return g,r,b end)
|
||||
```
|
||||
|
||||
Change channel order for a subset of pixels:
|
||||
```Lua
|
||||
buffer:map(function(r,g,b) return g,r,b end, nil, 2, 5)
|
||||
```
|
||||
|
||||
Extract one channel for a subset of pixels:
|
||||
```Lua
|
||||
outbuf = pixbuf.create(11, 1)
|
||||
outbuf:map(function(r,g,b) return b end, inbuf, 10, 20)
|
||||
```
|
||||
|
||||
Concatenate channels per pixel, possibly with different offsets in buffers:
|
||||
```Lua
|
||||
outbuf:map(function(...) return ... end, inbuf1, inbuf2)
|
||||
outbuf:map(function(...) return ... end, inbuf1, 5, 10, inbuf2, 3)
|
||||
```
|
|
@ -15,7 +15,9 @@ Send data to a led strip using native chip format.
|
|||
`tm1829.write(string)`
|
||||
|
||||
#### Parameters
|
||||
- `string` payload to be sent to one or more TM1829 leds.
|
||||
- `string` payload to be sent to one or more TM1829 leds. It is either
|
||||
a 3-channel [pixbuf](pixbuf) (e.g., `pixbuf.TYPE_RGB`) or a string of
|
||||
raw byte values to be sent.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
|
|
@ -31,8 +31,20 @@ In `ws2812.MODE_DUAL` mode you will be able to handle two strips in parallel but
|
|||
`nil`
|
||||
|
||||
## ws2812.write()
|
||||
Send data to one or two led strip using its native format which is generally Green,Red,Blue for RGB strips
|
||||
and Green,Red,Blue,White for RGBW strips.
|
||||
Send data to one or two led strip using its native format, which is generally
|
||||
Green, Red, Blue for RGB strips and Green, Red, Blue, White for RGBW strips.
|
||||
(However, ws2812 drivers have been observed wired up in other orders.)
|
||||
|
||||
Because this function uses the hardware UART(s), it is able to return and allow
|
||||
Lua to resume execution up to 300 microseconds before the data has finished
|
||||
being sent. If you wish to perform actions synchronous with the end of the
|
||||
data transmission, [`tmr.delay()`](../tmr#tmr.delay()) for 300 microseconds.
|
||||
|
||||
Separately, because this function returns early, back-to-back invocations may
|
||||
not leave enough time for the strip to latch, and so may appear to the ws2812
|
||||
drivers to be simply writes to a longer LED strip. Please ensure that you have
|
||||
more than 350 microseconds between the return of `ws2812.write()` to your Lua
|
||||
and the next invocation thereof.
|
||||
|
||||
#### Syntax
|
||||
`ws2812.write(data1, [data2])`
|
||||
|
@ -44,7 +56,7 @@ and Green,Red,Blue,White for RGBW strips.
|
|||
Payload type could be:
|
||||
- `nil` nothing is done
|
||||
- `string` representing bytes to send
|
||||
- `ws2812.buffer` see [Buffer module](#buffer-module)
|
||||
- a [pixbuf](pixbuf) object containing the bytes to send. The pixbuf's type is not checked!
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
@ -70,280 +82,31 @@ ws2812.init(ws2812.MODE_DUAL)
|
|||
ws2812.write(nil, string.char(0, 255, 0, 0, 255, 0)) -- turn the two first RGB leds to red on the second strip, do nothing on the first
|
||||
```
|
||||
|
||||
# Buffer module
|
||||
# Pixbuf support
|
||||
For more advanced animations, it is useful to keep a "framebuffer" of the strip,
|
||||
interact with it and flush it to the strip.
|
||||
|
||||
For this purpose, the ws2812 library offers a read/write buffer. This buffer has a `__tostring` method so that it can be printed. This is useful for debugging.
|
||||
For this purpose, the [pixbuf](pixbuf) library offers a read/write buffer and
|
||||
convenient functions for pixel value manipulation.
|
||||
|
||||
For backwards-compatibility, `pixbuf.newBuffer()` is aliased as
|
||||
`ws2812.newBuffer`, but this will be removed in the next nodemcu-firmware
|
||||
release.
|
||||
|
||||
#### Example
|
||||
Led chaser with a RGBW strip
|
||||
|
||||
```lua
|
||||
ws2812.init()
|
||||
local i, buffer = 0, ws2812.newBuffer(300, 4); buffer:fill(0, 0, 0, 0); tmr.create():alarm(50, 1, function()
|
||||
|
||||
i, buffer = 0, pixbuf.newBuffer(300, 4)
|
||||
|
||||
buffer:fill(0, 0, 0, 0)
|
||||
|
||||
tmr.create():alarm(50, 1, function()
|
||||
i = i + 1
|
||||
buffer:fade(2)
|
||||
buffer:set(i % buffer:size() + 1, 0, 0, 0, 255)
|
||||
ws2812.write(buffer)
|
||||
end)
|
||||
```
|
||||
|
||||
## ws2812.newBuffer()
|
||||
Allocate a new memory buffer to store led values.
|
||||
|
||||
#### Syntax
|
||||
`ws2812.newBuffer(numberOfLeds, bytesPerLed)`
|
||||
|
||||
#### Parameters
|
||||
- `numberOfLeds` length of the led strip
|
||||
- `bytesPerLed` 3 for RGB strips and 4 for RGBW strips
|
||||
|
||||
#### Returns
|
||||
`ws2812.buffer`
|
||||
|
||||
## ws2812.buffer:get()
|
||||
Return the value at the given position
|
||||
|
||||
#### Syntax
|
||||
`buffer:get(index)`
|
||||
|
||||
#### Parameters
|
||||
- `index` position in the buffer (1 for first led)
|
||||
|
||||
#### Returns
|
||||
`(color)`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer = ws2812.newBuffer(32, 4)
|
||||
print(buffer:get(1))
|
||||
0 0 0 0
|
||||
```
|
||||
|
||||
## ws2812.buffer:set()
|
||||
Set the value at the given position
|
||||
|
||||
#### Syntax
|
||||
`buffer:set(index, color)`
|
||||
|
||||
#### Parameters
|
||||
- `index` position in the buffer (1 for the first led)
|
||||
- `color` payload of the color
|
||||
|
||||
Payload could be:
|
||||
- `number, number, ...` you should pass as many arguments as `bytesPerLed`
|
||||
- `table` should contains `bytesPerLed` numbers
|
||||
- `string` should contains `bytesPerLed` bytes
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer = ws2812.newBuffer(32, 3)
|
||||
buffer:set(1, 255, 0, 0) -- set the first led green for a RGB strip
|
||||
```
|
||||
|
||||
```lua
|
||||
buffer = ws2812.newBuffer(32, 4)
|
||||
buffer:set(1, {0, 0, 0, 255}) -- set the first led white for a RGBW strip
|
||||
```
|
||||
|
||||
```lua
|
||||
buffer = ws2812.newBuffer(32, 3)
|
||||
buffer:set(1, string.char(255, 0, 0)) -- set the first led green for a RGB strip
|
||||
```
|
||||
|
||||
## ws2812.buffer:size()
|
||||
Return the size of the buffer in number of leds
|
||||
|
||||
#### Syntax
|
||||
`buffer:size()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`int`
|
||||
|
||||
## ws2812.buffer:fill()
|
||||
Fill the buffer with the given color.
|
||||
The number of given bytes must match the number of bytesPerLed of the buffer
|
||||
|
||||
#### Syntax
|
||||
`buffer:fill(color)`
|
||||
|
||||
#### Parameters
|
||||
- `color` bytes of the color, you should pass as many arguments as `bytesPerLed`
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip
|
||||
```
|
||||
|
||||
## ws2812.buffer:dump()
|
||||
Returns the contents of the buffer (the pixel values) as a string. This can then be saved to a file or sent over a network.
|
||||
|
||||
#### Syntax
|
||||
`buffer:dump()`
|
||||
|
||||
#### Returns
|
||||
A string containing the pixel values.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
local s = buffer:dump()
|
||||
```
|
||||
|
||||
## ws2812.buffer:replace()
|
||||
Inserts a string (or a buffer) into another buffer with an offset.
|
||||
The buffer must have the same number of colors per led or an error will be thrown.
|
||||
|
||||
#### Syntax
|
||||
`buffer:replace(source[, offset])`
|
||||
|
||||
#### Parameters
|
||||
- `source` the pixel values to be set into the buffer. This is either a string or a buffer.
|
||||
- `offset` the offset where the source is to be placed in the buffer. Default is 1. Negative values can be used.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:replace(anotherbuffer:dump()) -- copy one buffer into another via a string
|
||||
buffer:replace(anotherbuffer) -- copy one buffer into another
|
||||
newbuffer = buffer.sub(1) -- make a copy of a buffer into a new buffer
|
||||
```
|
||||
|
||||
## ws2812.buffer:mix()
|
||||
This is a general method that loads data into a buffer that is a linear combination of data from other buffers. It can be used to copy a buffer or,
|
||||
more usefully, do a cross fade. The pixel values are computed as integers and then range limited to [0, 255]. This means that negative
|
||||
factors work as expected, and that the order of combining buffers does not matter.
|
||||
|
||||
#### Syntax
|
||||
`buffer:mix(factor1, buffer1, ...)`
|
||||
|
||||
#### Parameters
|
||||
- `factor1` This is the factor that the contents of `buffer1` are multiplied by. This factor is scaled by a factor of 256. Thus `factor1` value of 256 is a factor of 1.0.
|
||||
- `buffer1` This is the source buffer. It must be of the same shape as the destination buffer.
|
||||
|
||||
There can be any number of factor/buffer pairs.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- loads buffer with a crossfade between buffer1 and buffer2
|
||||
buffer:mix(256 - crossmix, buffer1, crossmix, buffer2)
|
||||
|
||||
-- multiplies all values in buffer by 0.75
|
||||
-- This can be used in place of buffer:fade
|
||||
buffer:mix(192, buffer)
|
||||
```
|
||||
|
||||
## ws2812.buffer:power()
|
||||
Computes the total energy requirement for the buffer. This is merely the total sum of all the pixel values (which assumes that each color in each
|
||||
pixel consumes the same amount of power). A real WS2812 (or WS2811) has three constant current drivers of 20mA -- one for each of R, G and B. The
|
||||
pulse width modulation will cause the *average* current to scale linearly with pixel value.
|
||||
|
||||
#### Syntax
|
||||
`buffer:power()`
|
||||
|
||||
#### Returns
|
||||
An integer which is the sum of all the pixel values.
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
-- Dim the buffer to no more than the PSU can provide
|
||||
local psu_current_ma = 1000
|
||||
local led_current_ma = 20
|
||||
local led_sum = psu_current_ma * 255 / led_current_ma
|
||||
|
||||
local p = buffer:power()
|
||||
if p > led_sum then
|
||||
buffer:mix(256 * led_sum / p, buffer) -- power is now limited
|
||||
end
|
||||
```
|
||||
|
||||
## ws2812.buffer:fade()
|
||||
Fade in or out. Defaults to out. Multiply or divide each byte of each led with/by the given value. Useful for a fading effect.
|
||||
|
||||
#### Syntax
|
||||
`buffer:fade(value [, direction])`
|
||||
|
||||
#### Parameters
|
||||
- `value` value by which to divide or multiply each byte
|
||||
- `direction` ws2812.FADE\_IN or ws2812.FADE\_OUT. Defaults to ws2812.FADE\_OUT
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:fade(2)
|
||||
buffer:fade(2, ws2812.FADE_IN)
|
||||
```
|
||||
## ws2812.buffer:shift()
|
||||
Shift the content of (a piece of) the buffer in positive or negative direction. This allows simple animation effects. A slice of the buffer can be specified by using the
|
||||
standard start and end offset Lua notation. Negative values count backwards from the end of the buffer.
|
||||
|
||||
#### Syntax
|
||||
`buffer:shift(value [, mode[, i[, j]]])`
|
||||
|
||||
#### Parameters
|
||||
- `value` number of pixels by which to rotate the buffer. Positive values rotate forwards, negative values backwards.
|
||||
- `mode` is the shift mode to use. Can be one of `ws2812.SHIFT_LOGICAL` or `ws2812.SHIFT_CIRCULAR`. In case of SHIFT\_LOGICAL, the freed pixels are set to 0 (off). In case of SHIFT\_CIRCULAR, the buffer is treated like a ring buffer, inserting the pixels falling out on one end again on the other end. Defaults to SHIFT\_LOGICAL.
|
||||
- `i` is the first offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is 1.
|
||||
- `j` is the last offset in the buffer to be affected. Negative values are permitted and count backwards from the end. Default is -1.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
buffer:shift(3)
|
||||
```
|
||||
|
||||
## ws2812.buffer:sub()
|
||||
This implements the extraction function like `string.sub`. The indexes are in leds and all the same rules apply.
|
||||
|
||||
#### Syntax
|
||||
`buffer1:sub(i[, j])`
|
||||
|
||||
#### Parameters
|
||||
- `i` This is the start of the extracted data. Negative values can be used.
|
||||
- `j` this is the end of the extracted data. Negative values can be used. The default is -1.
|
||||
|
||||
#### Returns
|
||||
A buffer containing the extracted piece.
|
||||
|
||||
#### Example
|
||||
```
|
||||
b = buffer:sub(1,10)
|
||||
```
|
||||
|
||||
## ws2812.buffer:__concat()
|
||||
This implements the `..` operator to concatenate two buffers. They must have the same number of colors per led.
|
||||
|
||||
#### Syntax
|
||||
`buffer1 .. buffer2`
|
||||
|
||||
#### Parameters
|
||||
- `buffer1` this is the start of the resulting buffer
|
||||
- `buffer2` this is the end of the resulting buffer
|
||||
|
||||
#### Returns
|
||||
The concatenated buffer.
|
||||
|
||||
#### Example
|
||||
```
|
||||
ws2812.write(buffer1 .. buffer2)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
local N = ...
|
||||
N = (N or require "NTest")("pixbuf")
|
||||
|
||||
local function initBuffer(buf, ...)
|
||||
for i,v in ipairs({...}) do
|
||||
buf:set(i, v, v*2, v*3, v*4)
|
||||
end
|
||||
return buf
|
||||
end
|
||||
|
||||
N.test('initialize a buffer', function()
|
||||
local buffer = pixbuf.newBuffer(9, 3)
|
||||
nok(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")
|
||||
|
||||
fail(function() pixbuf.newBuffer(9, -1) end, "should be a positive integer")
|
||||
fail(function() pixbuf.newBuffer(0, 3) end, "should be a positive integer")
|
||||
fail(function() pixbuf.newBuffer(-1, 3) end, "should be a positive integer")
|
||||
end)
|
||||
|
||||
N.test('have correct size', function()
|
||||
local buffer = pixbuf.newBuffer(9, 3)
|
||||
ok(eq(buffer:size(), 9), "check size")
|
||||
buffer = pixbuf.newBuffer(9, 4)
|
||||
ok(eq(buffer:size(), 9), "check size")
|
||||
end)
|
||||
|
||||
N.test('fill a buffer with one color', function()
|
||||
local buffer = pixbuf.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 = pixbuf.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)
|
||||
|
||||
N.test('replace correctly', function()
|
||||
local buffer = pixbuf.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 = pixbuf.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 = pixbuf.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")
|
||||
|
||||
fail(function() buffer:replace(string.char(3,255,165,33,0,244,12,87,255), 4) end,
|
||||
"does not fit into destination")
|
||||
end)
|
||||
|
||||
N.test('replace correctly issue #2921', function()
|
||||
local buffer = pixbuf.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)
|
||||
|
||||
N.test('get/set correctly', function()
|
||||
local buffer = pixbuf.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")
|
||||
|
||||
fail(function() buffer:get(0) end, "index out of range")
|
||||
fail(function() buffer:get(4) end, "index out of range")
|
||||
fail(function() buffer:set(0,1,2,3,4) end, "index out of range")
|
||||
fail(function() buffer:set(4,1,2,3,4) end, "index out of range")
|
||||
fail(function() buffer:set(2,1,2,3) end, "number expected, got no value")
|
||||
fail(function() buffer:set(2,1,2,3,4,5) end, "extra values given")
|
||||
end)
|
||||
|
||||
N.test('get/set multiple with string', function()
|
||||
-- verify that :set does indeed return its input
|
||||
local buffer = pixbuf.newBuffer(4, 3):set(1,"ABCDEF")
|
||||
buffer:set(3,"LMNOPQ")
|
||||
ok(eq(buffer:dump(), "ABCDEFLMNOPQ"))
|
||||
|
||||
fail(function() buffer:set(4,"AAAAAA") end, "string size will exceed strip length")
|
||||
fail(function() buffer:set(2,"AAAAA") end, "string does not contain whole LEDs")
|
||||
end)
|
||||
|
||||
N.test('fade correctly', function()
|
||||
local buffer = pixbuf.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, pixbuf.FADE_OUT)
|
||||
ok(buffer:dump() == string.char(0,math.floor(222/3),math.floor(55/3)), "RGB")
|
||||
buffer:fill(1,222,55)
|
||||
buffer:fade(3, pixbuf.FADE_IN)
|
||||
ok(buffer:dump() == string.char(3,255,165), "RGB")
|
||||
buffer = pixbuf.newBuffer(1, 4)
|
||||
buffer:fill(1,222,55, 77)
|
||||
buffer:fade(2, pixbuf.FADE_OUT)
|
||||
ok(eq(buffer:dump(), string.char(0,111,27,38)), "RGBW")
|
||||
end)
|
||||
|
||||
N.test('mix correctly issue #1736', function()
|
||||
local buffer1 = pixbuf.newBuffer(1, 3)
|
||||
local buffer2 = pixbuf.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)
|
||||
|
||||
N.test('mix saturation correctly ', function()
|
||||
local buffer1 = pixbuf.newBuffer(1, 3)
|
||||
local buffer2 = pixbuf.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)
|
||||
|
||||
N.test('power', function()
|
||||
local buffer = pixbuf.newBuffer(2, 4)
|
||||
buffer:fill(10,22,54,234)
|
||||
ok(eq(buffer:power(), 2*(10+22+54+234)))
|
||||
end)
|
||||
|
||||
N.test('shift LOGICAL', function()
|
||||
local buffer1 = pixbuf.newBuffer(4, 4)
|
||||
local buffer2 = pixbuf.newBuffer(4, 4)
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,0,0,7,8)
|
||||
ok(buffer1 ~= buffer2, "disequality pre shift")
|
||||
buffer1:shift(2)
|
||||
ok(buffer1 == buffer2, "shift right")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,9,12,0,0)
|
||||
buffer1:shift(-2)
|
||||
ok(buffer1 == buffer2, "shift left")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,7,0,8,12)
|
||||
buffer1:shift(1, nil, 2,3)
|
||||
ok(buffer1 == buffer2, "shift middle right")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,7,9,0,12)
|
||||
buffer1:shift(-1, nil, 2,3)
|
||||
ok(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, pixbuf.SHIFT_LOGICAL, 0,5)
|
||||
ok(buffer1 == buffer2, "shift left out of bound")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,0,7,8,9)
|
||||
buffer1:shift(1, pixbuf.SHIFT_LOGICAL, 0,5)
|
||||
ok(buffer1 == buffer2, "shift right out of bound")
|
||||
|
||||
end)
|
||||
|
||||
N.test('shift LOGICAL issue #2946', function()
|
||||
local buffer1 = pixbuf.newBuffer(4, 4)
|
||||
local buffer2 = pixbuf.newBuffer(4, 4)
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,0,0,0,0)
|
||||
buffer1:shift(4)
|
||||
ok(buffer1 == buffer2, "shift all right")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,0,0,0,0)
|
||||
buffer1:shift(-4)
|
||||
ok(buffer1 == buffer2, "shift all left")
|
||||
|
||||
fail(function() buffer1:shift(10) end, "shifting more elements than buffer size")
|
||||
fail(function() buffer1:shift(-6) end, "shifting more elements than buffer size")
|
||||
end)
|
||||
|
||||
N.test('shift CIRCULAR', function()
|
||||
local buffer1 = pixbuf.newBuffer(4, 4)
|
||||
local buffer2 = pixbuf.newBuffer(4, 4)
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,9,12,7,8)
|
||||
buffer1:shift(2, pixbuf.SHIFT_CIRCULAR)
|
||||
ok(buffer1 == buffer2, "shift right")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,9,12,7,8)
|
||||
buffer1:shift(-2, pixbuf.SHIFT_CIRCULAR)
|
||||
ok(buffer1 == buffer2, "shift left")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,7,9,8,12)
|
||||
buffer1:shift(1, pixbuf.SHIFT_CIRCULAR, 2,3)
|
||||
ok(buffer1 == buffer2, "shift middle right")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,7,9,8,12)
|
||||
buffer1:shift(-1, pixbuf.SHIFT_CIRCULAR, 2,3)
|
||||
ok(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, pixbuf.SHIFT_CIRCULAR, 0,5)
|
||||
ok(buffer1 == buffer2, "shift left out of bound")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,12,7,8,9)
|
||||
buffer1:shift(1, pixbuf.SHIFT_CIRCULAR, 0,5)
|
||||
ok(buffer1 == buffer2, "shift right out of bound")
|
||||
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,12,7,8,9)
|
||||
buffer1:shift(1, pixbuf.SHIFT_CIRCULAR, -12,12)
|
||||
ok(buffer1 == buffer2, "shift right way out of bound")
|
||||
|
||||
end)
|
||||
|
||||
N.test('sub', function()
|
||||
local buffer1 = pixbuf.newBuffer(4, 4)
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
buffer1 = buffer1:sub(4,3)
|
||||
ok(eq(buffer1:size(), 0), "sub empty")
|
||||
|
||||
local buffer2 = pixbuf.newBuffer(2, 4)
|
||||
buffer1 = pixbuf.newBuffer(4, 4)
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,9,12)
|
||||
buffer1 = buffer1:sub(3,4)
|
||||
ok(buffer1 == buffer2, "sub")
|
||||
|
||||
buffer1 = pixbuf.newBuffer(4, 4)
|
||||
buffer2 = pixbuf.newBuffer(4, 4)
|
||||
initBuffer(buffer1,7,8,9,12)
|
||||
initBuffer(buffer2,7,8,9,12)
|
||||
buffer1 = buffer1:sub(-12,33)
|
||||
ok(buffer1 == buffer2, "out of bounds")
|
||||
end)
|
||||
|
||||
N.test('map', function()
|
||||
local buffer1 = pixbuf.newBuffer(4, 4)
|
||||
buffer1:fill(65,66,67,68)
|
||||
|
||||
buffer1:map(function(a,b,c,d) return b,a,c,d end)
|
||||
ok(eq("BACDBACDBACDBACD", buffer1:dump()), "swizzle")
|
||||
|
||||
local buffer2 = pixbuf.newBuffer(4, 1)
|
||||
buffer2:map(function(b,a,c,d) return c end, buffer1) -- luacheck: ignore
|
||||
ok(eq("CCCC", buffer2:dump()), "projection")
|
||||
|
||||
local buffer3 = pixbuf.newBuffer(4, 3)
|
||||
buffer3:map(function(b,a,c,d) return a,b,d end, buffer1) -- luacheck: ignore
|
||||
ok(eq("ABDABDABDABD", buffer3:dump()), "projection 2")
|
||||
|
||||
buffer1:fill(70,71,72,73)
|
||||
buffer1:map(function(c,a,b,d) return a,b,c,d end, buffer2, nil, nil, buffer3)
|
||||
ok(eq("ABCDABCDABCDABCD", buffer1:dump()), "zip")
|
||||
|
||||
buffer1 = pixbuf.newBuffer(2, 4)
|
||||
buffer1:fill(70,71,72,73)
|
||||
buffer2:set(1,"ABCD")
|
||||
buffer3:set(1,"EFGHIJKLM")
|
||||
buffer1:map(function(c,a,b,d) return a,b,c,d end, buffer2, 1, 2, buffer3, 2)
|
||||
ok(eq("HIAJKLBM", buffer1:dump()), "partial zip")
|
||||
end)
|
||||
|
||||
--[[
|
||||
pixbuf.buffer:__concat()
|
||||
--]]
|
|
@ -58,7 +58,7 @@ N.test('replace correctly', function()
|
|||
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")
|
||||
|
||||
fail(function() buffer:replace(string.char(3,255,165,33,0,244,12,87,255), 4) end, "Does not fit into destination")
|
||||
fail(function() buffer:replace(string.char(3,255,165,33,0,244,12,87,255), 4) end, "does not fit into destination")
|
||||
end)
|
||||
|
||||
N.test('replace correctly issue #2921', function()
|
||||
|
@ -91,7 +91,7 @@ N.test('fade correctly', function()
|
|||
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")
|
||||
ok(buffer:dump() == string.char(0,math.floor(222/3),math.floor(55/3)), "RGB")
|
||||
buffer:fill(1,222,55)
|
||||
buffer:fade(3, ws2812.FADE_IN)
|
||||
ok(buffer:dump() == string.char(3,255,165), "RGB")
|
||||
|
|
|
@ -492,6 +492,17 @@ stds.nodemcu_libs = {
|
|||
create = empty
|
||||
}
|
||||
},
|
||||
pixbuf = {
|
||||
fields = {
|
||||
FADE_IN = empty,
|
||||
FADE_OUT = empty,
|
||||
SHIFT_CIRCULAR = empty,
|
||||
SHIFT_LOGICAL = empty,
|
||||
init = empty,
|
||||
newBuffer = empty,
|
||||
write = empty
|
||||
}
|
||||
},
|
||||
pwm = {
|
||||
fields = {
|
||||
close = empty,
|
||||
|
|
Loading…
Reference in New Issue