diff --git a/README.md b/README.md index 299841c6..75bdcc7f 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ Tencent QQ group: 309957875
- cross compiler (done) # Change log +2015-03-31
+polish mqtt module, add queue for mqtt module.
+add reconnect option to mqtt.connect api, :connect( host, port, secure, auto_reconnect, function(client) )
+move node.readvdd33 to adc.readvdd33.
+tools/esptool.py supported NodeMCU devkit automatic flash. + 2015-03-18
update u8glib.
merge everything to master. @@ -225,8 +231,10 @@ m:on("message", function(conn, topic, data) end end) --- for secure: m:connect("192.168.11.118", 1880, 1) -m:connect("192.168.11.118", 1880, 0, function(conn) print("connected") end) +-- m:connect( host, port, secure, auto_reconnect, function(client) ) +-- for secure: m:connect("192.168.11.118", 1880, 1, 0) +-- for auto-reconnect: m:connect("192.168.11.118", 1880, 0, 1) +m:connect("192.168.11.118", 1880, 0, 0, function(conn) print("connected") end) -- subscribe topic with qos = 0 m:subscribe("/topic",0, function(conn) print("subscribe success") end) @@ -235,7 +243,7 @@ m:subscribe("/topic",0, function(conn) print("subscribe success") end) -- publish a message with data = hello, QoS = 0, retain = 0 m:publish("/topic","hello",0,0, function(conn) print("sent") end) -m:close(); +m:close(); -- if auto-reconnect == 1, will disable auto-reconnect and then disconnect from host. -- you can call m:connect again ``` diff --git a/app/cjson/strbuf.c b/app/cjson/strbuf.c index 44a1bd69..53ade23b 100644 --- a/app/cjson/strbuf.c +++ b/app/cjson/strbuf.c @@ -46,7 +46,7 @@ int strbuf_init(strbuf_t *s, int len) s->reallocs = 0; s->debug = 0; - s->buf = c_malloc(size); + s->buf = (char *)c_malloc(size); if (!s->buf){ NODE_ERR("not enough memory\n"); return -1; @@ -60,7 +60,7 @@ strbuf_t *strbuf_new(int len) { strbuf_t *s; - s = c_malloc(sizeof(strbuf_t)); + s = (strbuf_t *)c_malloc(sizeof(strbuf_t)); if (!s){ NODE_ERR("not enough memory\n"); return NULL; diff --git a/app/include/user_version.h b/app/include/user_version.h index bc64f36d..e9357259 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -7,6 +7,6 @@ #define NODE_VERSION_INTERNAL 0U #define NODE_VERSION "NodeMCU 0.9.5" -#define BUILD_DATE "build 20150318" +#define BUILD_DATE "build 20150405" #endif /* __USER_VERSION_H__ */ diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h index 88c368f6..c652f0bf 100644 --- a/app/libc/c_stdio.h +++ b/app/libc/c_stdio.h @@ -47,9 +47,9 @@ extern int c_stderr; #define SEEK_END 2 /* set file offset to EOF plus offset */ #endif -#define c_malloc os_malloc -#define c_zalloc os_zalloc -#define c_free os_free +// #define c_malloc os_malloc +// #define c_zalloc os_zalloc +// #define c_free os_free extern void output_redirect(const char *str); #define c_puts output_redirect diff --git a/app/libc/c_stdlib.h b/app/libc/c_stdlib.h index 3757c135..f0a6f265 100644 --- a/app/libc/c_stdlib.h +++ b/app/libc/c_stdlib.h @@ -29,9 +29,9 @@ #define os_realloc(p, s) mem_realloc((p), (s)) #endif -// #define c_free os_free -// #define c_malloc os_malloc -// #define c_zalloc os_zalloc +#define c_free os_free +#define c_malloc os_malloc +#define c_zalloc os_zalloc #define c_realloc os_realloc #define c_abs abs @@ -47,9 +47,9 @@ // c_getenv() get env "LUA_INIT" string for lua initialization. const char *c_getenv(const char *__string); -void *c_malloc(size_t __size); -void *c_zalloc(size_t __size); -void c_free(void *); +// void *c_malloc(size_t __size); +// void *c_zalloc(size_t __size); +// void c_free(void *); // int c_rand(void); // void c_srand(unsigned int __seed); diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h index 1a1760c7..7e0b182c 100644 --- a/app/lua/luaconf.h +++ b/app/lua/luaconf.h @@ -541,8 +541,12 @@ extern int readline4lua(const char *prompt, char *buffer, int length); /* @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +** Attention: This value should probably not be set higher than 1K. +** The size has direct impact on the C stack size needed be auxlib functions. +** For example: If set to 4K a call to string.gsub will need more than +** 5k C stack space. */ -#define LUAL_BUFFERSIZE ((BUFSIZ)*4) +#define LUAL_BUFFERSIZE BUFSIZ /* }================================================================== */ diff --git a/app/modules/adc.c b/app/modules/adc.c index b64697ba..eee15fe4 100644 --- a/app/modules/adc.c +++ b/app/modules/adc.c @@ -8,6 +8,7 @@ #include "lrotable.h" #include "c_types.h" +#include "user_interface.h" // Lua: read(id) , return system adc static int adc_sample( lua_State* L ) @@ -19,12 +20,33 @@ static int adc_sample( lua_State* L ) return 1; } +// Lua: readvdd33() +static int adc_readvdd33( lua_State* L ) +{ + uint32_t vdd33 = 0; + if(STATION_MODE == wifi_get_opmode()) + { + // Bug fix + wifi_set_opmode( STATIONAP_MODE ); + vdd33 = readvdd33(); + wifi_set_opmode( STATION_MODE ); + } + else + { + vdd33 = readvdd33(); + } + + lua_pushinteger(L, vdd33); + return 1; +} + // Module function map #define MIN_OPT_LEVEL 2 #include "lrodefs.h" const LUA_REG_TYPE adc_map[] = { { LSTRKEY( "read" ), LFUNCVAL( adc_sample ) }, + { LSTRKEY( "readvdd33" ), LFUNCVAL( adc_readvdd33) }, #if LUA_OPTIMIZE_MEMORY > 0 #endif diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h index 149b1af3..ece3ff56 100644 --- a/app/modules/auxmods.h +++ b/app/modules/auxmods.h @@ -80,7 +80,7 @@ LUALIB_API int ( luaopen_file )( lua_State *L ); LUALIB_API int ( luaopen_ow )( lua_State *L ); #define AUXLIB_CJSON "cjson" -LUALIB_API int ( luaopen_ow )( lua_State *L ); +LUALIB_API int ( luaopen_cjson )( lua_State *L ); // Helper macros #define MOD_CHECK_ID( mod, id )\ diff --git a/app/modules/gpio.c b/app/modules/gpio.c index 551dca8a..79e4e850 100644 --- a/app/modules/gpio.c +++ b/app/modules/gpio.c @@ -146,6 +146,82 @@ static int lgpio_write( lua_State* L ) return 0; } +#define DELAY_TABLE_MAX_LEN 256 +#define noInterrupts os_intr_lock +#define interrupts os_intr_unlock +#define delayMicroseconds os_delay_us +#define DIRECT_WRITE(pin, level) (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level)) +// Lua: serout( pin, firstLevel, delay_table, [repeatNum] ) +// -- serout( pin, firstLevel, delay_table, [repeatNum] ) +// gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) +// gpio.serout(1,1,{30,30,60,60,30,30}) -- serial one byte, b10110010 +// gpio.serout(1,1,{30,70},8) -- serial 30% pwm 10k, lasts 8 cycles +// gpio.serout(1,1,{3,7},8) -- serial 30% pwm 100k, lasts 8 cycles +// gpio.serout(1,1,{0,0},8) -- serial 50% pwm as fast as possible, lasts 8 cycles +// gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) +// gpio.serout(1,0,{20,10,10,20,10,10,10,100}) -- sim uart one byte 0x5A at about 100kbps +// gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles +static int lgpio_serout( lua_State* L ) +{ + unsigned level; + unsigned pin; + unsigned table_len = 0; + unsigned repeat = 0; + int delay_table[DELAY_TABLE_MAX_LEN]; + + pin = luaL_checkinteger( L, 1 ); + MOD_CHECK_ID( gpio, pin ); + level = luaL_checkinteger( L, 2 ); + if ( level!=HIGH && level!=LOW ) + return luaL_error( L, "wrong arg type" ); + if( lua_istable( L, 3 ) ) + { + table_len = lua_objlen( L, 3 ); + if (table_len <= 0 || table_len>DELAY_TABLE_MAX_LEN) + return luaL_error( L, "wrong arg range" ); + int i; + for( i = 0; i < table_len; i ++ ) + { + lua_rawgeti( L, 3, i + 1 ); + delay_table[i] = ( int )luaL_checkinteger( L, -1 ); + lua_pop( L, 1 ); + if( delay_table[i] < 0 || delay_table[i] > 1000000 ) // can not delay more than 1000000 us + return luaL_error( L, "delay must < 1000000 us" ); + } + } else { + return luaL_error( L, "wrong arg range" ); + } + + if(lua_isnumber(L, 4)) + repeat = lua_tointeger( L, 4 ); + if( repeat < 0 || repeat > DELAY_TABLE_MAX_LEN ) + return luaL_error( L, "delay must < 256" ); + + if(repeat==0) + repeat = 1; + int j; + bool skip_loop = true; + do + { + if(skip_loop){ // skip the first loop. + skip_loop = false; + continue; + } + for(j=0;j0); + + return 0; +} +#undef DELAY_TABLE_MAX_LEN + // Module function map #define MIN_OPT_LEVEL 2 #include "lrodefs.h" @@ -154,6 +230,7 @@ const LUA_REG_TYPE gpio_map[] = { LSTRKEY( "mode" ), LFUNCVAL( lgpio_mode ) }, { LSTRKEY( "read" ), LFUNCVAL( lgpio_read ) }, { LSTRKEY( "write" ), LFUNCVAL( lgpio_write ) }, + { LSTRKEY( "serout" ), LFUNCVAL( lgpio_serout ) }, #ifdef GPIO_INTERRUPT_ENABLE { LSTRKEY( "trig" ), LFUNCVAL( lgpio_trig ) }, #endif diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index 018e034e..c7afd0ff 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -15,8 +15,7 @@ #include "espconn.h" #include "mqtt_msg.h" - -static lua_State *gL = NULL; +#include "msg_queue.h" #define MQTT_BUF_SIZE 1024 #define MQTT_DEFAULT_KEEPALIVE 60 @@ -24,10 +23,11 @@ static lua_State *gL = NULL; #define MQTT_MAX_USER_LEN 64 #define MQTT_MAX_PASS_LEN 64 #define MQTT_SEND_TIMEOUT 5 +#define MQTT_CONNECT_TIMEOUT 5 typedef enum { MQTT_INIT, - MQTT_CONNECT_SEND, + MQTT_CONNECT_SENT, MQTT_CONNECT_SENDING, MQTT_DATA } tConnState; @@ -47,22 +47,15 @@ typedef struct mqtt_state_t uint16_t port; int auto_reconnect; mqtt_connect_info_t* connect_info; - uint8_t* in_buffer; - uint8_t* out_buffer; - int in_buffer_length; - int out_buffer_length; uint16_t message_length; uint16_t message_length_read; - mqtt_message_t* outbound_message; mqtt_connection_t mqtt_connection; - - uint16_t pending_msg_id; - int pending_msg_type; - int pending_publish_qos; + msg_queue_t* pending_msg_q; } mqtt_state_t; typedef struct lmqtt_userdata { + lua_State *L; struct espconn *pesp_conn; int self_ref; int cb_connect_ref; @@ -73,54 +66,104 @@ typedef struct lmqtt_userdata mqtt_state_t mqtt_state; mqtt_connect_info_t connect_info; uint32_t keep_alive_tick; - uint32_t send_timeout; + uint32_t event_timeout; uint8_t secure; - uint8_t connected; + bool connected; // indicate socket connected, not mqtt prot connected. ETSTimer mqttTimer; tConnState connState; }lmqtt_userdata; +static void socket_connect(struct espconn *pesp_conn); +static void mqtt_socket_reconnected(void *arg, sint8_t err); +static void mqtt_socket_connected(void *arg); + static void mqtt_socket_disconnected(void *arg) // tcp only { - NODE_DBG("mqtt_socket_disconnected is called.\n"); + NODE_DBG("enter mqtt_socket_disconnected.\n"); + struct espconn *pesp_conn = arg; + bool call_back = false; + if(pesp_conn == NULL) + return; + lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; + if(mud == NULL) + return; + + os_timer_disarm(&mud->mqttTimer); + + if(mud->connected){ // call back only called when socket is from connection to disconnection. + mud->connected = false; + if((mud->L != NULL) && (mud->cb_disconnect_ref != LUA_NOREF) && (mud->self_ref != LUA_NOREF)) { + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua + call_back = true; + } + } + + if(mud->mqtt_state.auto_reconnect){ + mud->pesp_conn->reverse = mud; + mud->pesp_conn->type = ESPCONN_TCP; + mud->pesp_conn->state = ESPCONN_NONE; + mud->connected = false; + mud->pesp_conn->proto.tcp->remote_port = mud->mqtt_state.port; + mud->pesp_conn->proto.tcp->local_port = espconn_port(); + espconn_regist_connectcb(mud->pesp_conn, mqtt_socket_connected); + espconn_regist_reconcb(mud->pesp_conn, mqtt_socket_reconnected); + socket_connect(pesp_conn); + } else { + if(mud->pesp_conn){ + 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; + } + + if(mud->L == NULL) + return; + lua_gc(mud->L, LUA_GCSTOP, 0); + if(mud->self_ref != LUA_NOREF){ // TODO: should we unref the client and delete it? + luaL_unref(mud->L, LUA_REGISTRYINDEX, mud->self_ref); + mud->self_ref = LUA_NOREF; // unref this, and the mqtt.socket userdata will delete it self + } + lua_gc(mud->L, LUA_GCRESTART, 0); + } + + if((mud->L != NULL) && call_back){ + lua_call(mud->L, 1, 0); + } + + NODE_DBG("leave mqtt_socket_disconnected.\n"); +} + +static void mqtt_socket_reconnected(void *arg, sint8_t err) +{ + NODE_DBG("enter mqtt_socket_reconnected.\n"); + // mqtt_socket_disconnected(arg); struct espconn *pesp_conn = arg; if(pesp_conn == NULL) return; lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; if(mud == NULL) return; - if(mud->cb_disconnect_ref != LUA_NOREF && mud->self_ref != LUA_NOREF) - { - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_disconnect_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua - lua_call(gL, 1, 0); - } - mud->connected = 0; + os_timer_disarm(&mud->mqttTimer); - if(pesp_conn->proto.tcp) - c_free(pesp_conn->proto.tcp); - pesp_conn->proto.tcp = NULL; - if(mud->pesp_conn) - c_free(mud->pesp_conn); - mud->pesp_conn = NULL; // espconn is already disconnected - lua_gc(gL, LUA_GCSTOP, 0); - if(mud->self_ref != LUA_NOREF){ - luaL_unref(gL, LUA_REGISTRYINDEX, mud->self_ref); - mud->self_ref = LUA_NOREF; // unref this, and the mqtt.socket userdata will delete it self + if(mud->mqtt_state.auto_reconnect){ + pesp_conn->proto.tcp->remote_port = mud->mqtt_state.port; + pesp_conn->proto.tcp->local_port = espconn_port(); + socket_connect(pesp_conn); + } else { + mqtt_socket_disconnected(arg); } - lua_gc(gL, LUA_GCRESTART, 0); -} - -static void mqtt_socket_reconnected(void *arg, sint8_t err) -{ - NODE_DBG("mqtt_socket_reconnected is called.\n"); - mqtt_socket_disconnected(arg); + NODE_DBG("leave mqtt_socket_reconnected.\n"); } static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, int length) { - const char comma[] = ","; + NODE_DBG("enter deliver_publish.\n"); + if(mud == NULL) + return; mqtt_event_data_t event_data; event_data.topic_length = length; @@ -133,28 +176,36 @@ static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, int length) return; if(mud->self_ref == LUA_NOREF) return; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_message_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua - // expose_array(gL, pdata, len); - // *(pdata+len) = 0; - // NODE_DBG(pdata); - // NODE_DBG("\n"); - lua_pushlstring(gL, event_data.topic, event_data.topic_length); - if(event_data.data_length > 0){ - lua_pushlstring(gL, event_data.data, event_data.data_length); - lua_call(gL, 3, 0); + if(mud->L == NULL) + return; + if(event_data.topic && (event_data.topic_length > 0)){ + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_message_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua + lua_pushlstring(mud->L, event_data.topic, event_data.topic_length); } else { - lua_call(gL, 2, 0); + NODE_DBG("get wrong packet.\n"); + return; } + if(event_data.data && (event_data.data_length > 0)){ + lua_pushlstring(mud->L, event_data.data, event_data.data_length); + lua_call(mud->L, 3, 0); + } else { + lua_call(mud->L, 2, 0); + } + NODE_DBG("leave deliver_publish.\n"); } static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) { - NODE_DBG("mqtt_socket_received is called.\n"); + NODE_DBG("enter mqtt_socket_received.\n"); uint8_t msg_type; uint8_t msg_qos; uint16_t msg_id; + msg_queue_t *node = NULL; + int length = (int)len; + // uint8_t in_buffer[MQTT_BUF_SIZE]; + uint8_t *in_buffer = (uint8_t *)pdata; struct espconn *pesp_conn = arg; if(pesp_conn == NULL) @@ -164,111 +215,152 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) return; READPACKET: - if(len > MQTT_BUF_SIZE && len <= 0) + if(length > MQTT_BUF_SIZE || length <= 0) return; - c_memcpy(mud->mqtt_state.in_buffer, pdata, len); - mud->mqtt_state.outbound_message = NULL; + // c_memcpy(in_buffer, pdata, length); + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + mqtt_message_t *temp_msg = NULL; switch(mud->connState){ case MQTT_CONNECT_SENDING: - if(mqtt_get_type(mud->mqtt_state.in_buffer) != MQTT_MSG_TYPE_CONNACK){ + case MQTT_CONNECT_SENT: + if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ NODE_DBG("MQTT: Invalid packet\r\n"); mud->connState = MQTT_INIT; - if(mud->secure){ + if(mud->secure) espconn_secure_disconnect(pesp_conn); - } - else { + else espconn_disconnect(pesp_conn); - } } else { mud->connState = MQTT_DATA; NODE_DBG("MQTT: Connected\r\n"); if(mud->cb_connect_ref == LUA_NOREF) - return; + break; if(mud->self_ref == LUA_NOREF) - return; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_connect_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua - lua_call(gL, 1, 0); - return; + break; + if(mud->L == NULL) + break; + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_connect_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata(client) to callback func in lua + lua_call(mud->L, 1, 0); + break; } break; case MQTT_DATA: - mud->mqtt_state.message_length_read = len; - mud->mqtt_state.message_length = mqtt_get_total_length(mud->mqtt_state.in_buffer, mud->mqtt_state.message_length_read); - msg_type = mqtt_get_type(mud->mqtt_state.in_buffer); - msg_qos = mqtt_get_qos(mud->mqtt_state.in_buffer); - msg_id = mqtt_get_id(mud->mqtt_state.in_buffer, mud->mqtt_state.in_buffer_length); + mud->mqtt_state.message_length_read = length; + mud->mqtt_state.message_length = mqtt_get_total_length(in_buffer, mud->mqtt_state.message_length_read); + msg_type = mqtt_get_type(in_buffer); + msg_qos = mqtt_get_qos(in_buffer); + msg_id = mqtt_get_id(in_buffer, mud->mqtt_state.message_length); + + msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); NODE_DBG("MQTT_DATA: type: %d, qos: %d, msg_id: %d, pending_id: %d\r\n", msg_type, msg_qos, msg_id, - mud->mqtt_state.pending_msg_id); + (pending_msg)?pending_msg->msg_id:0); switch(msg_type) { case MQTT_MSG_TYPE_SUBACK: - if(mud->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && mud->mqtt_state.pending_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"); - if (mud->cb_suback_ref == LUA_NOREF) - break; - if (mud->self_ref == LUA_NOREF) - break; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_suback_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); - lua_call(gL, 1, 0); + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); + if (mud->cb_suback_ref == LUA_NOREF) + break; + if (mud->self_ref == LUA_NOREF) + break; + if(mud->L == NULL) + break; + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_suback_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); + lua_call(mud->L, 1, 0); + } break; case MQTT_MSG_TYPE_UNSUBACK: - if(mud->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && mud->mqtt_state.pending_msg_id == msg_id) + if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && pending_msg->msg_id == msg_id){ NODE_DBG("MQTT: UnSubscribe successful\r\n"); + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); + } break; case MQTT_MSG_TYPE_PUBLISH: - if(msg_qos == 1) - mud->mqtt_state.outbound_message = mqtt_msg_puback(&mud->mqtt_state.mqtt_connection, msg_id); - else if(msg_qos == 2) - mud->mqtt_state.outbound_message = mqtt_msg_pubrec(&mud->mqtt_state.mqtt_connection, msg_id); - - deliver_publish(mud, mud->mqtt_state.in_buffer, mud->mqtt_state.message_length_read); + if(msg_qos == 1){ + temp_msg = mqtt_msg_puback(&mud->mqtt_state.mqtt_connection, msg_id); + node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PUBACK, (int)mqtt_get_qos(temp_msg->data) ); + } + else if(msg_qos == 2){ + temp_msg = mqtt_msg_pubrec(&mud->mqtt_state.mqtt_connection, msg_id); + node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PUBREC, (int)mqtt_get_qos(temp_msg->data) ); + } + if(msg_qos == 1 || msg_qos == 2){ + NODE_DBG("MQTT: Queue response QoS: %d\r\n", msg_qos); + } + deliver_publish(mud, in_buffer, mud->mqtt_state.message_length); break; case MQTT_MSG_TYPE_PUBACK: - if(mud->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && mud->mqtt_state.pending_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"); + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); if(mud->cb_puback_ref == LUA_NOREF) break; if(mud->self_ref == LUA_NOREF) break; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_puback_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua - lua_call(gL, 1, 0); + if(mud->L == NULL) + break; + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_puback_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua + lua_call(mud->L, 1, 0); } break; case MQTT_MSG_TYPE_PUBREC: - mud->mqtt_state.outbound_message = mqtt_msg_pubrel(&mud->mqtt_state.mqtt_connection, 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 = 2 Received PUBREC\r\n"); + // Note: actrually, should not destroy the msg until PUBCOMP is received. + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); + temp_msg = mqtt_msg_pubrel(&mud->mqtt_state.mqtt_connection, msg_id); + node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PUBREL, (int)mqtt_get_qos(temp_msg->data) ); NODE_DBG("MQTT: Response PUBREL\r\n"); + } break; case MQTT_MSG_TYPE_PUBREL: - mud->mqtt_state.outbound_message = mqtt_msg_pubcomp(&mud->mqtt_state.mqtt_connection, msg_id); + if(pending_msg && pending_msg->msg_type == MQTT_MSG_TYPE_PUBREC && pending_msg->msg_id == msg_id){ + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); + temp_msg = mqtt_msg_pubcomp(&mud->mqtt_state.mqtt_connection, msg_id); + node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PUBCOMP, (int)mqtt_get_qos(temp_msg->data) ); NODE_DBG("MQTT: Response PUBCOMP\r\n"); + } break; case MQTT_MSG_TYPE_PUBCOMP: - if(mud->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && mud->mqtt_state.pending_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"); + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); if(mud->cb_puback_ref == LUA_NOREF) break; if(mud->self_ref == LUA_NOREF) break; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_puback_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua - lua_call(gL, 1, 0); + if(mud->L == NULL) + break; + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_puback_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua + lua_call(mud->L, 1, 0); } break; case MQTT_MSG_TYPE_PINGREQ: - mud->mqtt_state.outbound_message = mqtt_msg_pingresp(&mud->mqtt_state.mqtt_connection); + temp_msg = mqtt_msg_pingresp(&mud->mqtt_state.mqtt_connection); + node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PINGRESP, (int)mqtt_get_qos(temp_msg->data) ); + NODE_DBG("MQTT: Response PINGRESP\r\n"); break; case MQTT_MSG_TYPE_PINGRESP: // Ignore + NODE_DBG("MQTT: PINGRESP received\r\n"); break; } // NOTE: this is done down here and not in the switch case above @@ -277,11 +369,11 @@ READPACKET: if(msg_type == MQTT_MSG_TYPE_PUBLISH) { - len = mud->mqtt_state.message_length_read; + length = mud->mqtt_state.message_length_read; if(mud->mqtt_state.message_length < mud->mqtt_state.message_length_read) { - len -= mud->mqtt_state.message_length; + length -= mud->mqtt_state.message_length; pdata += mud->mqtt_state.message_length; NODE_DBG("Get another published message\r\n"); @@ -291,19 +383,23 @@ READPACKET: break; } - if(mud->mqtt_state.outbound_message != NULL){ - if(mud->secure) - espconn_secure_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); + if(node && (1==msg_size(&(mud->mqtt_state.pending_msg_q))) && mud->event_timeout == 0){ + mud->event_timeout = MQTT_SEND_TIMEOUT; + NODE_DBG("Sent: %d\n", node->msg.length); + if( mud->secure ) + espconn_secure_sent( pesp_conn, node->msg.data, node->msg.length ); else - espconn_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - mud->mqtt_state.outbound_message = NULL; + espconn_sent( pesp_conn, node->msg.data, node->msg.length ); } + mud->keep_alive_tick = 0; + NODE_DBG("receive, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); + NODE_DBG("leave mqtt_socket_received.\n"); return; } static void mqtt_socket_sent(void *arg) { - // NODE_DBG("mqtt_socket_sent is called.\n"); + NODE_DBG("enter mqtt_socket_sent.\n"); struct espconn *pesp_conn = arg; if(pesp_conn == NULL) return; @@ -311,24 +407,44 @@ static void mqtt_socket_sent(void *arg) if(mud == NULL) return; if(!mud->connected) - return; + return; // call mqtt_sent() - mud->send_timeout = 0; - if(mud->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && mud->mqtt_state.pending_publish_qos == 0) { + mud->event_timeout = 0; + mud->keep_alive_tick = 0; + + if(mud->connState == MQTT_CONNECT_SENDING){ + mud->connState = MQTT_CONNECT_SENT; + // MQTT_CONNECT not queued. + return; + } + NODE_DBG("sent1, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); + // qos = 0, publish and forgot. + 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) { + msg_destroy(msg_dequeue(&(mud->mqtt_state.pending_msg_q))); if(mud->cb_puback_ref == LUA_NOREF) return; if(mud->self_ref == LUA_NOREF) return; - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->cb_puback_ref); - lua_rawgeti(gL, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua - lua_call(gL, 1, 0); + if(mud->L == NULL) + return; + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->cb_puback_ref); + lua_rawgeti(mud->L, LUA_REGISTRYINDEX, mud->self_ref); // pass the userdata to callback func in lua + lua_call(mud->L, 1, 0); + } else if(node && node->msg_type == MQTT_MSG_TYPE_PUBACK && node->publish_qos == 1) { + 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))); } + NODE_DBG("sent2, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); + NODE_DBG("leave mqtt_socket_sent.\n"); } -static int mqtt_socket_client( lua_State* L ); static void mqtt_socket_connected(void *arg) { - NODE_DBG("mqtt_socket_connected is called.\n"); + NODE_DBG("enter mqtt_socket_connected.\n"); struct espconn *pesp_conn = arg; if(pesp_conn == NULL) return; @@ -340,57 +456,113 @@ static void mqtt_socket_connected(void *arg) espconn_regist_sentcb(pesp_conn, mqtt_socket_sent); espconn_regist_disconcb(pesp_conn, mqtt_socket_disconnected); + uint8_t temp_buffer[MQTT_BUF_SIZE]; // call mqtt_connect() to start a mqtt connect stage. - mqtt_msg_init(&mud->mqtt_state.mqtt_connection, mud->mqtt_state.out_buffer, mud->mqtt_state.out_buffer_length); - mud->mqtt_state.outbound_message = mqtt_msg_connect(&mud->mqtt_state.mqtt_connection, mud->mqtt_state.connect_info); - NODE_DBG("Send MQTT connection infomation, data len: %d, d[0]=%d \r\n", mud->mqtt_state.outbound_message->length, mud->mqtt_state.outbound_message->data[0]); - if(mud->secure){ - espconn_secure_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - } + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + mqtt_message_t* temp_msg = mqtt_msg_connect(&mud->mqtt_state.mqtt_connection, mud->mqtt_state.connect_info); + 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; + // not queue this message. should send right now. or should enqueue this before head. + if(mud->secure) + espconn_secure_sent(pesp_conn, temp_msg->data, temp_msg->length); else - { - espconn_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - } - mud->mqtt_state.outbound_message = NULL; + espconn_sent(pesp_conn, temp_msg->data, temp_msg->length); + mud->keep_alive_tick = 0; + mud->connState = MQTT_CONNECT_SENDING; + NODE_DBG("leave mqtt_socket_connected.\n"); return; } void mqtt_socket_timer(void *arg) { + NODE_DBG("enter mqtt_socket_timer.\n"); lmqtt_userdata *mud = (lmqtt_userdata*) arg; - if(mud->connState == MQTT_DATA){ - mud->keep_alive_tick ++; - if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){ - mud->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; - mud->send_timeout = MQTT_SEND_TIMEOUT; - NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); - mud->mqtt_state.outbound_message = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); + if(mud == NULL) + return; + if(mud->pesp_conn == NULL){ + NODE_DBG("mud->pesp_conn is NULL.\n"); + os_timer_disarm(&mud->mqttTimer); + return; + } - if(mud->secure) - espconn_secure_sent(mud->pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - else - espconn_sent(mud->pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - mud->keep_alive_tick = 0; + 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->send_timeout > 0) - mud->send_timeout --; + + if(mud->connState == MQTT_INIT){ // socket connect time out. + NODE_DBG("Can not connect to broker.\n"); + // Never goes here. + } else if(mud->connState == MQTT_CONNECT_SENDING){ // MQTT_CONNECT send time out. + NODE_DBG("sSend MQTT_CONNECT failed.\n"); + mud->connState = MQTT_INIT; + if(mud->secure) + espconn_secure_disconnect(mud->pesp_conn); + else + espconn_disconnect(mud->pesp_conn); + mud->keep_alive_tick = 0; // not need count anymore + } else if(mud->connState == MQTT_CONNECT_SENT){ // wait for CONACK time out. + NODE_DBG("MQTT_CONNECT failed.\n"); + } else if(mud->connState == MQTT_DATA){ + msg_queue_t *pending_msg = msg_peek(&(mud->mqtt_state.pending_msg_q)); + if(pending_msg){ + mud->event_timeout = MQTT_SEND_TIMEOUT; + if(mud->secure) + espconn_secure_sent(mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length); + else + espconn_sent(mud->pesp_conn, pending_msg->msg.data, pending_msg->msg.length); + mud->keep_alive_tick = 0; + NODE_DBG("id: %d - qos: %d, length: %d\n", pending_msg->msg_id, pending_msg->publish_qos, pending_msg->msg.length); + } else { + // no queued event. + mud->keep_alive_tick ++; + if(mud->keep_alive_tick > mud->mqtt_state.connect_info->keepalive){ + mud->event_timeout = MQTT_SEND_TIMEOUT; + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + NODE_DBG("\r\nMQTT: Send keepalive packet\r\n"); + mqtt_message_t* temp_msg = mqtt_msg_pingreq(&mud->mqtt_state.mqtt_connection); + msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, + 0, MQTT_MSG_TYPE_PINGREQ, (int)mqtt_get_qos(temp_msg->data) ); + // only one message in queue, send immediately. + if(mud->secure) + espconn_secure_sent(mud->pesp_conn, temp_msg->data, temp_msg->length); + else + espconn_sent(mud->pesp_conn, temp_msg->data, temp_msg->length); + mud->keep_alive_tick = 0; + } + } + } + NODE_DBG("leave mqtt_socket_timer.\n"); } // Lua: mqtt.Client(clientid, keepalive, user, pass) static int mqtt_socket_client( lua_State* L ) { - NODE_DBG("mqtt_socket_client is called.\n"); + NODE_DBG("enter mqtt_socket_client.\n"); lmqtt_userdata *mud; char tempid[20] = {0}; c_sprintf(tempid, "%s%x", "NodeMCU_", system_get_chip_id() ); NODE_DBG(tempid); NODE_DBG("\n"); - size_t il = c_strlen(tempid); + const char *clientId = tempid, *username = NULL, *password = NULL; + size_t idl = c_strlen(tempid); + size_t unl = 0, pwl = 0; + int keepalive = 0; int stack = 1; unsigned secure = 0; int top = lua_gettop(L); @@ -398,6 +570,7 @@ static int mqtt_socket_client( lua_State* L ) // create a object mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata)); // pre-initialize it, in case of errors + mud->L = NULL; mud->self_ref = LUA_NOREF; mud->cb_connect_ref = LUA_NOREF; mud->cb_disconnect_ref = LUA_NOREF; @@ -409,8 +582,9 @@ static int mqtt_socket_client( lua_State* L ) mud->secure = 0; mud->keep_alive_tick = 0; - mud->send_timeout = 0; + mud->event_timeout = 0; mud->connState = MQTT_INIT; + mud->connected = false; c_memset(&mud->mqttTimer, 0, sizeof(ETSTimer)); c_memset(&mud->mqtt_state, 0, sizeof(mqtt_state_t)); c_memset(&mud->connect_info, 0, sizeof(mqtt_connect_info_t)); @@ -419,87 +593,80 @@ static int mqtt_socket_client( lua_State* L ) luaL_getmetatable(L, "mqtt.socket"); lua_setmetatable(L, -2); + mud->L = L; // L for mqtt module. + if( lua_isstring(L,stack) ) // deal with the clientid string { - clientId = luaL_checklstring( L, stack, &il ); + clientId = luaL_checklstring( L, stack, &idl ); stack++; } - // TODO: check the zalloc result. - mud->connect_info.client_id = (uint8_t *)c_zalloc(il+1); - if(!mud->connect_info.client_id){ - return luaL_error(L, "not enough memory"); - } - c_memcpy(mud->connect_info.client_id, clientId, il); - mud->connect_info.client_id[il] = 0; - - mud->mqtt_state.in_buffer = (uint8_t *)c_zalloc(MQTT_BUF_SIZE); - if(!mud->mqtt_state.in_buffer){ - return luaL_error(L, "not enough memory"); - } - - mud->mqtt_state.out_buffer = (uint8_t *)c_zalloc(MQTT_BUF_SIZE); - if(!mud->mqtt_state.out_buffer){ - return luaL_error(L, "not enough memory"); - } - - mud->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; - mud->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; - - mud->connState = MQTT_INIT; - mud->connect_info.clean_session = 1; - mud->connect_info.will_qos = 0; - mud->connect_info.will_retain = 0; - mud->keep_alive_tick = 0; - mud->connect_info.keepalive = 0; - mud->mqtt_state.connect_info = &mud->connect_info; - - gL = L; // global L for mqtt module. - if(lua_isnumber( L, stack )) { - mud->connect_info.keepalive = luaL_checkinteger( L, stack); + keepalive = luaL_checkinteger( L, stack); stack++; } - if(mud->connect_info.keepalive == 0){ - mud->connect_info.keepalive = MQTT_DEFAULT_KEEPALIVE; - return 1; + if(keepalive == 0){ + keepalive = MQTT_DEFAULT_KEEPALIVE; } if(lua_isstring( L, stack )){ - username = luaL_checklstring( L, stack, &il ); + username = luaL_checklstring( L, stack, &unl ); stack++; } if(username == NULL) - il = 0; - NODE_DBG("lengh username: %d\r\n", il); - mud->connect_info.username = (uint8_t *)c_zalloc(il + 1); - if(!mud->connect_info.username){ - return luaL_error(L, "not enough memory"); - } + unl = 0; + NODE_DBG("lengh username: %d\r\n", unl); - c_memcpy(mud->connect_info.username, username, il); - mud->connect_info.username[il] = 0; - if(lua_isstring( L, stack )){ - password = luaL_checklstring( L, stack, &il ); + password = luaL_checklstring( L, stack, &pwl ); stack++; } if(password == NULL) - il = 0; - NODE_DBG("lengh password: %d\r\n", il); + pwl = 0; + NODE_DBG("lengh password: %d\r\n", pwl); - mud->connect_info.password = (uint8_t *)c_zalloc(il + 1); - if(!mud->connect_info.password){ - return luaL_error(L, "not enough memory"); - } + // TODO: check the zalloc result. + mud->connect_info.client_id = (uint8_t *)c_zalloc(idl+1); + mud->connect_info.username = (uint8_t *)c_zalloc(unl + 1); + mud->connect_info.password = (uint8_t *)c_zalloc(pwl + 1); + if(!mud->connect_info.client_id || !mud->connect_info.username || !mud->connect_info.password){ + if(mud->connect_info.client_id) { + c_free(mud->connect_info.client_id); + mud->connect_info.client_id = NULL; + } + if(mud->connect_info.username) { + c_free(mud->connect_info.username); + mud->connect_info.username = NULL; + } + if(mud->connect_info.password) { + c_free(mud->connect_info.password); + mud->connect_info.password = NULL; + } + return luaL_error(L, "not enough memory"); + } - c_memcpy(mud->connect_info.password, password, il); - mud->connect_info.password[il] = 0; + c_memcpy(mud->connect_info.client_id, clientId, idl); + mud->connect_info.client_id[idl] = 0; + c_memcpy(mud->connect_info.username, username, unl); + mud->connect_info.username[unl] = 0; + c_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 = 1; + mud->connect_info.will_qos = 0; + mud->connect_info.will_retain = 0; + mud->connect_info.keepalive = keepalive; + + mud->mqtt_state.pending_msg_q = NULL; + mud->mqtt_state.auto_reconnect = 0; + mud->mqtt_state.port = 1883; + mud->mqtt_state.connect_info = &mud->connect_info; + + NODE_DBG("leave mqtt_socket_client.\n"); return 1; } @@ -508,7 +675,7 @@ static int mqtt_socket_client( lua_State* L ) // socket: unref everything static int mqtt_delete( lua_State* L ) { - NODE_DBG("mqtt_delete is called.\n"); + NODE_DBG("enter mqtt_delete.\n"); lmqtt_userdata *mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); luaL_argcheck(L, mud, 1, "mqtt.socket expected"); @@ -518,7 +685,9 @@ static int mqtt_delete( lua_State* L ) } os_timer_disarm(&mud->mqttTimer); - mud->connected = 0; + mud->connected = false; + + // ---- alloc-ed in mqtt_socket_connect() if(mud->pesp_conn){ // for client connected to tcp server, this should set NULL in disconnect cb mud->pesp_conn->reverse = NULL; if(mud->pesp_conn->proto.tcp) @@ -528,16 +697,19 @@ static int mqtt_delete( lua_State* L ) mud->pesp_conn = NULL; // for socket, it will free this when disconnected } + // ---- alloc-ed in mqtt_socket_lwt() if(mud->connect_info.will_topic){ c_free(mud->connect_info.will_topic); mud->connect_info.will_topic = NULL; } if(mud->connect_info.will_message){ - c_free(mud->connect_info.will_message); - mud->connect_info.will_message = NULL; - } + c_free(mud->connect_info.will_message); + mud->connect_info.will_message = NULL; + } + // ---- + //--------- alloc-ed in mqtt_socket_client() if(mud->connect_info.client_id){ c_free(mud->connect_info.client_id); mud->connect_info.client_id = NULL; @@ -550,14 +722,7 @@ static int mqtt_delete( lua_State* L ) c_free(mud->connect_info.password); mud->connect_info.password = NULL; } - if(mud->mqtt_state.in_buffer){ - c_free(mud->mqtt_state.in_buffer); - mud->mqtt_state.in_buffer = NULL; - } - if(mud->mqtt_state.out_buffer){ - c_free(mud->mqtt_state.out_buffer); - mud->mqtt_state.out_buffer = NULL; - } + // ------- // free (unref) callback ref if(LUA_NOREF!=mud->cb_connect_ref){ @@ -580,32 +745,35 @@ static int mqtt_delete( lua_State* L ) luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); mud->cb_puback_ref = LUA_NOREF; } - lua_gc(gL, LUA_GCSTOP, 0); + lua_gc(L, LUA_GCSTOP, 0); if(LUA_NOREF!=mud->self_ref){ luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref); mud->self_ref = LUA_NOREF; } - lua_gc(gL, LUA_GCRESTART, 0); + lua_gc(L, LUA_GCRESTART, 0); + NODE_DBG("leave mqtt_delete.\n"); return 0; } static void socket_connect(struct espconn *pesp_conn) { + NODE_DBG("enter socket_connect.\n"); if(pesp_conn == NULL) return; lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; if(mud == NULL) return; - if(mud->secure){ + mud->event_timeout = MQTT_CONNECT_TIMEOUT; + mud->connState = MQTT_INIT; + if(mud->secure) espconn_secure_connect(pesp_conn); - } else - { espconn_connect(pesp_conn); - } + + os_timer_arm(&mud->mqttTimer, 1000, 1); - NODE_DBG("socket_connect is called.\n"); + NODE_DBG("leave socket_connect.\n"); } static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg); @@ -613,7 +781,7 @@ static dns_reconn_count = 0; static ip_addr_t host_ip; // for dns static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { - NODE_DBG("socket_dns_found is called.\n"); + NODE_DBG("enter socket_dns_found.\n"); struct espconn *pesp_conn = arg; if(pesp_conn == NULL){ NODE_DBG("pesp_conn null.\n"); @@ -645,78 +813,20 @@ static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) NODE_DBG("\n"); socket_connect(pesp_conn); } + NODE_DBG("leave socket_dns_found.\n"); } -// Lua: mqtt:connect( host, port, secure, function(client) ) -static int mqtt_socket_lwt( lua_State* L ) -{ - uint8_t stack = 1; - size_t topicSize, il; - 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" ); - luaL_argcheck( L, mud, stack, "mqtt.socket expected" ); - - if(mud == NULL) - return 0; - - stack++; - lwtTopic = luaL_checklstring( L, stack, &topicSize ); - if (lwtTopic == NULL) - { - return luaL_error( L, "need lwt topic"); - } - - stack++; - lwtMsg = luaL_checklstring( L, stack, &il ); - if (lwtMsg == NULL) - { - return luaL_error( L, "need lwt message"); - } - - mud->connect_info.will_topic = (uint8_t*) c_zalloc( topicSize + 1 ); - if(!mud->connect_info.will_topic){ - return luaL_error( L, "not enough memory"); - } - c_memcpy(mud->connect_info.will_topic, lwtTopic, topicSize); - mud->connect_info.will_topic[topicSize] = 0; - - mud->connect_info.will_message = (uint8_t*) c_zalloc( il + 1 ); - if(!mud->connect_info.will_message){ - return luaL_error( L, "not enough memory"); - } - c_memcpy(mud->connect_info.will_message, lwtMsg, il); - mud->connect_info.will_message[il] = 0; - - - stack++; - mud->connect_info.will_qos = luaL_checkinteger( L, stack ); - - stack++; - mud->connect_info.will_retain = luaL_checkinteger( L, 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); - return 0; -} - -// Lua: mqtt:connect( host, port, secure, function(client) ) +// Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client) ) static int mqtt_socket_connect( lua_State* L ) { - NODE_DBG("mqtt_socket_connect is called.\n"); + NODE_DBG("enter mqtt_socket_connect.\n"); lmqtt_userdata *mud = NULL; unsigned port = 1883; size_t il; ip_addr_t ipaddr; const char *domain; int stack = 1; - unsigned secure = 0; + unsigned secure = 0, auto_reconnect = 0; int top = lua_gettop(L); mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); @@ -725,6 +835,10 @@ static int mqtt_socket_connect( lua_State* L ) if(mud == NULL) return 0; + if(mud->connected){ + return luaL_error(L, "already connected"); + } + 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) @@ -750,7 +864,7 @@ static int mqtt_socket_connect( lua_State* L ) pesp_conn->reverse = mud; pesp_conn->type = ESPCONN_TCP; pesp_conn->state = ESPCONN_NONE; - mud->connected = 0; + mud->connected = false; if( (stack<=top) && lua_isstring(L,stack) ) // deal with the domain string { @@ -776,6 +890,7 @@ static int mqtt_socket_connect( lua_State* L ) } pesp_conn->proto.tcp->remote_port = port; pesp_conn->proto.tcp->local_port = espconn_port(); + mud->mqtt_state.port = port; if ( (stack<=top) && lua_isnumber(L, stack) ) { @@ -789,6 +904,18 @@ static int mqtt_socket_connect( lua_State* L ) } mud->secure = secure; // save + if ( (stack<=top) && lua_isnumber(L, stack) ) + { + auto_reconnect = lua_tointeger(L, stack); + stack++; + if ( auto_reconnect != 0 && auto_reconnect != 1 ){ + auto_reconnect = 0; // default to 0 + } + } else { + auto_reconnect = 0; // default to 0 + } + mud->mqtt_state.auto_reconnect = auto_reconnect; + // call back function when a connection is obtained, tcp only if ((stack<=top) && (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)){ lua_pushvalue(L, stack); // copy argument (func) to the top of stack @@ -806,6 +933,10 @@ static int mqtt_socket_connect( lua_State* L ) espconn_regist_connectcb(pesp_conn, mqtt_socket_connected); espconn_regist_reconcb(pesp_conn, mqtt_socket_reconnected); + os_timer_disarm(&mud->mqttTimer); + os_timer_setfn(&mud->mqttTimer, (os_timer_func_t *)mqtt_socket_timer, mud); + // timer started in socket_connect() + if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0)) { host_ip.addr = 0; @@ -819,10 +950,7 @@ static int mqtt_socket_connect( lua_State* L ) socket_connect(pesp_conn); } - os_timer_disarm(&mud->mqttTimer); - os_timer_setfn(&mud->mqttTimer, (os_timer_func_t *)mqtt_socket_timer, mud); - os_timer_arm(&mud->mqttTimer, 1000, 1); - + NODE_DBG("leave mqtt_socket_connect.\n"); return 0; } @@ -830,7 +958,7 @@ static int mqtt_socket_connect( lua_State* L ) // client disconnect and unref itself static int mqtt_socket_close( lua_State* L ) { - NODE_DBG("mqtt_socket_close is called.\n"); + NODE_DBG("enter mqtt_socket_close.\n"); int i = 0; lmqtt_userdata *mud = NULL; @@ -843,6 +971,7 @@ static int mqtt_socket_close( lua_State* L ) return 0; // call mqtt_disconnect() + mud->mqtt_state.auto_reconnect = 0; // stop auto reconnect. if(mud->secure){ if(mud->pesp_conn->proto.tcp->remote_port || mud->pesp_conn->proto.tcp->local_port) @@ -853,13 +982,14 @@ static int mqtt_socket_close( lua_State* L ) if(mud->pesp_conn->proto.tcp->remote_port || mud->pesp_conn->proto.tcp->local_port) espconn_disconnect(mud->pesp_conn); } + NODE_DBG("leave mqtt_socket_close.\n"); return 0; } // Lua: mqtt:on( "method", function() ) static int mqtt_socket_on( lua_State* L ) { - NODE_DBG("mqtt_on is called.\n"); + NODE_DBG("enter mqtt_socket_on.\n"); lmqtt_userdata *mud; size_t sl; @@ -893,20 +1023,16 @@ static int mqtt_socket_on( lua_State* L ) lua_pop(L, 1); return luaL_error( L, "method not supported" ); } - + NODE_DBG("leave mqtt_socket_on.\n"); return 0; } -// Lua: mqtt:subscribe(topic, qos, function()) +// Lua: bool = mqtt:subscribe(topic, qos, function()) static int mqtt_socket_subscribe( lua_State* L ) { - NODE_DBG("mqtt_socket_subscribe is called.\n"); - typedef struct SUB_STORAGE { - uint32_t length; - uint8_t *data; - struct SUB_STORAGE *next; - } SUB_STORAGE; + NODE_DBG("enter mqtt_socket_subscribe.\n"); uint8_t stack = 1, qos = 0; + uint16_t msg_id = 0; const char *topic; size_t il; lmqtt_userdata *mud; @@ -915,133 +1041,145 @@ static int mqtt_socket_subscribe( lua_State* L ) { luaL_argcheck( L, mud, stack, "mqtt.socket expected" ); stack++; - if( mud->send_timeout != 0 ) - return luaL_error( L, "sending in process" ); + if(mud==NULL){ + NODE_DBG("userdata is nil.\n"); + lua_pushboolean(L, 0); + return 1; + } - if( !mud->connected ) - return luaL_error( L, "not connected" ); + if(mud->pesp_conn == NULL){ + NODE_DBG("mud->pesp_conn is NULL.\n"); + lua_pushboolean(L, 0); + return 1; + } + + if(!mud->connected){ + luaL_error( L, "not connected" ); + lua_pushboolean(L, 0); + return 1; + } + + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + mqtt_message_t *temp_msg = NULL; if( lua_istable( L, stack ) ) { NODE_DBG("subscribe table\n"); lua_pushnil( L ); /* first key */ - SUB_STORAGE *first, *last, *curr; - first = (SUB_STORAGE*) c_zalloc(sizeof(SUB_STORAGE)); - if( first == NULL ) - return luaL_error( L, "not enough memory" ); - first->length = 0; - last = first; - first->next = NULL; - while( lua_next( L, stack ) != 0 ) { - curr = (SUB_STORAGE*) c_zalloc(sizeof(SUB_STORAGE)); - if( curr == NULL ) - return luaL_error( L, "not enough memory" ); + uint8_t temp_buf[MQTT_BUF_SIZE]; + uint32_t temp_pos = 0; + + while( lua_next( L, stack ) != 0 ) { topic = luaL_checkstring( L, -2 ); qos = luaL_checkinteger( L, -1 ); - mud->mqtt_state.outbound_message = mqtt_msg_subscribe( &mud->mqtt_state.mqtt_connection, topic, qos, &mud->mqtt_state.pending_msg_id ); - NODE_DBG("topic: %s - qos: %d, length: %d\n", topic, qos, mud->mqtt_state.outbound_message->length); - curr->data = (uint8_t*) c_zalloc(mud->mqtt_state.outbound_message->length); - if( curr->data == NULL ) - return luaL_error( L, "not enough memory" ); - c_memcpy( curr->data, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length ); + temp_msg = mqtt_msg_subscribe( &mud->mqtt_state.mqtt_connection, topic, qos, &msg_id ); + NODE_DBG("topic: %s - qos: %d, length: %d\n", topic, qos, temp_msg->length); + + if (temp_pos + temp_msg->length > MQTT_BUF_SIZE){ + lua_pop(L, 1); + break; // too long message for the outbuffer. + } + c_memcpy( temp_buf + temp_pos, temp_msg->data, temp_msg->length ); + temp_pos += temp_msg->length; - curr->length = mud->mqtt_state.outbound_message->length; - curr->next = NULL; - last->next = curr; - last = curr; lua_pop( L, 1 ); } - curr = first; - uint32_t ptr = 0; - while( curr != NULL ) { - if( curr->length == 0 ) { - curr = curr->next; - continue; - } - if( ptr + curr->length < mud->mqtt_state.out_buffer_length ) { - c_memcpy( mud->mqtt_state.out_buffer + ptr, curr->data, curr->length ); - ptr += curr->length; - } - c_free(curr->data); - c_free(curr); - curr = curr->next; - } - c_free(first); - if( ptr == 0 ) { - return luaL_error( L, "invalid data" ); - } - mud->mqtt_state.outbound_message->data = mud->mqtt_state.out_buffer; - mud->mqtt_state.outbound_message->length = ptr; + if (temp_pos == 0){ + luaL_error( L, "invalid data" ); + lua_pushboolean(L, 0); + return 1; + } + + c_memcpy( temp_buffer, temp_buf, temp_pos ); + temp_msg->data = temp_buffer; + temp_msg->length = temp_pos; stack++; } else { NODE_DBG("subscribe string\n"); topic = luaL_checklstring( L, stack, &il ); stack++; - if( topic == NULL ) - return luaL_error( L, "need topic name" ); + if( topic == NULL ){ + luaL_error( L, "need topic name" ); + lua_pushboolean(L, 0); + return 1; + } qos = luaL_checkinteger( L, stack ); - mud->mqtt_state.outbound_message = mqtt_msg_subscribe( &mud->mqtt_state.mqtt_connection, topic, qos, &mud->mqtt_state.pending_msg_id ); + temp_msg = mqtt_msg_subscribe( &mud->mqtt_state.mqtt_connection, topic, qos, &msg_id ); stack++; } - mud->send_timeout = MQTT_SEND_TIMEOUT; - mud->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_SUBSCRIBE; - mud->mqtt_state.pending_publish_qos = mqtt_get_qos( mud->mqtt_state.outbound_message->data ); + if( lua_type( L, stack ) == LUA_TFUNCTION || lua_type( L, stack ) == LUA_TLIGHTFUNCTION ) { // TODO: this will overwrite the previous one. + lua_pushvalue( L, stack ); // copy argument (func) to the top of stack + if( mud->cb_suback_ref != LUA_NOREF ) + luaL_unref( L, LUA_REGISTRYINDEX, mud->cb_suback_ref ); + mud->cb_suback_ref = luaL_ref( L, LUA_REGISTRYINDEX ); + } - if( lua_type( L, stack ) == LUA_TFUNCTION || lua_type( L, stack ) == LUA_TLIGHTFUNCTION ) { - lua_pushvalue( L, stack ); // copy argument (func) to the top of stack - if( mud->cb_suback_ref != LUA_NOREF ) - luaL_unref( L, LUA_REGISTRYINDEX, mud->cb_suback_ref ); - mud->cb_suback_ref = luaL_ref( L, LUA_REGISTRYINDEX ); - } - NODE_DBG("Sent: %d\n", mud->mqtt_state.outbound_message->length); - if( mud->secure ) - espconn_secure_sent( mud->pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length ); - else - espconn_sent( mud->pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length ); + msg_queue_t *node = msg_enqueue( &(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_SUBSCRIBE, (int)mqtt_get_qos(temp_msg->data) ); - return 0; + NODE_DBG("topic: %s - id: %d - qos: %d, length: %d\n", topic, node->msg_id, node->publish_qos, node->msg.length); + + if(node && (1==msg_size(&(mud->mqtt_state.pending_msg_q))) && mud->event_timeout == 0){ + mud->event_timeout = MQTT_SEND_TIMEOUT; + NODE_DBG("Sent: %d\n", node->msg.length); + if( mud->secure ) + espconn_secure_sent( mud->pesp_conn, node->msg.data, node->msg.length ); + else + espconn_sent( mud->pesp_conn, node->msg.data, node->msg.length ); + mud->keep_alive_tick = 0; + } + + if(!node){ + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); // enqueued succeed. + } + NODE_DBG("subscribe, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); + NODE_DBG("leave mqtt_socket_subscribe.\n"); + return 1; } -// Lua: mqtt:publish( topic, payload, qos, retain, function() ) +// Lua: bool = mqtt:publish( topic, payload, qos, retain, function() ) static int mqtt_socket_publish( lua_State* L ) { - // NODE_DBG("mqtt_publish is called.\n"); + NODE_DBG("enter mqtt_socket_publish.\n"); struct espconn *pesp_conn = NULL; lmqtt_userdata *mud; size_t l; uint8_t stack = 1; + uint16_t msg_id = 0; mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); luaL_argcheck(L, mud, stack, "mqtt.socket expected"); stack++; if(mud==NULL){ NODE_DBG("userdata is nil.\n"); - return 0; + lua_pushboolean(L, 0); + return 1; } if(mud->pesp_conn == NULL){ NODE_DBG("mud->pesp_conn is NULL.\n"); - return 0; + lua_pushboolean(L, 0); + return 1; + } + + if(!mud->connected){ + luaL_error( L, "not connected" ); + lua_pushboolean(L, 0); + return 1; } - if(mud->send_timeout != 0) - return luaL_error( L, "sending in process" ); - pesp_conn = mud->pesp_conn; -#if 0 - char temp[20] = {0}; - c_sprintf(temp, IPSTR, IP2STR( &(pesp_conn->proto.tcp->remote_ip) ) ); - NODE_DBG("remote "); - NODE_DBG(temp); - NODE_DBG(":"); - NODE_DBG("%d",pesp_conn->proto.tcp->remote_port); - NODE_DBG(" sending data.\n"); -#endif const char *topic = luaL_checklstring( L, stack, &l ); stack ++; - if (topic == NULL) - return luaL_error( L, "need topic" ); + if (topic == NULL){ + luaL_error( L, "need topic" ); + lua_pushboolean(L, 0); + return 1; + } const char *payload = luaL_checklstring( L, stack, &l ); stack ++; @@ -1050,14 +1188,13 @@ static int mqtt_socket_publish( lua_State* L ) uint8_t retain = luaL_checkinteger( L, stack); stack ++; - - mud->mqtt_state.outbound_message = mqtt_msg_publish(&mud->mqtt_state.mqtt_connection, + uint8_t temp_buffer[MQTT_BUF_SIZE]; + mqtt_msg_init(&mud->mqtt_state.mqtt_connection, temp_buffer, MQTT_BUF_SIZE); + mqtt_message_t *temp_msg = mqtt_msg_publish(&mud->mqtt_state.mqtt_connection, topic, payload, l, qos, retain, - &mud->mqtt_state.pending_msg_id); - mud->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PUBLISH; - mud->mqtt_state.pending_publish_qos = qos; - mud->send_timeout = MQTT_SEND_TIMEOUT; + &msg_id); + if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ lua_pushvalue(L, stack); // copy argument (func) to the top of stack if(mud->cb_puback_ref != LUA_NOREF) @@ -1065,11 +1202,105 @@ static int mqtt_socket_publish( lua_State* L ) mud->cb_puback_ref = luaL_ref(L, LUA_REGISTRYINDEX); } - if(mud->secure) - espconn_secure_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - else - espconn_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); - mud->mqtt_state.outbound_message = NULL; + msg_queue_t *node = msg_enqueue(&(mud->mqtt_state.pending_msg_q), temp_msg, + msg_id, MQTT_MSG_TYPE_PUBLISH, (int)qos ); + + if(node && (1==msg_size(&(mud->mqtt_state.pending_msg_q))) && mud->event_timeout == 0){ + mud->event_timeout = MQTT_SEND_TIMEOUT; + NODE_DBG("Sent: %d\n", node->msg.length); + if( mud->secure ) + espconn_secure_sent( mud->pesp_conn, node->msg.data, node->msg.length ); + else + espconn_sent( mud->pesp_conn, node->msg.data, node->msg.length ); + mud->keep_alive_tick = 0; + } + + if(!node){ + lua_pushboolean(L, 0); + } else { + lua_pushboolean(L, 1); // enqueued succeed. + } + + NODE_DBG("publish, queue size: %d\n", msg_size(&(mud->mqtt_state.pending_msg_q))); + NODE_DBG("leave mqtt_socket_publish.\n"); + return 1; +} + +// Lua: mqtt:lwt( topic, message, qos, retain, function(client) ) +static int mqtt_socket_lwt( lua_State* L ) +{ + 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" ); + luaL_argcheck( L, mud, stack, "mqtt.socket expected" ); + + if(mud == NULL) + return 0; + + stack++; + lwtTopic = luaL_checklstring( L, stack, &topicSize ); + if (lwtTopic == NULL) + { + return luaL_error( L, "need lwt topic"); + } + + stack++; + lwtMsg = luaL_checklstring( L, stack, &msgSize ); + if (lwtMsg == NULL) + { + return luaL_error( L, "need lwt message"); + } + + if(mud->connect_info.will_topic){ // free the previous one if there is any + c_free(mud->connect_info.will_topic); + mud->connect_info.will_topic = NULL; + } + if(mud->connect_info.will_message){ + c_free(mud->connect_info.will_message); + mud->connect_info.will_message = NULL; + } + + mud->connect_info.will_topic = (uint8_t*) c_zalloc( topicSize + 1 ); + mud->connect_info.will_message = (uint8_t*) c_zalloc( msgSize + 1 ); + if(!mud->connect_info.will_topic || !mud->connect_info.will_message){ + if(mud->connect_info.will_topic){ + c_free(mud->connect_info.will_topic); + mud->connect_info.will_topic = NULL; + } + if(mud->connect_info.will_message){ + c_free(mud->connect_info.will_message); + mud->connect_info.will_message = NULL; + } + return luaL_error( L, "not enough memory"); + } + c_memcpy(mud->connect_info.will_topic, lwtTopic, topicSize); + mud->connect_info.will_topic[topicSize] = 0; + c_memcpy(mud->connect_info.will_message, lwtMsg, msgSize); + mud->connect_info.will_message[msgSize] = 0; + + if ( lua_isnumber(L, stack) ) + { + mud->connect_info.will_qos = lua_tointeger(L, stack); + stack++; + } + if ( lua_isnumber(L, stack) ) + { + mud->connect_info.will_retain = lua_tointeger(L, 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"); return 0; } @@ -1079,11 +1310,11 @@ static int mqtt_socket_publish( lua_State* L ) static const LUA_REG_TYPE mqtt_socket_map[] = { - { LSTRKEY( "lwt" ), LFUNCVAL ( mqtt_socket_lwt ) }, { LSTRKEY( "connect" ), LFUNCVAL ( mqtt_socket_connect ) }, { LSTRKEY( "close" ), LFUNCVAL ( mqtt_socket_close ) }, { LSTRKEY( "publish" ), LFUNCVAL ( mqtt_socket_publish ) }, { LSTRKEY( "subscribe" ), LFUNCVAL ( mqtt_socket_subscribe ) }, + { LSTRKEY( "lwt" ), LFUNCVAL ( mqtt_socket_lwt ) }, { LSTRKEY( "on" ), LFUNCVAL ( mqtt_socket_on ) }, { LSTRKEY( "__gc" ), LFUNCVAL ( mqtt_delete ) }, #if LUA_OPTIMIZE_MEMORY > 0 diff --git a/app/modules/node.c b/app/modules/node.c index 53d59ae0..5f6ae9bd 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -100,13 +100,15 @@ static int node_chipid( lua_State* L ) lua_pushinteger(L, id); return 1; } + +// deprecated, moved to adc module // Lua: readvdd33() -static int node_readvdd33( lua_State* L ) -{ - uint32_t vdd33 = readvdd33(); - lua_pushinteger(L, vdd33); - return 1; -} +// static int node_readvdd33( lua_State* L ) +// { +// uint32_t vdd33 = readvdd33(); +// lua_pushinteger(L, vdd33); +// return 1; +// } // Lua: flashid() static int node_flashid( lua_State* L ) @@ -430,7 +432,8 @@ const LUA_REG_TYPE node_map[] = #endif { LSTRKEY( "input" ), LFUNCVAL( node_input ) }, { LSTRKEY( "output" ), LFUNCVAL( node_output ) }, - { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, +// Moved to adc module, use adc.readvdd33() +// { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, { LSTRKEY( "compile" ), LFUNCVAL( node_compile) }, { LSTRKEY( "CPU80MHZ" ), LNUMVAL( CPU80MHZ ) }, { LSTRKEY( "CPU160MHZ" ), LNUMVAL( CPU160MHZ ) }, diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 635625c6..d2b2d2e5 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -8,6 +8,7 @@ #include "lrotable.h" #include "c_string.h" +#include "c_stdlib.h" #include "c_types.h" #include "user_interface.h" @@ -327,8 +328,8 @@ static int wifi_station_config( lua_State* L ) if (sl>32 || ssid == NULL) return luaL_error( L, "ssid:<32" ); const char *password = luaL_checklstring( L, 2, &pl ); - if (pl>64 || password == NULL) - return luaL_error( L, "pwd:<64" ); + if (pl<8 || pl>64 || password == NULL) + return luaL_error( L, "pwd:8~64" ); c_memset(sta_conf.ssid, 0, 32); c_memset(sta_conf.password, 0, 64); @@ -380,6 +381,7 @@ static int wifi_station_setauto( lua_State* L ) return 0; } +// Lua: table = wifi.sta.getap() static int wifi_station_listap( lua_State* L ) { if(wifi_get_opmode() == SOFTAP_MODE) @@ -437,44 +439,46 @@ static int wifi_ap_getbroadcast( lua_State* L ){ // Lua: wifi.ap.config(table) static int wifi_ap_config( lua_State* L ) { - struct softap_config config; - size_t len; - wifi_softap_get_config(&config); if (!lua_istable(L, 1)) return luaL_error( L, "wrong arg type" ); + struct softap_config config; + wifi_softap_get_config(&config); + + size_t len; + lua_getfield(L, 1, "ssid"); if (!lua_isnil(L, -1)){ /* found? */ if( lua_isstring(L, -1) ) // deal with the ssid string { const char *ssid = luaL_checklstring( L, -1, &len ); - if(len>32) - return luaL_error( L, "ssid:<32" ); + if(len<1 || len>32 || ssid == NULL) + return luaL_error( L, "ssid:1~32" ); c_memset(config.ssid, 0, 32); c_memcpy(config.ssid, ssid, len); - config.ssid_len = len; - config.ssid_hidden = 0; NODE_DBG(config.ssid); NODE_DBG("\n"); + config.ssid_len = len; + config.ssid_hidden = 0; } else return luaL_error( L, "wrong arg type" ); } else - return luaL_error( L, "wrong arg type" ); + return luaL_error( L, "ssid required" ); lua_getfield(L, 1, "pwd"); if (!lua_isnil(L, -1)){ /* found? */ if( lua_isstring(L, -1) ) // deal with the password string { const char *pwd = luaL_checklstring( L, -1, &len ); - if(len>64) - return luaL_error( L, "pwd:<64" ); + if(len<8 || len>64 || pwd == NULL) + return luaL_error( L, "pwd:8~64" ); c_memset(config.password, 0, 64); c_memcpy(config.password, pwd, len); - config.authmode = AUTH_WPA_WPA2_PSK; NODE_DBG(config.password); NODE_DBG("\n"); + config.authmode = AUTH_WPA_WPA2_PSK; } else return luaL_error( L, "wrong arg type" ); @@ -483,11 +487,162 @@ static int wifi_ap_config( lua_State* L ) config.authmode = AUTH_OPEN; } - config.max_connection = 4; + lua_getfield(L, 1, "auth"); + if (!lua_isnil(L, -1)) + { + config.authmode = (uint8_t)luaL_checkinteger(L, -1); + NODE_DBG(config.authmode); + NODE_DBG("\n"); + } + else + { + // keep whatever value resulted from "pwd" logic above + } + + lua_getfield(L, 1, "channel"); + if (!lua_isnil(L, -1)) + { + unsigned channel = luaL_checkinteger(L, -1); + if (channel < 1 || channel > 13) + return luaL_error( L, "channel:1~13" ); + + config.channel = (uint8_t)channel; + NODE_DBG(config.channel); + NODE_DBG("\n"); + } + else + { + config.channel = 6; + } + + lua_getfield(L, 1, "hidden"); + if (!lua_isnil(L, -1)) + { + config.ssid_hidden = (uint8_t)luaL_checkinteger(L, -1); + NODE_DBG(config.ssid_hidden); + NODE_DBG("\n"); + } + else + { + config.ssid_hidden = 0; + } + + lua_getfield(L, 1, "max"); + if (!lua_isnil(L, -1)) + { + unsigned max = luaL_checkinteger(L, -1); + if (max < 1 || max > 4) + return luaL_error( L, "max:1~4" ); + + config.max_connection = (uint8_t)max; + NODE_DBG(config.max_connection); + NODE_DBG("\n"); + } + else + { + config.max_connection = 4; + } + + lua_getfield(L, 1, "beacon"); + if (!lua_isnil(L, -1)) + { + unsigned beacon = luaL_checkinteger(L, -1); + if (beacon < 100 || beacon > 60000) + return luaL_error( L, "beacon:100~60000" ); + + config.beacon_interval = (uint16_t)beacon; + NODE_DBG(config.beacon_interval); + NODE_DBG("\n"); + } + else + { + config.beacon_interval = 100; + } wifi_softap_set_config(&config); // system_restart(); - return 0; + return 0; +} + +// Lua: table = wifi.ap.getclient() +static int wifi_ap_listclient( lua_State* L ) +{ + if (wifi_get_opmode() == STATION_MODE) + { + return luaL_error( L, "Can't list client in STATION_MODE mode" ); + } + + char temp[64]; + + lua_newtable(L); + + struct station_info * station = wifi_softap_get_station_info(); + struct station_info * next_station; + while (station != NULL) + { + c_sprintf(temp, IPSTR, IP2STR(&station->ip)); + lua_pushstring(L, temp); + + c_sprintf(temp, MACSTR, MAC2STR(station->bssid)); + lua_setfield(L, -2, temp); + + next_station = STAILQ_NEXT(station, next); + c_free(station); + station = next_station; + } + + return 1; +} + +// Lua: ip = wifi.ap.dhcp.config() +static int wifi_ap_dhcp_config( lua_State* L ) +{ + if (!lua_istable(L, 1)) + return luaL_error( L, "wrong arg type" ); + + struct dhcps_lease lease; + uint32_t ip; + + ip = parse_key(L, "start"); + if (ip == 0) + return luaL_error( L, "wrong arg type" ); + + lease.start_ip = ip; + NODE_DBG(IPSTR, IP2STR(&lease.start_ip)); + NODE_DBG("\n"); + + // use configured max_connection to determine end + struct softap_config config; + wifi_softap_get_config(&config); + lease.end_ip = lease.start_ip; + ip4_addr4(&lease.end_ip) += config.max_connection - 1; + + char temp[64]; + c_sprintf(temp, IPSTR, IP2STR(&lease.start_ip)); + lua_pushstring(L, temp); + c_sprintf(temp, IPSTR, IP2STR(&lease.end_ip)); + lua_pushstring(L, temp); + + // note: DHCP max range = 101 from start_ip to end_ip + wifi_softap_dhcps_stop(); + wifi_softap_set_dhcps_lease(&lease); + wifi_softap_dhcps_start(); + + return 2; +} + +// Lua: wifi.ap.dhcp.start() +static int wifi_ap_dhcp_start( lua_State* L ) +{ + lua_pushboolean(L, wifi_softap_dhcps_start()); + return 1; +} + +// Lua: wifi.ap.dhcp.stop() +static int wifi_ap_dhcp_stop( lua_State* L ) +{ + lua_pushboolean(L, wifi_softap_dhcps_stop()); + return 1; } // Module function map @@ -509,6 +664,14 @@ static const LUA_REG_TYPE wifi_station_map[] = { LNILKEY, LNILVAL } }; +static const LUA_REG_TYPE wifi_ap_dhcp_map[] = +{ + { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_dhcp_config ) }, + { LSTRKEY( "start" ), LFUNCVAL( wifi_ap_dhcp_start ) }, + { LSTRKEY( "stop" ), LFUNCVAL( wifi_ap_dhcp_stop ) }, + { LNILKEY, LNILVAL } +}; + static const LUA_REG_TYPE wifi_ap_map[] = { { LSTRKEY( "config" ), LFUNCVAL( wifi_ap_config ) }, @@ -517,6 +680,12 @@ static const LUA_REG_TYPE wifi_ap_map[] = { LSTRKEY( "getbroadcast" ), LFUNCVAL ( wifi_ap_getbroadcast) }, { LSTRKEY( "getmac" ), LFUNCVAL ( wifi_ap_getmac ) }, { LSTRKEY( "setmac" ), LFUNCVAL ( wifi_ap_setmac ) }, + { LSTRKEY( "getclient" ), LFUNCVAL ( wifi_ap_listclient ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "dhcp" ), LROVAL( wifi_ap_dhcp_map ) }, + +// { LSTRKEY( "__metatable" ), LROVAL( wifi_ap_map ) }, +#endif { LNILKEY, LNILVAL } }; @@ -540,6 +709,12 @@ const LUA_REG_TYPE wifi_map[] = { LSTRKEY( "LIGHT_SLEEP" ), LNUMVAL( LIGHT_SLEEP_T ) }, { LSTRKEY( "MODEM_SLEEP" ), LNUMVAL( MODEM_SLEEP_T ) }, + { LSTRKEY( "OPEN" ), LNUMVAL( AUTH_OPEN ) }, + // { LSTRKEY( "WEP" ), LNUMVAL( AUTH_WEP ) }, + { LSTRKEY( "WPA_PSK" ), LNUMVAL( AUTH_WPA_PSK ) }, + { LSTRKEY( "WPA2_PSK" ), LNUMVAL( AUTH_WPA2_PSK ) }, + { LSTRKEY( "WPA_WPA2_PSK" ), LNUMVAL( AUTH_WPA_WPA2_PSK ) }, + // { LSTRKEY( "STA_IDLE" ), LNUMVAL( STATION_IDLE ) }, // { LSTRKEY( "STA_CONNECTING" ), LNUMVAL( STATION_CONNECTING ) }, // { LSTRKEY( "STA_WRONGPWD" ), LNUMVAL( STATION_WRONG_PASSWORD ) }, @@ -573,6 +748,12 @@ LUALIB_API int luaopen_wifi( lua_State *L ) MOD_REG_NUMBER( L, "LIGHT_SLEEP", LIGHT_SLEEP_T ); MOD_REG_NUMBER( L, "MODEM_SLEEP", MODEM_SLEEP_T ); + MOD_REG_NUMBER( L, "OPEN", AUTH_OPEN ); + // MOD_REG_NUMBER( L, "WEP", AUTH_WEP ); + MOD_REG_NUMBER( L, "WPA_PSK", AUTH_WPA_PSK ); + MOD_REG_NUMBER( L, "WPA2_PSK", AUTH_WPA2_PSK ); + MOD_REG_NUMBER( L, "WPA_WPA2_PSK", AUTH_WPA_WPA2_PSK ); + // MOD_REG_NUMBER( L, "STA_IDLE", STATION_IDLE ); // MOD_REG_NUMBER( L, "STA_CONNECTING", STATION_CONNECTING ); // MOD_REG_NUMBER( L, "STA_WRONGPWD", STATION_WRONG_PASSWORD ); @@ -589,6 +770,11 @@ LUALIB_API int luaopen_wifi( lua_State *L ) luaL_register( L, NULL, wifi_ap_map ); lua_setfield( L, -2, "ap" ); + // Setup the new table (dhcp) inside ap + lua_newtable( L ); + luaL_register( L, NULL, wifi_ap_dhcp_map ); + lua_setfield( L, -1, "dhcp" ); + return 1; #endif // #if LUA_OPTIMIZE_MEMORY > 0 } diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index b9419181..b0975201 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -3,6 +3,8 @@ #include "platform.h" #include "auxmods.h" #include "lrotable.h" +#include "c_stdlib.h" +#include "c_string.h" /** * All this code is mostly from http://www.esp8266.com/viewtopic.php?f=21&t=1143&sid=a620a377672cfe9f666d672398415fcb * from user Markus Gritsch. @@ -35,7 +37,10 @@ static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) { const uint8_t pin = luaL_checkinteger(L, 1); size_t length; - char *buffer = (char *)luaL_checklstring(L, 2, &length); // Cast away the constness. + const char *rgb = luaL_checklstring(L, 2, &length); + // dont modify lua-internal lstring - make a copy instead + char *buffer = (char *)c_malloc(length); + c_memcpy(buffer, rgb, length); // Initialize the output pin: platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); @@ -59,17 +64,17 @@ static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) // Send the buffer: os_intr_lock(); - const char * const end = buffer + length; - while (buffer != end) { + for (i = 0; i < length; i++) { uint8_t mask = 0x80; while (mask) { - (*buffer & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); + (buffer[i] & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); mask >>= 1; } - ++buffer; } os_intr_unlock(); + c_free(buffer); + return 0; } diff --git a/app/mqtt/mqtt_msg.c b/app/mqtt/mqtt_msg.c index a58f6057..9c405a7c 100644 --- a/app/mqtt/mqtt_msg.c +++ b/app/mqtt/mqtt_msg.c @@ -29,7 +29,7 @@ * */ -#include +#include "c_string.h" #include "mqtt_msg.h" #define MQTT_MAX_FIXED_HEADER_SIZE 3 @@ -61,7 +61,7 @@ static int append_string(mqtt_connection_t* connection, const char* string, int connection->buffer[connection->message.length++] = len >> 8; connection->buffer[connection->message.length++] = len & 0xff; - memcpy(connection->buffer + connection->message.length, string, len); + c_memcpy(connection->buffer + connection->message.length, string, len); connection->message.length += len; return len + 2; @@ -121,7 +121,7 @@ static mqtt_message_t* fini_message(mqtt_connection_t* connection, int type, int void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) { - memset(connection, 0, sizeof(connection)); + c_memset(connection, 0, sizeof(connection)); connection->buffer = buffer; connection->buffer_length = buffer_length; } @@ -294,7 +294,7 @@ mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_inf variable_header->lengthMsb = 0; variable_header->lengthLsb = 4; - memcpy(variable_header->magic, "MQTT", 4); + c_memcpy(variable_header->magic, "MQTT", 4); variable_header->version = 4; variable_header->flags = 0; variable_header->keepaliveMsb = info->keepalive >> 8; @@ -305,7 +305,7 @@ mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_inf if(info->client_id != NULL && info->client_id[0] != '\0') { - if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) + if(append_string(connection, info->client_id, c_strlen(info->client_id)) < 0) return fail_message(connection); } else @@ -313,10 +313,10 @@ mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_inf if(info->will_topic != NULL && info->will_topic[0] != '\0') { - if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + if(append_string(connection, info->will_topic, c_strlen(info->will_topic)) < 0) return fail_message(connection); - if(append_string(connection, info->will_message, strlen(info->will_message)) < 0) + if(append_string(connection, info->will_message, c_strlen(info->will_message)) < 0) return fail_message(connection); variable_header->flags |= MQTT_CONNECT_FLAG_WILL; @@ -327,7 +327,7 @@ mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_inf if(info->username != NULL && info->username[0] != '\0') { - if(append_string(connection, info->username, strlen(info->username)) < 0) + if(append_string(connection, info->username, c_strlen(info->username)) < 0) return fail_message(connection); variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; @@ -335,7 +335,7 @@ mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_inf if(info->password != NULL && info->password[0] != '\0') { - if(append_string(connection, info->password, strlen(info->password)) < 0) + if(append_string(connection, info->password, c_strlen(info->password)) < 0) return fail_message(connection); variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; @@ -351,7 +351,7 @@ mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topi if(topic == NULL || topic[0] == '\0') return fail_message(connection); - if(append_string(connection, topic, strlen(topic)) < 0) + if(append_string(connection, topic, c_strlen(topic)) < 0) return fail_message(connection); if(qos > 0) @@ -364,7 +364,7 @@ mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topi if(connection->message.length + data_length > connection->buffer_length) return fail_message(connection); - memcpy(connection->buffer + connection->message.length, data, data_length); + c_memcpy(connection->buffer + connection->message.length, data, data_length); connection->message.length += data_length; return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); @@ -412,7 +412,7 @@ mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* to if((*message_id = append_message_id(connection, 0)) == 0) return fail_message(connection); - if(append_string(connection, topic, strlen(topic)) < 0) + if(append_string(connection, topic, c_strlen(topic)) < 0) return fail_message(connection); if(connection->message.length + 1 > connection->buffer_length) @@ -432,7 +432,7 @@ mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* if((*message_id = append_message_id(connection, 0)) == 0) return fail_message(connection); - if(append_string(connection, topic, strlen(topic)) < 0) + if(append_string(connection, topic, c_strlen(topic)) < 0) return fail_message(connection); return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); diff --git a/app/mqtt/msg_queue.c b/app/mqtt/msg_queue.c new file mode 100644 index 00000000..1258ad6e --- /dev/null +++ b/app/mqtt/msg_queue.c @@ -0,0 +1,82 @@ +#include "c_string.h" +#include "c_stdlib.h" +#include "c_stdio.h" +#include "msg_queue.h" + +msg_queue_t *msg_enqueue(msg_queue_t **head, mqtt_message_t *msg, uint16_t msg_id, int msg_type, int publish_qos){ + if(!head){ + return NULL; + } + if (!msg || !msg->data || msg->length == 0){ + NODE_DBG("empty message\n"); + return NULL; + } + msg_queue_t *node = (msg_queue_t *)c_zalloc(sizeof(msg_queue_t)); + if(!node){ + NODE_DBG("not enough memory\n"); + return NULL; + } + + node->msg.data = (uint8_t *)c_zalloc(msg->length); + if(!node->msg.data){ + NODE_DBG("not enough memory\n"); + c_free(node); + return NULL; + } + c_memcpy(node->msg.data, msg->data, msg->length); + node->msg.length = msg->length; + node->next = NULL; + node->msg_id = msg_id; + node->msg_type = msg_type; + node->publish_qos = publish_qos; + + msg_queue_t *tail = *head; + if(tail){ + while(tail->next!=NULL) tail = tail->next; + tail->next = node; + } else { + *head = node; + } + return node; +} + +void msg_destroy(msg_queue_t *node){ + if(!node) return; + if(node->msg.data){ + c_free(node->msg.data); + node->msg.data = NULL; + } + c_free(node); +} + +msg_queue_t * msg_dequeue(msg_queue_t **head){ + if(!head || !*head){ + return NULL; + } + msg_queue_t *node = *head; // fetch head. + *head = node->next; // update head. + node->next = NULL; + return node; +} + +msg_queue_t * msg_peek(msg_queue_t **head){ + if(!head || !*head){ + return NULL; + } + return *head; // fetch head. +} + +int msg_size(msg_queue_t **head){ + if(!head || !*head){ + return 0; + } + int i = 1; + msg_queue_t *tail = *head; + if(tail){ + while(tail->next!=NULL){ + tail = tail->next; + i++; + } + } + return i; +} diff --git a/app/mqtt/msg_queue.h b/app/mqtt/msg_queue.h new file mode 100644 index 00000000..05b910ae --- /dev/null +++ b/app/mqtt/msg_queue.h @@ -0,0 +1,28 @@ +#ifndef _MSG_QUEUE_H +#define _MSG_QUEUE_H 1 +#include "mqtt_msg.h" +#ifdef __cplusplus +extern "C" { +#endif + +struct msg_queue_t; + +typedef struct msg_queue_t { + struct msg_queue_t *next; + mqtt_message_t msg; + uint16_t msg_id; + int msg_type; + int publish_qos; +} msg_queue_t; + +msg_queue_t * msg_enqueue(msg_queue_t **head, mqtt_message_t *msg, uint16_t msg_id, int msg_type, int publish_qos); +void msg_destroy(msg_queue_t *node); +msg_queue_t * msg_dequeue(msg_queue_t **head); +msg_queue_t * msg_peek(msg_queue_t **head); +int msg_size(msg_queue_t **head); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/platform/flash_api.c b/app/platform/flash_api.c index a648d91b..a96619e9 100644 --- a/app/platform/flash_api.c +++ b/app/platform/flash_api.c @@ -321,7 +321,7 @@ bool flash_init_data_written(void) // FLASH SEC - 4 uint32_t data[2] ICACHE_STORE_ATTR; #if defined(FLASH_SAFE_API) - if (SPI_FLASH_RESULT_OK == flash_safe_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) + if (SPI_FLASH_RESULT_OK == flash_safe_read((flash_safe_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) #else if (SPI_FLASH_RESULT_OK == spi_flash_read((flash_rom_get_sec_num() - 4) * SPI_FLASH_SEC_SIZE, (uint32 *)data, sizeof(data))) #endif // defined(FLASH_SAFE_API) @@ -369,8 +369,8 @@ bool flash_init_data_blank(void) // It will init system config to blank! bool result = false; #if defined(FLASH_SAFE_API) - if ((SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 2))) && - (SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_rom_get_sec_num() - 1)))) + if ((SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_safe_get_sec_num() - 2))) && + (SPI_FLASH_RESULT_OK == flash_safe_erase_sector((flash_safe_get_sec_num() - 1)))) #else if ((SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 2))) && (SPI_FLASH_RESULT_OK == spi_flash_erase_sector((flash_rom_get_sec_num() - 1)))) diff --git a/app/platform/flash_fs.h b/app/platform/flash_fs.h index 9d9da7c7..66538b90 100644 --- a/app/platform/flash_fs.h +++ b/app/platform/flash_fs.h @@ -71,6 +71,9 @@ #define fs_rename myspiffs_rename #define fs_size myspiffs_size +#define fs_mount myspiffs_mount +#define fs_unmount myspiffs_unmount + #define FS_NAME_MAX_LENGTH SPIFFS_OBJ_NAME_LEN #endif diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index e96e15a0..bcb11d5c 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -42,7 +42,7 @@ The small 4KB sectors allow for greater flexibility in applications th ********************/ -void spiffs_mount() { +void myspiffs_mount() { spiffs_config cfg; cfg.phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ); cfg.phys_addr += 0x3000; @@ -69,6 +69,10 @@ void spiffs_mount() { NODE_DBG("mount res: %i\n", res); } +void myspiffs_unmount() { + SPIFFS_unmount(&fs); +} + // FS formatting function // Returns 1 if OK, 0 for error int myspiffs_format( void ) @@ -85,7 +89,7 @@ int myspiffs_format( void ) while( sect_first <= sect_last ) if( platform_flash_erase_sector( sect_first ++ ) == PLATFORM_ERR ) return 0; - spiffs_mount(); + myspiffs_mount(); return 1; } diff --git a/app/spiffs/spiffs.h b/app/spiffs/spiffs.h index c5a2c1d2..7132b92d 100644 --- a/app/spiffs/spiffs.h +++ b/app/spiffs/spiffs.h @@ -477,6 +477,8 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); #if SPIFFS_CACHE #endif +void myspiffs_mount(); +void myspiffs_unmount(); int myspiffs_open(const char *name, int flags); int myspiffs_close( int fd ); size_t myspiffs_write( int fd, const void* ptr, size_t len ); diff --git a/app/user/Makefile b/app/user/Makefile index 0dd1afe6..80c303ee 100644 --- a/app/user/Makefile +++ b/app/user/Makefile @@ -44,6 +44,7 @@ INCLUDES += -I ../libc INCLUDES += -I ../platform INCLUDES += -I ../lua INCLUDES += -I ../wofs +INCLUDES += -I ../spiffs PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/user/user_main.c b/app/user/user_main.c index dee11754..88938da8 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -14,8 +14,7 @@ #include "c_stdlib.h" #include "c_stdio.h" -#include "romfs.h" - +#include "flash_fs.h" #include "user_interface.h" #include "ets_sys.h" @@ -44,7 +43,6 @@ void task_init(void){ system_os_task(task_lua, USER_TASK_PRIO_0, taskQueue, TASK_QUEUE_LEN); } -extern void spiffs_mount(); // extern void test_spiffs(); // extern int test_romfs(); @@ -69,7 +67,16 @@ void nodemcu_init(void) // Flash init data at FLASHSIZE - 0x04000 Byte. flash_init_data_default(); // Flash blank data at FLASHSIZE - 0x02000 Byte. - flash_init_data_blank(); + flash_init_data_blank(); + if( !fs_format() ) + { + NODE_ERR( "\ni*** ERROR ***: unable to format. FS might be compromised.\n" ); + NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); + } + else{ + NODE_ERR( "format done.\n" ); + } + fs_unmount(); // mounted by format. } #endif // defined(FLASH_SAFE_API) @@ -94,7 +101,7 @@ void nodemcu_init(void) // test_romfs(); #elif defined ( BUILD_SPIFFS ) - spiffs_mount(); + fs_mount(); // test_spiffs(); #endif // endpoint_setup(); diff --git a/examples/fragment.lua b/examples/fragment.lua index 67aa2c37..11b28721 100644 --- a/examples/fragment.lua +++ b/examples/fragment.lua @@ -14,7 +14,7 @@ sk:connect(80,"115.239.210.27") sk:send("GET / HTTP/1.1\r\nHost: 115.239.210.27\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") sk:connect(80,"192.168.0.66") -sk:send("GET / HTTP/1.1\r\nHost: 1192.168.0.66\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") +sk:send("GET / HTTP/1.1\r\nHost: 192.168.0.66\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") i2c.setup(0,1,0,i2c.SLOW) function read_bmp(addr) i2c.start(0) i2c.address(0,119,i2c.RECEIVER) c=i2c.read(0,1) i2c.stop(0) print(string.byte(c)) end @@ -381,3 +381,141 @@ function TestDNSLeak() tmr.alarm(1, 3000, 0, function() print("hack socket close, MEM: "..node.heap()) c:close() end) -- socket timeout hack print("MEM: "..node.heap()) end + +v="abc%0D%0Adef" +print(string.gsub(v, "%%(%x%x)", function(x) return string.char(tonumber(x, 16)) end)) + +function ex(x) string.find("abc%0Ddef","bc") return 's' end +string.gsub("abc%0Ddef", "%%(%x%x)", ex) + +function ex(x) string.char(35) return 's' end +string.gsub("abc%0Ddef", "%%(%x%x)", ex) print("hello") + +function ex(x) string.lower('Ab') return 's' end +string.gsub("abc%0Ddef", "%%(%x%x)", ex) print("hello") + +v="abc%0D%0Adef" +pcall(function() print(string.gsub(v, "%%(%x%x)", function(x) return string.char(tonumber(x, 16)) end)) end) + +mosca -v | bunyan + +m=mqtt.Client() +m:connect("192.168.18.88",1883) +topic={} +topic["/topic1"]=0 +topic["/topic2"]=0 +m:subscribe(topic,function(m) print("sub done") end) +m:on("message",function(m,t,pl) print(t..":") if pl~=nil then print(pl) end end ) +m:publish("/topic1","hello",0,0) +m:publish("/topic3","hello",0,0) m:publish("/topic4","hello",0,0) + +m=mqtt.Client() +m:connect("192.168.18.88",1883) +m:subscribe("/topic1",0,function(m) print("sub done") end) +m:subscribe("/topic2",0,function(m) print("sub done") end) +m:on("message",function(m,t,pl) print(t..":") if pl~=nil then print(pl) end end ) +m:publish("/topic1","hello",0,0) +m:publish("/topic3","hello",0,0) m:publish("/topic4","hello",0,0) +m:publish("/topic1","hello1",0,0) m:publish("/topic2","hello2",0,0) +m:publish("/topic1","hello",1,0) +m:subscribe("/topic3",0,function(m) print("sub done") end) +m:publish("/topic3","hello3",2,0) + +m=mqtt.Client() +m:connect("192.168.18.88",1883, function(con) print("connected hello") end) + +m=mqtt.Client() +m:on("connect",function(m) print("connection") end ) +m:connect("192.168.18.88",1883) +m:on("offline",function(m) print("disconnection") end ) + +m=mqtt.Client() +m:on("connect",function(m) print("connection "..node.heap()) end ) +m:on("offline", function(conn) + if conn == nil then print("conn is nil") end + print("Reconnect to broker...") + print(node.heap()) + conn:connect("192.168.18.88",1883,0,1) +end) +m:connect("192.168.18.88",1883,0,1) + +m=mqtt.Client() +m:on("connect",function(m) print("connection "..node.heap()) end ) +m:on("offline", function(conn) + if conn == nil then print("conn is nil") end + print("Reconnect to broker...") + print(node.heap()) + conn:connect("192.168.18.88",1883) +end) +m:connect("192.168.18.88",1883) + +m:close() + +m=mqtt.Client() +m:connect("192.168.18.88",1883) +m:on("message",function(m,t,pl) print(t..":") if pl~=nil then print(pl) end end ) +m:subscribe("/topic1",0,function(m) print("sub done") end) +m:publish("/topic1","hello3",2,0) m:publish("/topic1","hello2",2,0) +m:publish("/topic1","hello3",0,0) m:publish("/topic1","hello2",2,0) + +m:subscribe("/topic2",2,function(m) print("sub done") end) +m:publish("/topic2","hello3",0,0) m:publish("/topic2","hello2",2,0) + +m=mqtt.Client() +m:on("connect",function(m) + print("connection "..node.heap()) + m:subscribe("/topic1",0,function(m) print("sub done") end) + m:publish("/topic1","hello3",0,0) m:publish("/topic1","hello2",2,0) + end ) +m:on("offline", function(conn) + print("disconnect to broker...") + print(node.heap()) +end) +m:connect("192.168.18.88",1883,0,1) + +-- serout( pin, firstLevel, delay_table, [repeatNum] ) +gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) +gpio.serout(1,1,{30,30,60,60,30,30}) -- serial one byte, b10110010 +gpio.serout(1,1,{30,70},8) -- serial 30% pwm 10k, lasts 8 cycles +gpio.serout(1,1,{3,7},8) -- serial 30% pwm 100k, lasts 8 cycles +gpio.serout(1,1,{0,0},8) -- serial 50% pwm as fast as possible, lasts 8 cycles + +gpio.mode(1,gpio.OUTPUT,gpio.PULLUP) +gpio.serout(1,0,{20,10,10,20,10,10,10,100}) -- sim uart one byte 0x5A at about 100kbps + +gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles + +-- Lua: mqtt.Client(clientid, keepalive, user, pass) +-- test with cloudmqtt.com +m_dis={} +function dispatch(m,t,pl) + if pl~=nil and m_dis[t] then + m_dis[t](pl) + end +end +function topic1func(pl) + print("get1: "..pl) +end +function topic2func(pl) + print("get2: "..pl) +end +m_dis["/topic1"]=topic1func +m_dis["/topic2"]=topic2func +m=mqtt.Client("nodemcu1",60,"test","test123") +m:on("connect",function(m) + print("connection "..node.heap()) + m:subscribe("/topic1",0,function(m) print("sub done") end) + m:subscribe("/topic2",0,function(m) print("sub done") end) + m:publish("/topic1","hello",0,0) m:publish("/topic2","world",0,0) + end ) +m:on("offline", function(conn) + print("disconnect to broker...") + print(node.heap()) +end) +m:on("message",dispatch ) +m:connect("m11.cloudmqtt.com",11214,0,1) +-- Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client) ) + +tmr.alarm(0,10000,1,function() local pl = "time: "..tmr.time() + m:publish("/topic1",pl,0,0) + end) diff --git a/examples/init.lua b/examples/init.lua new file mode 100644 index 00000000..a2b306ce --- /dev/null +++ b/examples/init.lua @@ -0,0 +1,18 @@ +--init.lua, something like this +countdown = 3 +tmr.alarm(0,1000,1,function() + print(countdown) + countdown = countdown-1 + if countdown<1 then + tmr.stop(0) + countdown = nil + local s,err + if file.open("user.lc") then + file.close() + s,err = pcall(function() dofile("user.lc") end) + else + s,err = pcall(function() dofile("user.lua") end) + end + if not s then print(err) end + end +end) diff --git a/examples/user.lua b/examples/user.lua new file mode 100644 index 00000000..7a58cfac --- /dev/null +++ b/examples/user.lua @@ -0,0 +1 @@ +print("hello NodeMCU") diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld index 141cab6d..0dc5bd07 100644 --- a/ld/eagle.app.v6.ld +++ b/ld/eagle.app.v6.ld @@ -5,7 +5,7 @@ MEMORY dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 - irom0_0_seg : org = 0x40210000, len = 0x5A000 + irom0_0_seg : org = 0x40210000, len = 0x60000 } PHDRS diff --git a/lua_examples/make_phone_call.lua b/lua_examples/make_phone_call.lua new file mode 100644 index 00000000..16295fdf --- /dev/null +++ b/lua_examples/make_phone_call.lua @@ -0,0 +1,98 @@ +--[[ +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]]-- + +-- Your access point's SSID and password +local SSID = "xxxxxx" +local SSID_PASSWORD = "xxxxxx" + +-- configure ESP as a station +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + +local TWILIO_ACCOUNT_SID = "xxxxxx" +local TWILIO_TOKEN = "xxxxxx" + +local HOST = "iot-https-relay.appspot.com" -- visit http://iot-https-relay.appspot.com/ to learn more about this service + -- Please be sure to understand the security issues of using this relay app and use at your own risk. +local URI = "/twilio/Calls.json" + +function build_post_request(host, uri, data_table) + + local data = "" + + for param,value in pairs(data_table) do + data = data .. param.."="..value.."&" + end + + request = "POST "..uri.." HTTP/1.1\r\n".. + "Host: "..host.."\r\n".. + "Connection: close\r\n".. + "Content-Type: application/x-www-form-urlencoded\r\n".. + "Content-Length: "..string.len(data).."\r\n".. + "\r\n".. + data + + print(request) + + return request +end + +local function display(sck,response) + print(response) +end + +-- When using send_sms: the "from" number HAS to be your twilio number. +-- If you have a free twilio account the "to" number HAS to be your twilio verified number. +local function make_call(from,to,body) + + local data = { + sid = TWILIO_ACCOUNT_SID, + token = TWILIO_TOKEN, + Body = string.gsub(body," ","+"), + From = from, + To = to + } + + socket = net.createConnection(net.TCP,0) + socket:on("receive",display) + socket:connect(80,HOST) + + socket:on("connection",function(sck) + + local post_request = build_post_request(HOST,URI,data) + sck:send(post_request) + end) +end + +function check_wifi() + local ip = wifi.sta.getip() + + if(ip==nil) then + print("Connecting...") + else + tmr.stop(0) + print("Connected to AP!") + print(ip) + -- make a call with a voice message "your house is on fire" + make_call("15558976687","1334856679","Your house is on fire!") + end + +end + +tmr.alarm(0,2000,1,check_wifi) diff --git a/lua_examples/mqtt/mqtt2cloud.lua b/lua_examples/mqtt/mqtt2cloud.lua new file mode 100644 index 00000000..b6b2893a --- /dev/null +++ b/lua_examples/mqtt/mqtt2cloud.lua @@ -0,0 +1,33 @@ +-- test with cloudmqtt.com +m_dis={} +function dispatch(m,t,pl) + if pl~=nil and m_dis[t] then + m_dis[t](m,pl) + end +end +function topic1func(m,pl) + print("get1: "..pl) +end +function topic2func(m,pl) + print("get2: "..pl) +end +m_dis["/topic1"]=topic1func +m_dis["/topic2"]=topic2func +-- Lua: mqtt.Client(clientid, keepalive, user, pass) +m=mqtt.Client("nodemcu1",60,"test","test123") +m:on("connect",function(m) + print("connection "..node.heap()) + m:subscribe("/topic1",0,function(m) print("sub done") end) + m:subscribe("/topic2",0,function(m) print("sub done") end) + m:publish("/topic1","hello",0,0) m:publish("/topic2","world",0,0) + end ) +m:on("offline", function(conn) + print("disconnect to broker...") + print(node.heap()) +end) +m:on("message",dispatch ) +-- Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client) ) +m:connect("m11.cloudmqtt.com",11214,0,1) +tmr.alarm(0,10000,1,function() local pl = "time: "..tmr.time() + m:publish("/topic1",pl,0,0) + end) diff --git a/lua_examples/mqtt/mqtt_file.lua b/lua_examples/mqtt/mqtt_file.lua new file mode 100644 index 00000000..b491177c --- /dev/null +++ b/lua_examples/mqtt/mqtt_file.lua @@ -0,0 +1,56 @@ +-- test transfer files over mqtt. +m_dis={} +function dispatch(m,t,pl) + if pl~=nil and m_dis[t] then + m_dis[t](m,pl) + end +end + +function pubfile(m,filename) + file.close() + file.open(filename) + repeat + local pl=file.read(1024) + if pl then m:publish("/topic2",pl,0,0) end + until not pl + file.close() +end +-- payload(json): {"cmd":xxx,"content":xxx} +function topic1func(m,pl) + print("get1: "..pl) + local pack = cjson.decode(pl) + if pack.content then + if pack.cmd == "open" then file.open(pack.content,"w+") + elseif pack.cmd == "write" then file.write(pack.content) + elseif pack.cmd == "close" then file.close() + elseif pack.cmd == "remove" then file.remove(pack.content) + elseif pack.cmd == "run" then dofile(pack.content) + elseif pack.cmd == "read" then pubfile(m, pack.content) + end + end +end + +m_dis["/topic1"]=topic1func +-- Lua: mqtt.Client(clientid, keepalive, user, pass) +m=mqtt.Client() +m:on("connect",function(m) + print("connection "..node.heap()) + m:subscribe("/topic1",0,function(m) print("sub done") end) + end ) +m:on("offline", function(conn) + print("disconnect to broker...") + print(node.heap()) +end) +m:on("message",dispatch ) +-- Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client) ) +m:connect(192.168.18.88,1883,0,1) + +-- usage: +-- another client(pc) subscribe to /topic2, will receive the test.lua content. +-- and publish below message to /topic1 +-- {"cmd":"open","content":"test.lua"} +-- {"cmd":"write","content":"print([[hello world]])\n"} +-- {"cmd":"write","content":"print(\"hello2 world2\")\n"} +-- {"cmd":"write","content":"test.lua"} +-- {"cmd":"run","content":"test.lua"} +-- {"cmd":"read","content":"test.lua"} diff --git a/lua_examples/send_text_message.lua b/lua_examples/send_text_message.lua new file mode 100644 index 00000000..abd1908f --- /dev/null +++ b/lua_examples/send_text_message.lua @@ -0,0 +1,98 @@ +--[[ +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +]]-- + +-- Your access point's SSID and password +local SSID = "xxxxxx" +local SSID_PASSWORD = "xxxxxx" + +-- configure ESP as a station +wifi.setmode(wifi.STATION) +wifi.sta.config(SSID,SSID_PASSWORD) +wifi.sta.autoconnect(1) + +local TWILIO_ACCOUNT_SID = "xxxxxx" +local TWILIO_TOKEN = "xxxxxx" + +local HOST = "iot-https-relay.appspot.com" -- visit http://iot-https-relay.appspot.com/ to learn more about this service + -- Please be sure to understand the security issues of using this relay app and use at your own risk. +local URI = "/twilio/Messages.json" + +function build_post_request(host, uri, data_table) + + local data = "" + + for param,value in pairs(data_table) do + data = data .. param.."="..value.."&" + end + + request = "POST "..uri.." HTTP/1.1\r\n".. + "Host: "..host.."\r\n".. + "Connection: close\r\n".. + "Content-Type: application/x-www-form-urlencoded\r\n".. + "Content-Length: "..string.len(data).."\r\n".. + "\r\n".. + data + + print(request) + + return request +end + +local function display(sck,response) + print(response) +end + +-- When using send_sms: the "from" number HAS to be your twilio number. +-- If you have a free twilio account the "to" number HAS to be your twilio verified number. +local function send_sms(from,to,body) + + local data = { + sid = TWILIO_ACCOUNT_SID, + token = TWILIO_TOKEN, + Body = string.gsub(body," ","+"), + From = from, + To = to + } + + socket = net.createConnection(net.TCP,0) + socket:on("receive",display) + socket:connect(80,HOST) + + socket:on("connection",function(sck) + + local post_request = build_post_request(HOST,URI,data) + sck:send(post_request) + end) +end + +function check_wifi() + local ip = wifi.sta.getip() + + if(ip==nil) then + print("Connecting...") + else + tmr.stop(0) + print("Connected to AP!") + print(ip) + -- send a text message with the text "Hello from your esp8266" + send_sms("15558889944","15559998845","Hello from your ESP8266") + end + +end + +tmr.alarm(0,7000,1,check_wifi) diff --git a/lua_modules/dht22/README.md b/lua_modules/dht22/README.md deleted file mode 100644 index b68a3278..00000000 --- a/lua_modules/dht22/README.md +++ /dev/null @@ -1,58 +0,0 @@ -# DHT22 module - -This module is compatible with DHT22 and DHT21. -Supports nodemcu with or without floating point. -No need to use a resistor to connect the pin data of DHT22 to ESP8266. - -## Example -```lua -PIN = 4 -- data pin, GPIO2 - -dht22 = require("dht22") -dht22.read(PIN) -t = dht22.getTemperature() -h = dht22.getHumidity() - -if h == nil then - print("Error reading from DHT22") -else - -- temperature in degrees Celsius and Farenheit - -- floating point and integer version: - print("Temperature: "..((t-(t % 10)) / 10).."."..(t % 10).." deg C") - -- only integer version: - print("Temperature: "..(9 * t / 50 + 32).."."..(9 * t / 5 % 10).." deg F") - -- only float point version: - print("Temperature: "..(9 * t / 50 + 32).." deg F") - - -- humidity - -- floating point and integer version - print("Humidity: "..((h - (h % 10)) / 10).."."..(h % 10).."%") -end - --- release module -dht22 = nil -package.loaded["dht22"]=nil -``` -## Functions -### read -read(pin) -Read humidity and temperature from DHT22. - -**Parameters:** - -* pin - ESP8266 pin connect to data pin in DHT22 - -### getHumidity -getHumidity() -Returns the humidity of the last reading. - -**Returns:** -* last humidity reading in per thousand - -### getTemperature -getTemperature() -Returns the temperature of the last reading. - -**Returns:** -* last temperature reading in 0.1ºC - diff --git a/lua_modules/dht22/dht22.lua b/lua_modules/dht22/dht22.lua deleted file mode 100644 index a80a1bd6..00000000 --- a/lua_modules/dht22/dht22.lua +++ /dev/null @@ -1,102 +0,0 @@ --- *************************************************************************** --- DHT22 module for ESP8266 with nodeMCU --- --- Written by Javier Yanez --- but based on a script of Pigs Fly from ESP8266.com forum --- --- MIT license, http://opensource.org/licenses/MIT --- *************************************************************************** - -local moduleName = ... -local M = {} -_G[moduleName] = M - -local humidity -local temperature - -function M.read(pin) - local checksum - local checksumTest - humidity = 0 - temperature = 0 - checksum = 0 - - -- Use Markus Gritsch trick to speed up read/write on GPIO - local gpio_read = gpio.read - - local bitStream = {} - for j = 1, 40, 1 do - bitStream[j] = 0 - end - local bitlength = 0 - - -- Step 1: send out start signal to DHT22 - gpio.mode(pin, gpio.OUTPUT) - gpio.write(pin, gpio.HIGH) - tmr.delay(100) - gpio.write(pin, gpio.LOW) - tmr.delay(20000) - gpio.write(pin, gpio.HIGH) - gpio.mode(pin, gpio.INPUT) - - -- Step 2: DHT22 send response signal - -- bus will always let up eventually, don't bother with timeout - while (gpio_read(pin) == 0 ) do end - local c=0 - while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end - -- bus will always let up eventually, don't bother with timeout - while (gpio_read(pin) == 0 ) do end - c=0 - while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end - - -- Step 3: DHT22 send data - for j = 1, 40, 1 do - while (gpio_read(pin) == 1 and bitlength < 10 ) do - bitlength = bitlength + 1 - end - bitStream[j] = bitlength - bitlength = 0 - -- bus will always let up eventually, don't bother with timeout - while (gpio_read(pin) == 0) do end - end - - --DHT data acquired, process. - for i = 1, 16, 1 do - if (bitStream[i] > 3) then - humidity = humidity + 2 ^ (16 - i) - end - end - for i = 1, 16, 1 do - if (bitStream[i + 16] > 3) then - temperature = temperature + 2 ^ (16 - i) - end - end - for i = 1, 8, 1 do - if (bitStream[i + 32] > 3) then - checksum = checksum + 2 ^ (8 - i) - end - end - - checksumTest = (bit.band(humidity, 0xFF) + bit.rshift(humidity, 8) + bit.band(temperature, 0xFF) + bit.rshift(temperature, 8)) - checksumTest = bit.band(checksumTest, 0xFF) - - if temperature > 0x8000 then - -- convert to negative format - temperature = -(temperature - 0x8000) - end - - -- conditions compatible con float point and integer - if (checksumTest - checksum >= 1) or (checksum - checksumTest >= 1) then - humidity = nil - end -end - -function M.getTemperature() - return temperature -end - -function M.getHumidity() - return humidity -end - -return M diff --git a/lua_modules/dht_lib/README.md b/lua_modules/dht_lib/README.md new file mode 100644 index 00000000..7b36f76c --- /dev/null +++ b/lua_modules/dht_lib/README.md @@ -0,0 +1,89 @@ +# DHTxx module + +This module is compatible with DHT11, DHT21 and DHT22. +And is able to auto-select wheather you are using DHT11 or DHT2x + +No need to use a resistor to connect the pin data of DHT22 to ESP8266. + +##Integer Verison[When using DHT11, Float version is useless...] +### Example +```lua +PIN = 4 -- data pin, GPIO2 + +DHT= require("dht_lib") + +DHT.read(PIN) + +t = DHT.getTemperature() +h = DHT.getHumidity() + +if h == nil then + print("Error reading from DHTxx") +else + -- temperature in degrees Celsius and Farenheit + + print("Temperature: "..((t-(t % 10)) / 10).."."..(t % 10).." deg C") + + print("Temperature: "..(9 * t / 50 + 32).."."..(9 * t / 5 % 10).." deg F") + + -- humidity + + print("Humidity: "..((h - (h % 10)) / 10).."."..(h % 10).."%") +end + +-- release module +DHT = nil +package.loaded["dht_lib"]=nil +``` +##Float Verison +###Example +```lua +PIN = 4 -- data pin, GPIO2 + +DHT= require("dht_lib") + +DHT.read(PIN) + +t = DHT.getTemperature() +h = DHT.getHumidity() + +if h == nil then + print("Error reading from DHT11/22") +else + -- temperature in degrees Celsius and Farenheit + -- floating point and integer version: + + print("Temperature: "..(t/10).." deg C") + print("Temperature: "..(9 * t / 50 + 32).." deg F") + + -- humidity + print("Humidity: "..(h/10).."%") +end + +-- release module +DHT = nil +package.loaded["dht_lib"]=nil +``` +## Functions + +###read +read(pin) +Read humidity and temperature from DHTxx(11,21,22...). +**Parameters:** + +* pin - ESP8266 pin connect to data pin + +### getHumidity +getHumidity() +Returns the humidity of the last reading. + +**Returns:** +* last humidity reading in per thousand + +### getTemperature +getTemperature() +Returns the temperature of the last reading. + +**Returns:** +* last temperature reading in(dht22) 0.1ºC (dht11)1ºC +* diff --git a/lua_modules/dht_lib/dht_lib.lua b/lua_modules/dht_lib/dht_lib.lua new file mode 100644 index 00000000..97823455 --- /dev/null +++ b/lua_modules/dht_lib/dht_lib.lua @@ -0,0 +1,178 @@ +-- *************************************************************************** +-- DHTxx(11,21,22) module for ESP8266 with nodeMCU +-- +-- Written by Javier Yanez mod by Martin +-- but based on a script of Pigs Fly from ESP8266.com forum +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** + +--Support list: + +--DHT11 Tested +--DHT21 Not Test yet +--DHT22(AM2302) Tested +--AM2320 Not Test yet + +--Output format-> Real temperature times 10(or DHT22 will miss it float part in Int Version) +--==========================Module Part====================== +local moduleName = ... +local M = {} +_G[moduleName] = M +--==========================Local the UMI and TEMP=========== +local humidity +local temperature +--==========================Local the bitStream============== +local bitStream = {} + +---------------------------Read bitStream from DHTXX-------------------------- +local function read(pin) + + local bitlength = 0 + humidity = 0 + temperature = 0 + + -- Use Markus Gritsch trick to speed up read/write on GPIO + local gpio_read = gpio.read + + + for j = 1, 40, 1 do + bitStream[j] = 0 + end + + -- Step 1: send out start signal to DHT22 + gpio.mode(pin, gpio.OUTPUT) + gpio.write(pin, gpio.HIGH) + tmr.delay(100) + gpio.write(pin, gpio.LOW) + tmr.delay(20000) + gpio.write(pin, gpio.HIGH) + gpio.mode(pin, gpio.INPUT) + + -- Step 2: Receive bitStream from DHT11/22 + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0 ) do end + local c=0 + while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0 ) do end + c=0 + while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end + + -- Step 3: DHT22 send data + for j = 1, 40, 1 do + while (gpio_read(pin) == 1 and bitlength < 10 ) do + bitlength = bitlength + 1 + end + bitStream[j] = bitlength + bitlength = 0 + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0) do end + end +end + +---------------------------Check out the data-------------------------- +----Auto Select the DHT11/DHT22 By check the byte[1] && byte[3] ------- +---------------Which is empty when using DHT11------------------------- +function M.read(pin) + read(pin) + + local byte_0 = 0 + local byte_1 = 0 + local byte_2 = 0 + local byte_3 = 0 + local byte_4 = 0 + + for i = 1, 8, 1 do -- Byte[0] + if (bitStream[i] > 3) then + byte_0 = byte_0 + 2 ^ (8 - i) + end + end + + for i = 1, 8, 1 do -- Byte[1] + if (bitStream[i+8] > 3) then + byte_1 = byte_1 + 2 ^ (8 - i) + end + end + + for i = 1, 8, 1 do -- Byte[2] + if (bitStream[i+16] > 3) then + byte_2 = byte_2 + 2 ^ (8 - i) + end + end + + for i = 1, 8, 1 do -- Byte[3] + if (bitStream[i+24] > 3) then + byte_2 = byte_2 + 2 ^ (8 - i) + end + end + + for i = 1, 8, 1 do -- Byte[4] + if (bitStream[i+32] > 3) then + byte_4 = byte_4 + 2 ^ (8 - i) + end + end + + + if byte_1==0 and byte_3 == 0 then + ---------------------------Convert the bitStream into Number through DHT11's Way-------------------------- + --As for DHT11 40Bit is consisit of 5Bytes + --First byte->Humidity Data's Int part + --Sencond byte->Humidity Data's Float Part(Which should be empty) + --Third byte->Temp Data;s Intpart + --Forth byte->Temp Data's Float Part(Which should be empty) + --Fifth byte->SUM Byte, Humi+Temp + + if(byte_4 ~= byte_0+byte_2) then + humidity = nil + temperature = nil + else + humidity = byte_0 *10 -- In order to universe with the DHT22 + temperature = byte_2 *10 + end + + else ---------------------------Convert the bitStream into Number through DHT22's Way-------------------------- + --As for DHT22 40Bit is consisit of 5Bytes + --First byte->Humidity Data's High Bit + --Sencond byte->Humidity Data's Low Bit(And if over 0x8000, use complement) + --Third byte->Temp Data's High Bit + --Forth byte->Temp Data's Low Bit + --Fifth byte->SUM Byte + + humidity = byte_0 * 256 + byte_1 + temperature = byte_2 * 256 + byte_3 + checksum = byte_4 + + checksumTest = (bit.band(humidity, 0xFF) + bit.rshift(humidity, 8) + bit.band(temperature, 0xFF) + bit.rshift(temperature, 8)) + checksumTest = bit.band(checksumTest, 0xFF) + + if temperature > 0x8000 then + -- convert to negative format + temperature = -(temperature - 0x8000) + end + + -- conditions compatible con float point and integer + if (checksumTest - checksum >= 1) or (checksum - checksumTest >= 1) then + humidity = nil + end + + end + + byte_0 = nil + byte_1 = nil + byte_2 = nil + byte_3 = nil + byte_4 = nil + +end +--------------API for geting the data out------------------ + +function M.getTemperature() + return temperature +end + +function M.getHumidity() + return humidity +end +-------------Return Index------------------------------------ +return M diff --git a/lua_modules/yeelink/yeelink_lib.lua b/lua_modules/yeelink/yeelink_lib.lua new file mode 100644 index 00000000..39f2b129 --- /dev/null +++ b/lua_modules/yeelink/yeelink_lib.lua @@ -0,0 +1,105 @@ + +-- *************************************************************************** +-- Yeelink Updata Libiary +-- +-- Written by Martin +-- but based on a script of zhouxu_o from bbs.nodemcu.com +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** + +if wifi.sta.getip() == nil then + print("Please Connect WIFI First") + return nil +end +--==========================Module Part====================== + +local moduleName = ... +local M = {} +_G[moduleName] = M +--=========================Local Args======================= +local dns = "0.0.0.0" + +local device = "" +local sensor = "" +local apikey = "" + +--================================ +local debug = true --<<<<<<<<<<<<< Don't forget to "false" it before using +--================================ +local sk=net.createConnection(net.TCP, 0) + +local datapoint = 0 +--====DNS the yeelink ip advance(in order to save RAM)===== +sk:dns("api.yeelink.net",function(conn,ip) + +dns=ip + +print("DNS YEELINK OK... IP: "..dns) + +end) + +--========Set the init function=========== +--device->number +--sensor->number +-- apikey must be -> string <- +-- e.g. xxx.init(00000,00000,"123j12b3jkb12k4b23bv54i2b5b3o4") +--======================================== +function M.init(_device, _sensor, _apikey) + device = tostring(_device) + sensor = tostring(_sensor) + apikey = _apikey + if dns == "0.0.0.0" then + return false + else + return dns + end + +end + + +--=====Update to Yeelink Sever(At least 10s per sencods))===== +-- datapoint->number +-- +--e.g. xxx.update(233.333) +--============================================================ +function M.update(_datapoint) + + datapoint = tostring(_datapoint) + + sk:on("connection", function(conn) + + print("connect OK...") + + + local a=[[{"value":]] + local b=[[}]] + + local st=a..datapoint..b + + sk:send("POST /v1.0/device/"..device.."/sensor/"..sensor.."/datapoints HTTP/1.1\r\n" +.."Host: www.yeelink.net\r\n" +.."Content-Length: "..string.len(st).."\r\n"--the length of json is important +.."Content-Type: application/x-www-form-urlencoded\r\n" +.."U-ApiKey:"..apikey.."\r\n" +.."Cache-Control: no-cache\r\n\r\n" +..st.."\r\n" ) + + end) + + sk:on("receive", function(sck, content) + + if debug then + print("\r\n"..content.."\r\n") + else + print("Date Receive") + end + + end) + + sk:connect(80,dns) + + +end +--================end========================== +return M diff --git a/tools/esptool.py b/tools/esptool.py index 130e80f0..88a4b4ed 100755 --- a/tools/esptool.py +++ b/tools/esptool.py @@ -41,7 +41,7 @@ class ESPROM: # Maximum block sized for RAM and Flash writes, respectively. ESP_RAM_BLOCK = 0x1800 - ESP_FLASH_BLOCK = 0x100 + ESP_FLASH_BLOCK = 0x400 # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. ESP_ROM_BAUD = 115200 @@ -56,6 +56,12 @@ class ESPROM: ESP_OTP_MAC0 = 0x3ff00050 ESP_OTP_MAC1 = 0x3ff00054 + # Sflash stub: an assembly routine to read from spi flash and send to host + SFLASH_STUB = "\x80\x3c\x00\x40\x1c\x4b\x00\x40\x21\x11\x00\x40\x00\x80" \ + "\xfe\x3f\xc1\xfb\xff\xd1\xf8\xff\x2d\x0d\x31\xfd\xff\x41\xf7\xff\x4a" \ + "\xdd\x51\xf9\xff\xc0\x05\x00\x21\xf9\xff\x31\xf3\xff\x41\xf5\xff\xc0" \ + "\x04\x00\x0b\xcc\x56\xec\xfd\x06\xff\xff\x00\x00" + def __init__(self, port = 0, baud = ESP_ROM_BAUD): self._port = serial.Serial(port, baud) @@ -78,15 +84,7 @@ class ESPROM: """ Write bytes to the serial port while performing SLIP escaping """ def write(self, packet): - buf = '\xc0' - for b in packet: - if b == '\xc0': - buf += '\xdb\xdc' - elif b == '\xdb': - buf += '\xdb\xdd' - else: - buf += b - buf += '\xc0' + buf = '\xc0'+(packet.replace('\xdb','\xdb\xdd').replace('\xc0','\xdb\xdc'))+'\xc0' self._port.write(buf) """ Calculate checksum of a blob, as it is defined by the ROM """ @@ -132,11 +130,25 @@ class ESPROM: # RTS = CH_PD (i.e reset) # DTR = GPIO0 + # self._port.setRTS(True) + # self._port.setDTR(True) + # self._port.setRTS(False) + # time.sleep(0.1) + # self._port.setDTR(False) + + # NodeMCU devkit self._port.setRTS(True) self._port.setDTR(True) - self._port.setRTS(False) time.sleep(0.1) + self._port.setRTS(False) self._port.setDTR(False) + time.sleep(0.1) + self._port.setRTS(True) + time.sleep(0.1) + self._port.setDTR(True) + self._port.setRTS(False) + time.sleep(0.3) + self._port.setDTR(True) self._port.timeout = 0.5 for i in xrange(10): @@ -209,16 +221,78 @@ class ESPROM: self.flash_begin(0, 0) self.flash_finish(reboot) + """ Read MAC from OTP ROM """ + def read_mac(self): + mac0 = esp.read_reg(esp.ESP_OTP_MAC0) + mac1 = esp.read_reg(esp.ESP_OTP_MAC1) + if ((mac1 >> 16) & 0xff) == 0: + oui = (0x18, 0xfe, 0x34) + elif ((mac1 >> 16) & 0xff) == 1: + oui = (0xac, 0xd0, 0x74) + else: + raise Exception("Unknown OUI") + return oui + ((mac1 >> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + + """ Read SPI flash manufacturer and device id """ + def flash_id(self): + self.flash_begin(0, 0) + self.write_reg(0x60000240, 0x0, 0xffffffff) + self.write_reg(0x60000200, 0x10000000, 0xffffffff) + flash_id = esp.read_reg(0x60000240) + self.flash_finish(False) + return flash_id + + """ Read SPI flash """ + def flash_read(self, offset, size, count = 1): + # Create a custom stub + stub = struct.pack(' 16: @@ -246,7 +320,8 @@ class ESPFirmwareImage: def save(self, filename): f = file(filename, 'wb') - f.write(struct.pack('> 8) & 0xff, mac1 & 0xff, (mac0 >> 24) & 0xff) + mac = esp.read_mac() + print 'MAC: %s' % ':'.join(map(lambda x: '%02x'%x, mac)) + + elif args.operation == 'flash_id': + flash_id = esp.flash_id() + print 'Manufacturer: %02x' % (flash_id & 0xff) + print 'Device: %02x%02x' % ((flash_id >> 8) & 0xff, (flash_id >> 16) & 0xff) + + elif args.operation == 'read_flash': + print 'Please wait...' + file(args.filename, 'wb').write(esp.flash_read(args.address, 1024, int(math.ceil(args.size / 1024.)))[:args.size]) + + elif args.operation == 'erase_flash': + esp.flash_erase()