Finalize work on ws2812 module
* Fix bug on first write Pin is 'HIGH' at reset, so we need to pull it down and generate a reset. * Move init code to flash section, not needed to be in iram. * Remove pin choice in API * Remove lock in ws2812_buffer_write * Remove naked malloc * Drop ws2812_writergb * Drop support of ws2812.buffers to ws2812_writegrb should use ws2812.buffers:write * Add support for <>3 colors per leds strips (RGBW) * Remove ICACHE_FLASH_ATTR * Add static const on _uartData to avoid initialization penalty
This commit is contained in:
parent
216c69f18b
commit
0577c8af0f
|
@ -1,5 +1,6 @@
|
||||||
#include "module.h"
|
#include "module.h"
|
||||||
#include "lauxlib.h"
|
#include "lauxlib.h"
|
||||||
|
#include "lmem.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "c_stdlib.h"
|
#include "c_stdlib.h"
|
||||||
#include "c_string.h"
|
#include "c_string.h"
|
||||||
|
@ -11,33 +12,51 @@
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int canary;
|
int canary;
|
||||||
int size;
|
int size;
|
||||||
|
uint8_t colorsPerLed;
|
||||||
uint8_t values[0];
|
uint8_t values[0];
|
||||||
} ws2812_buffer;
|
} ws2812_buffer;
|
||||||
|
|
||||||
// Stream data using UART1 routed to GPIO2
|
// Init UART1 to be able to stream WS2812 data
|
||||||
// NODE_DEBUG should not be activated because it also uses UART1
|
// We use GPIO2 as output pin
|
||||||
static void ICACHE_RAM_ATTR ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t length) {
|
static void ws2812_init() {
|
||||||
|
|
||||||
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
|
|
||||||
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
|
|
||||||
// 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
|
|
||||||
// 0b00110100 => 110100 => [0]001011[1] => 11101000 => 10
|
|
||||||
// 0b00000100 => 000100 => [0]001000[1] => 11101110 => 11
|
|
||||||
uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
|
|
||||||
|
|
||||||
// Configure UART1
|
// Configure UART1
|
||||||
// Set baudrate of UART1 to 3200000
|
// Set baudrate of UART1 to 3200000
|
||||||
WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000);
|
WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000);
|
||||||
// Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX
|
// Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX
|
||||||
WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S));
|
WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S));
|
||||||
|
|
||||||
|
// Pull GPIO2 down
|
||||||
|
platform_gpio_mode(4, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
|
||||||
|
platform_gpio_write(4, 0);
|
||||||
|
|
||||||
|
// Waits 10us to simulate a reset
|
||||||
|
os_delay_us(10);
|
||||||
|
|
||||||
// Redirect UART1 to GPIO2
|
// Redirect UART1 to GPIO2
|
||||||
// Disable GPIO2
|
// Disable GPIO2
|
||||||
GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, BIT2);
|
GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, BIT2);
|
||||||
// Enable Function 2 for GPIO2 (U1TXD)
|
// Enable Function 2 for GPIO2 (U1TXD)
|
||||||
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Stream data using UART1 routed to GPIO2
|
||||||
|
// ws2812.init() should be called first
|
||||||
|
//
|
||||||
|
// NODE_DEBUG should not be activated because it also uses UART1
|
||||||
|
static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) {
|
||||||
|
|
||||||
|
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
|
||||||
|
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
|
||||||
|
// 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
|
||||||
|
// 0b00110100 => 110100 => [0]001011[1] => 11101000 => 10
|
||||||
|
// 0b00000100 => 000100 => [0]001000[1] => 11101110 => 11
|
||||||
|
// Array declared as static const to avoid runtime generation
|
||||||
|
// But declared in ".data" section to avoid read penalty from FLASH
|
||||||
|
static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
|
||||||
|
|
||||||
uint8_t *end = pixels + length;
|
uint8_t *end = pixels + length;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
uint8_t value = *pixels++;
|
uint8_t value = *pixels++;
|
||||||
|
|
||||||
|
@ -52,94 +71,37 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t
|
||||||
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
|
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
|
||||||
|
|
||||||
} while(pixels < end);
|
} while(pixels < end);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua: ws2812.writergb(pin, "string")
|
// Lua: ws2812.write("string")
|
||||||
// Byte triples in the string or buffer are interpreted as R G B values and sent to the hardware as G R B.
|
// Byte triples in the string are interpreted as G R B values.
|
||||||
//
|
//
|
||||||
// ws2812.writergb(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red.
|
// ws2812.init() should be called first
|
||||||
// ws2812.writergb(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
|
//
|
||||||
// ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white.
|
// ws2812.write(string.char(0, 255, 0)) sets the first LED red.
|
||||||
static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L)
|
// ws2812.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
|
||||||
{
|
// ws2812.write(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
|
||||||
const uint8_t pin = luaL_checkinteger(L, 1);
|
static int ws2812_writegrb(lua_State* L) {
|
||||||
size_t length;
|
size_t length;
|
||||||
const char *rgb;
|
const char *values = luaL_checklstring(L, 1, &length);
|
||||||
|
|
||||||
// Buffer or string
|
|
||||||
if(lua_isuserdata(L, 2)) {
|
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
|
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
|
|
||||||
|
|
||||||
rgb = &buffer->values[0];
|
|
||||||
length = 3*buffer->size;
|
|
||||||
} else {
|
|
||||||
rgb = luaL_checklstring(L, 2, &length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// dont modify lua-internal lstring - make a copy instead
|
|
||||||
char *buffer = (char *)c_malloc(length);
|
|
||||||
c_memcpy(buffer, rgb, length);
|
|
||||||
|
|
||||||
// Ignore incomplete Byte triples at the end of buffer:
|
|
||||||
length -= length % 3;
|
|
||||||
|
|
||||||
// Rearrange R G B values to G R B order needed by WS2812 LEDs:
|
|
||||||
size_t i;
|
|
||||||
for (i = 0; i < length; i += 3) {
|
|
||||||
const char r = buffer[i];
|
|
||||||
const char g = buffer[i + 1];
|
|
||||||
buffer[i] = g;
|
|
||||||
buffer[i + 1] = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the buffer
|
// Send the buffer
|
||||||
ws2812_write(pin_num[pin], (uint8_t*) buffer, length);
|
ws2812_write((uint8_t*) values, length);
|
||||||
|
|
||||||
c_free(buffer);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lua: ws2812.write(pin, "string")
|
|
||||||
// Lua: ws2812.write(pin, ws2812.buffer)
|
|
||||||
// Byte triples in the string or buffer are interpreted as G R B values.
|
|
||||||
//
|
|
||||||
// ws2812.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red.
|
|
||||||
// ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
|
|
||||||
// ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_writegrb(lua_State* L) {
|
|
||||||
const uint8_t pin = luaL_checkinteger(L, 1);
|
|
||||||
size_t length;
|
|
||||||
const char *values;
|
|
||||||
|
|
||||||
// Buffer or string
|
|
||||||
if(lua_isuserdata(L, 2)) {
|
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
|
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
|
|
||||||
|
|
||||||
values = &buffer->values[0];
|
|
||||||
length = 3*buffer->size;
|
|
||||||
} else {
|
|
||||||
values = luaL_checklstring(L, 2, &length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the buffer
|
|
||||||
ws2812_write(pin_num[pin], (uint8_t*) values, length);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a buffer where we can store led values
|
// Handle a buffer where we can store led values
|
||||||
static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) {
|
static int ws2812_new_buffer(lua_State *L) {
|
||||||
const int leds = luaL_checkint(L, 1);
|
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, leds > 0, 1, "should be a positive integer");
|
||||||
|
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
|
||||||
|
|
||||||
// Allocate memory
|
// Allocate memory
|
||||||
size_t size = sizeof(ws2812_buffer) + 3*leds*sizeof(uint8_t);
|
size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t);
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
|
||||||
|
|
||||||
// Associate its metatable
|
// Associate its metatable
|
||||||
|
@ -148,6 +110,7 @@ static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) {
|
||||||
|
|
||||||
// Save led strip size
|
// Save led strip size
|
||||||
buffer->size = leds;
|
buffer->size = leds;
|
||||||
|
buffer->colorsPerLed = colorsPerLed;
|
||||||
|
|
||||||
// Store canary for future type checks
|
// Store canary for future type checks
|
||||||
buffer->canary = CANARY_VALUE;
|
buffer->canary = CANARY_VALUE;
|
||||||
|
@ -155,27 +118,37 @@ static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_fill(lua_State* L) {
|
static int ws2812_buffer_fill(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
||||||
|
|
||||||
const int g = luaL_checkinteger(L, 2);
|
// Grab colors
|
||||||
const int r = luaL_checkinteger(L, 3);
|
int i, j;
|
||||||
const int b = luaL_checkinteger(L, 4);
|
int * colors = luaM_malloc(L, buffer->colorsPerLed * sizeof(int));
|
||||||
|
|
||||||
uint8_t * p = &buffer->values[0];
|
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||||
int i;
|
{
|
||||||
for(i = 0; i < buffer->size; i++) {
|
colors[i] = luaL_checkinteger(L, 2+i);
|
||||||
*p++ = g;
|
|
||||||
*p++ = r;
|
|
||||||
*p++ = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory
|
||||||
|
luaM_free(L, colors);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_fade(lua_State* L) {
|
static int ws2812_buffer_fade(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
const int fade = luaL_checkinteger(L, 2);
|
const int fade = luaL_checkinteger(L, 2);
|
||||||
|
|
||||||
|
@ -184,32 +157,33 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_fade(lua_State* L) {
|
||||||
|
|
||||||
uint8_t * p = &buffer->values[0];
|
uint8_t * p = &buffer->values[0];
|
||||||
int i;
|
int i;
|
||||||
for(i = 0; i < buffer->size; i++) {
|
for(i = 0; i < buffer->size * buffer->colorsPerLed; i++)
|
||||||
*p++ /= fade;
|
{
|
||||||
*p++ /= fade;
|
|
||||||
*p++ /= fade;
|
*p++ /= fade;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_get(lua_State* L) {
|
static int ws2812_buffer_get(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
const int led = luaL_checkinteger(L, 2);
|
const int led = luaL_checkinteger(L, 2) - 1;
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
||||||
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
||||||
|
|
||||||
lua_pushnumber(L, buffer->values[3*led+0]);
|
int i;
|
||||||
lua_pushnumber(L, buffer->values[3*led+1]);
|
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||||
lua_pushnumber(L, buffer->values[3*led+2]);
|
{
|
||||||
|
lua_pushnumber(L, buffer->values[buffer->colorsPerLed*led+i]);
|
||||||
return 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) {
|
return buffer->colorsPerLed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ws2812_buffer_set(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
const int led = luaL_checkinteger(L, 2);
|
const int led = luaL_checkinteger(L, 2) - 1;
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
||||||
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
|
||||||
|
@ -217,18 +191,18 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) {
|
||||||
int type = lua_type(L, 3);
|
int type = lua_type(L, 3);
|
||||||
if(type == LUA_TTABLE)
|
if(type == LUA_TTABLE)
|
||||||
{
|
{
|
||||||
// Get g,r,b values and push them on stash
|
int i;
|
||||||
lua_rawgeti(L, 3, 1);
|
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||||
lua_rawgeti(L, 3, 2);
|
{
|
||||||
lua_rawgeti(L, 3, 3);
|
// Get value and push it on stack
|
||||||
|
lua_rawgeti(L, 3, i+1);
|
||||||
|
|
||||||
// Convert them as int and store them in buffer
|
// Convert it as int and store them in buffer
|
||||||
buffer->values[3*led+0] = lua_tonumber(L, -3);
|
buffer->values[buffer->colorsPerLed*led+i] = lua_tonumber(L, -1);
|
||||||
buffer->values[3*led+1] = lua_tonumber(L, -2);
|
}
|
||||||
buffer->values[3*led+2] = lua_tonumber(L, -1);
|
|
||||||
|
|
||||||
// Clean up the stack
|
// Clean up the stack
|
||||||
lua_pop(L, 3);
|
lua_pop(L, buffer->colorsPerLed);
|
||||||
}
|
}
|
||||||
else if(type == LUA_TSTRING)
|
else if(type == LUA_TSTRING)
|
||||||
{
|
{
|
||||||
|
@ -236,24 +210,26 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) {
|
||||||
const char * buf = lua_tolstring(L, 3, &len);
|
const char * buf = lua_tolstring(L, 3, &len);
|
||||||
|
|
||||||
// Overflow check
|
// Overflow check
|
||||||
if( 3*led + len > 3*buffer->size )
|
if( buffer->colorsPerLed*led + len > buffer->colorsPerLed*buffer->size )
|
||||||
{
|
{
|
||||||
return luaL_error(L, "string size will exceed strip length");
|
return luaL_error(L, "string size will exceed strip length");
|
||||||
}
|
}
|
||||||
|
|
||||||
c_memcpy(&buffer->values[3*led], buf, len);
|
c_memcpy(&buffer->values[buffer->colorsPerLed*led], buf, len);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer->values[3*led+0] = luaL_checkinteger(L, 3);
|
int i;
|
||||||
buffer->values[3*led+1] = luaL_checkinteger(L, 4);
|
for (i = 0; i < buffer->colorsPerLed; i++)
|
||||||
buffer->values[3*led+2] = luaL_checkinteger(L, 5);
|
{
|
||||||
|
buffer->values[buffer->colorsPerLed*led+i] = luaL_checkinteger(L, 3+i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_size(lua_State* L) {
|
static int ws2812_buffer_size(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
||||||
|
@ -263,20 +239,13 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_size(lua_State* L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ICACHE_FLASH_ATTR ws2812_buffer_write(lua_State* L) {
|
static int ws2812_buffer_write(lua_State* L) {
|
||||||
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
|
||||||
const uint8_t pin = luaL_checkinteger(L, 2);
|
|
||||||
|
|
||||||
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
|
||||||
|
|
||||||
// Initialize the output pin
|
|
||||||
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
|
|
||||||
platform_gpio_write(pin, 0);
|
|
||||||
|
|
||||||
// Send the buffer
|
// Send the buffer
|
||||||
ets_intr_lock();
|
ws2812_write(buffer->values, buffer->colorsPerLed*buffer->size);
|
||||||
ws2812_write(pin_num[pin], &buffer->values[0], 3*buffer->size);
|
|
||||||
ets_intr_unlock();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -295,9 +264,9 @@ static const LUA_REG_TYPE ws2812_buffer_map[] =
|
||||||
|
|
||||||
static const LUA_REG_TYPE ws2812_map[] =
|
static const LUA_REG_TYPE ws2812_map[] =
|
||||||
{
|
{
|
||||||
{ LSTRKEY( "writergb" ), LFUNCVAL( ws2812_writergb )},
|
|
||||||
{ LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )},
|
{ LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )},
|
||||||
{ LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )},
|
{ LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )},
|
||||||
|
{ LSTRKEY( "init" ), LFUNCVAL( ws2812_init )},
|
||||||
{ LNILKEY, LNILVAL}
|
{ LNILKEY, LNILVAL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,164 @@
|
||||||
# WS2812 Module
|
# WS2812 Module
|
||||||
| Since | Origin / Contributor | Maintainer | Source |
|
| Since | Origin / Contributor | Maintainer | Source |
|
||||||
| :----- | :-------------------- | :---------- | :------ |
|
| :----- | :-------------------- | :---------- | :------ |
|
||||||
| 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel) | [Till Klocke](https://github.com/dereulenspiegel) | [ws2812.c](../../../app/modules/ws2812.c)|
|
| 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel), [Thomas Soëte](https://github.com/Alkorin) | [Till Klocke](https://github.com/dereulenspiegel) | [ws2812.c](../../../app/modules/ws2812.c)|
|
||||||
|
|
||||||
|
ws2812 is a library to handle ws2812-like led strips.
|
||||||
|
It works at least on WS2812, WS2812b, APA104, SK6812 (RGB or RGBW).
|
||||||
|
|
||||||
## ws2812.write()
|
The library uses UART1 routed on GPIO2 (Pin D4 on NodeMCU DEVKIT) to
|
||||||
Send GRB data in 8 bits to a WS2812 chain.
|
generate the bitstream.
|
||||||
|
|
||||||
#### Syntax
|
## ws2812.init()
|
||||||
`ws2812.writegrb(pin, string)`
|
Initialize UART1 and GPIO2, should be called once and before write()
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
- `pin` is ignored. Only works with GPIO2.
|
none
|
||||||
- `string` payload to be sent to one or more WS2812 LEDs.
|
|
||||||
It should be composed from a GRB triplet per element.
|
|
||||||
- `G1` the first pixel's Green channel (0-255)
|
|
||||||
- `R1` the first pixel's Red channel (0-255)
|
|
||||||
- `B1` the first pixel's Blue channel (0-255)<br />
|
|
||||||
... You can connect a lot of WS2812 ...
|
|
||||||
- `G2`, `R2`, `B2` are the next WS2812's Green, Red, and Blue channel parameters
|
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
`nil`
|
`nil`
|
||||||
|
|
||||||
```lua
|
## ws2812.write()
|
||||||
g = 0
|
Send data to a led strip using its native format which is generally Green,Red,Blue for RGB strips
|
||||||
r = 255
|
and Green,Red,Blue,White for RGBW strips.
|
||||||
b = 0
|
|
||||||
leds_grb = string.char(g,r,b, g,r,b)
|
|
||||||
ws2812.write(2, leds_grb) -- turn two WS2812Bs to red, connected to pin GPIO2
|
|
||||||
```
|
|
||||||
|
|
||||||
## ws2812.writergb()
|
|
||||||
Send GRB data in 8bits to a WS2812 chain.
|
|
||||||
|
|
||||||
#### Syntax
|
#### Syntax
|
||||||
`ws2812.writergb(pin, string)`
|
`ws2812.write(string)`
|
||||||
|
|
||||||
#### Parameters
|
#### Parameters
|
||||||
- `pin` is ignored. Only works with GPIO2.
|
- `string` payload to be sent to one or more WS2812 like leds.
|
||||||
- `string` payload to be sent to one or more WS2812 LEDs.
|
|
||||||
It should be composed from an RGB triplet per element.
|
|
||||||
- `R1` the first pixel's Red channel (0-255)
|
|
||||||
- `G1` the first pixel's Green channel (0-255)
|
|
||||||
- `B1` the first pixel's Blue channel (0-255)<br />
|
|
||||||
... You can connect a lot of WS2812 ...
|
|
||||||
- `R2`, `G2`, `B2` are the next WS2812's Red, Green, and Blue channel parameters
|
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
`nil`
|
`nil`
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
```lua
|
```lua
|
||||||
leds_rgb = string.char(255,0,0, 0,255,0, 0,0,255)
|
ws2812.init()
|
||||||
ws2812.writergb(2, leds_rgb) -- turn three WS2812Bs to red, green, and blue respectively
|
ws2812.write(string.char(255,0,0,255,0,0) -- turn the two first RGB leds to green
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ws2812.init()
|
||||||
|
ws2812.write(string.char(0,0,0,255,0,0,0,255) -- turn the two first RGBW leds to white
|
||||||
|
```
|
||||||
|
|
||||||
|
# Buffer module
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
Led chaser with a RGBW strip
|
||||||
|
```lua
|
||||||
|
local i, b = 0, ws2812.newBuffer(300, 4); b:fill(0,0,0,0); tmr.alarm(0, 50, 1, function()
|
||||||
|
i=i+1
|
||||||
|
b:fade(2)
|
||||||
|
b:set(i%b:size()+1, 0, 0, 0, 255)
|
||||||
|
b:write()
|
||||||
|
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:get(2) -- return the color of the second led
|
||||||
|
```
|
||||||
|
## 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` bytes of the color
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
buffer:set(1, 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
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip
|
||||||
|
```
|
||||||
|
## ws2812.buffer:fade()
|
||||||
|
Divide each byte of each led by the given value. Useful for a fading effect
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`buffer:fade(value)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `value` value by which divide each byte
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
buffer:fade(2)
|
||||||
|
```
|
||||||
|
## ws2812.buffer:write()
|
||||||
|
Output the buffer to the led strip
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`buffer:write()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue