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
# MAKEFLAGS += --silent -w
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 ?=
ifeq ("$(DEBUG)","1")
FLAVOR = debug
......
......@@ -102,7 +102,7 @@ static void DumpNumber (lua_Number x, DumpState *D) {
** 0TTTNNNN or 1TTTNNNN (1NNNNNNN)* 0NNNNNNN
*/
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 *b = buf + sizeof(buf) - 1;
*b-- = x & 0x7f; x >>= 7;
......
......@@ -134,12 +134,17 @@ typedef union Value {
#define TValuefields Value value_; int tt_
#ifdef LUA_USE_ESP
# pragma pack(4)
#endif
typedef struct lua_TValue {
TValuefields;
} TValue;
#ifdef LUA_USE_ESP
# pragma pack()
#endif
/* macro defining a nil value */
#define NILCONSTANT {NULL}, LUA_TNIL
......
......@@ -85,9 +85,17 @@
# define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
#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_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.
......
......@@ -17,6 +17,7 @@ GEN_LIBS = libapp.a
endif
STD_CFLAGS=-std=gnu11
#############################################################
# Configuration i.e. compile options etc.
......
......@@ -42,6 +42,8 @@ static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#include "sys/socket.h"
#include "sys/espconn_mbedtls.h"
#include "lwip/app/espconn_tcp.h"
static os_event_t lwIPThreadQueue[lwIPThreadQueueLen];
static bool lwIPThreadFlag = false;
......
......@@ -34,6 +34,7 @@
#include "ets_sys.h"
#include "os_type.h"
#include <ctype.h>
#include "lwip/mem.h"
#include "sys/socket.h"
......@@ -42,6 +43,9 @@
static const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__;
#endif
void *pvPortZalloc (size_t sz, const char *, unsigned);
void vPortFree (void *p, const char *, unsigned);
/** The global array of available sockets */
static lwIP_sock sockets[NUM_SOCKETS];
......
......@@ -5,6 +5,8 @@
#include "platform.h"
#include "user_interface.h"
#include "pixbuf.h"
#define NOP asm volatile(" nop \n\t")
......@@ -79,12 +81,25 @@ static int apa102_write(lua_State* L) {
MOD_CHECK_ID(gpio, clock_pin);
uint32_t alt_clock_pin = pin_num[clock_pin];
size_t buf_len;
const char *buf = luaL_checklstring(L, 3, &buf_len);
uint32_t nbr_frames = buf_len / 4;
if (nbr_frames > 100000) {
return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark.");
const char *buf;
uint32_t nbr_frames;
switch(lua_type(L, 3)) {
case LUA_TSTRING: {
size_t buf_len;
buf = luaL_checklstring(L, 3, &buf_len);
nbr_frames = buf_len / 4;
break;
}
case LUA_TUSERDATA: {
pixbuf *buffer = pixbuf_from_lua_arg(L, 3);
luaL_argcheck(L, buffer->nchan == 4, 3, "Pixbuf not 4-channel");
buf = (const char *)buffer->values;
nbr_frames = buffer->npix;
break;
}
default:
return luaL_argerror(L, 3, "String or pixbuf expected");
}
// Initialize the output pins
......
......@@ -32,7 +32,7 @@ static int dht_lapi_read( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read_universal(id) );
lua_pushinteger( L, dht_read(id, DHT_NON11) );
aux_read( L );
return 5;
}
......@@ -42,7 +42,17 @@ static int dht_lapi_read11( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
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 );
return 5;
}
......@@ -52,48 +62,17 @@ static int dht_lapi_readxx( lua_State *L )
{
unsigned id = luaL_checkinteger( L, 1 );
MOD_CHECK_ID( dht, id );
lua_pushinteger( L, dht_read(id) );
lua_pushinteger( L, dht_read(id, DHT22) );
aux_read( L );
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
LROT_BEGIN(dht, NULL, 0)
LROT_FUNCENTRY( read, dht_lapi_read )
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( ERROR_CHECKSUM, DHTLIB_ERROR_CHECKSUM )
LROT_NUMENTRY( ERROR_TIMEOUT, DHTLIB_ERROR_TIMEOUT )
......
This diff is collapsed.
......@@ -435,12 +435,19 @@ static int pipe_reader(lua_State *L) {
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_FUNCENTRY( __len, pipe__len )
LROT_FUNCENTRY( __tostring, pipe__tostring )
LROT_FUNCENTRY( read, pipe_read )
LROT_FUNCENTRY( reader, pipe_reader )
LROT_FUNCENTRY( unread, pipe_unread )
LROT_FUNCENTRY( nrec, pipe_nrec )
LROT_END(pipe_funcs, NULL, 0)
/* 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)
}
return platform_gpio_register_intr_hook(mask, softuart_intr_handler);
}
return 1;
}
......@@ -206,30 +207,27 @@ static int softuart_setup(lua_State *L)
NODE_DBG("[SoftUART]: setup called\n");
baudrate = (uint32_t)luaL_checkinteger(L, 1); // Get Baudrate from
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_isnil(L, 1)) {
tx_gpio_id = 0xFF;
} else {
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");
}
rx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
if (lua_isnoneornil(L, 2)) {
tx_gpio_id = 0xFF;
} else {
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
, 2, "Invalid SoftUART tx GPIO");
}
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)
, 3, "Invalid SoftUART rx GPIO");
luaL_argcheck(L, softuart_gpio_instances[rx_gpio_id] == NULL
, 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
return luaL_error(L, "Not enough arguments");
}
// SoftUART object without receive and transmit part would be useless
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->pin_rx = rx_gpio_id;
......
......@@ -12,18 +12,37 @@
//#define NODE_DEBUG
#include <stdint.h>
#include "os_type.h"
#include "osapi.h"
#include "sections.h"
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "task/task.h"
#include "hw_timer.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 SOMFY_UP 0x2
#define SOMFY_STOP 0x1
......@@ -33,24 +52,27 @@
#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))
// ----------------------------------------------------------------------------------------------------//
// ------------------------------- transmitter part ---------------------------------------------------//
// ----------------------------------------------------------------------------------------------------//
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 sync;
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
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 signalindex;
static uint8_t subindex;
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) {
// NODE_DBG("remote: %x\n", remote);
......@@ -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]);
}
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) {
(void) p;
// NODE_DBG("%d\t%d\n", signalindex, subindex);
......@@ -103,7 +116,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
subindex = 0;
if(sync == 2) { // Only with the first frame.
//Wake-up pulse & Silence
DIRECT_WRITE_HIGH(pin);
DIRECT_WRITE_HIGH(TxPin);
signalindex++;
// delayMicroseconds(9415);
break;
......@@ -112,7 +125,7 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
}
case 1:
//Wake-up pulse & Silence
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
signalindex++;
// delayMicroseconds(89565);
break;
......@@ -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
case 3:
// Hardware sync: two sync for the first frame, seven for the following ones.
DIRECT_WRITE_HIGH(pin);
DIRECT_WRITE_HIGH(TxPin);
signalindex++;
// delayMicroseconds(4*SYMBOL);
break;
case 4:
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
subindex++;
if (subindex < sync) {signalindex--;} else {signalindex++;}
// delayMicroseconds(4*SYMBOL);
break;
case 5:
// Software sync
DIRECT_WRITE_HIGH(pin);
DIRECT_WRITE_HIGH(TxPin);
signalindex++;
// delayMicroseconds(4550);
break;
case 6:
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
signalindex++;
subindex=0;
// delayMicroseconds(SYMBOL);
......@@ -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.
bitcondition = ((frame[subindex/8] >> (7 - (subindex%8))) & 1) == 1;
if(bitcondition) {
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
}
else {
DIRECT_WRITE_HIGH(pin);
DIRECT_WRITE_HIGH(TxPin);
}
signalindex++;
// delayMicroseconds(SYMBOL);
......@@ -159,10 +172,10 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
case 8:
//Data: bits are sent one by one, starting with the MSB.
if(bitcondition) {
DIRECT_WRITE_HIGH(pin);
DIRECT_WRITE_HIGH(TxPin);
}
else {
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
}
if (subindex<56) {
......@@ -175,19 +188,19 @@ static void ICACHE_RAM_ATTR sendCommand(os_param_t p) {
// delayMicroseconds(SYMBOL);
break;
case 9:
DIRECT_WRITE_LOW(pin);
DIRECT_WRITE_LOW(TxPin);
signalindex++;
// delayMicroseconds(30415); // Inter-frame silence
break;
case 10:
repeatindex++;
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;
} else {
platform_hw_timer_close(TIMER_OWNER);
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;
......@@ -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)) {
return luaL_error(L, "wrong arg range");
}
pin = luaL_checkinteger(L, 1);
// ----------------------------------------------------------------------------------------------------//
// ------------------------------- receiver part ------------------------------------------------------//
// ----------------------------------------------------------------------------------------------------//
#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
}
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);
uint8_t cmd = luaL_checkinteger(L, 3);
uint16_t code = luaL_checkinteger(L, 4);
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_isnoneornil(L, 6)) {
lua_pushvalue(L, 6);
lua_done_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (lua_type(L, 6) == LUA_TFUNCTION) {
lua_pushvalue (L, 6);
register_lua_cb (L, &lua_done_ref);
} else {
lua_done_ref = LUA_NOREF;
unregister_lua_cb (L, &lua_done_ref);
}
MOD_CHECK_ID(gpio, pin);
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
MOD_CHECK_ID(gpio, TxPin);
platform_gpio_mode(TxPin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP);
buildFrame(frame, remote, cmd, code);
......@@ -233,18 +476,21 @@ static int somfy_lua_sendcommand(lua_State* L) { // pin, remote, command, rollin
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_FUNCENTRY( sendcommand, somfy_lua_sendcommand )
LROT_FUNCENTRY( listen, somfy_lua_listen )
LROT_NUMENTRY( UP, SOMFY_UP )
LROT_NUMENTRY( DOWN, SOMFY_DOWN )
LROT_NUMENTRY( PROG, SOMFY_PROG )
LROT_NUMENTRY( STOP, SOMFY_STOP )
LROT_FUNCENTRY( sendcommand, somfy_lua_sendcommand )
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);
......@@ -5,6 +5,8 @@
#include <string.h>
#include "user_interface.h"
#include "pixbuf.h"
static inline uint32_t _getCycleCount(void) {
uint32_t cycles;
__asm__ __volatile__("rsr %0,ccount":"=a" (cycles));
......@@ -13,8 +15,9 @@ static inline uint32_t _getCycleCount(void) {
// This algorithm reads the cpu clock cycles to calculate the correct
// pulse widths. It works in both 80 and 160 MHz mode.
static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, uint32_t length) {
uint8_t *p, *end;
static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, const uint8_t *pixels, size_t length) {
const uint8_t *p, *end;
uint8_t phasergb = 0;
p = pixels;
end = p + length;
......@@ -28,6 +31,13 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui
register int i;
register uint8_t pixel = *p++;
if ((phasergb == 0) && (pixel == 0xFF)) {
// clamp initial byte value to avoid constant-current shenanigans. Yuck!
pixel = 0xFE;
}
if (++phasergb == 3) {
phasergb = 0;
}
ets_intr_lock();
......@@ -55,35 +65,27 @@ static void ICACHE_RAM_ATTR tm1829_write_to_pin(uint8_t pin, uint8_t *pixels, ui
}
// Lua: tm1829.write(pin, "string")
// Byte triples in the string are interpreted as R G B values and sent to the hardware as G R B.
// WARNING: this function scrambles the input buffer :
// a = string.char(255,0,128)
// tm1829.write(3,a)
// =a.byte()
// (0,255,128)
// Byte triples in the string are interpreted as GRB values.
static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
{
const uint8_t pin = luaL_checkinteger(L, 1);
const uint8_t *pixels;
size_t length;
const char *rgb = luaL_checklstring(L, 2, &length);
// dont modify lua-internal lstring - make a copy instead
char *buffer = (char *)malloc(length);
// Ignore incomplete Byte triples at the end of buffer
length -= length % 3;
// Copy payload and make sure first byte is < 0xFF (triggers
// constant current command, instead of PWM duty command)
size_t i;
for (i = 0; i < length; i += 3) {
buffer[i] = rgb[i];
buffer[i + 1] = rgb[i + 1];
buffer[i + 2] = rgb[i + 2];
// Check for first byte
if (buffer[i] == 0xff)
buffer[i] = 0xfe;
switch(lua_type(L, 3)) {
case LUA_TSTRING: {
pixels = luaL_checklstring(L, 2, &length);
break;
}
case LUA_TUSERDATA: {
pixbuf *buffer = pixbuf_from_lua_arg(L, 2);
luaL_argcheck(L, pixbuf_channels(buffer) == 3, 2, "Bad pixbuf format");
pixels = buffer->values;
length = 3 * buffer->npix;
break;
}
default:
return luaL_argerror(L, 2, "String or pixbuf expected");
}
// Initialize the output pin and wait a bit
......@@ -91,12 +93,10 @@ static int ICACHE_FLASH_ATTR tm1829_write(lua_State* L)
platform_gpio_write(pin, 1);
// Send the buffer
tm1829_write_to_pin(pin_num[pin], (uint8_t*) buffer, length);
tm1829_write_to_pin(pin_num[pin], pixels, length);
os_delay_us(500); // reset time
free(buffer);
return 0;
}
......
......@@ -30,11 +30,11 @@ static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
typedef struct{
os_timer_t os;
sint32_t lua_ref; /* Reference to registered callback function */
sint32_t self_ref; /* Reference to UD registered slot */
uint32_t interval;
uint8_t mode;
os_timer_t os;
sint32_t lua_ref; /* Reference to registered callback function */
sint32_t self_ref; /* Reference to UD registered slot */
uint32_t interval;
uint8_t mode;
} tmr_t;
// The previous implementation extended the rtc counter to 64 bits, and then
......@@ -56,47 +56,47 @@ static sint32_t soft_watchdog = -1;
static os_timer_t rtc_timer;
static void alarm_timer_common(void* arg){
tmr_t *tmr = (tmr_t *) arg;
if(tmr->lua_ref > 0) {
lua_State* L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
if (tmr->mode != TIMER_MODE_AUTO) {
if(tmr->mode == TIMER_MODE_SINGLE) {
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
tmr->mode = TIMER_MODE_OFF;
} else if (tmr->mode == TIMER_MODE_SEMI) {
tmr->mode |= TIMER_IDLE_FLAG;
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
}
}
luaL_pcallx(L, 1, 0);
}
tmr_t *tmr = (tmr_t *) arg;
if(tmr->lua_ref > 0) {
lua_State* L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
if (tmr->mode != TIMER_MODE_AUTO) {
if(tmr->mode == TIMER_MODE_SINGLE) {
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
tmr->mode = TIMER_MODE_OFF;
} else if (tmr->mode == TIMER_MODE_SEMI) {
tmr->mode |= TIMER_IDLE_FLAG;
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
}
}
luaL_pcallx(L, 1, 0);
}
}
// Lua: tmr.delay( us )
static int tmr_delay( lua_State* L ){
sint32_t us = luaL_checkinteger(L, 1);
luaL_argcheck(L, us>0, 1, "wrong arg range");
while(us > 0){
os_delay_us(us >= 1000000 ? 1000000 : us);
system_soft_wdt_feed ();
us -= 1000000;
}
return 0;
sint32_t us = luaL_checkinteger(L, 1);
luaL_argcheck(L, us>0, 1, "wrong arg range");
while(us > 0){
os_delay_us(us >= 1000000 ? 1000000 : us);
system_soft_wdt_feed ();
us -= 1000000;
}
return 0;
}
// Lua: tmr.now() , return system timer in us
static int tmr_now(lua_State* L){
lua_pushinteger(L, (uint32_t) (0x7FFFFFFF & system_get_time()));
return 1;
lua_pushinteger(L, (uint32_t) (0x7FFFFFFF & system_get_time()));
return 1;
}
// Lua: tmr.ccount() , returns CCOUNT register
static int tmr_ccount(lua_State* L){
lua_pushinteger(L, CCOUNT_REG);
return 1;
lua_pushinteger(L, CCOUNT_REG);
return 1;
}
/*
......@@ -106,69 +106,69 @@ static int tmr_ccount(lua_State* L){
// Lua: t:register( interval, mode, function )
static int tmr_register(lua_State* L) {
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2);
uint8_t mode = luaL_checkinteger(L, 3);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
luaL_argcheck(L, lua_isfunction(L, 4), 4, "Must be function");
//get the lua function reference
lua_pushvalue(L, 4);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os);
luaL_reref(L, LUA_REGISTRYINDEX, &tmr->lua_ref);
tmr->mode = mode|TIMER_IDLE_FLAG;
tmr->interval = interval;
os_timer_setfn(&tmr->os, alarm_timer_common, tmr);
return 0;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2);
uint8_t mode = luaL_checkinteger(L, 3);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
luaL_argcheck(L, lua_isfunction(L, 4), 4, "Must be function");
//get the lua function reference
lua_pushvalue(L, 4);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os);
luaL_reref(L, LUA_REGISTRYINDEX, &tmr->lua_ref);
tmr->mode = mode|TIMER_IDLE_FLAG;
tmr->interval = interval;
os_timer_setfn(&tmr->os, alarm_timer_common, tmr);
return 0;
}
// Lua: t:start( [restart] )
static int tmr_start(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
lua_settop(L, 2);
luaL_argcheck(L, lua_isboolean(L, 2) || lua_isnil(L, 2), 2, "boolean expected");
int restart = lua_toboolean(L, 2);
lua_settop(L, 1); /* we need to have userdata on top of the stack */
if (tmr->self_ref == LUA_NOREF)
tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
//we return false if the timer is not idle and is not to be restarted
int idle = tmr->mode&TIMER_IDLE_FLAG;
if(!(idle || restart)){
lua_pushboolean(L, false);
}else{
if (!idle) {os_timer_disarm(&tmr->os);}
tmr->mode &= ~TIMER_IDLE_FLAG;
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
lua_pushboolean(L, true);
}
return 1;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
lua_settop(L, 2);
luaL_argcheck(L, lua_isboolean(L, 2) || lua_isnil(L, 2), 2, "boolean expected");
int restart = lua_toboolean(L, 2);
lua_settop(L, 1); /* we need to have userdata on top of the stack */
if (tmr->self_ref == LUA_NOREF)
tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
//we return false if the timer is not idle and is not to be restarted
int idle = tmr->mode&TIMER_IDLE_FLAG;
if(!(idle || restart)){
lua_pushboolean(L, false);
}else{
if (!idle) {os_timer_disarm(&tmr->os);}
tmr->mode &= ~TIMER_IDLE_FLAG;
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
lua_pushboolean(L, true);
}
return 1;
}
// Lua: t:alarm( interval, repeat, function )
static int tmr_alarm(lua_State* L){
tmr_register(L);
/* remove tmr.alarm's other then the 1st UD parameters from Lua stack.
tmr.start expects UD and optional restart parameter. */
lua_settop(L, 1);
return tmr_start(L);
tmr_register(L);
/* remove tmr.alarm's other then the 1st UD parameters from Lua stack.
tmr.start expects UD and optional restart parameter. */
lua_settop(L, 1);
return tmr_start(L);
}
// Lua: t:stop()
static int tmr_stop(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
int idle = tmr->mode == TIMER_MODE_OFF || (tmr->mode & TIMER_IDLE_FLAG);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
if(!idle)
os_timer_disarm(&tmr->os);
tmr->mode |= TIMER_IDLE_FLAG;
lua_pushboolean(L, !idle); /* return false if the timer is idle (or not registered) */
return 1;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
int idle = tmr->mode == TIMER_MODE_OFF || (tmr->mode & TIMER_IDLE_FLAG);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
if(!idle)
os_timer_disarm(&tmr->os);
tmr->mode |= TIMER_IDLE_FLAG;
lua_pushboolean(L, !idle); /* return false if the timer is idle (or not registered) */
return 1;
}
#ifdef TIMER_SUSPEND_ENABLE
......@@ -179,174 +179,173 @@ static int tmr_stop(lua_State* L){
#define tmr_suspend_all tmr_suspend_removed
#define tmr_resume_all tmr_suspend_removed
static int tmr_suspend_removed(lua_State* L){
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
}
#endif
// Lua: t:unregister()
static int tmr_unregister(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os);
tmr->mode = TIMER_MODE_OFF;
return 0;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os);
tmr->mode = TIMER_MODE_OFF;
return 0;
}
// Lua: t:interval( interval )
static int tmr_interval(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
if(tmr->mode != TIMER_MODE_OFF){
tmr->interval = interval;
if(!(tmr->mode&TIMER_IDLE_FLAG)){
os_timer_disarm(&tmr->os);
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
}
}
return 0;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
if(tmr->mode != TIMER_MODE_OFF){
tmr->interval = interval;
if(!(tmr->mode&TIMER_IDLE_FLAG)){
os_timer_disarm(&tmr->os);
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
}
}
return 0;
}
// Lua: t:state()
static int tmr_state(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
if(tmr->mode == TIMER_MODE_OFF){
lua_pushnil(L);
return 1;
}
lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG));
return 2;
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
if(tmr->mode == TIMER_MODE_OFF){
lua_pushnil(L);
return 1;
}
lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG));
return 2;
}
// Lua: tmr.wdclr()
static int tmr_wdclr( lua_State* L ){
system_soft_wdt_feed ();
return 0;
system_soft_wdt_feed ();
return 0;
}
// The on ESP8266 system_rtc_clock_cali_proc() returns a fixed point value
// (12 bit fraction part), giving how many rtc clock ticks represent 1us.
// The high 64 bits of the uint64_t multiplication are not needed)
static uint32_t rtc2usec(uint64_t rtc){
return (rtc*rtc_time_cali)>>12;
return (rtc*rtc_time_cali)>>12;
}
// This returns the number of microseconds uptime. Note that it relies on
// the rtc clock, which is notoriously temperature dependent
inline static uint64_t rtc_timer_update(bool do_calibration){
if (do_calibration || rtc_time_cali==0)
rtc_time_cali=system_rtc_clock_cali_proc();
uint32_t current = system_get_rtc_time();
uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound
uint32_t us_since_last=rtc2usec(since_last);
uint64_t now=last_rtc_time_us+us_since_last;
// Only update if at least 100ms has passed since we last updated.
// This prevents the rounding errors in rtc2usec from accumulating
if (us_since_last>=100000){
last_rtc_time=current;
last_rtc_time_us=now;
}
return now;
if (do_calibration || rtc_time_cali==0)
rtc_time_cali=system_rtc_clock_cali_proc();
uint32_t current = system_get_rtc_time();
uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound
uint32_t us_since_last=rtc2usec(since_last);
uint64_t now=last_rtc_time_us+us_since_last;
// Only update if at least 100ms has passed since we last updated.
// This prevents the rounding errors in rtc2usec from accumulating
if (us_since_last>=100000){
last_rtc_time=current;
last_rtc_time_us=now;
}
return now;
}
void rtc_callback(void *arg){
rtc_timer_update(true);
if(soft_watchdog > 0){
soft_watchdog--;
if(soft_watchdog == 0)
system_restart();
}
rtc_timer_update(true);
if(soft_watchdog >= 0){
soft_watchdog--;
if(soft_watchdog < 0)
system_restart();
}
}
// Lua: tmr.time() , return rtc time in second
static int tmr_time( lua_State* L ){
uint64_t us=rtc_timer_update(false);
lua_pushinteger(L, us/1000000);
return 1;
uint64_t us=rtc_timer_update(false);
lua_pushinteger(L, us/1000000);
return 1;
}
// Lua: tmr.softwd( value )
static int tmr_softwd( lua_State* L ){
int t = luaL_checkinteger(L, 1);
luaL_argcheck(L, t>0 , 2, "invalid time");
soft_watchdog = t;
return 0;
soft_watchdog = luaL_checkinteger(L, 1);
// NO check is required as negative Values mean that the timer is disabled.
return 0;
}
// Lua: tmr.create()
static int tmr_create( lua_State *L ) {
tmr_t *ud = (tmr_t *)lua_newuserdata(L, sizeof(*ud));
luaL_getmetatable(L, "tmr.timer");
lua_setmetatable(L, -2);
*ud = (tmr_t) {{0}, LUA_NOREF, LUA_NOREF, 0, TIMER_MODE_OFF};
return 1;
tmr_t *ud = (tmr_t *)lua_newuserdata(L, sizeof(*ud));
luaL_getmetatable(L, "tmr.timer");
lua_setmetatable(L, -2);
*ud = (tmr_t) {{0}, LUA_NOREF, LUA_NOREF, 0, TIMER_MODE_OFF};
return 1;
}
// Module function map
LROT_BEGIN(tmr_dyn, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, tmr_unregister )
LROT_TABENTRY( __index, tmr_dyn )
LROT_FUNCENTRY( register, tmr_register )
LROT_FUNCENTRY( alarm, tmr_alarm )
LROT_FUNCENTRY( start, tmr_start )
LROT_FUNCENTRY( stop, tmr_stop )
LROT_FUNCENTRY( unregister, tmr_unregister )
LROT_FUNCENTRY( state, tmr_state )
LROT_FUNCENTRY( interval, tmr_interval )
LROT_FUNCENTRY( __gc, tmr_unregister )
LROT_TABENTRY( __index, tmr_dyn )
LROT_FUNCENTRY( register, tmr_register )
LROT_FUNCENTRY( alarm, tmr_alarm )
LROT_FUNCENTRY( start, tmr_start )
LROT_FUNCENTRY( stop, tmr_stop )
LROT_FUNCENTRY( unregister, tmr_unregister )
LROT_FUNCENTRY( state, tmr_state )
LROT_FUNCENTRY( interval, tmr_interval )
#ifdef TIMER_SUSPEND_ENABLE
LROT_FUNCENTRY( suspend, tmr_suspend )
LROT_FUNCENTRY( resume, tmr_resume )
LROT_FUNCENTRY( suspend, tmr_suspend )
LROT_FUNCENTRY( resume, tmr_resume )
#endif
LROT_END(tmr_dyn, NULL, LROT_MASK_GC_INDEX)
LROT_BEGIN(tmr, NULL, 0)
LROT_FUNCENTRY( delay, tmr_delay )
LROT_FUNCENTRY( now, tmr_now )
LROT_FUNCENTRY( wdclr, tmr_wdclr )
LROT_FUNCENTRY( softwd, tmr_softwd )
LROT_FUNCENTRY( time, tmr_time )
LROT_FUNCENTRY( ccount, tmr_ccount )
LROT_FUNCENTRY( delay, tmr_delay )
LROT_FUNCENTRY( now, tmr_now )
LROT_FUNCENTRY( wdclr, tmr_wdclr )
LROT_FUNCENTRY( softwd, tmr_softwd )
LROT_FUNCENTRY( time, tmr_time )
LROT_FUNCENTRY( ccount, tmr_ccount )
#ifdef TIMER_SUSPEND_ENABLE
LROT_FUNCENTRY( suspend_all, tmr_suspend_all )
LROT_FUNCENTRY( resume_all, tmr_resume_all )
LROT_FUNCENTRY( suspend_all, tmr_suspend_all )
LROT_FUNCENTRY( resume_all, tmr_resume_all )
#endif
LROT_FUNCENTRY( create, tmr_create )
LROT_NUMENTRY( ALARM_SINGLE, TIMER_MODE_SINGLE )
LROT_NUMENTRY( ALARM_SEMI, TIMER_MODE_SEMI )
LROT_NUMENTRY( ALARM_AUTO, TIMER_MODE_AUTO )
LROT_FUNCENTRY( create, tmr_create )
LROT_NUMENTRY( ALARM_SINGLE, TIMER_MODE_SINGLE )
LROT_NUMENTRY( ALARM_SEMI, TIMER_MODE_SEMI )
LROT_NUMENTRY( ALARM_AUTO, TIMER_MODE_AUTO )
LROT_END(tmr, NULL, 0)
#include "pm/swtimer.h"
int luaopen_tmr( lua_State *L ){
luaL_rometatable(L, "tmr.timer", LROT_TABLEREF(tmr_dyn));
luaL_rometatable(L, "tmr.timer", LROT_TABLEREF(tmr_dyn));
last_rtc_time=system_get_rtc_time(); // Right now is time 0
last_rtc_time_us=0;
last_rtc_time=system_get_rtc_time(); // Right now is time 0
last_rtc_time_us=0;
os_timer_disarm(&rtc_timer);
os_timer_setfn(&rtc_timer, rtc_callback, NULL);
os_timer_arm(&rtc_timer, 1000, 1);
os_timer_disarm(&rtc_timer);
os_timer_setfn(&rtc_timer, rtc_callback, NULL);
os_timer_arm(&rtc_timer, 1000, 1);
// The function rtc_callback calls the a function that calibrates the SoftRTC
// for drift in the esp8266's clock. My guess: after the duration of light_sleep
// there is bound to be some drift in the clock, so a calibration is due.
SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
// The function rtc_callback calls the a function that calibrates the SoftRTC
// for drift in the esp8266's clock. My guess: after the duration of light_sleep
// there is bound to be some drift in the clock, so a calibration is due.
SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
// The function alarm_timer_common handles timers created by the developer via
// tmr.create(). No reason not to resume the timers, so resume em'.
SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
// The function alarm_timer_common handles timers created by the developer via
// tmr.create(). No reason not to resume the timers, so resume em'.
SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
return 0;
return 0;
}
NODEMCU_MODULE(TMR, "tmr", tmr, luaopen_tmr);
......@@ -5,6 +5,8 @@
#include <string.h>
#include "osapi.h"
#include "pixbuf.h"
/**
* Code is based on https://github.com/CHERTS/esp8266-devkit/blob/master/Espressif/examples/EspLightNode/user/ws2801.c
* and provides a similar api as the ws2812 module.
......@@ -110,13 +112,28 @@ static int ICACHE_FLASH_ATTR ws2801_init_lua(lua_State* L) {
*/
static int ICACHE_FLASH_ATTR ws2801_writergb(lua_State* L) {
size_t length;
const char *buffer = luaL_checklstring(L, 1, &length);
const uint8_t *values;
switch(lua_type(L,1)) {
case LUA_TSTRING:
values = (const uint8_t*) luaL_checklstring(L, 1, &length);
break;
case LUA_TUSERDATA: {
pixbuf *buffer = pixbuf_from_lua_arg(L, 1);
luaL_argcheck(L, buffer->nchan == 3, 1, "Pixbuf not 3-channel");
values = buffer->values;
length = pixbuf_size(buffer);
break;
}
default:
return luaL_argerror(L, 1, "pixbuf or string expected");
}
os_delay_us(10);
ets_intr_lock();
ws2801_strip(buffer, length);
ws2801_strip(values, length);
ets_intr_unlock();
......
This diff is collapsed.
#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