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;
switch(lua_type(L, 3)) {
if (nbr_frames > 100000) { case LUA_TSTRING: {
return luaL_error(L, "The supplied buffer is too long, and might cause the callback watchdog to bark."); 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 )
......
...@@ -22,21 +22,12 @@ ...@@ -22,21 +22,12 @@
#define MQTT_MAX_CLIENT_LEN 64 #define MQTT_MAX_CLIENT_LEN 64
#define MQTT_MAX_USER_LEN 64 #define MQTT_MAX_USER_LEN 64
#define MQTT_MAX_PASS_LEN 64 #define MQTT_MAX_PASS_LEN 64
#define MQTT_SEND_TIMEOUT 5 #define MQTT_SEND_TIMEOUT 5 /* seconds */
/*
* This timeout needs to be long enough for a typical TCP connect()
* *and* the TLS handshake, if any. Most network stacks seem to wait
* tens of seconds for connect(), and TLS can take a good deal of time
* and several round trips. Because this matters only rarely, it may
* as well be set pretty high.
*/
#define MQTT_CONNECT_TIMEOUT 60
typedef enum { typedef enum {
MQTT_INIT, MQTT_INIT,
MQTT_CONNECT_SENT,
MQTT_CONNECT_SENDING, MQTT_CONNECT_SENDING,
MQTT_CONNECT_SENT,
MQTT_DATA MQTT_DATA
} tConnState; } tConnState;
...@@ -59,7 +50,6 @@ typedef enum { ...@@ -59,7 +50,6 @@ typedef enum {
typedef struct mqtt_state_t typedef struct mqtt_state_t
{ {
uint16_t port;
msg_queue_t* pending_msg_q; msg_queue_t* pending_msg_q;
uint16_t next_message_id; uint16_t next_message_id;
...@@ -86,15 +76,28 @@ typedef struct lmqtt_userdata ...@@ -86,15 +76,28 @@ typedef struct lmqtt_userdata
int cb_suback_ref; int cb_suback_ref;
int cb_unsuback_ref; int cb_unsuback_ref;
int cb_puback_ref; int cb_puback_ref;
/* Configuration options */
struct {
int client_id_ref;
int username_ref;
int password_ref;
int will_topic_ref;
int will_message_ref;
int keepalive;
uint16_t max_message_length;
struct {
unsigned will_qos : 2;
bool will_retain : 1;
bool clean_session : 1;
bool secure : 1;
} flags;
} conf;
mqtt_state_t mqtt_state; mqtt_state_t mqtt_state;
mqtt_connect_info_t connect_info;
uint16_t keep_alive_tick;
uint32_t event_timeout;
#ifdef CLIENT_SSL_ENABLE
uint8_t secure;
#endif
bool connected; // indicate socket connected, not mqtt prot connected. bool connected; // indicate socket connected, not mqtt prot connected.
bool keepalive_sent; bool keepalive_sent;
bool sending; // data sent to network stack, awaiting local acknowledge
ETSTimer mqttTimer; ETSTimer mqttTimer;
tConnState connState; tConnState connState;
}lmqtt_userdata; }lmqtt_userdata;
...@@ -116,25 +119,28 @@ static uint16_t mqtt_next_message_id(lmqtt_userdata * mud) ...@@ -116,25 +119,28 @@ static uint16_t mqtt_next_message_id(lmqtt_userdata * mud)
return mud->mqtt_state.next_message_id; return mud->mqtt_state.next_message_id;
} }
static void mqtt_socket_cb_lua_noarg(lua_State *L, lmqtt_userdata *mud, int cb)
{
if (cb == LUA_NOREF)
return;
lua_rawgeti(L, LUA_REGISTRYINDEX, cb);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_call(L, 1, 0);
}
static void mqtt_socket_disconnected(void *arg) // tcp only static void mqtt_socket_disconnected(void *arg) // tcp only
{ {
NODE_DBG("enter mqtt_socket_disconnected.\n"); NODE_DBG("enter mqtt_socket_disconnected.\n");
bool call_back = false;
lmqtt_userdata *mud = arg; lmqtt_userdata *mud = arg;
if(mud == NULL) if(mud == NULL)
return; return;
os_timer_disarm(&mud->mqttTimer); os_timer_disarm(&mud->mqttTimer);
lua_State *L = lua_getstate(); while (mud->mqtt_state.pending_msg_q) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
if(mud->connected){ // call back only called when socket is from connection to disconnection.
mud->connected = false;
if((mud->cb_disconnect_ref != LUA_NOREF) && (mud->self_ref != LUA_NOREF)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua
call_back = true;
}
} }
if(mud->mqtt_state.recv_buffer) { if(mud->mqtt_state.recv_buffer) {
...@@ -148,21 +154,30 @@ static void mqtt_socket_disconnected(void *arg) // tcp only ...@@ -148,21 +154,30 @@ static void mqtt_socket_disconnected(void *arg) // tcp only
free(mud->pesp_conn.proto.tcp); free(mud->pesp_conn.proto.tcp);
mud->pesp_conn.proto.tcp = NULL; mud->pesp_conn.proto.tcp = NULL;
mud->connected = false; int selfref = mud->self_ref;
luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref); mud->self_ref = LUA_NOREF;
mud->self_ref = LUA_NOREF; // unref this, and the mqtt.socket userdata will delete it self
lua_State *L = lua_getstate();
if(call_back){ if(mud->connected){ // call back only called when socket is from connection to disconnection.
lua_call(L, 1, 0); mud->connected = false;
if((mud->cb_disconnect_ref != LUA_NOREF) && (selfref != LUA_NOREF)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, selfref);
lua_call(L, 1, 0);
}
} }
// unref this, and the mqtt.socket userdata will delete it self
luaL_unref(L, LUA_REGISTRYINDEX, selfref);
NODE_DBG("leave mqtt_socket_disconnected.\n"); NODE_DBG("leave mqtt_socket_disconnected.\n");
} }
static void mqtt_socket_do_disconnect(struct lmqtt_userdata *mud) static void mqtt_socket_do_disconnect(struct lmqtt_userdata *mud)
{ {
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if (mud->secure) { if (mud->conf.flags.secure) {
espconn_secure_disconnect(&mud->pesp_conn); espconn_secure_disconnect(&mud->pesp_conn);
} else } else
#endif #endif
...@@ -173,17 +188,12 @@ static void mqtt_socket_do_disconnect(struct lmqtt_userdata *mud) ...@@ -173,17 +188,12 @@ static void mqtt_socket_do_disconnect(struct lmqtt_userdata *mud)
static void mqtt_socket_reconnected(void *arg, sint8_t err) static void mqtt_socket_reconnected(void *arg, sint8_t err)
{ {
NODE_DBG("enter mqtt_socket_reconnected.\n"); NODE_DBG("enter mqtt_socket_reconnected (err=%d)\n", err);
lmqtt_userdata *mud = arg; lmqtt_userdata *mud = arg;
if(mud == NULL) if(mud == NULL)
return; return;
os_timer_disarm(&mud->mqttTimer); os_timer_disarm(&mud->mqttTimer);
mud->event_timeout = 0; // no need to count anymore
mqtt_socket_do_disconnect(mud);
mqtt_connack_fail(mud, MQTT_CONN_FAIL_SERVER_NOT_FOUND); mqtt_connack_fail(mud, MQTT_CONN_FAIL_SERVER_NOT_FOUND);
mqtt_socket_disconnected(arg); mqtt_socket_disconnected(arg);
...@@ -212,7 +222,7 @@ static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, uint16_t len ...@@ -212,7 +222,7 @@ static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, uint16_t len
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
if(event_data.topic && (event_data.topic_length > 0)){ if(event_data.topic && (event_data.topic_length > 0)){
lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_pushlstring(L, event_data.topic, event_data.topic_length); lua_pushlstring(L, event_data.topic, event_data.topic_length);
} else { } else {
NODE_DBG("get wrong packet.\n"); NODE_DBG("get wrong packet.\n");
...@@ -230,6 +240,8 @@ static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, uint16_t len ...@@ -230,6 +240,8 @@ static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, uint16_t len
static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code) static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code)
{ {
NODE_DBG("enter mqtt_connack_fail\n");
if(mud->cb_connect_fail_ref == LUA_NOREF || mud->self_ref == LUA_NOREF) if(mud->cb_connect_fail_ref == LUA_NOREF || mud->self_ref == LUA_NOREF)
{ {
return; return;
...@@ -238,35 +250,41 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code) ...@@ -238,35 +250,41 @@ static void mqtt_connack_fail(lmqtt_userdata * mud, int reason_code)
lua_State *L = lua_getstate(); lua_State *L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_pushinteger(L, reason_code); lua_pushinteger(L, reason_code);
lua_call(L, 2, 0); lua_call(L, 2, 0);
NODE_DBG("leave mqtt_connack_fail\n");
} }
static sint8 mqtt_send_if_possible(struct lmqtt_userdata *mud) static sint8 mqtt_send_if_possible(struct lmqtt_userdata *mud)
{ {
/* Waiting for the local network stack to get back to us? Can't send. */
if (mud->sending)
return ESPCONN_OK;
sint8 espconn_status = ESPCONN_OK; sint8 espconn_status = ESPCONN_OK;
// This indicates if we have sent something and are waiting for something to msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q));
// happen if (pending_msg && !pending_msg->sent) {
if (mud->event_timeout == 0) { pending_msg->sent = 1;
msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); NODE_DBG("Sent: %d\n", pending_msg->msg.length);
if (pending_msg) {
mud->event_timeout = MQTT_SEND_TIMEOUT;
NODE_DBG("Sent: %d\n", pending_msg->msg.length);
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if( mud->secure ) if( mud->conf.flags.secure )
{ {
espconn_status = espconn_secure_send(&mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); espconn_status = espconn_secure_send(&mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
} }
else else
#endif #endif
{ {
espconn_status = espconn_send(&mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length ); espconn_status = espconn_send(&mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length );
}
mud->keep_alive_tick = 0;
} }
mud->sending = true;
os_timer_disarm(&mud->mqttTimer);
os_timer_arm(&mud->mqttTimer, MQTT_SEND_TIMEOUT * 1000, 0);
} }
NODE_DBG("send_if_poss, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); NODE_DBG("send_if_poss, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
return espconn_status; return espconn_status;
} }
...@@ -388,11 +406,11 @@ READPACKET: ...@@ -388,11 +406,11 @@ READPACKET:
mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE); mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE);
mqtt_message_t *temp_msg = NULL; mqtt_message_t *temp_msg = NULL;
lua_State *L = lua_getstate();
switch(mud->connState){ switch(mud->connState){
case MQTT_CONNECT_SENDING: case MQTT_CONNECT_SENDING:
case MQTT_CONNECT_SENT: case MQTT_CONNECT_SENT:
mud->event_timeout = 0; os_timer_disarm(&mud->mqttTimer);
os_timer_arm(&mud->mqttTimer, mud->conf.keepalive * 1000, 0);
if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){
NODE_DBG("MQTT: Invalid packet\r\n"); NODE_DBG("MQTT: Invalid packet\r\n");
...@@ -414,13 +432,8 @@ READPACKET: ...@@ -414,13 +432,8 @@ READPACKET:
mud->connState = MQTT_DATA; mud->connState = MQTT_DATA;
NODE_DBG("MQTT: Connected\r\n"); NODE_DBG("MQTT: Connected\r\n");
mud->keepalive_sent = 0; mud->keepalive_sent = 0;
if(mud->cb_connect_ref == LUA_NOREF)
break; mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_connect_ref);
if(mud->self_ref == LUA_NOREF)
break;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_connect_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua
lua_call(L, 1, 0);
break; break;
} }
break; break;
...@@ -435,7 +448,7 @@ READPACKET: ...@@ -435,7 +448,7 @@ READPACKET:
message_length, message_length,
in_buffer_length); in_buffer_length);
if (message_length > mud->connect_info.max_message_length) { if (message_length > mud->conf.max_message_length) {
// The pending message length is larger than we was configured to allow // The pending message length is larger than we was configured to allow
if(msg_qos > 0 && msg_id == 0) { if(msg_qos > 0 && msg_id == 0) {
NODE_DBG("MQTT: msg too long, but not enough data to get msg_id: total=%u, deliver=%u\r\n", message_length, in_buffer_length); NODE_DBG("MQTT: msg too long, but not enough data to get msg_id: total=%u, deliver=%u\r\n", message_length, in_buffer_length);
...@@ -526,13 +539,8 @@ READPACKET: ...@@ -526,13 +539,8 @@ READPACKET:
if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_SUBSCRIBE && pending_msg->msg_id == msg_id){ if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_SUBSCRIBE && pending_msg->msg_id == msg_id){
NODE_DBG("MQTT: Subscribe successful\r\n"); NODE_DBG("MQTT: Subscribe successful\r\n");
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
if (mud->cb_suback_ref == LUA_NOREF)
break; mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_suback_ref);
if (mud->self_ref == LUA_NOREF)
break;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_suback_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_call(L, 1, 0);
} }
break; break;
case MQTT_MSG_TYPE_UNSUBACK: case MQTT_MSG_TYPE_UNSUBACK:
...@@ -540,13 +548,7 @@ READPACKET: ...@@ -540,13 +548,7 @@ READPACKET:
NODE_DBG("MQTT: UnSubscribe successful\r\n"); NODE_DBG("MQTT: UnSubscribe successful\r\n");
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
if (mud->cb_unsuback_ref == LUA_NOREF) mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_unsuback_ref);
break;
if (mud->self_ref == LUA_NOREF)
break;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_unsuback_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_call(L, 1, 0);
} }
break; break;
case MQTT_MSG_TYPE_PUBLISH: case MQTT_MSG_TYPE_PUBLISH:
...@@ -569,13 +571,8 @@ READPACKET: ...@@ -569,13 +571,8 @@ READPACKET:
if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg->msg_id == msg_id){ if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg->msg_id == msg_id){
NODE_DBG("MQTT: Publish with QoS = 1 successful\r\n"); NODE_DBG("MQTT: Publish with QoS = 1 successful\r\n");
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
if(mud->cb_puback_ref == LUA_NOREF)
break; mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_puback_ref);
if(mud->self_ref == LUA_NOREF)
break;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_puback_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua
lua_call(L, 1, 0);
} }
break; break;
...@@ -603,13 +600,8 @@ READPACKET: ...@@ -603,13 +600,8 @@ READPACKET:
if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_PUBREL && pending_msg->msg_id == msg_id){ if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_PUBREL && pending_msg->msg_id == msg_id){
NODE_DBG("MQTT: Publish with QoS = 2 successful\r\n"); NODE_DBG("MQTT: Publish with QoS = 2 successful\r\n");
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
if(mud->cb_puback_ref == LUA_NOREF)
break; mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_puback_ref);
if(mud->self_ref == LUA_NOREF)
break;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_puback_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua
lua_call(L, 1, 0);
} }
break; break;
case MQTT_MSG_TYPE_PINGREQ: case MQTT_MSG_TYPE_PINGREQ:
...@@ -669,40 +661,51 @@ static void mqtt_socket_sent(void *arg) ...@@ -669,40 +661,51 @@ static void mqtt_socket_sent(void *arg)
return; return;
if(!mud->connected) if(!mud->connected)
return; return;
// call mqtt_sent()
mud->event_timeout = 0; mud->sending = false;
mud->keep_alive_tick = 0;
os_timer_disarm(&mud->mqttTimer);
if(mud->connState == MQTT_CONNECT_SENDING){ if(mud->connState == MQTT_CONNECT_SENDING){
mud->connState = MQTT_CONNECT_SENT; mud->connState = MQTT_CONNECT_SENT;
mud->event_timeout = MQTT_SEND_TIMEOUT; os_timer_arm(&mud->mqttTimer, MQTT_SEND_TIMEOUT * 1000, 0);
// MQTT_CONNECT not queued. // MQTT_CONNECT not queued.
return; return;
} }
/* Ready for timeout */
os_timer_arm(&mud->mqttTimer, mud->conf.keepalive * 1000, 0);
NODE_DBG("sent1, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); NODE_DBG("sent1, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
uint8_t try_send = 1;
// qos = 0, publish and forgot.
msg_queue_t *node = msg_peek(&(mud->mqtt_state.pending_msg_q)); msg_queue_t *node = msg_peek(&(mud->mqtt_state.pending_msg_q));
if(node && node->msg_type == MQTT_MSG_TYPE_PUBLISH && node->publish_qos == 0) { if (node) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); switch (node->msg_type) {
if(mud->cb_puback_ref != LUA_NOREF && mud->self_ref != LUA_NOREF) { case MQTT_MSG_TYPE_PUBLISH:
lua_State *L = lua_getstate(); // qos = 0, publish and forget. Run the callback now because we
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); // won't get a puback from the server and it's not clear when else
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua // we should tell the user the message drained from the egress queue
lua_call(L, 1, 0); if (node->publish_qos == 0) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
mqtt_socket_cb_lua_noarg(lua_getstate(), mud, mud->cb_puback_ref);
mqtt_send_if_possible(mud);
}
break;
case MQTT_MSG_TYPE_PUBACK:
/* FALLTHROUGH */
case MQTT_MSG_TYPE_PUBCOMP:
/* FALLTHROUGH */
case MQTT_MSG_TYPE_PINGREQ:
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
mqtt_send_if_possible(mud);
break;
case MQTT_MSG_TYPE_DISCONNECT:
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
mqtt_socket_do_disconnect(mud);
break;
} }
} else if(node && node->msg_type == MQTT_MSG_TYPE_PUBACK) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} else if(node && node->msg_type == MQTT_MSG_TYPE_PUBCOMP) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} else if(node && node->msg_type == MQTT_MSG_TYPE_PINGREQ) {
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} else {
try_send = 0;
}
if (try_send) {
mqtt_send_if_possible(mud);
} }
NODE_DBG("sent2, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); NODE_DBG("sent2, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
NODE_DBG("leave mqtt_socket_sent.\n"); NODE_DBG("leave mqtt_socket_sent.\n");
} }
...@@ -723,13 +726,34 @@ static void mqtt_socket_connected(void *arg) ...@@ -723,13 +726,34 @@ static void mqtt_socket_connected(void *arg)
mqtt_message_buffer_t msgb; mqtt_message_buffer_t msgb;
mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE); mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE);
mqtt_message_t* temp_msg = mqtt_msg_connect(&msgb, &mud->connect_info); /* Build the mqtt_connect_info on the stack and assemble the message */
lua_State *L = lua_getstate();
int ltop = lua_gettop(L);
struct mqtt_connect_info mci;
mci.clean_session = mud->conf.flags.clean_session;
mci.will_retain = mud->conf.flags.will_retain;
mci.will_qos = mud->conf.flags.will_qos;
mci.keepalive = mud->conf.keepalive;
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->conf.client_id_ref);
mci.client_id = lua_tostring(L, -1);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->conf.username_ref);
mci.username = lua_tostring(L, -1);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->conf.password_ref);
mci.password = lua_tostring(L, -1);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->conf.will_topic_ref);
mci.will_topic = lua_tostring(L, -1);
lua_rawgeti(L, LUA_REGISTRYINDEX, mud->conf.will_message_ref);
mci.will_message = lua_tostring(L, -1);
mqtt_message_t* temp_msg = mqtt_msg_connect(&msgb, &mci);
NODE_DBG("Send MQTT connection infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]); NODE_DBG("Send MQTT connection infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]);
mud->event_timeout = MQTT_SEND_TIMEOUT; lua_settop(L, ltop); /* Done with strings, restore the stack */
// not queue this message. should send right now. or should enqueue this before head. // not queue this message. should send right now. or should enqueue this before head.
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) if(mud->conf.flags.secure)
{ {
espconn_secure_send(pesp_conn, temp_msg->data, temp_msg->length); espconn_secure_send(pesp_conn, temp_msg->data, temp_msg->length);
} }
...@@ -738,10 +762,14 @@ static void mqtt_socket_connected(void *arg) ...@@ -738,10 +762,14 @@ static void mqtt_socket_connected(void *arg)
{ {
espconn_send(pesp_conn, temp_msg->data, temp_msg->length); espconn_send(pesp_conn, temp_msg->data, temp_msg->length);
} }
mud->keep_alive_tick = 0; mud->sending = true;
os_timer_disarm(&mud->mqttTimer);
os_timer_arm(&mud->mqttTimer, MQTT_SEND_TIMEOUT * 1000, 0);
mud->connState = MQTT_CONNECT_SENDING; mud->connState = MQTT_CONNECT_SENDING;
NODE_DBG("leave mqtt_socket_connectet, heap = %u.\n", system_get_free_heap_size());
NODE_DBG("leave mqtt_socket_connected, heap = %u.\n", system_get_free_heap_size());
return; return;
} }
...@@ -755,70 +783,41 @@ void mqtt_socket_timer(void *arg) ...@@ -755,70 +783,41 @@ void mqtt_socket_timer(void *arg)
if(mud->pesp_conn.proto.tcp == NULL){ if(mud->pesp_conn.proto.tcp == NULL){
NODE_DBG("MQTT not connected\n"); NODE_DBG("MQTT not connected\n");
os_timer_disarm(&mud->mqttTimer);
return; return;
} }
NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); NODE_DBG("timer, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
if(mud->event_timeout > 0){
NODE_DBG("event_timeout: %d.\n", mud->event_timeout);
mud->event_timeout --;
if(mud->event_timeout > 0){
return;
} else {
NODE_DBG("event timeout. \n");
if(mud->connState == MQTT_DATA)
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
// should remove the head of the queue and re-send with DUP = 1
// Not implemented yet.
}
}
if(mud->connState == MQTT_INIT){ // socket connect time out. if(mud->connState == MQTT_CONNECT_SENDING){ // MQTT_CONNECT send time out.
NODE_DBG("Can not connect to broker.\n");
os_timer_disarm(&mud->mqttTimer);
mqtt_socket_do_disconnect(mud);
mqtt_connack_fail(mud, MQTT_CONN_FAIL_SERVER_NOT_FOUND);
} else if(mud->connState == MQTT_CONNECT_SENDING){ // MQTT_CONNECT send time out.
NODE_DBG("sSend MQTT_CONNECT failed.\n"); NODE_DBG("sSend MQTT_CONNECT failed.\n");
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
mqtt_socket_do_disconnect(mud); mqtt_socket_do_disconnect(mud);
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING); mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_SENDING);
mud->keep_alive_tick = 0; // not need count anymore
} else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out. } else if(mud->connState == MQTT_CONNECT_SENT) { // wait for CONACK time out.
NODE_DBG("MQTT_CONNECT timeout.\n"); NODE_DBG("MQTT_CONNECT timeout.\n");
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
mqtt_socket_do_disconnect(mud); mqtt_socket_do_disconnect(mud);
mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING); mqtt_connack_fail(mud, MQTT_CONN_FAIL_TIMEOUT_RECEIVING);
} else if(mud->connState == MQTT_DATA){ } else if(mud->connState == MQTT_DATA){
msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); if(!msg_peek(&(mud->mqtt_state.pending_msg_q))) {
if(pending_msg){
mqtt_send_if_possible(mud);
} else {
// no queued event. // no queued event.
mud->keep_alive_tick ++; if (mud->keepalive_sent) {
if(mud->keep_alive_tick > mud->connect_info.keepalive){ // Oh dear -- keepalive timer expired and still no ack of previous message
if (mud->keepalive_sent) { mqtt_socket_do_disconnect(mud);
// Oh dear -- keepalive timer expired and still no ack of previous message } else {
mqtt_socket_reconnected(&mud->pesp_conn, 0); uint8_t temp_buffer[MQTT_BUF_SIZE];
} else { mqtt_message_buffer_t msgb;
uint8_t temp_buffer[MQTT_BUF_SIZE]; mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE);
mqtt_message_buffer_t msgb;
mqtt_msg_init(&msgb, temp_buffer, MQTT_BUF_SIZE); NODE_DBG("\r\nMQTT: Send keepalive packet\r\n");
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&msgb);
NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg,
mqtt_message_t* temp_msg = mqtt_msg_pingreq(&msgb); 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) );
msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, mud->keepalive_sent = 1;
0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); mqtt_send_if_possible(mud);
mud->keepalive_sent = 1;
mud->keep_alive_tick = 0; // Need to reset to zero in case flow control stopped.
mqtt_send_if_possible(mud);
}
} }
} }
} }
NODE_DBG("keep_alive_tick: %d\n", mud->keep_alive_tick);
NODE_DBG("leave mqtt_socket_timer.\n"); NODE_DBG("leave mqtt_socket_timer.\n");
} }
...@@ -828,18 +827,7 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -828,18 +827,7 @@ static int mqtt_socket_client( lua_State* L )
NODE_DBG("enter mqtt_socket_client.\n"); NODE_DBG("enter mqtt_socket_client.\n");
lmqtt_userdata *mud; lmqtt_userdata *mud;
char tempid[20] = {0};
sprintf(tempid, "%s%x", "NodeMCU_", system_get_chip_id() );
NODE_DBG(tempid);
NODE_DBG("\n");
const char *clientId = tempid, *username = NULL, *password = NULL;
size_t idl = strlen(tempid);
size_t unl = 0, pwl = 0;
int keepalive = 0;
int stack = 1; int stack = 1;
int clean_session = 1;
int max_message_length = 0;
int top = lua_gettop(L); int top = lua_gettop(L);
// create a object // create a object
...@@ -857,6 +845,12 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -857,6 +845,12 @@ static int mqtt_socket_client( lua_State* L )
mud->cb_unsuback_ref = LUA_NOREF; mud->cb_unsuback_ref = LUA_NOREF;
mud->cb_puback_ref = LUA_NOREF; mud->cb_puback_ref = LUA_NOREF;
mud->conf.client_id_ref = LUA_NOREF;
mud->conf.username_ref = LUA_NOREF;
mud->conf.password_ref = LUA_NOREF;
mud->conf.will_topic_ref = LUA_NOREF;
mud->conf.will_message_ref = LUA_NOREF;
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
// set its metatable // set its metatable
...@@ -865,93 +859,52 @@ static int mqtt_socket_client( lua_State* L ) ...@@ -865,93 +859,52 @@ static int mqtt_socket_client( lua_State* L )
if( lua_isstring(L,stack) ) // deal with the clientid string if( lua_isstring(L,stack) ) // deal with the clientid string
{ {
clientId = luaL_checklstring( L, stack, &idl ); lua_pushvalue(L, stack);
stack++; stack++;
} else {
char tempid[20] = {0};
sprintf(tempid, "%s%x", "NodeMCU_", system_get_chip_id() );
lua_pushstring(L, tempid);
} }
mud->conf.client_id_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if(lua_isnumber( L, stack )) if(lua_isnumber( L, stack ))
{ {
keepalive = luaL_checkinteger( L, stack); mud->conf.keepalive = luaL_checkinteger( L, stack);
stack++; stack++;
} }
if(mud->conf.keepalive == 0) {
if(keepalive == 0){ mud->conf.keepalive = MQTT_DEFAULT_KEEPALIVE;
keepalive = MQTT_DEFAULT_KEEPALIVE;
} }
if(lua_isstring( L, stack )){ if(lua_isstring( L, stack )){
username = luaL_checklstring( L, stack, &unl ); lua_pushvalue(L, stack);
mud->conf.username_ref = luaL_ref(L, LUA_REGISTRYINDEX);
stack++; stack++;
} }
if(username == NULL)
unl = 0;
NODE_DBG("length username: %d\r\n", unl);
if(lua_isstring( L, stack )){ if(lua_isstring( L, stack )){
password = luaL_checklstring( L, stack, &pwl ); lua_pushvalue(L, stack);
mud->conf.password_ref = luaL_ref(L, LUA_REGISTRYINDEX);
stack++; stack++;
} }
if(password == NULL)
pwl = 0;
NODE_DBG("length password: %d\r\n", pwl);
if(lua_isnumber( L, stack )) if(lua_isnumber( L, stack ))
{ {
clean_session = luaL_checkinteger( L, stack); mud->conf.flags.clean_session = !!luaL_checkinteger(L, stack);
stack++; stack++;
} }
if(clean_session > 1){
clean_session = 1;
}
if(lua_isnumber( L, stack )) if(lua_isnumber( L, stack ))
{ {
max_message_length = luaL_checkinteger( L, stack); mud->conf.max_message_length = luaL_checkinteger( L, stack);
stack++; stack++;
} }
if(mud->conf.max_message_length == 0) {
if(max_message_length == 0) { mud->conf.max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
max_message_length = DEFAULT_MAX_MESSAGE_LENGTH;
} }
// TODO: check the zalloc result.
mud->connect_info.client_id = (uint8_t *)calloc(1,idl+1);
mud->connect_info.username = (uint8_t *)calloc(1,unl + 1);
mud->connect_info.password = (uint8_t *)calloc(1,pwl + 1);
if(!mud->connect_info.client_id || !mud->connect_info.username || !mud->connect_info.password){
if(mud->connect_info.client_id) {
free(mud->connect_info.client_id);
mud->connect_info.client_id = NULL;
}
if(mud->connect_info.username) {
free(mud->connect_info.username);
mud->connect_info.username = NULL;
}
if(mud->connect_info.password) {
free(mud->connect_info.password);
mud->connect_info.password = NULL;
}
return luaL_error(L, "not enough memory");
}
memcpy(mud->connect_info.client_id, clientId, idl);
mud->connect_info.client_id[idl] = 0;
memcpy(mud->connect_info.username, username, unl);
mud->connect_info.username[unl] = 0;
memcpy(mud->connect_info.password, password, pwl);
mud->connect_info.password[pwl] = 0;
NODE_DBG("MQTT: Init info: %s, %s, %s\r\n", mud->connect_info.client_id, mud->connect_info.username, mud->connect_info.password);
mud->connect_info.clean_session = clean_session;
mud->connect_info.will_qos = 0;
mud->connect_info.will_retain = 0;
mud->connect_info.keepalive = keepalive;
mud->connect_info.max_message_length = max_message_length;
mud->mqtt_state.pending_msg_q = NULL; mud->mqtt_state.pending_msg_q = NULL;
mud->mqtt_state.port = 1883;
mud->mqtt_state.recv_buffer = NULL; mud->mqtt_state.recv_buffer = NULL;
mud->mqtt_state.recv_buffer_size = 0; mud->mqtt_state.recv_buffer_size = 0;
mud->mqtt_state.recv_buffer_state = MQTT_RECV_NORMAL; mud->mqtt_state.recv_buffer_state = MQTT_RECV_NORMAL;
...@@ -968,11 +921,6 @@ static int mqtt_delete( lua_State* L ) ...@@ -968,11 +921,6 @@ static int mqtt_delete( lua_State* L )
NODE_DBG("enter mqtt_delete.\n"); NODE_DBG("enter mqtt_delete.\n");
lmqtt_userdata *mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); lmqtt_userdata *mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket");
luaL_argcheck(L, mud, 1, "mqtt.socket expected");
if(mud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
os_timer_disarm(&mud->mqttTimer); os_timer_disarm(&mud->mqttTimer);
mud->connected = false; mud->connected = false;
...@@ -986,18 +934,6 @@ static int mqtt_delete( lua_State* L ) ...@@ -986,18 +934,6 @@ static int mqtt_delete( lua_State* L )
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} }
// ---- alloc-ed in mqtt_socket_lwt()
if(mud->connect_info.will_topic){
free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL;
}
if(mud->connect_info.will_message){
free(mud->connect_info.will_message);
mud->connect_info.will_message = NULL;
}
// ----
//--------- alloc-ed in mqtt_socket_received() //--------- alloc-ed in mqtt_socket_received()
if(mud->mqtt_state.recv_buffer) { if(mud->mqtt_state.recv_buffer) {
free(mud->mqtt_state.recv_buffer); free(mud->mqtt_state.recv_buffer);
...@@ -1005,21 +941,6 @@ static int mqtt_delete( lua_State* L ) ...@@ -1005,21 +941,6 @@ static int mqtt_delete( lua_State* L )
} }
// ---- // ----
//--------- alloc-ed in mqtt_socket_client()
if(mud->connect_info.client_id){
free(mud->connect_info.client_id);
mud->connect_info.client_id = NULL;
}
if(mud->connect_info.username){
free(mud->connect_info.username);
mud->connect_info.username = NULL;
}
if(mud->connect_info.password){
free(mud->connect_info.password);
mud->connect_info.password = NULL;
}
// -------
// free (unref) callback ref // free (unref) callback ref
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref); luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref);
mud->cb_connect_ref = LUA_NOREF; mud->cb_connect_ref = LUA_NOREF;
...@@ -1038,6 +959,17 @@ static int mqtt_delete( lua_State* L ) ...@@ -1038,6 +959,17 @@ static int mqtt_delete( lua_State* L )
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref);
mud->cb_puback_ref = LUA_NOREF; mud->cb_puback_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->conf.client_id_ref);
mud->conf.client_id_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->conf.username_ref);
mud->conf.username_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->conf.password_ref);
mud->conf.password_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->conf.will_topic_ref);
mud->conf.will_topic_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->conf.will_message_ref);
mud->conf.will_message_ref = LUA_NOREF;
int selfref = mud->self_ref; int selfref = mud->self_ref;
mud->self_ref = LUA_NOREF; mud->self_ref = LUA_NOREF;
luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref); luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref);
...@@ -1049,14 +981,14 @@ static int mqtt_delete( lua_State* L ) ...@@ -1049,14 +981,14 @@ static int mqtt_delete( lua_State* L )
static sint8 mqtt_socket_do_connect(struct lmqtt_userdata *mud) static sint8 mqtt_socket_do_connect(struct lmqtt_userdata *mud)
{ {
NODE_DBG("enter socket_connect.\n"); NODE_DBG("enter mqtt_socket_do_connect.\n");
sint8 espconn_status; sint8 espconn_status;
mud->event_timeout = MQTT_CONNECT_TIMEOUT;
mud->connState = MQTT_INIT; mud->connState = MQTT_INIT;
#ifdef CLIENT_SSL_ENABLE #ifdef CLIENT_SSL_ENABLE
if(mud->secure) if(mud->conf.flags.secure)
{ {
NODE_DBG("mqtt_socket_do_connect using espconn_secure\n");
espconn_status = espconn_secure_connect(&mud->pesp_conn); espconn_status = espconn_secure_connect(&mud->pesp_conn);
} }
else else
...@@ -1065,48 +997,31 @@ static sint8 mqtt_socket_do_connect(struct lmqtt_userdata *mud) ...@@ -1065,48 +997,31 @@ static sint8 mqtt_socket_do_connect(struct lmqtt_userdata *mud)
espconn_status = espconn_connect(&mud->pesp_conn); espconn_status = espconn_connect(&mud->pesp_conn);
} }
os_timer_arm(&mud->mqttTimer, 1000, 1); NODE_DBG("leave mqtt_socket_do_connect espconn_status=%d\n", espconn_status);
NODE_DBG("leave socket_connect\n");
return espconn_status; return espconn_status;
} }
static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
{ {
lmqtt_userdata *mud = arg; lmqtt_userdata *mud = arg;
NODE_DBG("enter socket_dns_found.\n"); if((ipaddr == NULL) || (ipaddr->addr == 0))
sint8 espconn_status = ESPCONN_OK;
if(ipaddr == NULL)
{ {
NODE_DBG("socket_dns_found failure\n");
mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS); mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS);
// although not connected, but fire disconnect callback to release every thing. // although not connected, but fire disconnect callback to release every thing.
mqtt_socket_disconnected(arg); mqtt_socket_disconnected(arg);
return -1; return;
}
// ipaddr->addr is a uint32_t ip
if(ipaddr->addr != 0)
{
memcpy(&mud->pesp_conn.proto.tcp->remote_ip, &(ipaddr->addr), 4);
NODE_DBG("TCP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
NODE_DBG("\n");
espconn_status = mqtt_socket_do_connect(mud);
} }
NODE_DBG("leave socket_dns_found.\n"); memcpy(&mud->pesp_conn.proto.tcp->remote_ip, &(ipaddr->addr), 4);
NODE_DBG("socket_dns_found success: ");
return espconn_status; NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr)));
} NODE_DBG("\n");
/* wrapper for using socket_dns_found() as callback function */ mqtt_socket_do_connect(mud);
static void socket_dns_foundcb(const char *name, ip_addr_t *ipaddr, void *arg)
{
socket_dns_found(name, ipaddr, arg);
} }
#include "pm/swtimer.h" #include "pm/swtimer.h"
...@@ -1120,76 +1035,47 @@ static int mqtt_socket_connect( lua_State* L ) ...@@ -1120,76 +1035,47 @@ static int mqtt_socket_connect( lua_State* L )
ip_addr_t ipaddr; ip_addr_t ipaddr;
const char *domain; const char *domain;
int stack = 1; int stack = 1;
unsigned secure = 0;
int top = lua_gettop(L); int top = lua_gettop(L);
mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket");
luaL_argcheck(L, mud, stack, "mqtt.socket expected");
stack++; stack++;
if(mud == NULL)
return 0;
if(mud->connected){
return luaL_error(L, "already connected");
}
struct espconn *pesp_conn = &mud->pesp_conn; struct espconn *pesp_conn = &mud->pesp_conn;
if (!pesp_conn->proto.tcp) if(mud->connected){
pesp_conn->proto.tcp = (esp_tcp *)calloc(1,sizeof(esp_tcp)); return luaL_error(L, "already connected");
if(!pesp_conn->proto.tcp) {
return luaL_error(L, "not enough memory");
} }
// reverse is for the callback function
pesp_conn->type = ESPCONN_TCP;
pesp_conn->state = ESPCONN_NONE;
mud->connected = false; mud->connected = false;
mud->sending = false;
if( (stack<=top) && lua_isstring(L,stack) ) // deal with the domain string if( (stack<=top) && lua_isstring(L,stack) ) // deal with the domain string
{ {
domain = luaL_checklstring( L, stack, &il ); domain = luaL_checklstring( L, stack, &il );
if (!domain)
return luaL_error(L, "invalid domain");
stack++; stack++;
if (domain == NULL)
{
domain = "127.0.0.1";
}
ipaddr.addr = ipaddr_addr(domain);
memcpy(pesp_conn->proto.tcp->remote_ip, &ipaddr.addr, 4);
NODE_DBG("TCP ip is set: ");
NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
NODE_DBG("\n");
} }
if ( (stack<=top) && lua_isnumber(L, stack) ) if ( (stack<=top) && lua_isnumber(L, stack) )
{ {
port = lua_tointeger(L, stack); port = lua_tointeger(L, stack);
stack++; stack++;
NODE_DBG("TCP port is set: %d.\n", port);
} }
pesp_conn->proto.tcp->remote_port = port;
if (pesp_conn->proto.tcp->local_port == 0)
pesp_conn->proto.tcp->local_port = espconn_port();
mud->mqtt_state.port = port;
if ( (stack<=top) && (lua_isnumber(L, stack) || lua_isboolean(L, stack)) ) if ( (stack<=top) && (lua_isnumber(L, stack) || lua_isboolean(L, stack)) )
{ {
if (lua_isnumber(L, stack)) { if (lua_isnumber(L, stack)) {
platform_print_deprecation_note("mqtt.connect secure parameter as integer","in the future"); platform_print_deprecation_note("mqtt.connect secure parameter as integer","in the future");
secure = !!lua_tointeger(L, stack); mud->conf.flags.secure = !!lua_tointeger(L, stack);
} else { } else {
secure = lua_toboolean(L, stack); mud->conf.flags.secure = lua_toboolean(L, stack);
} }
stack++; stack++;
} else {
secure = 0; // default to 0
} }
#ifdef CLIENT_SSL_ENABLE
mud->secure = secure; // save #ifndef CLIENT_SSL_ENABLE
#else if ( mud->conf.flags.secure )
if ( secure )
{ {
return luaL_error(L, "ssl not available"); return luaL_error(L, "ssl not available");
} }
...@@ -1211,10 +1097,24 @@ static int mqtt_socket_connect( lua_State* L ) ...@@ -1211,10 +1097,24 @@ static int mqtt_socket_connect( lua_State* L )
mud->cb_connect_fail_ref = luaL_ref(L, LUA_REGISTRYINDEX); mud->cb_connect_fail_ref = luaL_ref(L, LUA_REGISTRYINDEX);
} }
lua_pushvalue(L, 1); // copy userdata to the top of stack if (!pesp_conn->proto.tcp)
pesp_conn->proto.tcp = (esp_tcp *)calloc(1,sizeof(esp_tcp));
if(!pesp_conn->proto.tcp) {
return luaL_error(L, "not enough memory");
}
luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref); luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref);
lua_pushvalue(L, 1); // copy userdata and persist it in the registry
mud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); mud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
pesp_conn->type = ESPCONN_TCP;
pesp_conn->state = ESPCONN_NONE;
pesp_conn->proto.tcp->remote_port = port;
if (pesp_conn->proto.tcp->local_port == 0)
pesp_conn->proto.tcp->local_port = espconn_port();
espconn_regist_connectcb(pesp_conn, mqtt_socket_connected); espconn_regist_connectcb(pesp_conn, mqtt_socket_connected);
espconn_regist_reconcb(pesp_conn, mqtt_socket_reconnected); espconn_regist_reconcb(pesp_conn, mqtt_socket_reconnected);
...@@ -1225,24 +1125,17 @@ static int mqtt_socket_connect( lua_State* L ) ...@@ -1225,24 +1125,17 @@ static int mqtt_socket_connect( lua_State* L )
//My guess: If in doubt, resume the timer //My guess: If in doubt, resume the timer
// timer started in socket_connect() // timer started in socket_connect()
if((ipaddr.addr == IPADDR_NONE) && (memcmp(domain,"255.255.255.255",16) != 0)) ip_addr_t host_ip;
{ switch (dns_gethostbyname(domain, &host_ip, socket_dns_found, mud))
ip_addr_t host_ip;
switch (dns_gethostbyname(domain, &host_ip, socket_dns_foundcb, mud))
{
case ERR_OK:
socket_dns_found(domain, &host_ip, mud); // ip is returned in host_ip.
break;
case ERR_INPROGRESS:
break;
default:
// Something has gone wrong; bail out?
mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS);
}
}
else
{ {
mqtt_socket_do_connect(mud); case ERR_OK:
socket_dns_found(domain, &host_ip, mud); // ip is returned in host_ip.
break;
case ERR_INPROGRESS:
break;
default:
// Something has gone wrong; bail out?
mqtt_connack_fail(mud, MQTT_CONN_FAIL_DNS);
} }
NODE_DBG("leave mqtt_socket_connect.\n"); NODE_DBG("leave mqtt_socket_connect.\n");
...@@ -1255,17 +1148,11 @@ static int mqtt_socket_connect( lua_State* L ) ...@@ -1255,17 +1148,11 @@ static int mqtt_socket_connect( lua_State* L )
static int mqtt_socket_close( lua_State* L ) static int mqtt_socket_close( lua_State* L )
{ {
NODE_DBG("enter mqtt_socket_close.\n"); NODE_DBG("enter mqtt_socket_close.\n");
int i = 0;
lmqtt_userdata *mud = NULL; lmqtt_userdata *mud = NULL;
mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket");
luaL_argcheck(L, mud, 1, "mqtt.socket expected");
if (mud == NULL) {
lua_pushboolean(L, 0);
return 1;
}
sint8 espconn_status = ESPCONN_CONN; sint8 espconn_status = 0;
if (mud->connected) { if (mud->connected) {
uint8_t temp_buffer[MQTT_BUF_SIZE]; uint8_t temp_buffer[MQTT_BUF_SIZE];
mqtt_message_buffer_t msgb; mqtt_message_buffer_t msgb;
...@@ -1275,34 +1162,23 @@ static int mqtt_socket_close( lua_State* L ) ...@@ -1275,34 +1162,23 @@ static int mqtt_socket_close( lua_State* L )
mqtt_message_t* temp_msg = mqtt_msg_disconnect(&msgb); mqtt_message_t* temp_msg = mqtt_msg_disconnect(&msgb);
NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]); NODE_DBG("Send MQTT disconnect infomation, data len: %d, d[0]=%d \r\n", temp_msg->length, temp_msg->data[0]);
/* XXX This fails to actually send the disconnect message before hanging up */ if (!msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, 0,
#ifdef CLIENT_SSL_ENABLE MQTT_MSG_TYPE_DISCONNECT, 0))
if(mud->secure) { goto err;
espconn_status = espconn_secure_send(&mud->pesp_conn, temp_msg->data, temp_msg->length);
if(mud->pesp_conn.proto.tcp->remote_port || mud->pesp_conn.proto.tcp->local_port)
espconn_status |= espconn_secure_disconnect(&mud->pesp_conn);
} else
#endif
{
espconn_status = espconn_send(&mud->pesp_conn, temp_msg->data, temp_msg->length);
if(mud->pesp_conn.proto.tcp->remote_port || mud->pesp_conn.proto.tcp->local_port)
espconn_status |= espconn_disconnect(&mud->pesp_conn);
}
}
mud->connected = false;
while (mud->mqtt_state.pending_msg_q) { mqtt_send_if_possible(mud);
msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q)));
} }
NODE_DBG("leave mqtt_socket_close.\n"); NODE_DBG("leave mqtt_socket_close.\n");
return 0;
if (espconn_status == ESPCONN_OK) { err:
lua_pushboolean(L, 1); /* OOM while trying to build the disconnect message. Fail the connection */
} else { mqtt_socket_do_disconnect(mud);
lua_pushboolean(L, 0);
} NODE_DBG("leave mqtt_socket_close on error path\n");
return 1;
return 0;
} }
// Lua: mqtt:on( "method", function() ) // Lua: mqtt:on( "method", function() )
...@@ -1313,47 +1189,51 @@ static int mqtt_socket_on( lua_State* L ) ...@@ -1313,47 +1189,51 @@ static int mqtt_socket_on( lua_State* L )
size_t sl; size_t sl;
mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket");
luaL_argcheck(L, mud, 1, "mqtt.socket expected");
if(mud==NULL){
NODE_DBG("userdata is nil.\n");
return 0;
}
const char *method = luaL_checklstring( L, 2, &sl );
if (method == NULL)
return luaL_error( L, "wrong arg type" );
luaL_checktype(L, 3, LUA_TFUNCTION); luaL_checktype(L, 3, LUA_TFUNCTION);
lua_pushvalue(L, 3); // copy argument (func) to the top of stack lua_pushvalue(L, 3); // copy argument (func) to the top of stack
if( sl == 7 && strcmp(method, "connect") == 0){ static const char * const cbnames[] = {
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref); "connect", "connfail", "offline",
mud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); "message", "overflow",
}else if( sl == 8 && strcmp(method, "connfail") == 0){ "puback", "suback", "unsuback",
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref); NULL
mud->cb_connect_fail_ref = luaL_ref(L, LUA_REGISTRYINDEX); };
}else if( sl == 7 && strcmp(method, "offline") == 0){ switch (luaL_checkoption(L, 2, NULL, cbnames)) {
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref); case 0:
mud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX); luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref);
}else if( sl == 7 && strcmp(method, "message") == 0){ mud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_message_ref); break;
mud->cb_message_ref = luaL_ref(L, LUA_REGISTRYINDEX); case 1:
}else if( sl == 8 && strcmp(method, "overflow") == 0){ luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_fail_ref);
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_overflow_ref); mud->cb_connect_fail_ref = luaL_ref(L, LUA_REGISTRYINDEX);
mud->cb_overflow_ref = luaL_ref(L, LUA_REGISTRYINDEX); break;
}else if( sl == 6 && strcmp(method, "puback") == 0){ case 2:
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref);
mud->cb_puback_ref = luaL_ref(L, LUA_REGISTRYINDEX); mud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}else if( sl == 6 && strcmp(method, "suback") == 0){ break;
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_suback_ref); case 3:
mud->cb_suback_ref = luaL_ref(L, LUA_REGISTRYINDEX); luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_message_ref);
}else if( sl == 8 && strcmp(method, "unsuback") == 0){ mud->cb_message_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_unsuback_ref); break;
mud->cb_unsuback_ref = luaL_ref(L, LUA_REGISTRYINDEX); case 4:
}else{ luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_overflow_ref);
lua_pop(L, 1); mud->cb_overflow_ref = luaL_ref(L, LUA_REGISTRYINDEX);
return luaL_error( L, "method not supported" ); break;
case 5:
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref);
mud->cb_puback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
break;
case 6:
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_suback_ref);
mud->cb_suback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
break;
case 7:
luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_unsuback_ref);
mud->cb_unsuback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
break;
} }
NODE_DBG("leave mqtt_socket_on.\n"); NODE_DBG("leave mqtt_socket_on.\n");
return 0; return 0;
} }
...@@ -1369,15 +1249,8 @@ static int mqtt_socket_unsubscribe( lua_State* L ) { ...@@ -1369,15 +1249,8 @@ static int mqtt_socket_unsubscribe( lua_State* L ) {
lmqtt_userdata *mud; lmqtt_userdata *mud;
mud = (lmqtt_userdata *) luaL_checkudata( L, stack, "mqtt.socket" ); mud = (lmqtt_userdata *) luaL_checkudata( L, stack, "mqtt.socket" );
luaL_argcheck( L, mud, stack, "mqtt.socket expected" );
stack++; stack++;
if(mud==NULL){
NODE_DBG("userdata is nil.\n");
lua_pushboolean(L, 0);
return 1;
}
if(!mud->connected){ if(!mud->connected){
luaL_error( L, "not connected" ); luaL_error( L, "not connected" );
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
...@@ -1451,7 +1324,7 @@ static int mqtt_socket_unsubscribe( lua_State* L ) { ...@@ -1451,7 +1324,7 @@ static int mqtt_socket_unsubscribe( lua_State* L ) {
msg_id, MQTT_MSG_TYPE_UNSUBSCRIBE, (int)mqtt_get_qos(temp_msg->data) ); msg_id, MQTT_MSG_TYPE_UNSUBSCRIBE, (int)mqtt_get_qos(temp_msg->data) );
NODE_DBG("topic: %s - id: %d - qos: %d, length: %d\n", topic, node->msg_id, node->publish_qos, node->msg.length); NODE_DBG("topic: %s - id: %d - qos: %d, length: %d\n", topic, node->msg_id, node->publish_qos, node->msg.length);
NODE_DBG("msg_size: %d, event_timeout: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)), mud->event_timeout); NODE_DBG("msg_size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
sint8 espconn_status = ESPCONN_IF; sint8 espconn_status = ESPCONN_IF;
...@@ -1478,15 +1351,8 @@ static int mqtt_socket_subscribe( lua_State* L ) { ...@@ -1478,15 +1351,8 @@ static int mqtt_socket_subscribe( lua_State* L ) {
lmqtt_userdata *mud; lmqtt_userdata *mud;
mud = (lmqtt_userdata *) luaL_checkudata( L, stack, "mqtt.socket" ); mud = (lmqtt_userdata *) luaL_checkudata( L, stack, "mqtt.socket" );
luaL_argcheck( L, mud, stack, "mqtt.socket expected" );
stack++; stack++;
if(mud==NULL){
NODE_DBG("userdata is nil.\n");
lua_pushboolean(L, 0);
return 1;
}
if(!mud->connected){ if(!mud->connected){
luaL_error( L, "not connected" ); luaL_error( L, "not connected" );
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
...@@ -1563,7 +1429,7 @@ static int mqtt_socket_subscribe( lua_State* L ) { ...@@ -1563,7 +1429,7 @@ static int mqtt_socket_subscribe( lua_State* L ) {
msg_id, MQTT_MSG_TYPE_SUBSCRIBE, (int)mqtt_get_qos(temp_msg->data) ); msg_id, MQTT_MSG_TYPE_SUBSCRIBE, (int)mqtt_get_qos(temp_msg->data) );
NODE_DBG("topic: %s - id: %d - qos: %d, length: %d\n", topic, node->msg_id, node->publish_qos, node->msg.length); NODE_DBG("topic: %s - id: %d - qos: %d, length: %d\n", topic, node->msg_id, node->publish_qos, node->msg.length);
NODE_DBG("msg_size: %d, event_timeout: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)), mud->event_timeout); NODE_DBG("msg_size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q)));
sint8 espconn_status = ESPCONN_IF; sint8 espconn_status = ESPCONN_IF;
...@@ -1589,13 +1455,7 @@ static int mqtt_socket_publish( lua_State* L ) ...@@ -1589,13 +1455,7 @@ static int mqtt_socket_publish( lua_State* L )
uint16_t msg_id = 0; uint16_t msg_id = 0;
mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket");
luaL_argcheck(L, mud, stack, "mqtt.socket expected");
stack++; stack++;
if(mud==NULL){
NODE_DBG("userdata is nil.\n");
lua_pushboolean(L, 0);
return 1;
}
if(!mud->connected){ if(!mud->connected){
return luaL_error( L, "not connected" ); return luaL_error( L, "not connected" );
...@@ -1650,80 +1510,33 @@ static int mqtt_socket_publish( lua_State* L ) ...@@ -1650,80 +1510,33 @@ static int mqtt_socket_publish( lua_State* L )
return 1; return 1;
} }
// Lua: mqtt:lwt( topic, message, qos, retain, function(client) ) // Lua: mqtt:lwt( topic, message, [qos, [retain]])
static int mqtt_socket_lwt( lua_State* L ) static int mqtt_socket_lwt( lua_State* L )
{ {
NODE_DBG("enter mqtt_socket_lwt.\n"); NODE_DBG("enter mqtt_socket_lwt.\n");
uint8_t stack = 1;
size_t topicSize, msgSize;
NODE_DBG("mqtt_socket_lwt.\n");
lmqtt_userdata *mud = NULL;
const char *lwtTopic, *lwtMsg;
uint8_t lwtQoS, lwtRetain;
mud = (lmqtt_userdata *)luaL_checkudata( L, stack, "mqtt.socket" ); lmqtt_userdata *mud = luaL_checkudata( L, 1, "mqtt.socket" );
luaL_argcheck( L, mud, stack, "mqtt.socket expected" ); luaL_argcheck( L, lua_isstring(L, 2), 2, "need lwt topic");
luaL_argcheck( L, lua_isstring(L, 3), 3, "need lwt message");
if(mud == NULL) lua_pushvalue(L, 2);
return 0; luaL_reref(L, LUA_REGISTRYINDEX, &mud->conf.will_topic_ref);
stack++;
lwtTopic = luaL_checklstring( L, stack, &topicSize );
if (lwtTopic == NULL)
{
return luaL_error( L, "need lwt topic");
}
stack++; lua_pushvalue(L, 3);
lwtMsg = luaL_checklstring( L, stack, &msgSize ); luaL_reref(L, LUA_REGISTRYINDEX, &mud->conf.will_message_ref);
if (lwtMsg == NULL)
{
return luaL_error( L, "need lwt message");
}
stack++;
if(mud->connect_info.will_topic){ // free the previous one if there is any
free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL;
}
if(mud->connect_info.will_message){
free(mud->connect_info.will_message);
mud->connect_info.will_message = NULL;
}
mud->connect_info.will_topic = (uint8_t*) calloc(1, topicSize + 1 );
mud->connect_info.will_message = (uint8_t*) calloc(1, msgSize + 1 );
if(!mud->connect_info.will_topic || !mud->connect_info.will_message){
if(mud->connect_info.will_topic){
free(mud->connect_info.will_topic);
mud->connect_info.will_topic = NULL;
}
if(mud->connect_info.will_message){
free(mud->connect_info.will_message);
mud->connect_info.will_message = NULL;
}
return luaL_error( L, "not enough memory");
}
memcpy(mud->connect_info.will_topic, lwtTopic, topicSize);
mud->connect_info.will_topic[topicSize] = 0;
memcpy(mud->connect_info.will_message, lwtMsg, msgSize);
mud->connect_info.will_message[msgSize] = 0;
int stack = 4;
if ( lua_isnumber(L, stack) ) if ( lua_isnumber(L, stack) )
{ {
mud->connect_info.will_qos = lua_tointeger(L, stack); mud->conf.flags.will_qos = lua_tointeger(L, stack) & 0x3;
stack++; stack++;
} }
if ( lua_isnumber(L, stack) ) if ( lua_isnumber(L, stack) )
{ {
mud->connect_info.will_retain = lua_tointeger(L, stack); mud->conf.flags.will_retain = !!lua_tointeger(L, stack);
stack++; stack++;
} }
NODE_DBG("mqtt_socket_lwt: topic: %s, message: %s, qos: %d, retain: %d\n",
mud->connect_info.will_topic,
mud->connect_info.will_message,
mud->connect_info.will_qos,
mud->connect_info.will_retain);
NODE_DBG("leave mqtt_socket_lwt.\n"); NODE_DBG("leave mqtt_socket_lwt.\n");
return 0; return 0;
} }
......
...@@ -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, 2);
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1); 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 // SoftUART object without receive and transmit part would be useless
rx_gpio_id = 0xFF; if ((rx_gpio_id == 0xFF) && (tx_gpio_id == 0xFF)) {return luaL_error(L, "Not enough arguments");}
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 = (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
pin = luaL_checkinteger(L, 1); #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); 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 switch(lua_type(L, 3)) {
length -= length % 3; case LUA_TSTRING: {
pixels = luaL_checklstring(L, 2, &length);
// Copy payload and make sure first byte is < 0xFF (triggers break;
// constant current command, instead of PWM duty command) }
size_t i; case LUA_TUSERDATA: {
for (i = 0; i < length; i += 3) { pixbuf *buffer = pixbuf_from_lua_arg(L, 2);
buffer[i] = rgb[i]; luaL_argcheck(L, pixbuf_channels(buffer) == 3, 2, "Bad pixbuf format");
buffer[i + 1] = rgb[i + 1]; pixels = buffer->values;
buffer[i + 2] = rgb[i + 2]; length = 3 * buffer->npix;
break;
// Check for first byte }
if (buffer[i] == 0xff) default:
buffer[i] = 0xfe; 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;
} }
......
...@@ -30,11 +30,11 @@ static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF; ...@@ -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); static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
typedef struct{ typedef struct{
os_timer_t os; os_timer_t os;
sint32_t lua_ref; /* Reference to registered callback function */ sint32_t lua_ref; /* Reference to registered callback function */
sint32_t self_ref; /* Reference to UD registered slot */ sint32_t self_ref; /* Reference to UD registered slot */
uint32_t interval; uint32_t interval;
uint8_t mode; uint8_t mode;
} tmr_t; } tmr_t;
// The previous implementation extended the rtc counter to 64 bits, and then // The previous implementation extended the rtc counter to 64 bits, and then
...@@ -56,47 +56,47 @@ static sint32_t soft_watchdog = -1; ...@@ -56,47 +56,47 @@ static sint32_t soft_watchdog = -1;
static os_timer_t rtc_timer; static os_timer_t rtc_timer;
static void alarm_timer_common(void* arg){ static void alarm_timer_common(void* arg){
tmr_t *tmr = (tmr_t *) arg; tmr_t *tmr = (tmr_t *) arg;
if(tmr->lua_ref > 0) { if(tmr->lua_ref > 0) {
lua_State* L = lua_getstate(); lua_State* L = lua_getstate();
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref); lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->self_ref);
if (tmr->mode != TIMER_MODE_AUTO) { if (tmr->mode != TIMER_MODE_AUTO) {
if(tmr->mode == TIMER_MODE_SINGLE) { if(tmr->mode == TIMER_MODE_SINGLE) {
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref); luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref); luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
tmr->mode = TIMER_MODE_OFF; tmr->mode = TIMER_MODE_OFF;
} else if (tmr->mode == TIMER_MODE_SEMI) { } else if (tmr->mode == TIMER_MODE_SEMI) {
tmr->mode |= TIMER_IDLE_FLAG; tmr->mode |= TIMER_IDLE_FLAG;
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref); luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
} }
} }
luaL_pcallx(L, 1, 0); luaL_pcallx(L, 1, 0);
} }
} }
// Lua: tmr.delay( us ) // Lua: tmr.delay( us )
static int tmr_delay( lua_State* L ){ static int tmr_delay( lua_State* L ){
sint32_t us = luaL_checkinteger(L, 1); sint32_t us = luaL_checkinteger(L, 1);
luaL_argcheck(L, us>0, 1, "wrong arg range"); luaL_argcheck(L, us>0, 1, "wrong arg range");
while(us > 0){ while(us > 0){
os_delay_us(us >= 1000000 ? 1000000 : us); os_delay_us(us >= 1000000 ? 1000000 : us);
system_soft_wdt_feed (); system_soft_wdt_feed ();
us -= 1000000; us -= 1000000;
} }
return 0; return 0;
} }
// Lua: tmr.now() , return system timer in us // Lua: tmr.now() , return system timer in us
static int tmr_now(lua_State* L){ static int tmr_now(lua_State* L){
lua_pushinteger(L, (uint32_t) (0x7FFFFFFF & system_get_time())); lua_pushinteger(L, (uint32_t) (0x7FFFFFFF & system_get_time()));
return 1; return 1;
} }
// Lua: tmr.ccount() , returns CCOUNT register // Lua: tmr.ccount() , returns CCOUNT register
static int tmr_ccount(lua_State* L){ static int tmr_ccount(lua_State* L){
lua_pushinteger(L, CCOUNT_REG); lua_pushinteger(L, CCOUNT_REG);
return 1; return 1;
} }
/* /*
...@@ -106,69 +106,69 @@ static int tmr_ccount(lua_State* L){ ...@@ -106,69 +106,69 @@ static int tmr_ccount(lua_State* L){
// Lua: t:register( interval, mode, function ) // Lua: t:register( interval, mode, function )
static int tmr_register(lua_State* L) { static int tmr_register(lua_State* L) {
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2); uint32_t interval = luaL_checkinteger(L, 2);
uint8_t mode = luaL_checkinteger(L, 3); uint8_t mode = luaL_checkinteger(L, 3);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR); 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, (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"); luaL_argcheck(L, lua_isfunction(L, 4), 4, "Must be function");
//get the lua function reference //get the lua function reference
lua_pushvalue(L, 4); lua_pushvalue(L, 4);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
luaL_reref(L, LUA_REGISTRYINDEX, &tmr->lua_ref); luaL_reref(L, LUA_REGISTRYINDEX, &tmr->lua_ref);
tmr->mode = mode|TIMER_IDLE_FLAG; tmr->mode = mode|TIMER_IDLE_FLAG;
tmr->interval = interval; tmr->interval = interval;
os_timer_setfn(&tmr->os, alarm_timer_common, tmr); os_timer_setfn(&tmr->os, alarm_timer_common, tmr);
return 0; return 0;
} }
// Lua: t:start( [restart] ) // Lua: t:start( [restart] )
static int tmr_start(lua_State* L){ static int tmr_start(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
lua_settop(L, 2); lua_settop(L, 2);
luaL_argcheck(L, lua_isboolean(L, 2) || lua_isnil(L, 2), 2, "boolean expected"); luaL_argcheck(L, lua_isboolean(L, 2) || lua_isnil(L, 2), 2, "boolean expected");
int restart = lua_toboolean(L, 2); int restart = lua_toboolean(L, 2);
lua_settop(L, 1); /* we need to have userdata on top of the stack */ lua_settop(L, 1); /* we need to have userdata on top of the stack */
if (tmr->self_ref == LUA_NOREF) if (tmr->self_ref == LUA_NOREF)
tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); tmr->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
//we return false if the timer is not idle and is not to be restarted //we return false if the timer is not idle and is not to be restarted
int idle = tmr->mode&TIMER_IDLE_FLAG; int idle = tmr->mode&TIMER_IDLE_FLAG;
if(!(idle || restart)){ if(!(idle || restart)){
lua_pushboolean(L, false); lua_pushboolean(L, false);
}else{ }else{
if (!idle) {os_timer_disarm(&tmr->os);} if (!idle) {os_timer_disarm(&tmr->os);}
tmr->mode &= ~TIMER_IDLE_FLAG; tmr->mode &= ~TIMER_IDLE_FLAG;
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
lua_pushboolean(L, true); lua_pushboolean(L, true);
} }
return 1; return 1;
} }
// Lua: t:alarm( interval, repeat, function ) // Lua: t:alarm( interval, repeat, function )
static int tmr_alarm(lua_State* L){ static int tmr_alarm(lua_State* L){
tmr_register(L); tmr_register(L);
/* remove tmr.alarm's other then the 1st UD parameters from Lua stack. /* remove tmr.alarm's other then the 1st UD parameters from Lua stack.
tmr.start expects UD and optional restart parameter. */ tmr.start expects UD and optional restart parameter. */
lua_settop(L, 1); lua_settop(L, 1);
return tmr_start(L); return tmr_start(L);
} }
// Lua: t:stop() // Lua: t:stop()
static int tmr_stop(lua_State* L){ static int tmr_stop(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
int idle = tmr->mode == TIMER_MODE_OFF || (tmr->mode & TIMER_IDLE_FLAG); int idle = tmr->mode == TIMER_MODE_OFF || (tmr->mode & TIMER_IDLE_FLAG);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref); luaL_unref2(L, LUA_REGISTRYINDEX, tmr->self_ref);
if(!idle) if(!idle)
os_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
tmr->mode |= TIMER_IDLE_FLAG; tmr->mode |= TIMER_IDLE_FLAG;
lua_pushboolean(L, !idle); /* return false if the timer is idle (or not registered) */ lua_pushboolean(L, !idle); /* return false if the timer is idle (or not registered) */
return 1; return 1;
} }
#ifdef TIMER_SUSPEND_ENABLE #ifdef TIMER_SUSPEND_ENABLE
...@@ -179,174 +179,173 @@ static int tmr_stop(lua_State* L){ ...@@ -179,174 +179,173 @@ static int tmr_stop(lua_State* L){
#define tmr_suspend_all tmr_suspend_removed #define tmr_suspend_all tmr_suspend_removed
#define tmr_resume_all tmr_suspend_removed #define tmr_resume_all tmr_suspend_removed
static int tmr_suspend_removed(lua_State* L){ 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 #endif
// Lua: t:unregister() // Lua: t:unregister()
static int tmr_unregister(lua_State* L){ static int tmr_unregister(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); 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->self_ref);
luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref); luaL_unref2(L, LUA_REGISTRYINDEX, tmr->lua_ref);
if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
os_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
tmr->mode = TIMER_MODE_OFF; tmr->mode = TIMER_MODE_OFF;
return 0; return 0;
} }
// Lua: t:interval( interval ) // Lua: t:interval( interval )
static int tmr_interval(lua_State* L){ static int tmr_interval(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
uint32_t interval = luaL_checkinteger(L, 2); uint32_t interval = luaL_checkinteger(L, 2);
luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR); luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
if(tmr->mode != TIMER_MODE_OFF){ if(tmr->mode != TIMER_MODE_OFF){
tmr->interval = interval; tmr->interval = interval;
if(!(tmr->mode&TIMER_IDLE_FLAG)){ if(!(tmr->mode&TIMER_IDLE_FLAG)){
os_timer_disarm(&tmr->os); os_timer_disarm(&tmr->os);
os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO);
} }
} }
return 0; return 0;
} }
// Lua: t:state() // Lua: t:state()
static int tmr_state(lua_State* L){ static int tmr_state(lua_State* L){
tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer"); tmr_t *tmr = (tmr_t *) luaL_checkudata(L, 1, "tmr.timer");
if(tmr->mode == TIMER_MODE_OFF){ if(tmr->mode == TIMER_MODE_OFF){
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0); lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG)); lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG));
return 2; return 2;
} }
// Lua: tmr.wdclr() // Lua: tmr.wdclr()
static int tmr_wdclr( lua_State* L ){ static int tmr_wdclr( lua_State* L ){
system_soft_wdt_feed (); system_soft_wdt_feed ();
return 0; return 0;
} }
// The on ESP8266 system_rtc_clock_cali_proc() returns a fixed point value // 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. // (12 bit fraction part), giving how many rtc clock ticks represent 1us.
// The high 64 bits of the uint64_t multiplication are not needed) // The high 64 bits of the uint64_t multiplication are not needed)
static uint32_t rtc2usec(uint64_t rtc){ 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 // This returns the number of microseconds uptime. Note that it relies on
// the rtc clock, which is notoriously temperature dependent // the rtc clock, which is notoriously temperature dependent
inline static uint64_t rtc_timer_update(bool do_calibration){ inline static uint64_t rtc_timer_update(bool do_calibration){
if (do_calibration || rtc_time_cali==0) if (do_calibration || rtc_time_cali==0)
rtc_time_cali=system_rtc_clock_cali_proc(); rtc_time_cali=system_rtc_clock_cali_proc();
uint32_t current = system_get_rtc_time(); uint32_t current = system_get_rtc_time();
uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound
uint32_t us_since_last=rtc2usec(since_last); uint32_t us_since_last=rtc2usec(since_last);
uint64_t now=last_rtc_time_us+us_since_last; uint64_t now=last_rtc_time_us+us_since_last;
// Only update if at least 100ms has passed since we last updated. // Only update if at least 100ms has passed since we last updated.
// This prevents the rounding errors in rtc2usec from accumulating // This prevents the rounding errors in rtc2usec from accumulating
if (us_since_last>=100000){ if (us_since_last>=100000){
last_rtc_time=current; last_rtc_time=current;
last_rtc_time_us=now; last_rtc_time_us=now;
} }
return now; return now;
} }
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();
} }
} }
// Lua: tmr.time() , return rtc time in second // Lua: tmr.time() , return rtc time in second
static int tmr_time( lua_State* L ){ static int tmr_time( lua_State* L ){
uint64_t us=rtc_timer_update(false); uint64_t us=rtc_timer_update(false);
lua_pushinteger(L, us/1000000); lua_pushinteger(L, us/1000000);
return 1; return 1;
} }
// 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;
} }
// Lua: tmr.create() // Lua: tmr.create()
static int tmr_create( lua_State *L ) { static int tmr_create( lua_State *L ) {
tmr_t *ud = (tmr_t *)lua_newuserdata(L, sizeof(*ud)); tmr_t *ud = (tmr_t *)lua_newuserdata(L, sizeof(*ud));
luaL_getmetatable(L, "tmr.timer"); luaL_getmetatable(L, "tmr.timer");
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
*ud = (tmr_t) {{0}, LUA_NOREF, LUA_NOREF, 0, TIMER_MODE_OFF}; *ud = (tmr_t) {{0}, LUA_NOREF, LUA_NOREF, 0, TIMER_MODE_OFF};
return 1; return 1;
} }
// Module function map // Module function map
LROT_BEGIN(tmr_dyn, NULL, LROT_MASK_GC_INDEX) LROT_BEGIN(tmr_dyn, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, tmr_unregister ) LROT_FUNCENTRY( __gc, tmr_unregister )
LROT_TABENTRY( __index, tmr_dyn ) LROT_TABENTRY( __index, tmr_dyn )
LROT_FUNCENTRY( register, tmr_register ) LROT_FUNCENTRY( register, tmr_register )
LROT_FUNCENTRY( alarm, tmr_alarm ) LROT_FUNCENTRY( alarm, tmr_alarm )
LROT_FUNCENTRY( start, tmr_start ) LROT_FUNCENTRY( start, tmr_start )
LROT_FUNCENTRY( stop, tmr_stop ) LROT_FUNCENTRY( stop, tmr_stop )
LROT_FUNCENTRY( unregister, tmr_unregister ) LROT_FUNCENTRY( unregister, tmr_unregister )
LROT_FUNCENTRY( state, tmr_state ) LROT_FUNCENTRY( state, tmr_state )
LROT_FUNCENTRY( interval, tmr_interval ) LROT_FUNCENTRY( interval, tmr_interval )
#ifdef TIMER_SUSPEND_ENABLE #ifdef TIMER_SUSPEND_ENABLE
LROT_FUNCENTRY( suspend, tmr_suspend ) LROT_FUNCENTRY( suspend, tmr_suspend )
LROT_FUNCENTRY( resume, tmr_resume ) LROT_FUNCENTRY( resume, tmr_resume )
#endif #endif
LROT_END(tmr_dyn, NULL, LROT_MASK_GC_INDEX) LROT_END(tmr_dyn, NULL, LROT_MASK_GC_INDEX)
LROT_BEGIN(tmr, NULL, 0) LROT_BEGIN(tmr, NULL, 0)
LROT_FUNCENTRY( delay, tmr_delay ) LROT_FUNCENTRY( delay, tmr_delay )
LROT_FUNCENTRY( now, tmr_now ) LROT_FUNCENTRY( now, tmr_now )
LROT_FUNCENTRY( wdclr, tmr_wdclr ) LROT_FUNCENTRY( wdclr, tmr_wdclr )
LROT_FUNCENTRY( softwd, tmr_softwd ) LROT_FUNCENTRY( softwd, tmr_softwd )
LROT_FUNCENTRY( time, tmr_time ) LROT_FUNCENTRY( time, tmr_time )
LROT_FUNCENTRY( ccount, tmr_ccount ) LROT_FUNCENTRY( ccount, tmr_ccount )
#ifdef TIMER_SUSPEND_ENABLE #ifdef TIMER_SUSPEND_ENABLE
LROT_FUNCENTRY( suspend_all, tmr_suspend_all ) LROT_FUNCENTRY( suspend_all, tmr_suspend_all )
LROT_FUNCENTRY( resume_all, tmr_resume_all ) LROT_FUNCENTRY( resume_all, tmr_resume_all )
#endif #endif
LROT_FUNCENTRY( create, tmr_create ) LROT_FUNCENTRY( create, tmr_create )
LROT_NUMENTRY( ALARM_SINGLE, TIMER_MODE_SINGLE ) LROT_NUMENTRY( ALARM_SINGLE, TIMER_MODE_SINGLE )
LROT_NUMENTRY( ALARM_SEMI, TIMER_MODE_SEMI ) LROT_NUMENTRY( ALARM_SEMI, TIMER_MODE_SEMI )
LROT_NUMENTRY( ALARM_AUTO, TIMER_MODE_AUTO ) LROT_NUMENTRY( ALARM_AUTO, TIMER_MODE_AUTO )
LROT_END(tmr, NULL, 0) LROT_END(tmr, NULL, 0)
#include "pm/swtimer.h" #include "pm/swtimer.h"
int luaopen_tmr( lua_State *L ){ 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=system_get_rtc_time(); // Right now is time 0
last_rtc_time_us=0; last_rtc_time_us=0;
os_timer_disarm(&rtc_timer); os_timer_disarm(&rtc_timer);
os_timer_setfn(&rtc_timer, rtc_callback, NULL); os_timer_setfn(&rtc_timer, rtc_callback, NULL);
os_timer_arm(&rtc_timer, 1000, 1); os_timer_arm(&rtc_timer, 1000, 1);
// The function rtc_callback calls the a function that calibrates the SoftRTC // 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 // 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. // there is bound to be some drift in the clock, so a calibration is due.
SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME); SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
// The function alarm_timer_common handles timers created by the developer via // The function alarm_timer_common handles timers created by the developer via
// tmr.create(). No reason not to resume the timers, so resume em'. // tmr.create(). No reason not to resume the timers, so resume em'.
SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME); SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
return 0; return 0;
} }
NODEMCU_MODULE(TMR, "tmr", tmr, luaopen_tmr); NODEMCU_MODULE(TMR, "tmr", tmr, luaopen_tmr);
...@@ -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