diff --git a/Makefile b/Makefile index 6b4a545b..7de24a3b 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ else endif endif ############################################################# -ESPTOOL = ../tools/esptool.py +ESPTOOL ?= ../tools/esptool.py CSRCS ?= $(wildcard *.c) diff --git a/README.md b/README.md index 35c5d127..9b37316e 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ pre_build/latest/nodemcu_512k_latest.bin is removed. use pre_build/latest/nodemc #define LUA_USE_MODULES_UART #define LUA_USE_MODULES_OW #define LUA_USE_MODULES_BIT +#define LUA_USE_MODULES_WS2812 #endif /* LUA_USE_MODULES */ ... // LUA_NUMBER_INTEGRAL @@ -330,3 +331,13 @@ cu:send("hello") ds18b20 = nil package.loaded["ds18b20"]=nil ``` + +####Control a WS2812 based light strip +```lua + -- set the color of one LED on GPIO 2 to red + ws2812.write(4, string.char(0, 255, 0)) + -- set the color of 10 LEDs on GPIO 0 to blue + ws2812.write(3, string.char(0, 0, 255):rep(10)) + -- first LED green, second LED white + ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) +``` diff --git a/app/include/lwip/app/espconn.h b/app/include/lwip/app/espconn.h index e6e60fd1..6eda9e3a 100644 --- a/app/include/lwip/app/espconn.h +++ b/app/include/lwip/app/espconn.h @@ -394,5 +394,21 @@ extern sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); *******************************************************************************/ extern sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip); +/****************************************************************************** + * FunctionName : espconn_recv_hold + * Description : hold tcp receive + * Parameters : espconn -- espconn to hold + * Returns : none +*******************************************************************************/ +extern sint8 espconn_recv_hold(struct espconn *pespconn); + +/****************************************************************************** + * FunctionName : espconn_recv_unhold + * Description : unhold tcp receive + * Parameters : espconn -- espconn to unhold + * Returns : none +*******************************************************************************/ +extern sint8 espconn_recv_unhold(struct espconn *pespconn); + #endif diff --git a/app/include/lwip/tcp.h b/app/include/lwip/tcp.h index 909ff9b7..7856a92d 100644 --- a/app/include/lwip/tcp.h +++ b/app/include/lwip/tcp.h @@ -277,6 +277,8 @@ struct tcp_pcb { /* KEEPALIVE counter */ u8_t keep_cnt_sent; + + u8_t hold; }; struct tcp_pcb_listen { diff --git a/app/include/user_config.h b/app/include/user_config.h index f8e57477..b62fd87b 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -63,6 +63,7 @@ #define LUA_USE_MODULES_OW #define LUA_USE_MODULES_BIT #define LUA_USE_MODULES_MQTT +#define LUA_USE_MODULES_WS2812 #endif /* LUA_USE_MODULES */ // #define LUA_NUMBER_INTEGRAL diff --git a/app/lwip/app/espconn.c b/app/lwip/app/espconn.c index bd81e4d6..114f1b59 100644 --- a/app/lwip/app/espconn.c +++ b/app/lwip/app/espconn.c @@ -718,3 +718,32 @@ espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t return dns_gethostbyname(hostname, addr, found, pespconn); } +sint8 espconn_recv_hold(struct espconn *pespconn) { + espconn_msg *pnode = NULL; + + if (pespconn == NULL) { + return ESPCONN_ARG; + } + pespconn->state = ESPCONN_WRITE; + if (!espconn_find_connection(pespconn, &pnode)) { + return ESPCONN_ARG; + } + + espconn_tcp_hold(pnode); + return ESPCONN_OK; +} + +sint8 espconn_recv_unhold(struct espconn *pespconn) { + espconn_msg *pnode = NULL; + + if (pespconn == NULL) { + return ESPCONN_ARG; + } + pespconn->state = ESPCONN_WRITE; + if (!espconn_find_connection(pespconn, &pnode)) { + return ESPCONN_ARG; + } + + espconn_tcp_unhold(pnode); + return ESPCONN_OK; +} diff --git a/app/lwip/app/espconn_tcp.c b/app/lwip/app/espconn_tcp.c index 91843887..da5c441a 100644 --- a/app/lwip/app/espconn_tcp.c +++ b/app/lwip/app/espconn_tcp.c @@ -948,3 +948,19 @@ sint8 ICACHE_FLASH_ATTR espconn_tcp_delete(struct espconn *pdeletecon) return ESPCONN_ARG; } } + +void espconn_tcp_hold(void *arg) { + espconn_msg *ptcp_sent = arg; + struct tcp_pcb *pcb = NULL; + pcb = ptcp_sent->pcommon.pcb; + + pcb->hold = 1; +} + +void espconn_tcp_unhold(void *arg) { + espconn_msg *ptcp_sent = arg; + struct tcp_pcb *pcb = NULL; + pcb = ptcp_sent->pcommon.pcb; + + pcb->hold = 0; +} diff --git a/app/lwip/core/tcp.c b/app/lwip/core/tcp.c index e3193cc7..fe0bb913 100644 --- a/app/lwip/core/tcp.c +++ b/app/lwip/core/tcp.c @@ -1259,6 +1259,7 @@ tcp_alloc(u8_t prio) #endif /* LWIP_TCP_KEEPALIVE */ pcb->keep_cnt_sent = 0; //���ķ��ʹ��� + pcb->hold = 0; } return pcb; } diff --git a/app/lwip/core/tcp_in.c b/app/lwip/core/tcp_in.c index 2f4a1c42..2aca07d4 100644 --- a/app/lwip/core/tcp_in.c +++ b/app/lwip/core/tcp_in.c @@ -1137,7 +1137,7 @@ tcp_receive(struct tcp_pcb *pcb) /* If the incoming segment contains data, we must process it further. */ - if (tcplen > 0) { + if ((tcplen > 0) && (!pcb->hold)) { /* This code basically does three things: +) If the incoming segment contains data that is the next diff --git a/app/modules/modules.h b/app/modules/modules.h index f28775d5..aaa98053 100644 --- a/app/modules/modules.h +++ b/app/modules/modules.h @@ -117,6 +117,15 @@ #define ROM_MODULES_BIT #endif +#if defined(LUA_USE_MODULES_WS2812) +#define MODULES_WS2812 "ws2812" +#define ROM_MODULES_WS2812 \ + _ROM(MODULES_WS2812, luaopen_ws2812, ws2812_map) +#else +#define ROM_MODULES_WS2812 +#endif + + #define LUA_MODULES_ROM \ ROM_MODULES_GPIO \ ROM_MODULES_PWM \ @@ -131,7 +140,8 @@ ROM_MODULES_ADC \ ROM_MODULES_UART \ ROM_MODULES_OW \ - ROM_MODULES_BIT + ROM_MODULES_BIT \ + ROM_MODULES_WS2812 #endif diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index a37974ec..13b182bc 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -725,8 +725,15 @@ static int mqtt_socket_connect( lua_State* L ) if(mud == NULL) return 0; - if(mud->pesp_conn) - c_free(mud->pesp_conn); + if(mud->pesp_conn){ //TODO: should I free tcp struct directly or ask user to call close()??? + mud->pesp_conn->reverse = NULL; + if(mud->pesp_conn->proto.tcp) + c_free(mud->pesp_conn->proto.tcp); + mud->pesp_conn->proto.tcp = NULL; + c_free(mud->pesp_conn); + mud->pesp_conn = NULL; + } + struct espconn *pesp_conn = NULL; pesp_conn = mud->pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn)); if(!pesp_conn) diff --git a/app/modules/net.c b/app/modules/net.c index 92514c9b..51639b54 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -665,6 +665,10 @@ static int net_start( lua_State* L, const char* mt ) return 0; } + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } pesp_conn = nud->pesp_conn; port = luaL_checkinteger( L, stack ); stack++; @@ -1074,6 +1078,10 @@ static int net_dns( lua_State* L, const char* mt ) return 0; } + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } pesp_conn = nud->pesp_conn; if(!isserver || pesp_conn->type == ESPCONN_UDP){ // self_ref is only needed by socket userdata, or udp server @@ -1185,6 +1193,54 @@ static int net_socket_send( lua_State* L ) return net_send(L, mt); } +static int net_socket_hold( lua_State* L ) +{ + const char *mt = "net.socket"; + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + size_t l; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } + pesp_conn = nud->pesp_conn; + espconn_recv_hold(pesp_conn); + + return 0; +} + +static int net_socket_unhold( lua_State* L ) +{ + const char *mt = "net.socket"; + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + size_t l; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } + pesp_conn = nud->pesp_conn; + espconn_recv_unhold(pesp_conn); + + return 0; +} + // Lua: socket:dns( string, function(ip) ) static int net_socket_dns( lua_State* L ) { @@ -1243,6 +1299,8 @@ static const LUA_REG_TYPE net_socket_map[] = { LSTRKEY( "close" ), LFUNCVAL ( net_socket_close ) }, { LSTRKEY( "on" ), LFUNCVAL ( net_socket_on ) }, { LSTRKEY( "send" ), LFUNCVAL ( net_socket_send ) }, + { LSTRKEY( "hold" ), LFUNCVAL ( net_socket_hold ) }, + { LSTRKEY( "unhold" ), LFUNCVAL ( net_socket_unhold ) }, { LSTRKEY( "dns" ), LFUNCVAL ( net_socket_dns ) }, // { LSTRKEY( "delete" ), LFUNCVAL ( net_socket_delete ) }, { LSTRKEY( "__gc" ), LFUNCVAL ( net_socket_delete ) }, diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c new file mode 100644 index 00000000..aab28e92 --- /dev/null +++ b/app/modules/ws2812.c @@ -0,0 +1,82 @@ +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" +/** + * All this code is mostly from http://www.esp8266.com/viewtopic.php?f=21&t=1143&sid=a620a377672cfe9f666d672398415fcb + * from user Markus Gritsch. + * I just put this code into its own module and pushed into a forked repo, + * to easily create a pull request. Thanks to Markus Gritsch for the code. + * + */ + +// ---------------------------------------------------------------------------- +// -- This WS2812 code must be compiled with -O2 to get the timing right. +// -- http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/ +// The ICACHE_FLASH_ATTR is there to trick the compiler and get the very first pulse width correct. +static void ICACHE_FLASH_ATTR send_ws_0(uint8_t gpio) { + uint8_t i; + i = 4; + while (i--) + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio); + i = 9; + while (i--) + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio); +} + +static void ICACHE_FLASH_ATTR send_ws_1(uint8_t gpio) { + uint8_t i; + i = 8; + while (i--) + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio); + i = 6; + while (i--) + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio); +} + +// Lua: ws2812.write(pin, "string") +// Byte triples in the string are interpreted as G R B values. +// ws2812.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red. +// ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. +// ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white. +static int ICACHE_FLASH_ATTR ws2812_write(lua_State* L) { + const uint8_t pin = luaL_checkinteger(L, 1); + size_t length; + const char *buffer = luaL_checklstring(L, 2, &length); + + platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); + platform_gpio_write(pin, 0); + os_delay_us(10); + + os_intr_lock(); + const char * const end = buffer + length; + while (buffer != end) { + uint8_t mask = 0x80; + while (mask) { + (*buffer & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); + mask >>= 1; + } + ++buffer; + } + os_intr_unlock(); + + return 0; +} + +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE ws2812_map[] = +{ + { LSTRKEY( "write" ), LFUNCVAL( ws2812_write )}, + { LNILKEY, LNILVAL} +}; + +LUALIB_API int luaopen_ws2812(lua_State *L) { + // TODO: Make sure that the GPIO system is initialized + LREGISTER(L, "ws2812", ws2812_map); + return 1; +} + +// ---------------------------------------------------------------------------- + diff --git a/lua_examples/irsend.lua b/lua_examples/irsend.lua index 4ccd2ebb..6469a4c0 100644 --- a/lua_examples/irsend.lua +++ b/lua_examples/irsend.lua @@ -5,7 +5,7 @@ -- Vladimir Dronnikov -- -- Example: --- require("irsend").nec(4, 0x00ff00ff) +-- dofile("irsend.lua").nec(4, 0x00ff00ff) ------------------------------------------------------------------------------ local M do diff --git a/lua_examples/yet-another-bmp085.lua b/lua_examples/yet-another-bmp085.lua index 5de2de09..d9b94202 100644 --- a/lua_examples/yet-another-bmp085.lua +++ b/lua_examples/yet-another-bmp085.lua @@ -6,7 +6,7 @@ -- Heavily based on work of Christee -- -- Example: --- require("bmp085").read(sda, scl) +-- dofile("bmp085.lua").read(sda, scl) ------------------------------------------------------------------------------ local M do @@ -59,20 +59,13 @@ do MD = r16(0xBE) end -- get raw P - local p - -- NB: optimize for oss = 0 if not oss then oss = 0 end - if oss == 0 then - oss = 0 - w8(0xF4, 0x34) - tmr.delay(5000) - p = r8(0xF6) * 256 + r8(0xF7) - else - w8(0xF4, 0x34 + 64 * oss) - tmr.delay(30000) - p = r8(0xF6) * 65536 + r8(0xF7) * 256 + r8(0xF8) - p = p / 2^(8 - oss) - end + if oss <= 0 then oss = 0 end + if oss > 3 then oss = 3 end + w8(0xF4, 0x34 + 64 * oss) + tmr.delay((4 + 3 ^ oss) * 1000) + local p = r8(0xF6) * 65536 + r8(0xF7) * 256 + r8(0xF8) + p = p / 2 ^ (8 - oss) -- get T w8(0xF4, 0x2E) tmr.delay(5000) @@ -86,14 +79,14 @@ do local X1 = B2 * (B6 * B6 / 4096) / 2048 local X2 = AC2 * B6 / 2048 local X3 = X1 + X2 - local B3 = ((AC1 * 4 + X3) * 2^oss + 2) / 4 + local B3 = ((AC1 * 4 + X3) * 2 ^ oss + 2) / 4 X1 = AC3 * B6 / 8192 X2 = (B1 * (B6 * B6 / 4096)) / 65536 X3 = (X1 + X2 + 2) / 4 local B4 = AC4 * (X3 + 32768) / 32768 - local B7 = (p - B3) * (50000 / 2^oss) + local B7 = (p - B3) * (50000 / 2 ^ oss) p = B7 / B4 * 2 - X1 = (p / 256)^2 + X1 = (p / 256) ^ 2 X1 = (X1 * 3038) / 65536 X2 = (-7357 * p) / 65536 p = p + (X1 + X2 + 3791) / 16 diff --git a/lua_examples/yet-another-dht22.lua b/lua_examples/yet-another-dht22.lua index 1bf65458..90ac8d51 100644 --- a/lua_examples/yet-another-dht22.lua +++ b/lua_examples/yet-another-dht22.lua @@ -5,8 +5,8 @@ -- Vladimir Dronnikov -- -- Example: --- print("DHT11", require("dht22").read(4)) --- print("DHT22", require("dht22").read(4, true)) +-- print("DHT11", dofile("dht22.lua").read(4)) +-- print("DHT22", dofile("dht22.lua").read(4, true)) -- NB: the very first read sometimes fails ------------------------------------------------------------------------------ local M diff --git a/lua_examples/yet-another-ds18b20.lua b/lua_examples/yet-another-ds18b20.lua index e02a2fe9..b6c225c5 100644 --- a/lua_examples/yet-another-ds18b20.lua +++ b/lua_examples/yet-another-ds18b20.lua @@ -5,7 +5,7 @@ -- Vladimir Dronnikov -- -- Example: --- require("ds18b20").read(4, function(r) for k, v in pairs(r) do print(k, v) end end) +-- dofile("ds18b20.lua").read(4, function(r) for k, v in pairs(r) do print(k, v) end end) ------------------------------------------------------------------------------ local M do diff --git a/lua_modules/redis/redis.lua b/lua_modules/redis/redis.lua new file mode 100644 index 00000000..cc4bf64d --- /dev/null +++ b/lua_modules/redis/redis.lua @@ -0,0 +1,83 @@ +------------------------------------------------------------------------------ +-- Redis client module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +-- +-- Example: +-- local redis = dofile("redis.lua").connect(host, port) +-- redis:publish("chan1", foo") +-- redis:subscribe("chan1", function(channel, msg) print(channel, msg) end) +------------------------------------------------------------------------------ +local M +do + -- const + local REDIS_PORT = 6379 + -- cache + local pairs, tonumber = pairs, tonumber + -- + local publish = function(self, chn, s) + self._fd:send(("*3\r\n$7\r\npublish\r\n$%d\r\n%s\r\n$%d\r\n%s\r\n"):format( + #chn, chn, #s, s + )) + -- TODO: confirmation? then queue of answers needed + end + local subscribe = function(self, chn, handler) + -- TODO: subscription to all channels, with single handler + self._fd:send(("*2\r\n$9\r\nsubscribe\r\n$%d\r\n%s\r\n"):format( + #chn, chn + )) + self._handlers[chn] = handler + -- TODO: confirmation? then queue of answers needed + end + local unsubscribe = function(self, chn) + self._handlers[chn] = false + end + -- NB: pity we can not just augment what net.createConnection returns + local close = function(self) + self._fd:close() + end + local connect = function(host, port) + local _fd = net.createConnection(net.TCP, 0) + local self = { + _fd = _fd, + _handlers = { }, + -- TODO: consider metatables? + close = close, + publish = publish, + subscribe = subscribe, + unsubscribe = unsubscribe, + } + _fd:on("connection", function() + --print("+FD") + end) + _fd:on("disconnection", function() + -- FIXME: this suddenly occurs. timeout? + --print("-FD") + end) + _fd:on("receive", function(fd, s) + --print("IN", s) + -- TODO: subscription to all channels + -- lookup message pattern to determine channel and payload + -- NB: pairs() iteration gives no fixed order! + for chn, handler in pairs(self._handlers) do + local p = ("*3\r\n$7\r\nmessage\r\n$%d\r\n%s\r\n$"):format(#chn, chn) + if s:find(p, 1, true) then + -- extract and check message length + -- NB: only the first TCP packet considered! + local _, start, len = s:find("(%d-)\r\n", #p) + if start and tonumber(len) == #s - start - 2 and handler then + handler(chn, s:sub(start + 1, -2)) -- ends with \r\n + end + end + end + end) + _fd:connect(port or REDIS_PORT, host) + return self + end + -- expose + M = { + connect = connect, + } +end +return M diff --git a/tools/esptool.py b/tools/esptool.py index 130e80f0..c6027eb7 100755 --- a/tools/esptool.py +++ b/tools/esptool.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # ESP8266 ROM Bootloader Utility # https://github.com/themadinventor/esptool