mirror of
https://github.com/nodemcu/nodemcu-firmware.git
synced 2025-02-06 21:18:25 +08:00
The PR removed the bulk of non-newlib headers from the NodeMCU source base. app/libc has now been cut down to the bare minimum overrides to shadow the corresponding functions in the SDK's libc. The old c_xyz.h headerfiles have been nuked in favour of the standard <xyz.h> headers, with a few exceptions over in sdk-overrides. Again, shipping a libc.a without headers is a terrible thing to do. We're still living on a prayer that libc was configured the same was as a default-configured xtensa gcc toolchain assumes it is. That part I cannot do anything about, unfortunately, but it's no worse than it has been before. This enables our source files to compile successfully using the standard header files, and use the typical malloc()/calloc()/realloc()/free(), the strwhatever()s and memwhatever()s. These end up, through macro and linker magic, mapped to the appropriate SDK or ROM functions.
1058 lines
26 KiB
C
1058 lines
26 KiB
C
#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"
|
|
#include "pm/swtimer.h"
|
|
|
|
#include "ws2812.h"
|
|
#include "color_utils.h"
|
|
|
|
#define CANARY_VALUE 0x32372132
|
|
|
|
#define DEFAULT_MODE 0
|
|
#define DEFAULT_COLOR 0xFF0000
|
|
|
|
#define SPEED_MIN 0
|
|
#define SPEED_MAX 255
|
|
#define SPEED_DEFAULT 150
|
|
#define DELAY_DEFAULT 100
|
|
|
|
#define BRIGHTNESS_MIN 0
|
|
#define BRIGHTNESS_MAX 255
|
|
#define BRIGHTNESS_DEFAULT 100
|
|
|
|
#define EFFECT_PARAM_INVALID -10000
|
|
|
|
#define LIBRARY_NOT_INITIALIZED_ERROR_MSG "please call init() first"
|
|
|
|
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
#define max(a,b) ((a) > (b) ? (a) : (b))
|
|
#define abs(a) ((a) > 0 ? (a) : (0-a))
|
|
#define min3(a,b, c) min((a), min((b), (c)))
|
|
#define max3(a,b, c) max((a), max((b), (c)))
|
|
|
|
|
|
|
|
typedef struct {
|
|
ws2812_buffer *buffer;
|
|
int buffer_ref;
|
|
uint32_t mode_delay;
|
|
uint32_t counter_mode_call;
|
|
uint32_t counter_mode_step;
|
|
uint8_t mode_color_index;
|
|
uint8_t speed;
|
|
uint8_t brightness;
|
|
os_timer_t os_t;
|
|
uint8_t running;
|
|
uint8_t effect_type;
|
|
uint8_t color[4];
|
|
int effect_int_param1;
|
|
} ws2812_effects;
|
|
|
|
|
|
|
|
enum ws2812_effects_type {
|
|
WS2812_EFFECT_STATIC,
|
|
WS2812_EFFECT_BLINK,
|
|
WS2812_EFFECT_GRADIENT,
|
|
WS2812_EFFECT_GRADIENT_RGB,
|
|
WS2812_EFFECT_RANDOM_COLOR,
|
|
WS2812_EFFECT_RAINBOW,
|
|
WS2812_EFFECT_RAINBOW_CYCLE,
|
|
WS2812_EFFECT_FLICKER,
|
|
WS2812_EFFECT_FIRE_FLICKER,
|
|
WS2812_EFFECT_FIRE_FLICKER_SOFT,
|
|
WS2812_EFFECT_FIRE_FLICKER_INTENSE,
|
|
WS2812_EFFECT_HALLOWEEN,
|
|
WS2812_EFFECT_CIRCUS_COMBUSTUS,
|
|
WS2812_EFFECT_LARSON_SCANNER,
|
|
WS2812_EFFECT_CYCLE,
|
|
WS2812_EFFECT_COLOR_WIPE,
|
|
WS2812_EFFECT_RANDOM_DOT
|
|
};
|
|
|
|
|
|
static ws2812_effects *state;
|
|
|
|
|
|
//-----------------
|
|
// UTILITY METHODS
|
|
//-----------------
|
|
|
|
|
|
static int ws2812_write(ws2812_buffer* buffer) {
|
|
size_t length1, length2;
|
|
const char *buffer1, *buffer2;
|
|
|
|
buffer1 = 0;
|
|
length1 = 0;
|
|
|
|
buffer1 = buffer->values;
|
|
length1 = buffer->colorsPerLed*buffer->size;
|
|
|
|
buffer2 = 0;
|
|
length2 = 0;
|
|
|
|
// Send the buffers
|
|
ws2812_write_data(buffer1, length1, buffer2, length2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
int offset = pixel * buffer->colorsPerLed;
|
|
buffer->values[offset] = g;
|
|
buffer->values[offset+1] = r;
|
|
buffer->values[offset+2] = b;
|
|
if (buffer->colorsPerLed == 4) {
|
|
buffer->values[offset+3] = w;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
|
|
*/
|
|
static uint8_t get_random_wheel_index(uint8_t pos)
|
|
{
|
|
uint8_t r = 0;
|
|
uint8_t x = 0;
|
|
uint8_t y = 0;
|
|
uint8_t d = 0;
|
|
|
|
while(d < 42) {
|
|
r = rand() % 360;
|
|
x = abs(pos - r);
|
|
y = 360 - x;
|
|
d = min(x, y);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
//-----------------
|
|
// EFFECTS LIBRARY
|
|
//-----------------
|
|
|
|
/**
|
|
* initialized ws2812_effects with the buffer to use
|
|
*/
|
|
static int ws2812_effects_init(lua_State *L) {
|
|
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
|
|
luaL_argcheck(L, buffer != NULL, 1, "no valid buffer provided");
|
|
// get rid of old state
|
|
if (state != NULL) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, state->buffer_ref);
|
|
free((void *) state);
|
|
}
|
|
// Allocate memory and set all to zero
|
|
size_t size = sizeof(ws2812_effects) + buffer->colorsPerLed*sizeof(uint8_t);
|
|
state = (ws2812_effects *) calloc(1,size);
|
|
// initialize
|
|
state->speed = SPEED_DEFAULT;
|
|
state->mode_delay = DELAY_DEFAULT;
|
|
state->brightness = BRIGHTNESS_DEFAULT;
|
|
state->buffer = buffer;
|
|
|
|
state->buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* set color for single color effects
|
|
*/
|
|
static int ws2812_effects_set_color(lua_State* L) {
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
|
|
uint8_t g = luaL_checkinteger(L, 1);
|
|
uint8_t r = luaL_checkinteger(L, 2);
|
|
uint8_t b = luaL_checkinteger(L, 3);
|
|
uint8_t w = luaL_optinteger(L, 4, 0 );
|
|
|
|
state->color[0] = g;
|
|
state->color[1] = r;
|
|
state->color[2] = b;
|
|
state->color[3] = w;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int ws2812_effects_get_speed(lua_State* L) {
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
lua_pushnumber(L, state->speed);
|
|
return 1;
|
|
}
|
|
|
|
static int ws2812_effects_set_speed(lua_State* L) {
|
|
uint8_t speed = luaL_checkinteger(L, 1);
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
luaL_argcheck(L, speed >= 0 && speed <= 255, 1, "should be a 0-255");
|
|
state->speed = speed;
|
|
state->mode_delay = 10;
|
|
return 0;
|
|
}
|
|
|
|
static int ws2812_effects_get_delay(lua_State* L) {
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
lua_pushnumber(L, state->mode_delay);
|
|
return 1;
|
|
}
|
|
|
|
static int ws2812_effects_set_delay(lua_State* L) {
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
const int delay = luaL_checkinteger(L, 1);
|
|
luaL_argcheck(L, delay >= 10, 1, "must be equal / larger than 10");
|
|
|
|
state->mode_delay = delay;
|
|
state->speed = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_set_brightness(lua_State* L) {
|
|
uint8_t brightness = luaL_checkint(L, 1);
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
luaL_argcheck(L, brightness >= 0 && brightness < 256, 1, "should be a 0-255");
|
|
state->brightness = brightness;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_fill_buffer(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;
|
|
|
|
// Fill buffer
|
|
int i;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; i++) {
|
|
*p++ = g * state->brightness / 255;
|
|
*p++ = r * state->brightness / 255;
|
|
*p++ = b * state->brightness / 255;
|
|
if (buffer->colorsPerLed == 4) {
|
|
*p++ = w * state->brightness / 255;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//------------------
|
|
// basic methods
|
|
//------------------
|
|
|
|
|
|
/*
|
|
* Cycles all LEDs at once through a rainbow.
|
|
*/
|
|
static int ws2812_effects_fill_color() {
|
|
|
|
uint8_t g = state->color[0];
|
|
uint8_t r = state->color[1];
|
|
uint8_t b = state->color[2];
|
|
uint8_t w = state->color[3];
|
|
|
|
uint32_t color = (w << 24) | (g << 16) | (r << 8) | b;
|
|
|
|
ws2812_effects_fill_buffer(color);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//-----------------
|
|
// EFFECFTS
|
|
//-----------------
|
|
|
|
|
|
|
|
/*
|
|
* blink with set color
|
|
*/
|
|
static int ws2812_effects_mode_blink() {
|
|
if(state->counter_mode_call % 2 == 1) {
|
|
// on
|
|
ws2812_effects_fill_color();
|
|
}
|
|
else {
|
|
// off
|
|
ws2812_buffer * buffer = state->buffer;
|
|
memset(&buffer->values[0], 0, buffer->size * buffer->colorsPerLed);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
int segments = (length1 / buffer->colorsPerLed) - 1;
|
|
int segmentSize = buffer->size / segments;
|
|
|
|
uint8_t g1, r1, b1, g2, r2, b2;
|
|
int i,j,k;
|
|
|
|
g2 = *gradient_spec++;
|
|
r2 = *gradient_spec++;
|
|
b2 = *gradient_spec++;
|
|
// skip non-rgb components
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*gradient_spec++;
|
|
}
|
|
|
|
// reference to buffer memory
|
|
uint8_t * p = &buffer->values[0];
|
|
|
|
uint16_t h1,h2;
|
|
uint8_t s,v,s1,v1,s2,v2;
|
|
|
|
for (k = 0; k < segments; k++) {
|
|
g1 = g2;
|
|
r1 = r2;
|
|
b1 = b2;
|
|
uint32_t hsv1 = grb2hsv(g1, r1, b1);
|
|
h1 = (hsv1 & 0xFFFF0000) >> 16;
|
|
s1 = (hsv1 & 0x0000FF00) >> 8;
|
|
v1 = (hsv1 & 0x000000FF);
|
|
|
|
g2 = *gradient_spec++;
|
|
r2 = *gradient_spec++;
|
|
b2 = *gradient_spec++;
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*gradient_spec++;
|
|
}
|
|
|
|
uint32_t hsv2 = grb2hsv(g2, r2, b2);
|
|
h2 = (hsv2 & 0xFFFF0000) >> 16;
|
|
s2 = (hsv1 & 0x0000FF00) >> 8;
|
|
v2 = (hsv1 & 0x000000FF);
|
|
|
|
// get distance and direction to use
|
|
int maxCCW = h1 > h2 ? h1 - h2 : 360 + h1 - h2;
|
|
int maxCW = h1 > h2 ? 360 + h2 - h1 : h2 - h1;
|
|
|
|
// Fill buffer
|
|
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));
|
|
}
|
|
|
|
int steps = numPixels - 1;
|
|
|
|
for(i = 0; i < numPixels; i++) {
|
|
// calculate HSV values
|
|
//h = h1 + ((h2-h1) * i / fillSize);
|
|
int h = maxCCW > maxCW ? h1 + ((maxCW * i / steps) % 360) : h1 - (maxCCW * i / steps);
|
|
if (h < 0) h = h + 360;
|
|
if (h > 359) h = h - 360;
|
|
s = s1 + ((s2-s1) * i / steps);
|
|
v = v1 + ((v2-v1) * i / steps);
|
|
// convert to RGB
|
|
uint32_t grb = hsv2grb(h, s, v);
|
|
|
|
*p++ = ((grb & 0x00FF0000) >> 16) * state->brightness / 255;
|
|
*p++ = ((grb & 0x0000FF00) >> 8) * state->brightness / 255;
|
|
*p++ = (grb & 0x000000FF) * state->brightness / 255;
|
|
|
|
for (j = 3; j < buffer->colorsPerLed; j++) {
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
int segments = (length1 / buffer->colorsPerLed) - 1;
|
|
int segmentSize = buffer->size / segments;
|
|
|
|
uint8_t g1, r1, b1, g2, r2, b2;
|
|
int i,j,k;
|
|
|
|
g2 = *buffer1++;
|
|
r2 = *buffer1++;
|
|
b2 = *buffer1++;
|
|
// skip non-rgb components
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*buffer1++;
|
|
}
|
|
|
|
// reference to buffer memory
|
|
uint8_t * p = &buffer->values[0];
|
|
for (k = 0; k < segments; k++) {
|
|
g1 = g2;
|
|
r1 = r2;
|
|
b1 = b2;
|
|
|
|
g2 = *buffer1++;
|
|
r2 = *buffer1++;
|
|
b2 = *buffer1++;
|
|
|
|
for (j = 3; j < buffer->colorsPerLed; j++) {
|
|
*buffer1++;
|
|
}
|
|
|
|
// Fill buffer
|
|
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));
|
|
}
|
|
|
|
int steps = numPixels - 1;
|
|
|
|
for(i = 0; i < numPixels; i++) {
|
|
*p++ = (g1 + ((g2-g1) * i / steps)) * state->brightness / 255;
|
|
*p++ = (r1 + ((r2-r1) * i / steps)) * state->brightness / 255;
|
|
*p++ = (b1 + ((b2-b1) * i / steps)) * state->brightness / 255;
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Lights all LEDs in one random color up. Then switches them
|
|
* to the next random color.
|
|
*/
|
|
static int ws2812_effects_mode_random_color() {
|
|
state->mode_color_index = get_random_wheel_index(state->mode_color_index);
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
uint32_t color = color_wheel(state->mode_color_index);
|
|
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255;
|
|
uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255;
|
|
uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255;
|
|
|
|
// Fill buffer
|
|
int i,j;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; i++) {
|
|
*p++ = g;
|
|
*p++ = r;
|
|
*p++ = b;
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Cycles all LEDs at once through a rainbow.
|
|
*/
|
|
static int ws2812_effects_mode_rainbow() {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
uint32_t color = color_wheel(state->counter_mode_step);
|
|
uint8_t r = (color & 0x00FF0000) >> 16;
|
|
uint8_t g = (color & 0x0000FF00) >> 8;
|
|
uint8_t b = (color & 0x000000FF) >> 0;
|
|
|
|
// Fill buffer
|
|
int i,j;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; i++) {
|
|
*p++ = g * state->brightness / 255;
|
|
*p++ = r * state->brightness / 255;
|
|
*p++ = b * state->brightness / 255;
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
state->counter_mode_step = (state->counter_mode_step + 1) % 360;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Cycles a rainbow over the entire string of LEDs.
|
|
*/
|
|
static int ws2812_effects_mode_rainbow_cycle(int repeat_count) {
|
|
|
|
ws2812_buffer * 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;
|
|
uint32_t color = color_wheel(wheel_index);
|
|
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255;
|
|
uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255;
|
|
uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255;
|
|
*p++ = g;
|
|
*p++ = r;
|
|
*p++ = b;
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Random flickering.
|
|
*/
|
|
static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
uint8_t p_g = state->color[0];
|
|
uint8_t p_r = state->color[1];
|
|
uint8_t p_b = state->color[2];
|
|
|
|
// Fill buffer
|
|
int i,j;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; i++) {
|
|
int flicker = rand() % (max_flicker > 0 ? max_flicker : 1);
|
|
int r1 = p_r-flicker;
|
|
int g1 = p_g-flicker;
|
|
int b1 = p_b-flicker;
|
|
if(g1<0) g1=0;
|
|
if(r1<0) r1=0;
|
|
if(b1<0) b1=0;
|
|
*p++ = g1 * state->brightness / 255;
|
|
*p++ = r1 * state->brightness / 255;
|
|
*p++ = b1 * state->brightness / 255;
|
|
for (j = 3; j < buffer->colorsPerLed; j++) {
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Halloween effect
|
|
*/
|
|
static int ws2812_effects_mode_halloween() {
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
int g1 = 50 * state->brightness / 255;
|
|
int r1 = 255 * state->brightness / 255;
|
|
int b1 = 0 * state->brightness / 255;
|
|
|
|
int g2 = 0 * state->brightness / 255;
|
|
int r2 = 255 * state->brightness / 255;
|
|
int b2 = 130 * state->brightness / 255;
|
|
|
|
|
|
// Fill buffer
|
|
int i,j;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; 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++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_mode_circus_combustus() {
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
int g1 = 0 * state->brightness / 255;
|
|
int r1 = 255 * state->brightness / 255;
|
|
int b1 = 0 * state->brightness / 255;
|
|
|
|
int g2 = 255 * state->brightness / 255;
|
|
int r2 = 255 * state->brightness / 255;
|
|
int b2 = 255 * state->brightness / 255;
|
|
|
|
// Fill buffer
|
|
int i,j;
|
|
uint8_t * p = &buffer->values[0];
|
|
for(i = 0; i < buffer->size; i++) {
|
|
if (i % 6 < 2) {
|
|
*p++ = g1;
|
|
*p++ = r1;
|
|
*p++ = b1;
|
|
}
|
|
else if (i % 6 < 4) {
|
|
*p++ = g2;
|
|
*p++ = r2;
|
|
*p++ = b2;
|
|
}
|
|
else {
|
|
*p++ = 0;
|
|
*p++ = 0;
|
|
*p++ = 0;
|
|
}
|
|
for (j = 3; j < buffer->colorsPerLed; j++)
|
|
{
|
|
*p++ = 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* K.I.T.T.
|
|
*/
|
|
static int ws2812_effects_mode_larson_scanner() {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
int led_index = 0;
|
|
|
|
for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) {
|
|
buffer->values[i] = buffer->values[i] >> 1;
|
|
}
|
|
|
|
uint16_t pos = 0;
|
|
|
|
if(state->counter_mode_step < buffer->size) {
|
|
pos = state->counter_mode_step;
|
|
} else {
|
|
pos = (buffer->size * 2) - state->counter_mode_step - 2;
|
|
}
|
|
pos = pos * buffer->colorsPerLed;
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
static int ws2812_effects_mode_color_wipe() {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
int led_index = (state->counter_mode_step % buffer->size) * buffer->colorsPerLed;
|
|
|
|
if (state->counter_mode_step >= buffer->size)
|
|
{
|
|
buffer->values[led_index] = 0;
|
|
buffer->values[led_index + 1] = 0;
|
|
buffer->values[led_index + 2] = 0;
|
|
}
|
|
else
|
|
{
|
|
uint8_t px_r = state->color[1] * state->brightness / 255;
|
|
uint8_t px_g = state->color[0] * state->brightness / 255;
|
|
uint8_t px_b = state->color[2] * state->brightness / 255;
|
|
buffer->values[led_index] = px_g;
|
|
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);
|
|
}
|
|
|
|
static int ws2812_effects_mode_random_dot(uint8_t dots) {
|
|
|
|
ws2812_buffer * buffer = state->buffer;
|
|
|
|
// fade out
|
|
for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) {
|
|
buffer->values[i] = buffer->values[i] >> 1;
|
|
}
|
|
|
|
for(int i=0; i < dots; i++) {
|
|
// pick random pixel
|
|
int led_index = rand() % buffer->size;
|
|
|
|
uint32_t color = (state->color[0] << 16) | (state->color[1] << 8) | state->color[2];
|
|
if (buffer->colorsPerLed == 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);
|
|
}
|
|
|
|
|
|
|
|
static uint32_t ws2812_effects_mode_delay()
|
|
{
|
|
// check if delay has been set explicitly
|
|
if (state->speed == 0 && state->mode_delay > 0)
|
|
{
|
|
return state->mode_delay;
|
|
}
|
|
|
|
uint32_t delay = 10;
|
|
switch (state->effect_type) {
|
|
case WS2812_EFFECT_BLINK:
|
|
case WS2812_EFFECT_RAINBOW:
|
|
case WS2812_EFFECT_RAINBOW_CYCLE:
|
|
delay = 10 + ((1000 * (uint32_t)(SPEED_MAX - state->speed)) / SPEED_MAX);
|
|
break;
|
|
case WS2812_EFFECT_FLICKER:
|
|
case WS2812_EFFECT_FIRE_FLICKER:
|
|
case WS2812_EFFECT_FIRE_FLICKER_SOFT:
|
|
case WS2812_EFFECT_FIRE_FLICKER_INTENSE:
|
|
delay = 30 + (rand() % 100) + (200 * (SPEED_MAX - state->speed) / SPEED_MAX);
|
|
break;
|
|
|
|
case WS2812_EFFECT_RANDOM_COLOR:
|
|
case WS2812_EFFECT_HALLOWEEN:
|
|
case WS2812_EFFECT_CIRCUS_COMBUSTUS:
|
|
case WS2812_EFFECT_LARSON_SCANNER:
|
|
case WS2812_EFFECT_CYCLE:
|
|
case WS2812_EFFECT_COLOR_WIPE:
|
|
case WS2812_EFFECT_RANDOM_DOT:
|
|
delay = 10 + ((1000 * (uint32_t)(SPEED_MAX - state->speed)) / SPEED_MAX);
|
|
break;
|
|
|
|
}
|
|
return delay;
|
|
}
|
|
|
|
|
|
/**
|
|
* run loop for the effects.
|
|
*/
|
|
static void ws2812_effects_loop(void *p)
|
|
{
|
|
|
|
if (state->effect_type == WS2812_EFFECT_BLINK)
|
|
{
|
|
ws2812_effects_mode_blink();
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_RAINBOW)
|
|
{
|
|
ws2812_effects_mode_rainbow();
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_RAINBOW_CYCLE)
|
|
{
|
|
// the rainbow cycle effect can be achieved by shifting the buffer
|
|
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_FLICKER)
|
|
{
|
|
int flicker_value = state->effect_int_param1 != EFFECT_PARAM_INVALID ? state->effect_int_param1 : 100;
|
|
if (flicker_value == 0) {
|
|
flicker_value = 50;
|
|
}
|
|
ws2812_effects_mode_flicker_int(flicker_value);
|
|
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER)
|
|
{
|
|
ws2812_effects_mode_flicker_int(110);
|
|
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER_SOFT)
|
|
{
|
|
ws2812_effects_mode_flicker_int(70);
|
|
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER_INTENSE)
|
|
{
|
|
ws2812_effects_mode_flicker_int(170);
|
|
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_RANDOM_COLOR)
|
|
{
|
|
ws2812_effects_mode_random_color();
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_HALLOWEEN)
|
|
{
|
|
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_CIRCUS_COMBUSTUS)
|
|
{
|
|
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_LARSON_SCANNER)
|
|
{
|
|
ws2812_effects_mode_larson_scanner();
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_CYCLE)
|
|
{
|
|
ws2812_buffer_shift(state->buffer, state->effect_int_param1, SHIFT_CIRCULAR, 1, -1);
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_COLOR_WIPE)
|
|
{
|
|
ws2812_effects_mode_color_wipe();
|
|
}
|
|
else if (state->effect_type == WS2812_EFFECT_RANDOM_DOT)
|
|
{
|
|
uint8_t dots = state->effect_int_param1 != EFFECT_PARAM_INVALID ? state->effect_int_param1 : 1;
|
|
ws2812_effects_mode_random_dot(dots);
|
|
}
|
|
|
|
// set the new delay for this effect
|
|
state->mode_delay = ws2812_effects_mode_delay();
|
|
// call count
|
|
state->counter_mode_call = (state->counter_mode_call + 1) % UINT32_MAX;
|
|
// write the buffer
|
|
ws2812_write(state->buffer);
|
|
// set the timer
|
|
if (state->running == 1 && state->mode_delay >= 10)
|
|
{
|
|
os_timer_disarm(&(state->os_t));
|
|
os_timer_arm(&(state->os_t), state->mode_delay, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the active effect mode
|
|
*/
|
|
static int ws2812_effects_set_mode(lua_State* L) {
|
|
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
|
|
// opts must be same order as effect type enum
|
|
static const char * const opts[] = {"static", "blink", "gradient", "gradient_rgb", "random_color", "rainbow",
|
|
"rainbow_cycle", "flicker", "fire", "fire_soft", "fire_intense", "halloween", "circus_combustus",
|
|
"larson_scanner", "cycle", "color_wipe", "random_dot", NULL};
|
|
|
|
int type = luaL_checkoption(L, 1, NULL, opts);
|
|
|
|
state->effect_type = type;
|
|
int effect_param = EFFECT_PARAM_INVALID;
|
|
// check additional int parameter
|
|
// First mandatory parameter
|
|
int arg_type = lua_type(L, 2);
|
|
if (arg_type == LUA_TNONE || arg_type == LUA_TNIL)
|
|
{
|
|
// we don't have a second parameter
|
|
}
|
|
else if(arg_type == LUA_TNUMBER)
|
|
{
|
|
effect_param = luaL_optinteger( L, 2, EFFECT_PARAM_INVALID );
|
|
}
|
|
|
|
// initialize the effect
|
|
state->counter_mode_step = 0;
|
|
|
|
switch (state->effect_type) {
|
|
case WS2812_EFFECT_STATIC:
|
|
// fill with currently set color
|
|
ws2812_effects_fill_color();
|
|
state->mode_delay = 250;
|
|
break;
|
|
case WS2812_EFFECT_BLINK:
|
|
ws2812_effects_mode_blink();
|
|
break;
|
|
case WS2812_EFFECT_GRADIENT:
|
|
if(arg_type == LUA_TSTRING)
|
|
{
|
|
size_t length1;
|
|
const char *buffer1 = lua_tolstring(L, 2, &length1);
|
|
|
|
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
|
|
{
|
|
luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors");
|
|
}
|
|
|
|
ws2812_effects_gradient(buffer1, length1);
|
|
ws2812_write(state->buffer);
|
|
}
|
|
else
|
|
{
|
|
luaL_argerror(L, 2, "string expected");
|
|
}
|
|
|
|
break;
|
|
case WS2812_EFFECT_GRADIENT_RGB:
|
|
if(arg_type == LUA_TSTRING)
|
|
{
|
|
size_t length1;
|
|
const char *buffer1 = lua_tolstring(L, 2, &length1);
|
|
|
|
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
|
|
{
|
|
luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors");
|
|
}
|
|
|
|
ws2812_effects_gradient_rgb(buffer1, length1);
|
|
ws2812_write(state->buffer);
|
|
}
|
|
else
|
|
{
|
|
luaL_argerror(L, 2, "string expected");
|
|
}
|
|
|
|
break;
|
|
case WS2812_EFFECT_RANDOM_COLOR:
|
|
ws2812_effects_mode_random_color();
|
|
break;
|
|
case WS2812_EFFECT_RAINBOW:
|
|
ws2812_effects_mode_rainbow();
|
|
break;
|
|
case WS2812_EFFECT_RAINBOW_CYCLE:
|
|
ws2812_effects_mode_rainbow_cycle(effect_param != EFFECT_PARAM_INVALID ? effect_param : 1);
|
|
break;
|
|
// flicker
|
|
case WS2812_EFFECT_FLICKER:
|
|
state->effect_int_param1 = effect_param;
|
|
break;
|
|
case WS2812_EFFECT_FIRE_FLICKER:
|
|
case WS2812_EFFECT_FIRE_FLICKER_SOFT:
|
|
case WS2812_EFFECT_FIRE_FLICKER_INTENSE:
|
|
{
|
|
state->color[0] = 255-40;
|
|
state->color[1] = 255;
|
|
state->color[2] = 40;
|
|
state->color[3] = 0;
|
|
}
|
|
break;
|
|
case WS2812_EFFECT_HALLOWEEN:
|
|
ws2812_effects_mode_halloween();
|
|
break;
|
|
case WS2812_EFFECT_CIRCUS_COMBUSTUS:
|
|
ws2812_effects_mode_circus_combustus();
|
|
break;
|
|
case WS2812_EFFECT_LARSON_SCANNER:
|
|
ws2812_effects_mode_larson_scanner();
|
|
break;
|
|
case WS2812_EFFECT_CYCLE:
|
|
if (effect_param != EFFECT_PARAM_INVALID) {
|
|
state->effect_int_param1 = effect_param;
|
|
}
|
|
break;
|
|
case WS2812_EFFECT_COLOR_WIPE:
|
|
{
|
|
uint32_t black = 0;
|
|
ws2812_effects_fill_buffer(black);
|
|
ws2812_effects_mode_color_wipe();
|
|
break;
|
|
}
|
|
case WS2812_EFFECT_RANDOM_DOT:
|
|
{
|
|
// check if more than 1 dot shall be set
|
|
state->effect_int_param1 = effect_param;
|
|
uint32_t black = 0;
|
|
ws2812_effects_fill_buffer(black);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Start the effect execution
|
|
*/
|
|
static int ws2812_effects_start(lua_State* L) {
|
|
|
|
//NODE_DBG("pin:%d, level:%d \n", pin, level);
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
if (state != NULL) {
|
|
os_timer_disarm(&(state->os_t));
|
|
state->running = 1;
|
|
state->counter_mode_call = 0;
|
|
state->counter_mode_step = 0;
|
|
// set the timer
|
|
os_timer_setfn(&(state->os_t), ws2812_effects_loop, NULL);
|
|
os_timer_arm(&(state->os_t), state->mode_delay, FALSE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Stop the effect execution
|
|
*/
|
|
static int ws2812_effects_stop(lua_State* L) {
|
|
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
|
|
if (state != NULL) {
|
|
os_timer_disarm(&(state->os_t));
|
|
state->running = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int ws2812_effects_tostring(lua_State* L) {
|
|
|
|
luaL_Buffer result;
|
|
luaL_buffinit(L, &result);
|
|
|
|
luaL_addchar(&result, '[');
|
|
luaL_addstring(&result, "effects");
|
|
luaL_addchar(&result, ']');
|
|
luaL_pushresult(&result);
|
|
|
|
return 1;
|
|
}
|
|
|
|
LROT_BEGIN(ws2812_effects)
|
|
LROT_FUNCENTRY( init, ws2812_effects_init )
|
|
LROT_FUNCENTRY( set_brightness, ws2812_effects_set_brightness )
|
|
LROT_FUNCENTRY( set_color, ws2812_effects_set_color )
|
|
LROT_FUNCENTRY( set_speed, ws2812_effects_set_speed )
|
|
LROT_FUNCENTRY( set_delay, ws2812_effects_set_delay )
|
|
LROT_FUNCENTRY( set_mode, ws2812_effects_set_mode )
|
|
LROT_FUNCENTRY( start, ws2812_effects_start )
|
|
LROT_FUNCENTRY( stop, ws2812_effects_stop )
|
|
LROT_FUNCENTRY( get_delay, ws2812_effects_get_delay )
|
|
LROT_FUNCENTRY( get_speed, ws2812_effects_get_speed )
|
|
|
|
LROT_TABENTRY( __index, ws2812_effects )
|
|
LROT_FUNCENTRY( __tostring, ws2812_effects_tostring )
|
|
LROT_END( ws2812_effects, ws2812_effects, LROT_MASK_INDEX )
|
|
|
|
|
|
|
|
NODEMCU_MODULE(WS2812_EFFECTS, "ws2812_effects", ws2812_effects, NULL);
|