Commit 136e0973 authored by Nathaniel Wesley Filardo's avatar Nathaniel Wesley Filardo
Browse files

Merge dev into release

While we intend our release strategy to be that we just fast-forward our
`release` branch to `dev`, things have come a little off the wheels.
This is a "git merge -s recursive -X theirs" of `dev` into `release`
instead.
parents 4f679277 c212b30a
...@@ -23,6 +23,18 @@ else ...@@ -23,6 +23,18 @@ else
# MAKEFLAGS += --silent -w # MAKEFLAGS += --silent -w
endif # $(V)==1 endif # $(V)==1
# Validate LUA setting
ifeq ("$(LUA)","53")
# ok
else ifeq ("$(LUA)","51")
$(error Your variable LUA="$(LUA)" looks like you probably want \
app/lua/luac_cross/Makefile instead)
else
$(error Expected environment variable "LUA" to be "53", not "$(LUA)")
endif
DEBUG ?= DEBUG ?=
ifeq ("$(DEBUG)","1") ifeq ("$(DEBUG)","1")
FLAVOR = debug FLAVOR = debug
......
...@@ -102,7 +102,7 @@ static void DumpNumber (lua_Number x, DumpState *D) { ...@@ -102,7 +102,7 @@ static void DumpNumber (lua_Number x, DumpState *D) {
** 0TTTNNNN or 1TTTNNNN (1NNNNNNN)* 0NNNNNNN ** 0TTTNNNN or 1TTTNNNN (1NNNNNNN)* 0NNNNNNN
*/ */
static void DumpIntTT (lu_byte tt, lua_Integer y, DumpState *D) { static void DumpIntTT (lu_byte tt, lua_Integer y, DumpState *D) {
int x = y < 0 ? -(y + 1) : y; lua_Integer x = y < 0 ? -(y + 1) : y;
lu_byte buf[sizeof(lua_Integer) + 3]; lu_byte buf[sizeof(lua_Integer) + 3];
lu_byte *b = buf + sizeof(buf) - 1; lu_byte *b = buf + sizeof(buf) - 1;
*b-- = x & 0x7f; x >>= 7; *b-- = x & 0x7f; x >>= 7;
......
...@@ -134,12 +134,17 @@ typedef union Value { ...@@ -134,12 +134,17 @@ typedef union Value {
#define TValuefields Value value_; int tt_ #define TValuefields Value value_; int tt_
#ifdef LUA_USE_ESP
# pragma pack(4)
#endif
typedef struct lua_TValue { typedef struct lua_TValue {
TValuefields; TValuefields;
} TValue; } TValue;
#ifdef LUA_USE_ESP
# pragma pack()
#endif
/* macro defining a nil value */ /* macro defining a nil value */
#define NILCONSTANT {NULL}, LUA_TNIL #define NILCONSTANT {NULL}, LUA_TNIL
......
...@@ -85,9 +85,17 @@ ...@@ -85,9 +85,17 @@
# define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE # define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#endif #endif
#endif #endif
# define LUA_INT_TYPE LUA_INT_INT #ifdef LUA_NUMBER_64BITS
# define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
# define LUA_INT_TYPE LUA_INT_LONGLONG
#else
# define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT # define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
//# define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE # define LUA_INT_TYPE LUA_INT_INT
#endif
#ifdef LUA_NUMBER_INTEGRAL
#error LUA_NUMBER_INTEGRAL is not supported in LUA5.3 builds
#endif
/* /*
** Configuration for Paths. ** Configuration for Paths.
......
...@@ -17,6 +17,7 @@ GEN_LIBS = libapp.a ...@@ -17,6 +17,7 @@ GEN_LIBS = libapp.a
endif endif
STD_CFLAGS=-std=gnu11
############################################################# #############################################################
# Configuration i.e. compile options etc. # Configuration i.e. compile options etc.
......
...@@ -42,6 +42,8 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; ...@@ -42,6 +42,8 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#include "sys/socket.h" #include "sys/socket.h"
#include "sys/espconn_mbedtls.h" #include "sys/espconn_mbedtls.h"
#include "lwip/app/espconn_tcp.h"
static os_event_t lwIPThreadQueue[lwIPThreadQueueLen]; static os_event_t lwIPThreadQueue[lwIPThreadQueueLen];
static bool lwIPThreadFlag = false; static bool lwIPThreadFlag = false;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "ets_sys.h" #include "ets_sys.h"
#include "os_type.h" #include "os_type.h"
#include <ctype.h>
#include "lwip/mem.h" #include "lwip/mem.h"
#include "sys/socket.h" #include "sys/socket.h"
...@@ -42,6 +43,9 @@ ...@@ -42,6 +43,9 @@
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif #endif
void *pvPortZalloc (size_t sz, const char *, unsigned);
void vPortFree (void *p, const char *, unsigned);
/** The global array of available sockets */ /** The global array of available sockets */
static lwIP_sock sockets[NUM_SOCKETS]; static lwIP_sock sockets[NUM_SOCKETS];
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include "platform.h" #include "platform.h"
#include "user_interface.h" #include "user_interface.h"
#include "pixbuf.h"
#define NOP asm volatile(" nop \n\t") #define NOP asm volatile(" nop \n\t")
...@@ -79,12 +81,25 @@ static int apa102_write(lua_State* L) { ...@@ -79,12 +81,25 @@ static int apa102_write(lua_State* L) {
MOD_CHECK_ID(gpio, clock_pin); MOD_CHECK_ID(gpio, clock_pin);
uint32_t alt_clock_pin = pin_num[clock_pin]; uint32_t alt_clock_pin = pin_num[clock_pin];
size_t buf_len; const char *buf;
const char *buf = luaL_checklstring(L, 3, &buf_len); uint32_t nbr_frames;
uint32_t nbr_frames = buf_len / 4;
if (nbr_frames > 100000) { switch(lua_type(L, 3)) {
return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark."); 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");
} }
// Initialize the output pins // Initialize the output pins
......
...@@ -32,7 +32,7 @@ static int dht_lapi_read( lua_State *L ) ...@@ -32,7 +32,7 @@ static int dht_lapi_read( lua_State *L )
{ {
unsigned id = luaL_checkinteger( L, 1 ); unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id ); MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read_universal(id) ); lua_pushinteger( L, dht_read(id, DHT_NON11) );
aux_read( L ); aux_read( L );
return 5; return 5;
} }
...@@ -42,7 +42,17 @@ static int dht_lapi_read11( lua_State *L ) ...@@ -42,7 +42,17 @@ static int dht_lapi_read11( lua_State *L )
{ {
unsigned id = luaL_checkinteger( L, 1 ); unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id ); MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read11(id) ); lua_pushinteger( L, dht_read(id, DHT11) );
aux_read( L );
return 5;
}
// Lua: status, temp, humi, tempdec, humidec = dht.read12( id ))
static int dht_lapi_read12( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read(id, DHT12) );
aux_read( L ); aux_read( L );
return 5; return 5;
} }
...@@ -52,48 +62,17 @@ static int dht_lapi_readxx( lua_State *L ) ...@@ -52,48 +62,17 @@ static int dht_lapi_readxx( lua_State *L )
{ {
unsigned id = luaL_checkinteger( L, 1 ); unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id ); MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read(id) ); lua_pushinteger( L, dht_read(id, DHT22) );
aux_read( L ); aux_read( L );
return 5; return 5;
} }
// // Lua: result = dht.humidity()
// static int dht_lapi_humidity( lua_State *L )
// {
// lua_pushnumber( L, dht_getHumidity() );
// return 1;
// }
// // Lua: result = dht.humiditydecimal()
// static int dht_lapi_humiditydecimal( lua_State *L )
// {
// double value = dht_getHumidity();
// int result = (int)((value - (int)value) * 1000);
// lua_pushnumber( L, result );
// return 1;
// }
// // Lua: result = dht.temperature()
// static int dht_lapi_temperature( lua_State *L )
// {
// lua_pushnumber( L, dht_getTemperature() );
// return 1;
// }
// // Lua: result = dht.temperaturedecimal()
// static int dht_lapi_temperaturedecimal( lua_State *L )
// {
// double value = dht_getTemperature();
// int result = (int)((value - (int)value) * 1000);
// lua_pushnumber( L, result );
// return 1;
// }
// Module function map // Module function map
LROT_BEGIN(dht, NULL, 0) LROT_BEGIN(dht, NULL, 0)
LROT_FUNCENTRY( read, dht_lapi_read ) LROT_FUNCENTRY( read, dht_lapi_read )
LROT_FUNCENTRY( read11, dht_lapi_read11 ) LROT_FUNCENTRY( read11, dht_lapi_read11 )
LROT_FUNCENTRY( readxx, dht_lapi_readxx ) LROT_FUNCENTRY( read12, dht_lapi_read12 )
LROT_FUNCENTRY( readxx, dht_lapi_read )
LROT_NUMENTRY( OK, DHTLIB_OK ) LROT_NUMENTRY( OK, DHTLIB_OK )
LROT_NUMENTRY( ERROR_CHECKSUM, DHTLIB_ERROR_CHECKSUM ) LROT_NUMENTRY( ERROR_CHECKSUM, DHTLIB_ERROR_CHECKSUM )
LROT_NUMENTRY( ERROR_TIMEOUT, DHTLIB_ERROR_TIMEOUT ) LROT_NUMENTRY( ERROR_TIMEOUT, DHTLIB_ERROR_TIMEOUT )
......
This diff is collapsed.
...@@ -435,12 +435,19 @@ static int pipe_reader(lua_State *L) { ...@@ -435,12 +435,19 @@ static int pipe_reader(lua_State *L) {
return 1; return 1;
} }
// return number of records
static int pipe_nrec (lua_State *L) {
lua_pushinteger(L, lua_objlen(L, 1) - 1);
return 1;
}
LROT_BEGIN(pipe_funcs, NULL, 0) LROT_BEGIN(pipe_funcs, NULL, 0)
LROT_FUNCENTRY( __len, pipe__len ) LROT_FUNCENTRY( __len, pipe__len )
LROT_FUNCENTRY( __tostring, pipe__tostring ) LROT_FUNCENTRY( __tostring, pipe__tostring )
LROT_FUNCENTRY( read, pipe_read ) LROT_FUNCENTRY( read, pipe_read )
LROT_FUNCENTRY( reader, pipe_reader ) LROT_FUNCENTRY( reader, pipe_reader )
LROT_FUNCENTRY( unread, pipe_unread ) LROT_FUNCENTRY( unread, pipe_unread )
LROT_FUNCENTRY( nrec, pipe_nrec )
LROT_END(pipe_funcs, NULL, 0) LROT_END(pipe_funcs, NULL, 0)
/* Using a index func is needed because the write method is at pipe[1] */ /* Using a index func is needed because the write method is at pipe[1] */
......
#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);
#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
...@@ -194,6 +194,7 @@ static int softuart_init(softuart_t *s) ...@@ -194,6 +194,7 @@ static int softuart_init(softuart_t *s)
} }
return platform_gpio_register_intr_hook(mask, softuart_intr_handler); return platform_gpio_register_intr_hook(mask, softuart_intr_handler);
} }
return 1;
} }
...@@ -206,30 +207,27 @@ static int softuart_setup(lua_State *L) ...@@ -206,30 +207,27 @@ static int softuart_setup(lua_State *L)
NODE_DBG("[SoftUART]: setup called\n"); NODE_DBG("[SoftUART]: setup called\n");
baudrate = (uint32_t)luaL_checkinteger(L, 1); // Get Baudrate from baudrate = (uint32_t)luaL_checkinteger(L, 1); // Get Baudrate from
luaL_argcheck(L, (baudrate > 0 && baudrate < 230400), 1, "Invalid baud rate"); luaL_argcheck(L, (baudrate > 0 && baudrate < 230400), 1, "Invalid baud rate");
lua_remove(L, 1); // Remove baudrate argument from stack
if (lua_gettop(L) == 2) { // 2 arguments: 1st can be nil if (lua_isnoneornil(L, 2)) {
if (lua_isnil(L, 1)) {
tx_gpio_id = 0xFF; tx_gpio_id = 0xFF;
} else { } else {
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1); tx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0) luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
, 2, "Invalid SoftUART tx GPIO"); , 2, "Invalid SoftUART tx GPIO");
} }
rx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
if (lua_isnoneornil(L, 3)) {
rx_gpio_id = 0xFF;
} else {
rx_gpio_id = (uint8_t)luaL_checkinteger(L, 3);
luaL_argcheck(L, (platform_gpio_exists(rx_gpio_id) && rx_gpio_id != 0) luaL_argcheck(L, (platform_gpio_exists(rx_gpio_id) && rx_gpio_id != 0)
, 3, "Invalid SoftUART rx GPIO"); , 3, "Invalid SoftUART rx GPIO");
luaL_argcheck(L, softuart_gpio_instances[rx_gpio_id] == NULL luaL_argcheck(L, softuart_gpio_instances[rx_gpio_id] == NULL
, 3, "SoftUART rx already configured on the pin"); , 3, "SoftUART rx already configured on the pin");
}
} else if (lua_gettop(L) == 1) { // 1 argument: transmit part only
rx_gpio_id = 0xFF;
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1);
luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
, 2, "Invalid SoftUART tx GPIO");
} else {
// SoftUART object without receive and transmit part would be useless // SoftUART object without receive and transmit part would be useless
return luaL_error(L, "Not enough arguments"); if ((rx_gpio_id == 0xFF) && (tx_gpio_id == 0xFF)) {return luaL_error(L, "Not enough arguments");}
}
softuart = (softuart_t*)lua_newuserdata(L, sizeof(softuart_t)); softuart = (softuart_t*)lua_newuserdata(L, sizeof(softuart_t));
softuart->pin_rx = rx_gpio_id; softuart->pin_rx = rx_gpio_id;
......
...@@ -12,18 +12,37 @@ ...@@ -12,18 +12,37 @@
//#define NODE_DEBUG //#define NODE_DEBUG
#include <stdint.h> #include <stdint.h>
#include "os_type.h"
#include "osapi.h"
#include "sections.h"
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lmem.h"
#include "platform.h" #include "platform.h"
#include "task/task.h" #include "task/task.h"
#include "hw_timer.h" #include "hw_timer.h"
#include "user_interface.h" #include "user_interface.h"
#ifdef LUA_USE_MODULES_SOMFY
#if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
#error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using SOMFY module
#endif
#endif
#ifdef NODE_DEBUG
#define PULLUP PLATFORM_GPIO_PULLUP
#define OUTPUT PLATFORM_GPIO_OUTPUT
#define HIGH PLATFORM_GPIO_HIGH
#define LOW PLATFORM_GPIO_LOW
#define MODE_TP1 platform_gpio_mode( 3, OUTPUT, PULLUP ); // GPIO 00
#define SET_TP1 platform_gpio_write(3, HIGH);
#define CLR_TP1 platform_gpio_write(3, LOW);
#define WAIT os_delay_us(1);
#else
#define MODE_TP1
#define SET_TP1
#define CLR_TP1
#define WAIT
#endif
#define SYMBOL 640 // symbol width in microseconds #define SYMBOL 640 // symbol width in microseconds
#define SOMFY_UP 0x2 #define SOMFY_UP 0x2
#define SOMFY_STOP 0x1 #define SOMFY_STOP 0x1
...@@ -33,24 +52,27 @@ ...@@ -33,24 +52,27 @@
#define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0)) #define DIRECT_WRITE_LOW(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 0))
#define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1)) #define DIRECT_WRITE_HIGH(pin) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), 1))
// ----------------------------------------------------------------------------------------------------//
// ------------------------------- transmitter part ---------------------------------------------------//
// ----------------------------------------------------------------------------------------------------//
static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf" static const os_param_t TIMER_OWNER = 0x736f6d66; // "somf"
static task_handle_t done_taskid; static task_handle_t SendDone_taskid;
static uint8_t pin; static uint8_t TxPin;
static uint8_t frame[7]; static uint8_t frame[7];
static uint8_t sync; static uint8_t sync;
static uint8_t repeat; static uint8_t repeat;
//static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // in us //static uint32_t delay[10] = {9415, 89565, 4*SYMBOL, 4*SYMBOL, 4*SYMBOL, 4550, SYMBOL, SYMBOL, SYMBOL, 30415}; // inc us
// the `delay` array of constants must be in RAM as it is accessed from the timer interrupt // the `delay` array of constants must be in RAM as it is accessed from the timer interrupt
static const RAM_CONST_SECTION_ATTR uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate) static const uint32_t delay[10] = {US_TO_RTC_TIMER_TICKS(9415), US_TO_RTC_TIMER_TICKS(89565), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4*SYMBOL), US_TO_RTC_TIMER_TICKS(4550), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(SYMBOL), US_TO_RTC_TIMER_TICKS(30415)}; // in ticks (no need to recalculate)
static uint8_t repeatindex; static uint8_t repeatindex;
static uint8_t signalindex; static uint8_t signalindex;
static uint8_t subindex; static uint8_t subindex;
static uint8_t bitcondition; static uint8_t bitcondition;
int lua_done_ref; // callback when transmission is done static int lua_done_ref = LUA_NOREF; // callback when transmission is done
void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) { void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) {
// NODE_DBG("remote: %x\n", remote); // NODE_DBG("remote: %x\n", remote);
...@@ -86,15 +108,6 @@ void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code) ...@@ -86,15 +108,6 @@ void buildFrame(uint8_t *frame, uint64_t remote, uint8_t button, uint16_t code)
// NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]); // NODE_DBG("Obfuscated:\t\t%02x %02x %02x %02x %02x %02x %02x\n", frame[0], frame[1], frame[2], frame[3], frame[4], frame[5], frame[6]);
} }
static void somfy_transmissionDone (task_param_t arg)
{
lua_State *L = lua_getstate();
lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
luaL_unref (L, LUA_REGISTRYINDEX, lua_done_ref);
lua_done_ref = LUA_NOREF;
luaL_pcallx (L, 0, 0);
}
static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
(void) p; (void) p;
// NODE_DBG("%d\t%d\n", signalindex, subindex); // NODE_DBG("%d\t%d\n", signalindex, subindex);
...@@ -103,7 +116,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -103,7 +116,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
subindex = 0; subindex = 0;
if(sync == 2) { // Only with the first frame. if(sync == 2) { // Only with the first frame.
//Wake-up pulse & Silence //Wake-up pulse & Silence
DIRECT_WRITE_HIGH(pin); DIRECT_WRITE_HIGH(TxPin);
signalindex++; signalindex++;
// delayMicroseconds(9415); // delayMicroseconds(9415);
break; break;
...@@ -112,7 +125,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -112,7 +125,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
} }
case 1: case 1:
//Wake-up pulse & Silence //Wake-up pulse & Silence
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
signalindex++; signalindex++;
// delayMicroseconds(89565); // delayMicroseconds(89565);
break; break;
...@@ -122,24 +135,24 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -122,24 +135,24 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
// a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse // a "useless" step to allow repeating the hardware sync w/o the silence after wake-up pulse
case 3: case 3:
// Hardware sync: two sync for the first frame, seven for the following ones. // Hardware sync: two sync for the first frame, seven for the following ones.
DIRECT_WRITE_HIGH(pin); DIRECT_WRITE_HIGH(TxPin);
signalindex++; signalindex++;
// delayMicroseconds(4*SYMBOL); // delayMicroseconds(4*SYMBOL);
break; break;
case 4: case 4:
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
subindex++; subindex++;
if (subindex < sync) {signalindex--;} else {signalindex++;} if (subindex < sync) {signalindex--;} else {signalindex++;}
// delayMicroseconds(4*SYMBOL); // delayMicroseconds(4*SYMBOL);
break; break;
case 5: case 5:
// Software sync // Software sync
DIRECT_WRITE_HIGH(pin); DIRECT_WRITE_HIGH(TxPin);
signalindex++; signalindex++;
// delayMicroseconds(4550); // delayMicroseconds(4550);
break; break;
case 6: case 6:
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
signalindex++; signalindex++;
subindex=0; subindex=0;
// delayMicroseconds(SYMBOL); // delayMicroseconds(SYMBOL);
...@@ -148,10 +161,10 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -148,10 +161,10 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
//Data: bits are sent one by one, starting with the MSB. //Data: bits are sent one by one, starting with the MSB.
bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1; bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1;
if(bitcondition) { if(bitcondition) {
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
} }
else { else {
DIRECT_WRITE_HIGH(pin); DIRECT_WRITE_HIGH(TxPin);
} }
signalindex++; signalindex++;
// delayMicroseconds(SYMBOL); // delayMicroseconds(SYMBOL);
...@@ -159,10 +172,10 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -159,10 +172,10 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
case 8: case 8:
//Data: bits are sent one by one, starting with the MSB. //Data: bits are sent one by one, starting with the MSB.
if(bitcondition) { if(bitcondition) {
DIRECT_WRITE_HIGH(pin); DIRECT_WRITE_HIGH(TxPin);
} }
else { else {
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
} }
if (subindex<56) { if (subindex<56) {
...@@ -175,19 +188,19 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -175,19 +188,19 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
// delayMicroseconds(SYMBOL); // delayMicroseconds(SYMBOL);
break; break;
case 9: case 9:
DIRECT_WRITE_LOW(pin); DIRECT_WRITE_LOW(TxPin);
signalindex++; signalindex++;
// delayMicroseconds(30415); // Inter-frame silence // delayMicroseconds(30415); // Inter-frame silence
break; break;
case 10: case 10:
repeatindex++; repeatindex++;
if (repeatindex<repeat) { if (repeatindex<repeat) {
DIRECT_WRITE_HIGH(pin); //start repeat from step 3, but don't wait as after step 1 DIRECT_WRITE_HIGH(TxPin); //start repeat from step 3, but don't wait as after step 1
signalindex=4; subindex=0; sync=7; signalindex=4; subindex=0; sync=7;
} else { } else {
platform_hw_timer_close(TIMER_OWNER); platform_hw_timer_close(TIMER_OWNER);
if (lua_done_ref != LUA_NOREF) { if (lua_done_ref != LUA_NOREF) {
task_post_low (done_taskid, (task_param_t)0); task_post_low (SendDone_taskid, (task_param_t)0);
} }
} }
break; break;
...@@ -197,28 +210,258 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) { ...@@ -197,28 +210,258 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
} }
} }
static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rolling_code, num_repeat, callback // ----------------------------------------------------------------------------------------------------//
if (!lua_isnumber(L, 4)) { // ------------------------------- receiver part ------------------------------------------------------//
return luaL_error(L, "wrong arg range"); // ----------------------------------------------------------------------------------------------------//
#define TOLERANCE_MIN 0.7
#define TOLERANCE_MAX 1.3
static const uint32_t tempo_wakeup_pulse = 9415;
static const uint32_t tempo_wakeup_silence = 89565;
// static const uint32_t tempo_synchro_hw = SYMBOL*4;
static const uint32_t tempo_synchro_hw_min = SYMBOL*4*TOLERANCE_MIN;
static const uint32_t tempo_synchro_hw_max = SYMBOL*4*TOLERANCE_MAX;
// static const uint32_t k_tempo_synchro_sw = 4550;
static const uint32_t tempo_synchro_sw_min = 4550*TOLERANCE_MIN;
static const uint32_t tempo_synchro_sw_max = 4550*TOLERANCE_MAX;
// static const uint32_t tempo_half_symbol = SYMBOL;
static const uint32_t tempo_half_symbol_min = SYMBOL*TOLERANCE_MIN;
static const uint32_t tempo_half_symbol_max = SYMBOL*TOLERANCE_MAX;
// static const uint32_t tempo_symbol = SYMBOL*2;
static const uint32_t tempo_symbol_min = SYMBOL*2*TOLERANCE_MIN;
static const uint32_t tempo_symbol_max = SYMBOL*2*TOLERANCE_MAX;
static const uint32_t tempo_inter_frame_gap = 30415;
static int16_t bitMin = SYMBOL*TOLERANCE_MIN;
typedef enum {
waiting_synchro = 0,
receiving_data = 1,
complete = 2
}
t_status;
static struct SomfyRx_t
{
t_status status;
uint8_t cpt_synchro_hw;
uint8_t cpt_bits;
uint8_t previous_bit;
bool waiting_half_symbol;
uint8_t payload[9];
} SomfyRx;
static task_handle_t DataReady_taskid;
static uint8_t RxPin;
static uint8_t IntBitmask;
static int lua_dataready_ref = LUA_NOREF;
static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status) {
// This function really is running at interrupt level with everything
// else masked off. It should take as little time as necessary.
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
if ((gpio_status & IntBitmask) == 0) {
return ret_gpio_status;
}
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & IntBitmask);
uint32_t actMicros = system_get_time();
ret_gpio_status &= ~(IntBitmask);
static unsigned long lastMicros = 0;
unsigned long bitMicros;
bitMicros = actMicros-lastMicros;
if ( bitMicros < bitMin ) {
// too short - may be false interrupt due to glitch or false protocol -> ignore
return ret_gpio_status; // abort IRQ
} }
pin = luaL_checkinteger(L, 1); lastMicros = actMicros;
switch(SomfyRx.status) {
case waiting_synchro:
if (bitMicros > tempo_synchro_hw_min && bitMicros < tempo_synchro_hw_max) {
SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
++SomfyRx.cpt_synchro_hw;
CLR_TP1
}
else if (bitMicros > tempo_synchro_sw_min && bitMicros < tempo_synchro_sw_max && SomfyRx.cpt_synchro_hw >= 4) {
SET_TP1 //WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
memset( &SomfyRx, 0, sizeof( SomfyRx) );
SomfyRx.status = receiving_data;
} else {
SomfyRx.cpt_synchro_hw = 0;
}
break;
case receiving_data:
if (bitMicros > tempo_symbol_min && bitMicros < tempo_symbol_max && !SomfyRx.waiting_half_symbol) {
SET_TP1
SomfyRx.previous_bit = 1 - SomfyRx.previous_bit;
SomfyRx.payload[SomfyRx.cpt_bits/8] += SomfyRx.previous_bit << (7 - SomfyRx.cpt_bits%8);
++SomfyRx.cpt_bits;
} else if (bitMicros > tempo_half_symbol_min && bitMicros < tempo_half_symbol_max) {
SET_TP1 WAIT CLR_TP1 WAIT SET_TP1 WAIT CLR_TP1 WAIT SET_TP1
if (SomfyRx.waiting_half_symbol) {
SomfyRx.waiting_half_symbol = false;
SomfyRx.payload[SomfyRx.cpt_bits/8] += SomfyRx.previous_bit << (7 - SomfyRx.cpt_bits%8);
++SomfyRx.cpt_bits;
} else {
SomfyRx.waiting_half_symbol = true;
}
} else {
SomfyRx.cpt_synchro_hw = 0;
SomfyRx.status = waiting_synchro;
}
CLR_TP1
break;
default:
break;
}
if (SomfyRx.status == receiving_data && SomfyRx.cpt_bits == 80) { //56) { experiment
task_post_high(DataReady_taskid, (task_param_t)0);
SomfyRx.status = waiting_synchro;
}
return ret_gpio_status;
}
static void somfy_decode (os_param_t param, uint8_t prio)
{
#ifdef NODE_DEBUG
NODE_DBG("Payload:\t");
for(uint8_t i = 0; i < 10; i++) {
NODE_DBG("%02x ", SomfyRx.payload[i]);
}
NODE_DBG("\n");
#endif
// Deobfuscation
uint8_t frame[10];
frame[0] = SomfyRx.payload[0];
for(int i = 1; i < 7; ++i) frame[i] = SomfyRx.payload[i] ^ SomfyRx.payload[i-1];
frame[7] = SomfyRx.payload[7] ^ SomfyRx.payload[0];
for(int i = 8; i < 10; ++i) frame[i] = SomfyRx.payload[i] ^ SomfyRx.payload[i-1];
#ifdef NODE_DEBUG
NODE_DBG("Frame:\t");
for(uint8_t i = 0; i < 10; i++) {
NODE_DBG("%02x ", frame[i]);
}
NODE_DBG("\n");
#endif
// Checksum check
uint8_t cksum = 0;
for(int i = 0; i < 7; ++i) cksum = cksum ^ frame[i] ^ (frame[i] >> 4);
cksum = cksum & 0x0F;
if (cksum != 0) {
NODE_DBG("Checksum incorrect!\n");
return;
}
unsigned long rolling_code = (frame[2] << 8) || frame[3];
unsigned long address = ((unsigned long)frame[4] << 16) || (frame[5] << 8) || frame[6];
if (lua_dataready_ref == LUA_NOREF)
return;
lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_dataready_ref);
lua_pushinteger(L, address);
lua_pushinteger(L, frame[1] >> 4);
lua_pushinteger(L, rolling_code);
lua_pushlstring(L, frame, 10);
luaL_pcallx(L, 4, 0);
}
// ----------------------------------------------------------------------------------------------------//
// ------------------------------- Lua part -----------------------------------------------------------//
// ----------------------------------------------------------------------------------------------------//
static inline void register_lua_cb(lua_State* L, int* cb_ref){
int ref=luaL_ref(L, LUA_REGISTRYINDEX);
if( *cb_ref != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
}
*cb_ref = ref;
}
static inline void unregister_lua_cb(lua_State* L, int* cb_ref){
if(*cb_ref != LUA_NOREF){
luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
*cb_ref = LUA_NOREF;
}
}
int somfy_lua_listen(lua_State* L) { // pin, callback
NODE_DBG("[somfy_lua_listen]\n");
#if LUA_VERSION_NUM == 501
if (lua_isnumber(L, 1) && lua_type(L, 2) == LUA_TFUNCTION) {
#else
if (lua_isinteger(L, 1) && lua_type(L, 2) == LUA_TFUNCTION) {
#endif
RxPin = luaL_checkinteger(L, 1);
luaL_argcheck(L, platform_gpio_exists(RxPin) && RxPin>0, 1, "Invalid interrupt pin");
lua_pushvalue(L, 2);
register_lua_cb(L, &lua_dataready_ref);
memset( &SomfyRx, 0, sizeof( SomfyRx) );
IntBitmask = 1 << pin_num[RxPin];
MODE_TP1
NODE_DBG("[somfy_lua_listen] Enabling interrupt on PIN %d\n", RxPin);
platform_gpio_mode(RxPin, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
NODE_DBG("[somfy_lua_listen] platform_gpio_register_intr_hook - pin: %d, mask: %d\n", RxPin, IntBitmask);
platform_gpio_register_intr_hook(IntBitmask, InterruptHandler);
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[RxPin]), GPIO_PIN_INTR_ANYEDGE);
#if LUA_VERSION_NUM == 501
} else if ((lua_isnoneornil(L, 1) || lua_isnumber(L, 1)) && lua_isnoneornil(L, 2)) {
#else
} else if ((lua_isnoneornil(L, 1) || lua_isinteger(L, 1)) && lua_isnoneornil(L, 2)) {
#endif
NODE_DBG("[somfy_lua_listen] Desabling interrupt on PIN %d\n", RxPin);
platform_gpio_mode(RxPin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
unregister_lua_cb(L, &lua_dataready_ref);
RxPin = 0;
} else {
luaL_error(L, "Invalid parameters");
}
return 0;
}
static void somfy_transmissionDone (task_param_t arg)
{
lua_State *L = lua_getstate();
lua_rawgeti (L, LUA_REGISTRYINDEX, lua_done_ref);
unregister_lua_cb (L, &lua_done_ref);
luaL_pcallx (L, 0, 0);
}
int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rolling_code, num_repeat, callback
TxPin = luaL_checkinteger(L, 1);
uint64_t remote = luaL_checkinteger(L, 2); uint64_t remote = luaL_checkinteger(L, 2);
uint8_t cmd = luaL_checkinteger(L, 3); uint8_t cmd = luaL_checkinteger(L, 3);
uint16_t code = luaL_checkinteger(L, 4); uint16_t code = luaL_checkinteger(L, 4);
repeat=luaL_optint( L, 5, 2 ); repeat=luaL_optint( L, 5, 2 );
luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin"); luaL_argcheck(L, platform_gpio_exists(TxPin), 1, "Invalid pin");
luaL_unref(L, LUA_REGISTRYINDEX, lua_done_ref); if (lua_type(L, 6) == LUA_TFUNCTION) {
if (!lua_isnoneornil(L, 6)) { lua_pushvalue (L, 6);
lua_pushvalue(L, 6); register_lua_cb (L, &lua_done_ref);
lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} else { } else {
lua_done_ref = LUA_NOREF; unregister_lua_cb (L, &lua_done_ref);
} }
MOD_CHECK_ID(gpio, pin); MOD_CHECK_ID(gpio, TxPin);
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP); platform_gpio_mode(TxPin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
buildFrame(frame, remote, cmd, code); buildFrame(frame, remote, cmd, code);
...@@ -233,18 +476,21 @@ static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rollin ...@@ -233,18 +476,21 @@ static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rollin
return 0; return 0;
} }
int luaopen_somfy( lua_State *L ) {
SendDone_taskid = task_get_id((task_callback_t) somfy_transmissionDone);
DataReady_taskid = task_get_id((task_callback_t) somfy_decode);
return 0;
}
// Module function map
LROT_BEGIN(somfy, NULL, 0) LROT_BEGIN(somfy, NULL, 0)
LROT_FUNCENTRY( sendcommand, somfy_lua_sendcommand )
LROT_FUNCENTRY( listen, somfy_lua_listen )
LROT_NUMENTRY( UP, SOMFY_UP ) LROT_NUMENTRY( UP, SOMFY_UP )
LROT_NUMENTRY( DOWN, SOMFY_DOWN ) LROT_NUMENTRY( DOWN, SOMFY_DOWN )
LROT_NUMENTRY( PROG, SOMFY_PROG ) LROT_NUMENTRY( PROG, SOMFY_PROG )
LROT_NUMENTRY( STOP, SOMFY_STOP ) LROT_NUMENTRY( STOP, SOMFY_STOP )
LROT_FUNCENTRY( sendcommand, somfy_lua_sendcommand )
LROT_END(somfy, NULL, 0) LROT_END(somfy, NULL, 0)
int luaopen_somfy( lua_State *L ) {
done_taskid = task_get_id((task_callback_t) somfy_transmissionDone);
return 0;
}
NODEMCU_MODULE(SOMFY, "somfy", somfy, luaopen_somfy); NODEMCU_MODULE(SOMFY, "somfy", somfy, luaopen_somfy);
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include <string.h> #include <string.h>
#include "user_interface.h" #include "user_interface.h"
#include "pixbuf.h"
static inline uint32_t _getCycleCount(void) { static inline uint32_t _getCycleCount(void) {
uint32_t cycles; uint32_t cycles;
__asm__ __volatile__("rsr %0,ccount":"=a" (cycles)); __asm__ __volatile__("rsr %0,ccount":"=a" (cycles));
...@@ -13,8 +15,9 @@ static inline uint32_t _getCycleCount(void) { ...@@ -13,8 +15,9 @@ static inline uint32_t _getCycleCount(void) {
// This algorithm reads the cpu clock cycles to calculate the correct // This algorithm reads the cpu clock cycles to calculate the correct
// pulse widths. It works in both 80 and 160 MHz mode. // 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) { static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, const uint8_t *pixels, size_t length) {
uint8_t *p, *end; const uint8_t *p, *end;
uint8_t phasergb = 0;
p = pixels; p = pixels;
end = p + length; end = p + length;
...@@ -28,6 +31,13 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui ...@@ -28,6 +31,13 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui
register int i; register int i;
register uint8_t pixel = *p++; 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(); ets_intr_lock();
...@@ -55,35 +65,27 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui ...@@ -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") // 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. // Byte triples in the string are interpreted as GRB values.
// WARNING: this function scrambles the input buffer :
// a = string.char(255,0,128)
// tm1829.write(3,a)
// =a.byte()
// (0,255,128)
static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L) static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
{ {
const uint8_t pin = luaL_checkinteger(L, 1); const uint8_t pin = luaL_checkinteger(L, 1);
const uint8_t *pixels;
size_t length; 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 switch(lua_type(L, 3)) {
// constant current command, instead of PWM duty command) case LUA_TSTRING: {
size_t i; pixels = luaL_checklstring(L, 2, &length);
for (i = 0; i < length; i += 3) { break;
buffer[i] = rgb[i]; }
buffer[i + 1] = rgb[i + 1]; case LUA_TUSERDATA: {
buffer[i + 2] = rgb[i + 2]; pixbuf *buffer = pixbuf_from_lua_arg(L, 2);
luaL_argcheck(L, pixbuf_channels(buffer) == 3, 2, "Bad pixbuf format");
// Check for first byte pixels = buffer->values;
if (buffer[i] == 0xff) length = 3 * buffer->npix;
buffer[i] = 0xfe; break;
}
default:
return luaL_argerror(L, 2, "String or pixbuf expected");
} }
// Initialize the output pin and wait a bit // Initialize the output pin and wait a bit
...@@ -91,12 +93,10 @@ static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L) ...@@ -91,12 +93,10 @@ static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
platform_gpio_write(pin, 1); platform_gpio_write(pin, 1);
// Send the buffer // 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 os_delay_us(500); // reset time
free(buffer);
return 0; return 0;
} }
......
...@@ -257,9 +257,9 @@ inline static uint64_t rtc_timer_update(bool do_calibration){ ...@@ -257,9 +257,9 @@ inline static uint64_t rtc_timer_update(bool do_calibration){
void rtc_callback(void *arg){ void rtc_callback(void *arg){
rtc_timer_update(true); rtc_timer_update(true);
if(soft_watchdog > 0){ if(soft_watchdog >= 0){
soft_watchdog--; soft_watchdog--;
if(soft_watchdog == 0) if(soft_watchdog < 0)
system_restart(); system_restart();
} }
} }
...@@ -273,9 +273,8 @@ static int tmr_time( lua_State* L ){ ...@@ -273,9 +273,8 @@ static int tmr_time( lua_State* L ){
// Lua: tmr.softwd( value ) // Lua: tmr.softwd( value )
static int tmr_softwd( lua_State* L ){ static int tmr_softwd( lua_State* L ){
int t = luaL_checkinteger(L, 1); soft_watchdog = luaL_checkinteger(L, 1);
luaL_argcheck(L, t>0 , 2, "invalid time"); // NO check is required as negative Values mean that the timer is disabled.
soft_watchdog = t;
return 0; return 0;
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#include <string.h> #include <string.h>
#include "osapi.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 * 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. * and provides a similar api as the ws2812 module.
...@@ -110,13 +112,28 @@ static int ICACHE_FLASH_ATTR ws2801_init_lua(lua_State* L) { ...@@ -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) { static int ICACHE_FLASH_ATTR ws2801_writergb(lua_State* L) {
size_t length; 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); os_delay_us(10);
ets_intr_lock(); ets_intr_lock();
ws2801_strip(buffer, length); ws2801_strip(values, length);
ets_intr_unlock(); ets_intr_unlock();
......
...@@ -8,14 +8,13 @@ ...@@ -8,14 +8,13 @@
#include "user_interface.h" #include "user_interface.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "osapi.h" #include "osapi.h"
#include "cpu_esp8266_irq.h"
#include "ws2812.h" #include "pixbuf.h"
#define CANARY_VALUE 0x32383132
#define MODE_SINGLE 0 #define MODE_SINGLE 0
#define MODE_DUAL 1 #define MODE_DUAL 1
// Init UART1 to be able to stream WS2812 data to GPIO2 pin // 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 // If DUAL mode is selected, init UART0 to stream to TXD0 as well
// You HAVE to redirect LUA's output somewhere else // You HAVE to redirect LUA's output somewhere else
...@@ -53,12 +52,18 @@ static int ws2812_init(lua_State* L) { ...@@ -53,12 +52,18 @@ static int ws2812_init(lua_State* L) {
return 0; return 0;
} }
// Stream data using UART1 routed to GPIO2 static bool
// ws2812.init() should be called first ws2812_can_write(int uart)
// {
// NODE_DEBUG should not be activated because it also uses UART1 // If something to send for first buffer and enough room
void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) { // in FIFO buffer (we wants to write 4 bytes, so less than
// 124 in the buffer)
return (((READ_PERI_REG(UART_STATUS(uart)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124);
}
static void
ws2812_write_byte(int uart, uint8_t value)
{
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted // 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 // 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
// 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01 // 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
...@@ -68,31 +73,37 @@ void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, c ...@@ -68,31 +73,37 @@ void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, c
// But declared in ".data" section to avoid read penalty from FLASH // 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 }; static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 6) & 3]);
WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 4) & 3]);
WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 2) & 3]);
WRITE_PERI_REG(UART_FIFO(uart), _uartData[(value >> 0) & 3]);
}
// Stream data using UART1 routed to GPIO2
// ws2812.init() should be called first
//
// NODE_DEBUG should not be activated because it also uses UART1
void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
const uint8_t *end = pixels + length; const uint8_t *end = pixels + length;
const uint8_t *end2 = pixels2 + length2; const uint8_t *end2 = pixels2 + length2;
do { /* Fill the UART fifos with IRQs disabled */
// If something to send for first buffer and enough room uint32_t irq_state = esp8266_defer_irqs();
// in FIFO buffer (we wants to write 4 bytes, so less than while ((pixels < end) && ws2812_can_write(1)) {
// 124 in the buffer) ws2812_write_byte(1, *pixels++);
if (pixels < end && (((READ_PERI_REG(UART_STATUS(1)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) { }
uint8_t value = *pixels++; while ((pixels2 < end2) && ws2812_can_write(0)) {
ws2812_write_byte(0, *pixels2++);
}
esp8266_restore_irqs(irq_state);
// Fill the buffer do {
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 6) & 3]); if (pixels < end && ws2812_can_write(1)) {
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 4) & 3]); ws2812_write_byte(1, *pixels++);
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 2) & 3]);
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
} }
// Same for the second buffer // Same for the second buffer
if (pixels2 < end2 && (((READ_PERI_REG(UART_STATUS(0)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT) <= 124)) { if (pixels2 < end2 && ws2812_can_write(0)) {
uint8_t value = *pixels2++; ws2812_write_byte(0, *pixels2++);
// Fill the buffer
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 6) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 4) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 2) & 3]);
WRITE_PERI_REG(UART_FIFO(0), _uartData[(value >> 0) & 3]);
} }
} while(pixels < end || pixels2 < end2); // Until there is still something to send } while(pixels < end || pixels2 < end2); // Until there is still something to send
} }
...@@ -125,14 +136,14 @@ static int ws2812_write(lua_State* L) { ...@@ -125,14 +136,14 @@ static int ws2812_write(lua_State* L) {
} }
else if (type == LUA_TUSERDATA) 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; buffer1 = buffer->values;
length1 = buffer->colorsPerLed*buffer->size; length1 = pixbuf_size(buffer);
} }
else else
{ {
luaL_argerror(L, 1, "ws2812.buffer or string expected"); luaL_argerror(L, 1, "pixbuf or string expected");
} }
// Second optionnal parameter // Second optionnal parameter
...@@ -148,14 +159,14 @@ static int ws2812_write(lua_State* L) { ...@@ -148,14 +159,14 @@ static int ws2812_write(lua_State* L) {
} }
else if (type == LUA_TUSERDATA) 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; buffer2 = buffer->values;
length2 = buffer->colorsPerLed*buffer->size; length2 = pixbuf_size(buffer);
} }
else else
{ {
luaL_argerror(L, 2, "ws2812.buffer or string expected"); luaL_argerror(L, 2, "pixbuf or string expected");
} }
// Send the buffers // Send the buffers
...@@ -164,475 +175,20 @@ static int ws2812_write(lua_State* L) { ...@@ -164,475 +175,20 @@ static int ws2812_write(lua_State* L) {
return 0; 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_BEGIN(ws2812, NULL, 0)
LROT_FUNCENTRY( init, ws2812_init ) 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_FUNCENTRY( write, ws2812_write )
LROT_NUMENTRY( FADE_IN, FADE_IN ) LROT_NUMENTRY( FADE_IN, PIXBUF_FADE_IN ) // BC
LROT_NUMENTRY( FADE_OUT, FADE_OUT ) LROT_NUMENTRY( FADE_OUT, PIXBUF_FADE_OUT ) // BC
LROT_NUMENTRY( MODE_SINGLE, MODE_SINGLE ) LROT_NUMENTRY( MODE_SINGLE, MODE_SINGLE )
LROT_NUMENTRY( MODE_DUAL, MODE_DUAL ) LROT_NUMENTRY( MODE_DUAL, MODE_DUAL )
LROT_NUMENTRY( SHIFT_LOGICAL, SHIFT_LOGICAL ) LROT_NUMENTRY( SHIFT_LOGICAL, PIXBUF_SHIFT_LOGICAL ) // BC
LROT_NUMENTRY( SHIFT_CIRCULAR, SHIFT_CIRCULAR ) LROT_NUMENTRY( SHIFT_CIRCULAR, PIXBUF_SHIFT_CIRCULAR ) // BC
LROT_END(ws2812, NULL, 0) 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 // TODO: Make sure that the GPIO system is initialized
luaL_rometatable(L, "ws2812.buffer", LROT_TABLEREF(ws2812_buffer_map));
return 0; return 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_ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment