diff --git a/Makefile b/Makefile index 85bc27a9..19feb38d 100644 --- a/Makefile +++ b/Makefile @@ -103,9 +103,15 @@ OIMAGES := $(GEN_IMAGES:%=$(IMAGEODIR)/%) BINODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/bin OBINS := $(GEN_BINS:%=$(BINODIR)/%) +# +# Note: +# https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html +# If you add global optimize options like "-O2" here +# they will override "-Os" defined above. +# "-Os" should be used to reduce code size +# CCFLAGS += \ -g \ - -O2 \ -Wpointer-arith \ -Wundef \ -Werror \ diff --git a/README.md b/README.md index 90ec76e3..295ea337 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 ``` @@ -402,7 +410,8 @@ u8glib comes with a wide range of fonts for small displays. Since they need to b They'll be available as `u8g.` in Lua. #####Bitmaps -Bitmaps and XBMs are supplied as strings to `drawBitmap()` and `drawXBM()`. This off-loads all data handling from the u8g module to generic methods for binary files. See `lua_examples/u8glib/u8g_bitmaps.lua`. Binary files can be uploaded with [nodemcu-uploader.py](https://github.com/kmpm/nodemcu-uploader). +Bitmaps and XBMs are supplied as strings to `drawBitmap()` and `drawXBM()`. This off-loads all data handling from the u8g module to generic methods for binary files. See `lua_examples/u8glib/u8g_bitmaps.lua`. +In contrast to the source code based inclusion of XBMs into u8glib, it's required to provide precompiled binary files. This can be performed online with [Online-Utility's Image Converter](http://www.online-utility.org/image_converter.jsp): Convert from XBM to MONO format and upload the binary result with [nodemcu-uploader.py](https://github.com/kmpm/nodemcu-uploader). #####Unimplemented functions - [ ] Cursor handling @@ -417,6 +426,7 @@ Bitmaps and XBMs are supplied as strings to `drawBitmap()` and `drawXBM()`. This - [ ] setPrintPos() - [ ] setHardwareBackup() - [ ] setRGB() + - [ ] setDefaultMidColor() ####Control a WS2812 based light strip diff --git a/app/Makefile b/app/Makefile index a1c0d66a..f52b890b 100644 --- a/app/Makefile +++ b/app/Makefile @@ -37,7 +37,8 @@ SUBDIRS= \ wofs \ modules \ spiffs \ - cjson + cjson \ + crypto \ endif # } PDIR @@ -86,6 +87,7 @@ COMPONENTS_eagle.app.v6 = \ wofs/wofs.a \ spiffs/spiffs.a \ cjson/libcjson.a \ + crypto/libcrypto.a \ modules/libmodules.a LINKFLAGS_eagle.app.v6 = \ 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/crypto/Makefile b/app/crypto/Makefile new file mode 100644 index 00000000..a3aef405 --- /dev/null +++ b/app/crypto/Makefile @@ -0,0 +1,44 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR +GEN_LIBS = libcrypto.a +endif + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +INCLUDES += -I ../libc +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/crypto/digests.c b/app/crypto/digests.c new file mode 100644 index 00000000..ccac6e67 --- /dev/null +++ b/app/crypto/digests.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015, DiUS Computing Pty Ltd (jmattsson@dius.com.au) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include "digests.h" +#include "user_config.h" +#include "rom.h" +#include "lwip/mem.h" +#include +#include + +#ifdef MD2_ENABLE +#include "ssl/ssl_crypto.h" +#endif + +#ifdef SHA2_ENABLE +#include "sha2.h" +#endif + +typedef char ensure_int_and_size_t_same[(sizeof(int)==sizeof(size_t)) ? 0 : -1]; + +/* None of the functions match the prototype fully due to the void *, and in + some cases also the int vs size_t len, so wrap declarations in a macro. */ +#define MECH(pfx, u, ds, bs) \ + { #pfx, \ + (create_ctx_fn)pfx ## u ## Init, \ + (update_ctx_fn)pfx ## u ## Update, \ + (finalize_ctx_fn)pfx ## u ## Final, \ + sizeof(pfx ## _CTX), \ + ds, \ + bs } + +static const digest_mech_info_t hash_mechs[] ICACHE_RODATA_ATTR = +{ +#ifdef MD2_ENABLE + MECH(MD2, _ , MD2_SIZE, 16), +#endif + MECH(MD5, , MD5_DIGEST_LENGTH, 64) + ,MECH(SHA1, , SHA1_DIGEST_LENGTH, 64) +#ifdef SHA2_ENABLE + ,MECH(SHA256, _ , SHA256_DIGEST_LENGTH, SHA256_BLOCK_LENGTH) + ,MECH(SHA384, _ , SHA384_DIGEST_LENGTH, SHA384_BLOCK_LENGTH) + ,MECH(SHA512, _ , SHA512_DIGEST_LENGTH, SHA512_BLOCK_LENGTH) +#endif +}; + +#undef MECH + +const digest_mech_info_t *ICACHE_FLASH_ATTR crypto_digest_mech (const char *mech) +{ + if (!mech) + return 0; + + size_t i; + for (i = 0; i < (sizeof (hash_mechs) / sizeof (digest_mech_info_t)); ++i) + { + const digest_mech_info_t *mi = hash_mechs + i; + if (strcasecmp (mech, mi->name) == 0) + return mi; + } + return 0; +} + +const char crypto_hexbytes[] = "0123456789abcdef"; + +// note: supports in-place encoding +void ICACHE_FLASH_ATTR crypto_encode_asciihex (const char *bin, size_t binlen, char *outbuf) +{ + size_t aidx = binlen * 2 -1; + int i; + for (i = binlen -1; i >= 0; --i) + { + outbuf[aidx--] = crypto_hexbytes[bin[i] & 0xf]; + outbuf[aidx--] = crypto_hexbytes[bin[i] >> 4]; + } +} + + +int ICACHE_FLASH_ATTR crypto_hash (const digest_mech_info_t *mi, + const char *data, size_t data_len, + uint8_t *digest) +{ + if (!mi) + return EINVAL; + + void *ctx = os_malloc (mi->ctx_size); + if (!ctx) + return ENOMEM; + + mi->create (ctx); + mi->update (ctx, data, data_len); + mi->finalize (digest, ctx); + + os_free (ctx); + return 0; +} + + +int ICACHE_FLASH_ATTR crypto_hmac (const digest_mech_info_t *mi, + const char *data, size_t data_len, + const char *key, size_t key_len, + uint8_t *digest) +{ + if (!mi) + return EINVAL; + + void *ctx = os_malloc (mi->ctx_size); + if (!ctx) + return ENOMEM; + + // If key too long, it needs to be hashed before use + if (key_len > mi->block_size) + { + mi->create (ctx); + mi->update (ctx, key, key_len); + mi->finalize (digest, ctx); + key = digest; + key_len = mi->block_size; + } + + const size_t bs = mi->block_size; + uint8_t k_ipad[bs]; + uint8_t k_opad[bs]; + + os_memset (k_ipad, 0x36, bs); + os_memset (k_opad, 0x5c, bs); + size_t i; + for (i = 0; i < key_len; ++i) + { + k_ipad[i] ^= key[i]; + k_opad[i] ^= key[i]; + } + + mi->create (ctx); + mi->update (ctx, k_ipad, bs); + mi->update (ctx, data, data_len); + mi->finalize (digest, ctx); + + mi->create (ctx); + mi->update (ctx, k_opad, bs); + mi->update (ctx, digest, mi->digest_size); + mi->finalize (digest, ctx); + + os_free (ctx); + return 0; +} diff --git a/app/crypto/digests.h b/app/crypto/digests.h new file mode 100644 index 00000000..05700701 --- /dev/null +++ b/app/crypto/digests.h @@ -0,0 +1,85 @@ +#ifndef _CRYPTO_DIGESTS_H_ +#define _CRYPTO_DIGESTS_H_ + +#include + +typedef void (*create_ctx_fn)(void *ctx); +typedef void (*update_ctx_fn)(void *ctx, const uint8_t *msg, int len); +typedef void (*finalize_ctx_fn)(uint8_t *digest, void *ctx); + +/** + * Description of a message digest mechanism. + * + * Typical usage (if not using the crypto_xxxx() functions below): + * digest_mech_info_t *mi = crypto_digest_mech (chosen_algorithm); + * void *ctx = os_malloc (mi->ctx_size); + * mi->create (ctx); + * mi->update (ctx, data, len); + * ... + * uint8_t *digest = os_malloc (mi->digest_size); + * mi->finalize (digest, ctx); + * ... + * os_free (ctx); + * os_free (digest); + */ +typedef struct +{ + /* Note: All entries are 32bit to enable placement using ICACHE_RODATA_ATTR.*/ + const char * name; + create_ctx_fn create; + update_ctx_fn update; + finalize_ctx_fn finalize; + uint32_t ctx_size; + uint32_t digest_size; + uint32_t block_size; +} digest_mech_info_t; + + +/** + * Looks up the mech data for a specified digest algorithm. + * @param mech The name of the algorithm, e.g. "MD5", "SHA256" + * @returns The mech data, or null if the mech is unknown. + */ +const digest_mech_info_t *crypto_digest_mech (const char *mech); + +/** + * Wrapper function for performing a one-in-all hashing operation. + * @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi + * is harmless, but will of course result in an error return. + * @param data The data to create a digest for. + * @param data_len Number of bytes at @c data to digest. + * @param digest Output buffer, must be at least @c mi->digest_size in size. + * @return 0 on success, non-zero on error. + */ +int crypto_hash (const digest_mech_info_t *mi, const char *data, size_t data_len, uint8_t *digest); + + +/** + * Generate a HMAC signature. + * @param mi A mech from @c crypto_digest_mech(). A null pointer @c mi + * is harmless, but will of course result in an error return. + * @param data The data to generate a signature for. + * @param data_len Number of bytes at @c data to process. + * @param key The key to use. + * @param key_len Number of bytes the @c key comprises. + * @param digest Output buffer, must be at least @c mi->digest_size in size. + * @return 0 on success, non-zero on error. + */ +int crypto_hmac (const digest_mech_info_t *mi, const char *data, size_t data_len, const char *key, size_t key_len, uint8_t *digest); + +/** + * Perform ASCII Hex encoding. Does not null-terminate the buffer. + * + * @param bin The buffer to ascii-hex encode. + * @param bin_len Number of bytes in @c bin to encode. + * @param outbuf Output buffer, must be at least @c bin_len*2 bytes in size. + * Note that in-place encoding is supported, and as such + * bin==outbuf is safe, provided the buffer is large enough. + */ +void crypto_encode_asciihex (const char *bin, size_t bin_len, char *outbuf); + + +/** Text string "0123456789abcdef" */ +const char crypto_hexbytes[17]; + +#endif diff --git a/app/crypto/sha2.c b/app/crypto/sha2.c new file mode 100644 index 00000000..5fadfcc4 --- /dev/null +++ b/app/crypto/sha2.c @@ -0,0 +1,913 @@ +/* + * FILE: sha2.c + * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ + * + * Copyright (c) 2000-2001, Aaron D. Gifford + * Copyright (c) 2015, DiUS Computing Pty Ltd (jmattsson@dius.com.au) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "user_config.h" + +#ifdef SHA2_ENABLE +#include "sha2.h" +#include /* memcpy()/memset() or bcopy()/bzero() */ +#define assert(x) do {} while (0) + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define REVERSE32(w,x) { \ + sha2_word32 tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} +#define REVERSE64(w,x) { \ + sha2_word64 tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ */ + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (sha2_word64)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +/* + * Macros for copying blocks of memory and for zeroing out ranges + * of memory. Using these macros makes it easy to switch from + * using memset()/memcpy() and using bzero()/bcopy(). + * + * Please define either SHA2_USE_MEMSET_MEMCPY or define + * SHA2_USE_BZERO_BCOPY depending on which function set you + * choose to use: + */ +#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY) +/* Default to memset()/memcpy() if no option is specified */ +#define SHA2_USE_MEMSET_MEMCPY 1 +#endif +#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY) +/* Abort with an error if BOTH options are defined */ +#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both! +#endif + +#ifdef SHA2_USE_MEMSET_MEMCPY +#define MEMSET_BZERO(p,l) memset((p), 0, (l)) +#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) +#endif +#ifdef SHA2_USE_BZERO_BCOPY +#define MEMSET_BZERO(p,l) bzero((p), (l)) +#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l)) +#endif + + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +void SHA512_Last(SHA512_CTX*); +void SHA256_Transform(SHA256_CTX*, const sha2_word32*); +void SHA512_Transform(SHA512_CTX*, const sha2_word64*); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-256: */ +const static sha2_word32 K256[64] ICACHE_RODATA_ATTR = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-256: */ +const static sha2_word32 sha256_initial_hash_value[8] ICACHE_RODATA_ATTR = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +const static sha2_word64 K512[80] ICACHE_RODATA_ATTR = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-384 */ +const static sha2_word64 sha384_initial_hash_value[8] ICACHE_RODATA_ATTR = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL +}; + +/* Initial hash value H for SHA-512 */ +const static sha2_word64 sha512_initial_hash_value[8] ICACHE_RODATA_ATTR = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; + + +/*** SHA-256: *********************************************************/ +void ICACHE_FLASH_ATTR SHA256_Init(SHA256_CTX* context) { + if (context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE32(*data++, W256[j]); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + + +#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void ICACHE_FLASH_ATTR SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h, s0, s1; + sha2_word32 T1, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void ICACHE_FLASH_ATTR SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { + sha2_word32 a, b, c, d, e, f, g, h, s0, s1; + sha2_word32 T1, T2, *W256; + int j; + + W256 = (sha2_word32*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Copy data while converting to host byte order */ + REVERSE32(*data++,W256[j]); + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; +#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void ICACHE_FLASH_ATTR SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + SHA256_Transform(context, (sha2_word32*)context->buffer); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA256_Transform(context, (sha2_word32*)data); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void ICACHE_FLASH_ATTR SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { + sha2_word32 *d = (sha2_word32*)digest; + unsigned int usedspace; + + /* Sanity check: */ + assert(context != (SHA256_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Convert FROM host byte order */ + REVERSE64(context->bitcount,context->bitcount); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA256_BLOCK_LENGTH) { + MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + + /* And set-up for the last transform: */ + MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; + + /* Final transform: */ + SHA256_Transform(context, (sha2_word32*)context->buffer); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH); +#endif + } + + /* Clean up state data: */ + MEMSET_BZERO(context, sizeof(SHA256_CTX)); + usedspace = 0; +} + + +/*** SHA-512: *********************************************************/ +void ICACHE_FLASH_ATTR SHA512_Init(SHA512_CTX* context) { + if (context == (SHA512_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); + MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE64(*data++, W512[j]); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + W512[j]; \ + (d) += T1, \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \ + j++ + + +#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + +#define ROUND512(a,b,c,d,e,f,g,h) \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void ICACHE_FLASH_ATTR SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { + sha2_word64 a, b, c, d, e, f, g, h, s0, s1; + sha2_word64 T1, *W512 = (sha2_word64*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void ICACHE_FLASH_ATTR SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { + sha2_word64 a, b, c, d, e, f, g, h, s0, s1; + sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Convert TO host byte order */ + REVERSE64(*data++, W512[j]); + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; +#else /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); +#endif /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN_ */ + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void ICACHE_FLASH_ATTR SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0); + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + SHA512_Transform(context, (sha2_word64*)context->buffer); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(&context->buffer[usedspace], data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while (len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + SHA512_Transform(context, (sha2_word64*)data); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if (len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void ICACHE_FLASH_ATTR SHA512_Last(SHA512_CTX* context) { + unsigned int usedspace; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0],context->bitcount[0]); + REVERSE64(context->bitcount[1],context->bitcount[1]); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < SHA512_BLOCK_LENGTH) { + MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + SHA512_Transform(context, (sha2_word64*)context->buffer); + + /* And set-up for the last transform: */ + MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits): */ + *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; + *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; + + /* Final transform: */ + SHA512_Transform(context, (sha2_word64*)context->buffer); +} + +void ICACHE_FLASH_ATTR SHA512_Final(sha2_byte digest[], SHA512_CTX* context) { + sha2_word64 *d = (sha2_word64*)digest; + + /* Sanity check: */ + assert(context != (SHA512_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + SHA512_Last(context); + + /* Save the hash data for output: */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH); +#endif + } + + /* Zero out state data */ + MEMSET_BZERO(context, sizeof(SHA512_CTX)); +} + + +/*** SHA-384: *********************************************************/ +void ICACHE_FLASH_ATTR SHA384_Init(SHA384_CTX* context) { + if (context == (SHA384_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH); + MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +void ICACHE_FLASH_ATTR SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) { + SHA512_Update((SHA512_CTX*)context, data, len); +} + +void ICACHE_FLASH_ATTR SHA384_Final(sha2_byte digest[], SHA384_CTX* context) { + sha2_word64 *d = (sha2_word64*)digest; + + /* Sanity check: */ + assert(context != (SHA384_CTX*)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (sha2_byte*)0) { + SHA512_Last((SHA512_CTX*)context); + + /* Save the hash data for output: */ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 6; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH); +#endif + } + + /* Zero out state data */ + MEMSET_BZERO(context, sizeof(SHA384_CTX)); +} + +#endif // SHA2_ENABLE diff --git a/app/crypto/sha2.h b/app/crypto/sha2.h new file mode 100644 index 00000000..7ec49634 --- /dev/null +++ b/app/crypto/sha2.h @@ -0,0 +1,47 @@ +#ifndef __SHA2_H__ +#define __SHA2_H__ + +#include + +/************************************************************************** + * SHA256/384/512 declarations + **************************************************************************/ + +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 + +typedef struct +{ + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; + + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX *, const uint8_t *msg, size_t len); +void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); + +#define SHA384_BLOCK_LENGTH 128 +#define SHA384_DIGEST_LENGTH 48 + +typedef struct +{ + uint64_t state[8]; + uint64_t bitcount[2]; + uint8_t buffer[SHA384_BLOCK_LENGTH]; +} SHA384_CTX; + +void SHA384_Init(SHA384_CTX*); +void SHA384_Update(SHA384_CTX*, const uint8_t *msg, size_t len); +void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); + +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +typedef SHA384_CTX SHA512_CTX; + +void SHA512_Init(SHA512_CTX*); +void SHA512_Update(SHA512_CTX*, const uint8_t *msg, size_t len); +void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); + +#endif diff --git a/app/driver/pwm.c b/app/driver/pwm.c index cc147d05..c34f1437 100644 --- a/app/driver/pwm.c +++ b/app/driver/pwm.c @@ -365,7 +365,7 @@ pwm_init(uint16 freq, uint16 *duty) for (i = 0; i < PWM_CHANNEL; i++) { // pwm_gpio |= (1 << pwm_out_io_num[i]); pwm_gpio = 0; - pwm.duty[0] = 0; + pwm.duty[i] = 0; } pwm_set_freq(500, 0); diff --git a/app/include/rom.h b/app/include/rom.h new file mode 100644 index 00000000..88d7c81d --- /dev/null +++ b/app/include/rom.h @@ -0,0 +1,39 @@ +// Headers to the various functions in the rom (as we discover them) + +// SHA1 is assumed to match the netbsd sha1.h headers +#define SHA1_DIGEST_LENGTH 20 +#define SHA1_DIGEST_STRING_LENGTH 41 + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} SHA1_CTX; + +extern void SHA1Transform(uint32_t[5], const uint8_t[64]); +extern void SHA1Init(SHA1_CTX *); +extern void SHA1Final(uint8_t[SHA1_DIGEST_LENGTH], SHA1_CTX *); +extern void SHA1Update(SHA1_CTX *, const uint8_t *, unsigned int); + + +// MD5 is assumed to match the NetBSD md5.h header +#define MD5_DIGEST_LENGTH 16 +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} MD5_CTX; + +extern void MD5Init(MD5_CTX *); +extern void MD5Update(MD5_CTX *, const unsigned char *, unsigned int); +extern void MD5Final(unsigned char[MD5_DIGEST_LENGTH], MD5_CTX *); + +// base64_encode/decode derived by Cal +// Appears to match base64.h from netbsd wpa utils. +extern unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); +extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len); +// Unfortunately it that seems to require the ROM memory management to be +// initialized because it uses mem_malloc + +extern void mem_init(void * start_addr); diff --git a/app/include/user_config.h b/app/include/user_config.h index 96ad3322..4be67347 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -41,6 +41,8 @@ #define CLIENT_SSL_ENABLE #define GPIO_INTERRUPT_ENABLE +//#define MD2_ENABLE +#define SHA2_ENABLE // #define BUILD_WOFS 1 #define BUILD_SPIFFS 1 diff --git a/app/include/user_modules.h b/app/include/user_modules.h index d8ddc654..7e2564f7 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -31,6 +31,9 @@ #define LUA_USE_MODULES_U8G #define LUA_USE_MODULES_WS2812 #define LUA_USE_MODULES_CJSON +#define LUA_USE_MODULES_CRYPTO +#define LUA_USE_MODULES_RC + #endif /* LUA_USE_MODULES */ #endif /* __USER_MODULES_H__ */ diff --git a/app/include/user_version.h b/app/include/user_version.h index bc64f36d..4db7edca 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -3,10 +3,10 @@ #define NODE_VERSION_MAJOR 0U #define NODE_VERSION_MINOR 9U -#define NODE_VERSION_REVISION 5U +#define NODE_VERSION_REVISION 6U #define NODE_VERSION_INTERNAL 0U -#define NODE_VERSION "NodeMCU 0.9.5" -#define BUILD_DATE "build 20150318" +#define NODE_VERSION "NodeMCU 0.9.6" +#define BUILD_DATE "build 20150617" #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/lmathlib.c b/app/lua/lmathlib.c index a9f553bf..c7f22502 100644 --- a/app/lua/lmathlib.c +++ b/app/lua/lmathlib.c @@ -336,7 +336,7 @@ const LUA_REG_TYPE math_map[] = { {LSTRKEY("floor"), LFUNCVAL(math_floor)}, // {LSTRKEY("fmod"), LFUNCVAL(math_fmod)}, #if LUA_OPTIMIZE_MEMORY > 0 && defined(LUA_COMPAT_MOD) - {LSTRKEY("mod"), LFUNCVAL(math_fmod)}, + // {LSTRKEY("mod"), LFUNCVAL(math_fmod)}, #endif // {LSTRKEY("frexp"), LFUNCVAL(math_frexp)}, // {LSTRKEY("ldexp"), LFUNCVAL(math_ldexp)}, 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/lwip/app/dummy.c b/app/lwip/app/dummy.c new file mode 100644 index 00000000..a7a74ed9 --- /dev/null +++ b/app/lwip/app/dummy.c @@ -0,0 +1,10 @@ +/****************************************************************************** + * FunctionName : espconn_init + * Description : dummy the espconn_init + * Parameters : none + * Returns : none +*******************************************************************************/ +void espconn_init() +{ + // dummy function, do nothing. +} diff --git a/app/modules/adc.c b/app/modules/adc.c index b64697ba..799d7e72 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,38 @@ 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 + if (wifi_station_get_connect_status()!=0) + { + return luaL_error( L, "Can't read vdd33 while station is connected" ); + } + else + { + vdd33 = readvdd33(); + } + } + 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/crypto.c b/app/modules/crypto.c new file mode 100644 index 00000000..3639e436 --- /dev/null +++ b/app/modules/crypto.c @@ -0,0 +1,183 @@ +// Module for cryptography + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" +#include "c_types.h" +#include "c_stdlib.h" +#include "../crypto/digests.h" + +#include "user_interface.h" + +#include "rom.h" + +/** + * hash = crypto.sha1(input) + * + * Calculates raw SHA1 hash of input string. + * Input is arbitrary string, output is raw 20-byte hash as string. + */ +static int crypto_sha1( lua_State* L ) +{ + SHA1_CTX ctx; + uint8_t digest[20]; + // Read the string from lua (with length) + int len; + const char* msg = luaL_checklstring(L, 1, &len); + // Use the SHA* functions in the rom + SHA1Init(&ctx); + SHA1Update(&ctx, msg, len); + SHA1Final(digest, &ctx); + + // Push the result as a lua string + lua_pushlstring(L, digest, 20); + return 1; +} + + +static const char* bytes64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +/** + * encoded = crypto.toBase64(raw) + * + * Encodes raw binary string as base64 string. + */ +static int crypto_base64_encode( lua_State* L ) +{ + int len; + const char* msg = luaL_checklstring(L, 1, &len); + int blen = (len + 2) / 3 * 4; + char* out = (char*)c_malloc(blen); + int j = 0, i; + for (i = 0; i < len; i += 3) { + int a = msg[i]; + int b = (i + 1 < len) ? msg[i + 1] : 0; + int c = (i + 2 < len) ? msg[i + 2] : 0; + out[j++] = bytes64[a >> 2]; + out[j++] = bytes64[((a & 3) << 4) | (b >> 4)]; + out[j++] = (i + 1 < len) ? bytes64[((b & 15) << 2) | (c >> 6)] : 61; + out[j++] = (i + 2 < len) ? bytes64[(c & 63)] : 61; + } + lua_pushlstring(L, out, j); + c_free(out); + return 1; +} + +/** + * encoded = crypto.toHex(raw) + * + * Encodes raw binary string as hex string. + */ +static int crypto_hex_encode( lua_State* L) +{ + int len; + const char* msg = luaL_checklstring(L, 1, &len); + char* out = (char*)c_malloc(len * 2); + int i, j = 0; + for (i = 0; i < len; i++) { + out[j++] = crypto_hexbytes[msg[i] >> 4]; + out[j++] = crypto_hexbytes[msg[i] & 0xf]; + } + lua_pushlstring(L, out, len*2); + c_free(out); + return 1; +} + +/** + * masked = crypto.mask(message, mask) + * + * Apply a mask (repeated if shorter than message) as XOR to each byte. + */ +static int crypto_mask( lua_State* L ) +{ + int len, mask_len; + const char* msg = luaL_checklstring(L, 1, &len); + const char* mask = luaL_checklstring(L, 2, &mask_len); + int i; + char* copy = (char*)c_malloc(len); + for (i = 0; i < len; i++) { + copy[i] = msg[i] ^ mask[i % 4]; + } + lua_pushlstring(L, copy, len); + c_free(copy); + return 1; +} + + +static inline int bad_mech (lua_State *L) { return luaL_error (L, "unknown hash mech"); } +static inline int bad_mem (lua_State *L) { return luaL_error (L, "insufficient memory"); } + + +/* rawdigest = crypto.hash("MD5", str) + * strdigest = crypto.toHex(rawdigest) + */ +static int crypto_lhash (lua_State *L) +{ + const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1)); + if (!mi) + return bad_mech (L); + size_t len = 0; + const char *data = luaL_checklstring (L, 2, &len); + + uint8_t digest[mi->digest_size]; + if (crypto_hash (mi, data, len, digest) != 0) + return bad_mem (L); + + lua_pushlstring (L, digest, sizeof (digest)); + return 1; +} + + +/* rawsignature = crypto.hmac("SHA1", str, key) + * strsignature = crypto.toHex(rawsignature) + */ +static int crypto_lhmac (lua_State *L) +{ + const digest_mech_info_t *mi = crypto_digest_mech (luaL_checkstring (L, 1)); + if (!mi) + return bad_mech (L); + size_t len = 0; + const char *data = luaL_checklstring (L, 2, &len); + size_t klen = 0; + const char *key = luaL_checklstring (L, 3, &klen); + + uint8_t digest[mi->digest_size]; + if (crypto_hmac (mi, data, len, key, klen, digest) != 0) + return bad_mem (L); + + lua_pushlstring (L, digest, sizeof (digest)); + return 1; +} + + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE crypto_map[] = +{ + { LSTRKEY( "sha1" ), LFUNCVAL( crypto_sha1 ) }, + { LSTRKEY( "toBase64" ), LFUNCVAL( crypto_base64_encode ) }, + { LSTRKEY( "toHex" ), LFUNCVAL( crypto_hex_encode ) }, + { LSTRKEY( "mask" ), LFUNCVAL( crypto_mask ) }, + { LSTRKEY( "hash" ), LFUNCVAL( crypto_lhash ) }, + { LSTRKEY( "hmac" ), LFUNCVAL( crypto_lhmac ) }, + +#if LUA_OPTIMIZE_MEMORY > 0 + +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int luaopen_crypto( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_CRYPTO, crypto_map ); + // Add constants + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} 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/modules.h b/app/modules/modules.h index fddc45cc..76a357a8 100644 --- a/app/modules/modules.h +++ b/app/modules/modules.h @@ -149,6 +149,22 @@ #define ROM_MODULES_CJSON #endif +#if defined(LUA_USE_MODULES_CRYPTO) +#define MODULES_CRYPTO "crypto" +#define ROM_MODULES_CRYPTO \ + _ROM(MODULES_CRYPTO, luaopen_crypto, crypto_map) +#else +#define ROM_MODULES_CRYPTO +#endif + +#if defined(LUA_USE_MODULES_RC) +#define MODULES_RC "rc" +#define ROM_MODULES_RC \ +_ROM(MODULES_RC, luaopen_rc, rc_map) +#else +#define ROM_MODULES_RC +#endif + #define LUA_MODULES_ROM \ ROM_MODULES_GPIO \ ROM_MODULES_PWM \ @@ -167,7 +183,8 @@ ROM_MODULES_OW \ ROM_MODULES_BIT \ ROM_MODULES_WS2812 \ - ROM_MODULES_CJSON + ROM_MODULES_CJSON \ + ROM_MODULES_CRYPTO \ + ROM_MODULES_RC #endif - diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index 018e034e..0cd2398b 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,12 +369,12 @@ 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; - pdata += mud->mqtt_state.message_length; + length -= mud->mqtt_state.message_length; + in_buffer += mud->mqtt_state.message_length; NODE_DBG("Get another published message\r\n"); goto READPACKET; @@ -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/net.c b/app/modules/net.c index dbd072f1..bb36d45b 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -204,13 +204,12 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) NODE_DBG("self_ref null.\n"); return; } - +/* original if(ipaddr == NULL) { NODE_ERR( "DNS Fail!\n" ); goto end; } - // ipaddr->addr is a uint32_t ip char ip_str[20]; c_memset(ip_str, 0, sizeof(ip_str)); @@ -220,9 +219,30 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) } lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function - lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua + //lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua lua_pushstring(gL, ip_str); // the ip para - lua_call(gL, 2, 0); +*/ + + // "enhanced" + lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function + + if(ipaddr == NULL) + { + NODE_DBG( "DNS Fail!\n" ); + lua_pushnil(gL); + }else{ + // ipaddr->addr is a uint32_t ip + char ip_str[20]; + c_memset(ip_str, 0, sizeof(ip_str)); + if(host_ip.addr == 0 && ipaddr->addr != 0) + { + c_sprintf(ip_str, IPSTR, IP2STR(&(ipaddr->addr))); + } + lua_pushstring(gL, ip_str); // the ip para + } + // "enhanced" end + + lua_call(gL, 1, 0); end: if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0) @@ -1119,6 +1139,71 @@ static int net_dns( lua_State* L, const char* mt ) return 0; } +// Lua: net.dns.resolve( domain, function(ip) ) +static int net_dns_static( lua_State* L ) +{ + const char *mt = "net.socket"; + if (!lua_isstring( L, 1 )) + return luaL_error( L, "wrong parameter type (domain)" ); + + int rfunc = LUA_NOREF; //save reference to func + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION){ + rfunc = luaL_ref(L, LUA_REGISTRYINDEX); + } + int rdom = luaL_ref(L, LUA_REGISTRYINDEX); //save reference to domain + + lua_settop(L,0); //empty stack + lua_getfield(L, LUA_GLOBALSINDEX, "net"); + lua_getfield(L, -1, "createConnection"); + lua_remove(L, -2); //remove "net" from stack + lua_pushinteger(L, UDP); // we are going to create a new dummy UDP socket + lua_call(L,1,1);// after this the stack should have a socket + + lua_rawgeti(gL, LUA_REGISTRYINDEX, rdom); // load domain back to the stack + lua_rawgeti(gL, LUA_REGISTRYINDEX, rfunc); // load the callback function back to the stack + + luaL_unref(L, LUA_REGISTRYINDEX, rdom); //free reference + luaL_unref(L, LUA_REGISTRYINDEX, rfunc); //free reference + + bool isserver = false; + struct espconn *pesp_conn = NULL; + lnet_userdata *nud; + size_t l; + + nud = (lnet_userdata *)luaL_checkudata(L, 1, mt); + luaL_argcheck(L, nud, 1, "Server/Socket expected"); + if(nud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + if(nud->pesp_conn == NULL){ + NODE_DBG("nud->pesp_conn is NULL.\n"); + return 0; + } + pesp_conn = nud->pesp_conn; + + lua_pushvalue(L, 1); // copy to the top of stack + if(nud->self_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->self_ref); + nud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + const char *domain = luaL_checklstring( L, 2, &l ); + if (l>128 || domain == NULL) + return luaL_error( L, "need <128 domain" ); + + if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){ + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + if(nud->cb_dns_found_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); + nud->cb_dns_found_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + host_ip.addr = 0; + espconn_gethostbyname(pesp_conn, domain, &host_ip, net_dns_found); + + return 0; +} + // Lua: s = net.createServer(type, function(server)) static int net_createServer( lua_State* L ) { @@ -1332,6 +1417,48 @@ static int net_multicastLeave( lua_State* L ) } +// Lua: s = net.dns.setdnsserver(ip_addr, [index]) +static int net_setdnsserver( lua_State* L ) +{ + size_t l; + u32_t ip32; + + const char *server = luaL_checklstring( L, 1, &l ); + if (l>16 || server == NULL || (ip32 = ipaddr_addr(server)) == IPADDR_NONE || ip32 == IPADDR_ANY) + return luaL_error( L, "invalid dns server ip" ); + + int numdns = luaL_optint(L, 2, 0); + if (numdns >= DNS_MAX_SERVERS) + return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1); + + ip_addr_t ipaddr; + ip4_addr_set_u32(&ipaddr, ip32); + dns_setserver(numdns,&ipaddr); + + return 0; +} + +// Lua: s = net.dns.getdnsserver([index]) +static int net_getdnsserver( lua_State* L ) +{ + int numdns = luaL_optint(L, 1, 0); + if (numdns >= DNS_MAX_SERVERS) + return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1); + + ip_addr_t ipaddr; + dns_getserver(numdns,&ipaddr); + + if ( ip_addr_isany(&ipaddr) ) { + lua_pushnil( L ); + } else { + char temp[20] = {0}; + c_sprintf(temp, IPSTR, IP2STR( &ipaddr ) ); + lua_pushstring( L, temp ); + } + + return 1; +} + #if 0 static int net_array_index( lua_State* L ) { @@ -1402,6 +1529,15 @@ static const LUA_REG_TYPE net_array_map[] = { LNILKEY, LNILVAL } }; #endif + +static const LUA_REG_TYPE net_dns_map[] = +{ + { LSTRKEY( "setdnsserver" ), LFUNCVAL ( net_setdnsserver ) }, + { LSTRKEY( "getdnsserver" ), LFUNCVAL ( net_getdnsserver ) }, + { LSTRKEY( "resolve" ), LFUNCVAL ( net_dns_static ) }, + { LNILKEY, LNILVAL } +}; + const LUA_REG_TYPE net_map[] = { { LSTRKEY( "createServer" ), LFUNCVAL ( net_createServer ) }, @@ -1409,6 +1545,7 @@ const LUA_REG_TYPE net_map[] = { LSTRKEY( "multicastJoin"), LFUNCVAL( net_multicastJoin ) }, { LSTRKEY( "multicastLeave"), LFUNCVAL( net_multicastLeave ) }, #if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "dns" ), LROVAL( net_dns_map ) }, { LSTRKEY( "TCP" ), LNUMVAL( TCP ) }, { LSTRKEY( "UDP" ), LNUMVAL( UDP ) }, @@ -1471,6 +1608,12 @@ LUALIB_API int luaopen_net( lua_State *L ) // Setup the methods inside metatable luaL_register( L, NULL, net_array_map ); #endif + + lua_settop(L, n); + lua_newtable( L ); + luaL_register( L, NULL, net_dns_map ); + lua_setfield( L, -2, "dns" ); + return 1; #endif // #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/rc.c b/app/modules/rc.c new file mode 100644 index 00000000..41ad6d3f --- /dev/null +++ b/app/modules/rc.c @@ -0,0 +1,96 @@ +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" +//#include "driver/easygpio.h" +//static Ping_Data pingA; +#define defPulseLen 185 +#define defProtocol 1 +#define defRepeat 10 +#define defBits 24 +void transmit(int pin, int pulseLen, int nHighPulses, int nLowPulses) { + platform_gpio_write(pin, 1); + os_delay_us(pulseLen*nHighPulses); + platform_gpio_write(pin, 0); + os_delay_us(pulseLen*nLowPulses); +} +//rc.send(0,267715,24,185,1) --GPIO, code, bits, pulselen, protocol +static int ICACHE_FLASH_ATTR rc_send(lua_State* L) { + const uint8_t pin = luaL_checkinteger(L, 1); + platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); + //platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLUP); + //platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_PULLDOWN); + platform_gpio_write(pin, 0); + long code = luaL_checklong(L, 2); + //const uint8_t bits = luaL_checkinteger(L, 3); + uint8_t bits = luaL_checkinteger(L, 3); + const uint8_t pulseLen = luaL_checkinteger(L, 4); + const uint8_t Protocol = luaL_checkinteger(L, 5); + const uint8_t repeat = luaL_checkinteger(L, 6); + NODE_ERR("pulseLen:%d\n",pulseLen); + NODE_ERR("Protocol:%d\n",Protocol); + NODE_ERR("repeat:%d\n",repeat); + NODE_ERR("send:"); + int c,k,nRepeat; + bits = bits-1; + for (c = bits; c >= 0; c--) + { + k = code >> c; + if (k & 1) + NODE_ERR("1"); + else + NODE_ERR("0"); + } + NODE_ERR("\n"); + for (nRepeat=0; nRepeat= 0; c--) + { + k = code >> c; + if (k & 1){ + //send1 + if(Protocol==1){ + transmit(pin,pulseLen,3,1); + }else if(Protocol==2){ + transmit(pin,pulseLen,2,1); + }else if(Protocol==3){ + transmit(pin,pulseLen,9,6); + } + } + else{ + //send0 + if(Protocol==1){ + transmit(pin,pulseLen,1,3); + }else if(Protocol==2){ + transmit(pin,pulseLen,1,2); + }else if(Protocol==3){ + transmit(pin,pulseLen,4,11); + } + } + } + //sendSync(); + if(Protocol==1){ + transmit(pin,pulseLen,1,31); + }else if(Protocol==2){ + transmit(pin,pulseLen,1,10); + }else if(Protocol==3){ + transmit(pin,pulseLen,1,71); + } + } + + return 1; +} +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE rc_map[] = +{ + { LSTRKEY( "send" ), LFUNCVAL( rc_send )}, + { LNILKEY, LNILVAL} +}; + +//LUALIB_API int luaopen_ultra(lua_State *L) { +LUALIB_API int luaopen_rc(lua_State *L) { + // TODO: Make sure that the GPIO system is initialized + LREGISTER(L, "rc", rc_map); + return 1; +} diff --git a/app/modules/tmr.c b/app/modules/tmr.c index ee92f4d5..65007778 100644 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -11,11 +11,18 @@ static os_timer_t alarm_timer[NUM_TMR]; static int alarm_timer_cb_ref[NUM_TMR] = {LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF}; +static bool alarm_timer_repeat[NUM_TMR]= {0,0,0,0,0,0,0}; void alarm_timer_common(lua_State* L, unsigned id){ if(alarm_timer_cb_ref[id] == LUA_NOREF) return; lua_rawgeti(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]); + if(alarm_timer_repeat[id]==0) + { + if(alarm_timer_cb_ref[id] != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]); + + } lua_call(L, 0, 0); } @@ -118,6 +125,7 @@ static int tmr_alarm( lua_State* L ) stack++; if ( repeat != 1 && repeat != 0 ) return luaL_error( L, "wrong arg type" ); + alarm_timer_repeat[id]=repeat; } // luaL_checkanyfunction(L, stack); @@ -141,6 +149,9 @@ static int tmr_stop( lua_State* L ) MOD_CHECK_ID( tmr, id ); os_timer_disarm(&(alarm_timer[id])); + if(alarm_timer_cb_ref[id] != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]); + return 0; } diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 59cb20d9..3596ab06 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -760,6 +760,39 @@ static int lu8g_getHeight( lua_State *L ) return 1; } +// Lua: width = u8g.getStrWidth( self, string ) +static int lu8g_getStrWidth( lua_State *L ) +{ + lu8g_userdata_t *lud; + + if ((lud = get_lud( L )) == NULL) + return 0; + + const char *s = luaL_checkstring( L, 2 ); + if (s == NULL) + return 0; + + lua_pushinteger( L, u8g_GetStrWidth( LU8G, s ) ); + + return 1; +} + +// Lua: u8g.setFontLineSpacingFactor( self, factor ) +static int lu8g_setFontLineSpacingFactor( lua_State *L ) +{ + lu8g_userdata_t *lud; + + if ((lud = get_lud( L )) == NULL) + return 0; + + u8g_uint_t factor = luaL_checkinteger( L, 2 ); + + u8g_SetFontLineSpacingFactor( LU8G, factor ); + + return 0; +} + + // ------------------------------------------------------------ // comm functions // @@ -916,7 +949,7 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi case U8G_COM_MSG_WRITE_BYTE: //u8g->pin_list[U8G_PI_SET_A0] = 1; if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) - return platform_i2c_stop( ESP_I2C_ID ), 0; + return platform_i2c_send_stop( ESP_I2C_ID ), 0; // ignore return value -> tolerate missing ACK if ( platform_i2c_send_byte( ESP_I2C_ID, arg_val) == 0 ) ; //return platform_i2c_send_stop( ESP_I2C_ID ), 0; @@ -1140,53 +1173,55 @@ static int lu8g_pcd8544_84x48( lua_State *L ) static const LUA_REG_TYPE lu8g_display_map[] = { { LSTRKEY( "begin" ), LFUNCVAL( lu8g_begin ) }, - { LSTRKEY( "setFont" ), LFUNCVAL( lu8g_setFont ) }, - { LSTRKEY( "setFontRefHeightAll" ), LFUNCVAL( lu8g_setFontRefHeightAll ) }, - { LSTRKEY( "setFontRefHeightExtendedText" ), LFUNCVAL( lu8g_setFontRefHeightExtendedText ) }, - { LSTRKEY( "setFontRefHeightText" ), LFUNCVAL( lu8g_setFontRefHeightText ) }, - { LSTRKEY( "setDefaultBackgroundColor" ), LFUNCVAL( lu8g_setDefaultBackgroundColor ) }, - { LSTRKEY( "setDefaultForegroundColor" ), LFUNCVAL( lu8g_setDefaultForegroundColor ) }, - { LSTRKEY( "setFontPosBaseline" ), LFUNCVAL( lu8g_setFontPosBaseline ) }, - { LSTRKEY( "setFontPosBottom" ), LFUNCVAL( lu8g_setFontPosBottom ) }, - { LSTRKEY( "setFontPosCenter" ), LFUNCVAL( lu8g_setFontPosCenter ) }, - { LSTRKEY( "setFontPosTop" ), LFUNCVAL( lu8g_setFontPosTop ) }, - { LSTRKEY( "getFontAscent" ), LFUNCVAL( lu8g_getFontAscent ) }, - { LSTRKEY( "getFontDescent" ), LFUNCVAL( lu8g_getFontDescent ) }, - { LSTRKEY( "getFontLineSpacing" ), LFUNCVAL( lu8g_getFontLineSpacing ) }, - { LSTRKEY( "getMode" ), LFUNCVAL( lu8g_getMode ) }, - { LSTRKEY( "setColorIndex" ), LFUNCVAL( lu8g_setColorIndex ) }, - { LSTRKEY( "getColorIndex" ), LFUNCVAL( lu8g_getColorIndex ) }, + { LSTRKEY( "drawBitmap" ), LFUNCVAL( lu8g_drawBitmap ) }, + { LSTRKEY( "drawBox" ), LFUNCVAL( lu8g_drawBox ) }, + { LSTRKEY( "drawCircle" ), LFUNCVAL( lu8g_drawCircle ) }, + { LSTRKEY( "drawDisc" ), LFUNCVAL( lu8g_drawDisc ) }, + { LSTRKEY( "drawEllipse" ), LFUNCVAL( lu8g_drawEllipse ) }, + { LSTRKEY( "drawFilledEllipse" ), LFUNCVAL( lu8g_drawFilledEllipse ) }, + { LSTRKEY( "drawFrame" ), LFUNCVAL( lu8g_drawFrame ) }, + { LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) }, + { LSTRKEY( "drawLine" ), LFUNCVAL( lu8g_drawLine ) }, + { LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) }, + { LSTRKEY( "drawRBox" ), LFUNCVAL( lu8g_drawRBox ) }, + { LSTRKEY( "drawRFrame" ), LFUNCVAL( lu8g_drawRFrame ) }, { LSTRKEY( "drawStr" ), LFUNCVAL( lu8g_drawStr ) }, { LSTRKEY( "drawStr90" ), LFUNCVAL( lu8g_drawStr90 ) }, { LSTRKEY( "drawStr180" ), LFUNCVAL( lu8g_drawStr180 ) }, { LSTRKEY( "drawStr270" ), LFUNCVAL( lu8g_drawStr270 ) }, - { LSTRKEY( "drawBox" ), LFUNCVAL( lu8g_drawBox ) }, - { LSTRKEY( "drawLine" ), LFUNCVAL( lu8g_drawLine ) }, { LSTRKEY( "drawTriangle" ), LFUNCVAL( lu8g_drawTriangle ) }, - { LSTRKEY( "drawRBox" ), LFUNCVAL( lu8g_drawRBox ) }, - { LSTRKEY( "drawFrame" ), LFUNCVAL( lu8g_drawFrame ) }, - { LSTRKEY( "drawRFrame" ), LFUNCVAL( lu8g_drawRFrame ) }, - { LSTRKEY( "drawDisc" ), LFUNCVAL( lu8g_drawDisc ) }, - { LSTRKEY( "drawCircle" ), LFUNCVAL( lu8g_drawCircle ) }, - { LSTRKEY( "drawEllipse" ), LFUNCVAL( lu8g_drawEllipse ) }, - { LSTRKEY( "drawFilledEllipse" ), LFUNCVAL( lu8g_drawFilledEllipse ) }, - { LSTRKEY( "drawPixel" ), LFUNCVAL( lu8g_drawPixel ) }, - { LSTRKEY( "drawHLine" ), LFUNCVAL( lu8g_drawHLine ) }, { LSTRKEY( "drawVLine" ), LFUNCVAL( lu8g_drawVLine ) }, - { LSTRKEY( "drawBitmap" ), LFUNCVAL( lu8g_drawBitmap ) }, { LSTRKEY( "drawXBM" ), LFUNCVAL( lu8g_drawXBM ) }, - { LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) }, - { LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) }, { LSTRKEY( "firstPage" ), LFUNCVAL( lu8g_firstPage ) }, + { LSTRKEY( "getColorIndex" ), LFUNCVAL( lu8g_getColorIndex ) }, + { LSTRKEY( "getFontAscent" ), LFUNCVAL( lu8g_getFontAscent ) }, + { LSTRKEY( "getFontDescent" ), LFUNCVAL( lu8g_getFontDescent ) }, + { LSTRKEY( "getFontLineSpacing" ), LFUNCVAL( lu8g_getFontLineSpacing ) }, + { LSTRKEY( "getHeight" ), LFUNCVAL( lu8g_getHeight ) }, + { LSTRKEY( "getMode" ), LFUNCVAL( lu8g_getMode ) }, + { LSTRKEY( "getStrWidth" ), LFUNCVAL( lu8g_getStrWidth ) }, + { LSTRKEY( "getWidth" ), LFUNCVAL( lu8g_getWidth ) }, { LSTRKEY( "nextPage" ), LFUNCVAL( lu8g_nextPage ) }, - { LSTRKEY( "sleepOn" ), LFUNCVAL( lu8g_sleepOn ) }, - { LSTRKEY( "sleepOff" ), LFUNCVAL( lu8g_sleepOff ) }, + { LSTRKEY( "setColorIndex" ), LFUNCVAL( lu8g_setColorIndex ) }, + { LSTRKEY( "setDefaultBackgroundColor" ), LFUNCVAL( lu8g_setDefaultBackgroundColor ) }, + { LSTRKEY( "setDefaultForegroundColor" ), LFUNCVAL( lu8g_setDefaultForegroundColor ) }, + { LSTRKEY( "setFont" ), LFUNCVAL( lu8g_setFont ) }, + { LSTRKEY( "setFontLineSpacingFactor" ), LFUNCVAL( lu8g_setFontLineSpacingFactor ) }, + { LSTRKEY( "setFontPosBaseline" ), LFUNCVAL( lu8g_setFontPosBaseline ) }, + { LSTRKEY( "setFontPosBottom" ), LFUNCVAL( lu8g_setFontPosBottom ) }, + { LSTRKEY( "setFontPosCenter" ), LFUNCVAL( lu8g_setFontPosCenter ) }, + { LSTRKEY( "setFontPosTop" ), LFUNCVAL( lu8g_setFontPosTop ) }, + { LSTRKEY( "setFontRefHeightAll" ), LFUNCVAL( lu8g_setFontRefHeightAll ) }, + { LSTRKEY( "setFontRefHeightExtendedText" ), LFUNCVAL( lu8g_setFontRefHeightExtendedText ) }, + { LSTRKEY( "setFontRefHeightText" ), LFUNCVAL( lu8g_setFontRefHeightText ) }, { LSTRKEY( "setRot90" ), LFUNCVAL( lu8g_setRot90 ) }, { LSTRKEY( "setRot180" ), LFUNCVAL( lu8g_setRot180 ) }, { LSTRKEY( "setRot270" ), LFUNCVAL( lu8g_setRot270 ) }, + { LSTRKEY( "setScale2x2" ), LFUNCVAL( lu8g_setScale2x2 ) }, + { LSTRKEY( "sleepOff" ), LFUNCVAL( lu8g_sleepOff ) }, + { LSTRKEY( "sleepOn" ), LFUNCVAL( lu8g_sleepOn ) }, { LSTRKEY( "undoRotation" ), LFUNCVAL( lu8g_undoRotation ) }, - { LSTRKEY( "getWidth" ), LFUNCVAL( lu8g_getWidth ) }, - { LSTRKEY( "getHeight" ), LFUNCVAL( lu8g_getHeight ) }, + { LSTRKEY( "undoScale" ), LFUNCVAL( lu8g_undoScale ) }, { LSTRKEY( "__gc" ), LFUNCVAL( lu8g_close_display ) }, #if LUA_OPTIMIZE_MEMORY > 0 { LSTRKEY( "__index" ), LROVAL ( lu8g_display_map ) }, diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 635625c6..b796babb 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" @@ -15,6 +16,7 @@ #include "smartconfig.h" static int wifi_smart_succeed = LUA_NOREF; +static uint8 getap_output_format=0; static void wifi_smart_succeed_cb(void *arg){ NODE_DBG("wifi_smart_succeed_cb is called.\n"); @@ -70,22 +72,35 @@ static void wifi_scan_done(void *arg, STATUS status) { c_memcpy(ssid, bss_link->ssid, 32); } - c_sprintf(temp,"%d,%d,"MACSTR",%d", bss_link->authmode, bss_link->rssi, - MAC2STR(bss_link->bssid),bss_link->channel); - - lua_pushstring(gL, temp); - lua_setfield( gL, -2, ssid ); - - // NODE_DBG(temp); + if(getap_output_format==1) //use new format(BSSID : SSID, RSSI, Authmode, Channel) + { + c_sprintf(temp,"%s,%d,%d,%d", ssid, bss_link->rssi, bss_link->authmode, bss_link->channel); + lua_pushstring(gL, temp); + NODE_DBG(MACSTR" : %s\n",MAC2STR(bss_link->bssid) , temp); + c_sprintf(temp,MACSTR, MAC2STR(bss_link->bssid)); + lua_setfield( gL, -2, temp); + } + else//use old format(SSID : Authmode, RSSI, BSSID, Channel) + { + c_sprintf(temp,"%d,%d,"MACSTR",%d", bss_link->authmode, bss_link->rssi, MAC2STR(bss_link->bssid),bss_link->channel); + lua_pushstring(gL, temp); + lua_setfield( gL, -2, ssid ); + NODE_DBG("%s : %s\n", ssid, temp); + } bss_link = bss_link->next.stqe_next; } } else { - lua_pushnil(gL); + lua_newtable( gL ); } lua_call(gL, 1, 0); + if(wifi_scan_succeed != LUA_NOREF) + { + luaL_unref(gL, LUA_REGISTRYINDEX, wifi_scan_succeed); + wifi_scan_succeed = LUA_NOREF; + } } // Lua: smart(channel, function succeed_cb) @@ -149,6 +164,7 @@ static int wifi_setmode( lua_State* L ) } // Lua: realmode = getmode() + static int wifi_getmode( lua_State* L ) { unsigned mode; @@ -156,7 +172,77 @@ static int wifi_getmode( lua_State* L ) lua_pushinteger( L, mode ); return 1; } +/** + * wifi.getchannel() + * Description: + * Get current wifi Channel + * + * Syntax: + * wifi.getchannel() + * Parameters: + * nil + * + * Returns: + * Current wifi channel + */ +static int wifi_getchannel( lua_State* L ) +{ + unsigned channel; + channel = (unsigned)wifi_get_channel(); + lua_pushinteger( L, channel ); + return 1; +} + +/** + * wifi.setphymode() + * Description: + * Set wifi physical mode(802.11 b/g/n) + * Note: SoftAP only supports 802.11 b/g. + * Syntax: + * wifi.setphymode(mode) + * Parameters: + * mode: + * wifi.PHYMODE_B + * wifi.PHYMODE_G + * wifi.PHYMODE_N + * Returns: + * Current physical mode after setup + */ + +static int wifi_setphymode( lua_State* L ) +{ + unsigned mode; + + mode = luaL_checkinteger( L, 1 ); + + if ( mode != PHY_MODE_11B && mode != PHY_MODE_11G && mode != PHY_MODE_11N ) + return luaL_error( L, "wrong arg type" ); + wifi_set_phy_mode( (uint8_t)mode); + mode = (unsigned)wifi_get_phy_mode(); + lua_pushinteger( L, mode ); + return 1; +} + +/** + * wifi.getphymode() + * Description: + * Get wifi physical mode(802.11 b/g/n) + * Syntax: + * wifi.getphymode() + * Parameters: + * nil + * Returns: + * Current physical mode. + * + */ +static int wifi_getphymode( lua_State* L ) +{ + unsigned mode; + mode = (unsigned)wifi_get_phy_mode(); + lua_pushinteger( L, mode ); + return 1; +} // Lua: mac = wifi.xx.getmac() static int wifi_getmac( lua_State* L, uint8_t mode ) @@ -164,7 +250,7 @@ static int wifi_getmac( lua_State* L, uint8_t mode ) char temp[64]; uint8_t mac[6]; wifi_get_macaddr(mode, mac); - c_sprintf(temp, "%02X-%02X-%02X-%02X-%02X-%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] ); + c_sprintf(temp, MACSTR, MAC2STR(mac)); lua_pushstring( L, temp ); return 1; } @@ -172,11 +258,13 @@ static int wifi_getmac( lua_State* L, uint8_t mode ) // Lua: mac = wifi.xx.setmac() static int wifi_setmac( lua_State* L, uint8_t mode ) { + uint8_t mac[6]; unsigned len = 0; - const char *mac = luaL_checklstring( L, 1, &len ); - if(len!=6) - return luaL_error( L, "wrong arg type" ); + const char *macaddr = luaL_checklstring( L, 1, &len ); + if(len!=17) + return luaL_error( L, "wrong arg type" ); + os_str2macaddr(mac, macaddr); lua_pushboolean(L,wifi_set_macaddr(mode, (uint8 *)mac)); return 1; } @@ -254,7 +342,7 @@ static int wifi_setip( lua_State* L, uint8_t mode ) pTempIp.netmask.addr = ip; ip = parse_key(L, "gateway"); - if(ip!=0) + if(mode==SOFTAP_IF || ip!=0) pTempIp.gw.addr = ip; if(STATION_IF == mode) @@ -317,36 +405,161 @@ static int wifi_station_getbroadcast( lua_State* L ){ return wifi_getbroadcast(L, STATION_IF); } -// Lua: wifi.sta.config(ssid, password) +/** + * wifi.sta.getconfig() + * Description: + * Get current Station configuration. + * Note: if bssid_set==1 STATION is configured to connect to specified BSSID + * if bssid_set==0 specified BSSID address is irrelevant. + * Syntax: + * ssid, pwd, bssid_set, bssid=wifi.sta.getconfig() + * Parameters: + * none + * Returns: + * SSID, Password, BSSID_set, BSSID + */ +static int wifi_station_getconfig( lua_State* L ) +{ + struct station_config sta_conf; + char bssid[17]; + wifi_station_get_config(&sta_conf); + if(sta_conf.ssid==0) + { + lua_pushnil(L); + return 1; + } + else + { + lua_pushstring( L, sta_conf.ssid ); + lua_pushstring( L, sta_conf.password ); + lua_pushinteger( L, sta_conf.bssid_set); + c_sprintf(bssid, MACSTR, MAC2STR(sta_conf.bssid)); + lua_pushstring( L, bssid); + return 4; + } +} + +/** + * wifi.sta.config() + * Description: + * Set current Station configuration. + * Note: If there are multiple APs with the same ssid, you can connect to a specific one by entering it's MAC address into the "bssid" field. + * Syntax: + * wifi.sta.getconfig(ssid, password) --Set STATION configuration, Auto-connect by default, Connects to any BSSID + * wifi.sta.getconfig(ssid, password, Auto_connect) --Set STATION configuration, Auto-connect(0 or 1), Connects to any BSSID + * wifi.sta.getconfig(ssid, password, bssid) --Set STATION configuration, Auto-connect by default, Connects to specific BSSID + * wifi.sta.getconfig(ssid, password, Auto_connect, bssid) --Set STATION configuration, Auto-connect(0 or 1), Connects to specific BSSID + * Parameters: + * ssid: string which is less than 32 bytes. + * Password: string which is less than 64 bytes. + * Auto_connect: 0 (disable Auto-connect) or 1 (to enable Auto-connect). + * bssid: MAC address of Access Point you would like to connect to. + * Returns: + * Nothing. + * + * Example: + --Connect to Access Point automatically when in range + wifi.sta.getconfig("myssid", "password") + + --Connect to Access Point, User decides when to connect/disconnect to/from AP + wifi.sta.getconfig("myssid", "mypassword", 0) + wifi.sta.connect() + --do some wifi stuff + wifi.sta.disconnect() + + --Connect to specific Access Point automatically when in range + wifi.sta.getconfig("myssid", "mypassword", "12:34:56:78:90:12") + + --Connect to specific Access Point, User decides when to connect/disconnect to/from AP + wifi.sta.getconfig("myssid", "mypassword", 0) + wifi.sta.connect() + --do some wifi stuff + wifi.sta.disconnect() + * + */ static int wifi_station_config( lua_State* L ) { - size_t sl, pl; + size_t sl, pl, ml; struct station_config sta_conf; - int i; + int auto_connect=0; const char *ssid = luaL_checklstring( L, 1, &sl ); 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!=0 && (pl<8 || pl>64) || password == NULL) + return luaL_error( L, "pwd:0,8~64" ); + + if(lua_isnumber(L, 3)) + { + auto_connect=luaL_checkinteger( L, 3 );; + if ( auto_connect != 0 && auto_connect != 1) + return luaL_error( L, "wrong arg type" ); + } + else if (lua_isstring(L, 3)&& !(lua_isnumber(L, 3))) + { + lua_pushnil(L); + lua_insert(L, 3); + auto_connect=1; + + } + else + { + if(lua_isnil(L, 3)) + return luaL_error( L, "wrong arg type" ); + auto_connect=1; + } + + if(lua_isnumber(L, 4)) + { + sta_conf.bssid_set = 0; + c_memset(sta_conf.bssid, 0, 6); + } + else + { + if (lua_isstring(L, 4)) + { + const char *macaddr = luaL_checklstring( L, 4, &ml ); + if (ml!=17) + return luaL_error( L, "MAC:FF:FF:FF:FF:FF:FF" ); + c_memset(sta_conf.bssid, 0, 6); + os_str2macaddr(sta_conf.bssid, macaddr); + sta_conf.bssid_set = 1; + } + else + { + sta_conf.bssid_set = 0; + c_memset(sta_conf.bssid, 0, 6); + } + } c_memset(sta_conf.ssid, 0, 32); c_memset(sta_conf.password, 0, 64); - c_memset(sta_conf.bssid, 0, 6); c_memcpy(sta_conf.ssid, ssid, sl); c_memcpy(sta_conf.password, password, pl); - sta_conf.bssid_set = 0; NODE_DBG(sta_conf.ssid); NODE_DBG(" %d\n", sl); NODE_DBG(sta_conf.password); NODE_DBG(" %d\n", pl); + NODE_DBG(" %d\n", sta_conf.bssid_set); + NODE_DBG( MACSTR, MAC2STR(sta_conf.bssid)); + NODE_DBG("\n"); + wifi_station_set_config(&sta_conf); - wifi_station_set_auto_connect(true); wifi_station_disconnect(); - wifi_station_connect(); - // station_check_connect(0); + + if(auto_connect==0) + { + wifi_station_set_auto_connect(false); + + } + else + { + wifi_station_set_auto_connect(true); + wifi_station_connect(); + } +// station_check_connect(0); return 0; } @@ -380,6 +593,42 @@ static int wifi_station_setauto( lua_State* L ) return 0; } +/** + * wifi.sta.listap() + * Description: + * scan and get ap list as a lua table into callback function. + * Syntax: + * wifi.sta.getap(function(table)) + * wifi.sta.getap(format, function(table)) + * wifi.sta.getap(cfg, function(table)) + * wifi.sta.getap(cfg, format, function(table)) + * Parameters: + * cfg: table that contains scan configuration + * Format:Select output table format. + * 0 for the old format (SSID : Authmode, RSSI, BSSID, Channel) (Default) + * 1 for the new format (BSSID : SSID, RSSI, Authmode, Channel) + * function(table): a callback function to receive ap table when scan is done + this function receive a table, the key is the ssid, + value is other info in format: authmode,rssi,bssid,channel + * Returns: + * nil + * + * Example: + --original function left intact to preserve backward compatibility + wifi.sta.getap(function(T) for k,v in pairs(T) do print(k..":"..v) end end) + + --if no scan configuration is desired cfg can be set to nil or previous example can be used + wifi.sta.getap(nil, function(T) for k,v in pairs(T) do print(k..":"..v) end end) + + --scan configuration + scan_cfg={} + scan_cfg.ssid="myssid" --if set to nil, ssid is not filtered + scan_cfg.bssid="AA:AA:AA:AA:AA:AA" --if set to nil, MAC address is not filtered + scan_cfg.channel=0 --if set to nil, channel will default to 0(scans all channels), if set scan will be faster + scan_cfg.show_hidden=1 --if set to nil, show_hidden will default to 0 + wifi.sta.getap(scan_cfg, function(T) for k,v in pairs(T) do print(k..":"..v) end end) + + */ static int wifi_station_listap( lua_State* L ) { if(wifi_get_opmode() == SOFTAP_MODE) @@ -387,14 +636,146 @@ static int wifi_station_listap( lua_State* L ) return luaL_error( L, "Can't list ap in SOFTAP mode" ); } gL = L; - // luaL_checkanyfunction(L, 1); - if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION){ - lua_pushvalue(L, 1); // copy argument (func) to the top of stack + struct scan_config scan_cfg; + getap_output_format=0; + + if (lua_type(L, 1)==LUA_TTABLE) + { + char ssid[32]; + char bssid[6]; + uint8 channel=0; + uint8 show_hidden=0; + 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 *ssidstr = luaL_checklstring( L, -1, &len ); + if(len>32) + return luaL_error( L, "ssid:<32" ); + c_memset(ssid, 0, 32); + c_memcpy(ssid, ssidstr, len); + scan_cfg.ssid=ssid; + NODE_DBG(scan_cfg.ssid); + NODE_DBG("\n"); + } + else + return luaL_error( L, "wrong arg type" ); + } + else + scan_cfg.ssid=NULL; + + lua_getfield(L, 1, "bssid"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isstring(L, -1) ) // deal with the ssid string + { + const char *macaddr = luaL_checklstring( L, -1, &len ); + if(len!=17) + return luaL_error( L, "bssid: FF:FF:FF:FF:FF:FF" ); + c_memset(bssid, 0, 6); + os_str2macaddr(bssid, macaddr); + scan_cfg.bssid=bssid; + NODE_DBG(MACSTR, MAC2STR(scan_cfg.bssid)); + NODE_DBG("\n"); + + } + else + return luaL_error( L, "wrong arg type" ); + } + else + scan_cfg.bssid=NULL; + + + lua_getfield(L, 1, "channel"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isnumber(L, -1) ) // deal with the ssid string + { + channel = luaL_checknumber( L, -1); + if(!(channel>=0 && channel<=13)) + return luaL_error( L, "channel: 0 or 1-13" ); + scan_cfg.channel=channel; + NODE_DBG("%d\n", scan_cfg.channel); + } + else + return luaL_error( L, "wrong arg type" ); + } + else + scan_cfg.channel=0; + + lua_getfield(L, 1, "show_hidden"); + if (!lua_isnil(L, -1)){ /* found? */ + if( lua_isnumber(L, -1) ) // deal with the ssid string + { + show_hidden = luaL_checknumber( L, -1); + if(show_hidden!=0 && show_hidden!=1) + return luaL_error( L, "show_hidden: 0 or 1" ); + scan_cfg.show_hidden=show_hidden; + NODE_DBG("%d\n", scan_cfg.show_hidden); + + } + else + return luaL_error( L, "wrong arg type" ); + } + else + scan_cfg.show_hidden=0; + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 2); + } + lua_pop(L, -4); + } + else if (lua_type(L, 1) == LUA_TNUMBER) + { + lua_pushnil(L); + lua_insert(L, 1); + } + else if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 1); + lua_pushnil(L); + lua_insert(L, 1); + } + else if(lua_isnil(L, 1)) + { + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 2); + } + } + else + { + return luaL_error( L, "wrong arg type" ); + } + + + if (lua_type(L, 2) == LUA_TNUMBER) //this section changes the output format + { + getap_output_format=luaL_checkinteger( L, 2 ); + if ( getap_output_format != 0 && getap_output_format != 1) + return luaL_error( L, "wrong arg type" ); + } + NODE_DBG("Use alternate output format: %d\n", getap_output_format); + if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) + { + lua_pushvalue(L, 3); // copy argument (func) to the top of stack if(wifi_scan_succeed != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed); wifi_scan_succeed = luaL_ref(L, LUA_REGISTRYINDEX); - wifi_station_scan(NULL,wifi_scan_done); - } else { + if (lua_type(L, 1)==LUA_TTABLE) + { + wifi_station_scan(&scan_cfg,wifi_scan_done); + } + else + { + wifi_station_scan(NULL,wifi_scan_done); + } + } + else + { if(wifi_scan_succeed != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, wifi_scan_succeed); wifi_scan_succeed = LUA_NOREF; @@ -437,44 +818,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 +866,158 @@ 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("%d\n", config.authmode); + } + 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("%d\n", config.channel); + } + 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("%d\n", 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("%d\n", config.max_connection); + } + 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("%d\n", config.beacon_interval); + } + 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 @@ -495,6 +1025,7 @@ static int wifi_ap_config( lua_State* L ) #include "lrodefs.h" static const LUA_REG_TYPE wifi_station_map[] = { + { LSTRKEY( "getconfig" ), LFUNCVAL ( wifi_station_getconfig ) }, { LSTRKEY( "config" ), LFUNCVAL ( wifi_station_config ) }, { LSTRKEY( "connect" ), LFUNCVAL ( wifi_station_connect4lua ) }, { LSTRKEY( "disconnect" ), LFUNCVAL ( wifi_station_disconnect4lua ) }, @@ -509,6 +1040,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 +1056,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 } }; @@ -524,6 +1069,9 @@ const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "setmode" ), LFUNCVAL( wifi_setmode ) }, { LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) }, + { LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) }, + { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, + { LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, { LSTRKEY( "stopsmart" ), LFUNCVAL( wifi_exit_smart ) }, { LSTRKEY( "sleeptype" ), LFUNCVAL( wifi_sleeptype ) }, @@ -536,10 +1084,20 @@ const LUA_REG_TYPE wifi_map[] = { LSTRKEY( "SOFTAP" ), LNUMVAL( SOFTAP_MODE ) }, { LSTRKEY( "STATIONAP" ), LNUMVAL( STATIONAP_MODE ) }, + { LSTRKEY( "PHYMODE_B" ), LNUMVAL( PHY_MODE_B ) }, + { LSTRKEY( "PHYMODE_G" ), LNUMVAL( PHY_MODE_G ) }, + { LSTRKEY( "PHYMODE_N" ), LNUMVAL( PHY_MODE_N ) }, + { LSTRKEY( "NONE_SLEEP" ), LNUMVAL( NONE_SLEEP_T ) }, { 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 +1131,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 +1153,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..0a6cd5b1 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. @@ -26,16 +28,25 @@ static void ICACHE_FLASH_ATTR send_ws_1(uint8_t gpio) { i = 6; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio); } -// Lua: ws2812.write(pin, "string") +// Lua: ws2812.writergb(pin, "string") // Byte triples in the string are interpreted as R G B values and sent to the hardware as G R B. -// ws2812.write(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red. -// ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. -// ws2812.write(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white. +// WARNING: this function scrambles the input buffer : +// a = string.char(255,0,128) +// ws212.writergb(3,a) +// =a.byte() +// (0,255,128) + +// ws2812.writergb(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red. +// ws2812.writergb(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. +// ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white. 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); @@ -58,6 +69,37 @@ static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) os_delay_us(1); // Send the buffer: + os_intr_lock(); + for (i = 0; i < length; i++) { + uint8_t mask = 0x80; + while (mask) { + (buffer[i] & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]); + mask >>= 1; + } + } + os_intr_unlock(); + + c_free(buffer); + + return 0; +} + +// Lua: ws2812.write(pin, "string") +// Byte triples in the string are interpreted as G R B values. +// This function does not corrupt your buffer. +// +// ws2812.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red. +// ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. +// ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white. +static int ICACHE_FLASH_ATTR ws2812_writegrb(lua_State* L) { + const uint8_t pin = luaL_checkinteger(L, 1); + size_t length; + const char *buffer = luaL_checklstring(L, 2, &length); + + platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT); + platform_gpio_write(pin, 0); + os_delay_us(10); + os_intr_lock(); const char * const end = buffer + length; while (buffer != end) { @@ -78,6 +120,7 @@ static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) const LUA_REG_TYPE ws2812_map[] = { { LSTRKEY( "writergb" ), LFUNCVAL( ws2812_writergb )}, + { LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )}, { LNILKEY, LNILVAL} }; 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..5f9772f6 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)))) @@ -396,11 +396,25 @@ uint8_t byte_of_aligned_array(const uint8_t *aligned_array, uint32_t index) NODE_DBG("aligned_array is not 4-byte aligned.\n"); return 0; } - uint32_t v = ((uint32_t *)aligned_array)[ index / 4 ]; + volatile uint32_t v = ((uint32_t *)aligned_array)[ index / 4 ]; uint8_t *p = (uint8_t *) (&v); return p[ (index % 4) ]; } +uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index) +{ + if ( (((uint32_t)aligned_array) % 4) != 0 ) + { + NODE_DBG("aligned_array is not 4-byte aligned.\n"); + return 0; + } + volatile uint32_t v = ((uint32_t *)aligned_array)[ index / 2 ]; + uint16_t *p = (uint16_t *) (&v); + return (index % 2 == 0) ? p[ 0 ] : p[ 1 ]; + // return p[ (index % 2) ]; // -- why error??? + // (byte_of_aligned_array((uint8_t *)aligned_array, index * 2 + 1) << 8) | byte_of_aligned_array((uint8_t *)aligned_array, index * 2); +} + // uint8_t flash_rom_get_checksum(void) // { // // SPIFlashInfo spi_flash_info ICACHE_STORE_ATTR = flash_rom_getinfo(); diff --git a/app/platform/flash_api.h b/app/platform/flash_api.h index ff276a83..0063ee1c 100644 --- a/app/platform/flash_api.h +++ b/app/platform/flash_api.h @@ -107,6 +107,7 @@ bool flash_init_data_default(void); bool flash_init_data_blank(void); bool flash_self_destruct(void); uint8_t byte_of_aligned_array(const uint8_t* aligned_array, uint32_t index); +uint16_t word_of_aligned_array(const uint16_t *aligned_array, uint32_t index); // uint8_t flash_rom_get_checksum(void); // uint8_t flash_rom_calc_checksum(void); 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/smart/smart.c b/app/smart/smart.c index b8114836..c362191a 100644 --- a/app/smart/smart.c +++ b/app/smart/smart.c @@ -127,11 +127,19 @@ int smart_check(uint8_t *nibble, uint16_t len, uint8_t *dst, uint8_t *got){ return res; } -void detect(uint8 *buf, uint16 len){ +void detect(uint8 *arg, uint16 len){ uint16_t seq; int16_t seq_delta = 0; uint16_t byte_num = 0, bit_num = 0; int16_t c = 0; + uint8 *buf = NULL; + if( len == 12 ){ + return; + } else if (len >= 64){ + buf = arg + sizeof(struct RxControl); + } else { + return; + } if( ( (buf[0]) & TYPE_SUBTYPE_MASK) != TYPE_SUBTYPE_QOS_DATA){ return; } diff --git a/app/smart/smart.h b/app/smart/smart.h index a702830a..434aecbe 100644 --- a/app/smart/smart.h +++ b/app/smart/smart.h @@ -59,6 +59,40 @@ extern "C" { #define STATION_CHECK_TIME (2*1000) +struct RxControl{ + signed rssi:8;//表示该包的信号强度 + unsigned rate:4; + unsigned is_group:1; + unsigned:1; + unsigned sig_mode:2;//表示该包是否是11n 的包,0 表示非11n,非0 表示11n + unsigned legacy_length:12;//如果不是11n 的包,它表示包的长度 + unsigned damatch0:1; + unsigned damatch1:1; + unsigned bssidmatch0:1; + unsigned bssidmatch1:1; + unsigned MCS:7;//如果是11n 的包,它表示包的调制编码序列,有效值:0-76 + unsigned CWB:1;//如果是11n 的包,它表示是否为HT40 的包 + unsigned HT_length:16;//如果是11n 的包,它表示包的长度 + unsigned Smoothing:1; + unsigned Not_Sounding:1; + unsigned:1; + unsigned Aggregation:1; + unsigned STBC:2; + unsigned FEC_CODING:1;//如果是11n 的包,它表示是否为LDPC 的包 + unsigned SGI:1; + unsigned rxend_state:8; + unsigned ampdu_cnt:8; + unsigned channel:4;//表示该包所在的信道 + unsigned:12; +}; + +struct sniffer_buf{ + struct RxControl rx_ctrl; // 12-bytes + u8 buf[48];//包含ieee80211 包头 + u16 cnt;//包的个数 + u16 len[1];//包的长度 +}; + struct _my_addr_map { uint8 addr[ADDR_LENGTH*3]; uint8_t addr_len; 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/spiffs/spiffs_nucleus.h b/app/spiffs/spiffs_nucleus.h index b4a34bcf..cc414432 100644 --- a/app/spiffs/spiffs_nucleus.h +++ b/app/spiffs/spiffs_nucleus.h @@ -395,13 +395,11 @@ typedef struct __attribute(( packed )) { // common page header spiffs_page_header p_hdr; // alignment - u8_t _align[4 - (sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3)]; + u8_t _align[4 - ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)==0 ? 4 : ((sizeof(spiffs_page_header)+sizeof(spiffs_obj_type)+SPIFFS_OBJ_NAME_LEN)&3)]; // size of object u32_t size; // type of object spiffs_obj_type type; - // alignment2 - u8_t _align2[4 - (sizeof(spiffs_obj_type)&3)==0 ? 4 : (sizeof(spiffs_obj_type)&3)]; // name of object u8_t name[SPIFFS_OBJ_NAME_LEN]; } spiffs_page_object_ix_header; 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 fc426326..11b28721 100644 --- a/examples/fragment.lua +++ b/examples/fragment.lua @@ -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/include/espconn.h b/include/espconn.h index 2af5cf77..690d8dc9 100644 --- a/include/espconn.h +++ b/include/espconn.h @@ -1,392 +1,407 @@ -#ifndef __ESPCONN_H__ -#define __ESPCONN_H__ - -#include "lwip/ip_addr.h" - -typedef sint8 err_t; - -typedef void *espconn_handle; -typedef void (* espconn_connect_callback)(void *arg); -typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); - -/* Definitions for error constants. */ - -#define ESPCONN_OK 0 /* No error, everything OK. */ -#define ESPCONN_MEM -1 /* Out of memory error. */ -#define ESPCONN_TIMEOUT -3 /* Timeout. */ -#define ESPCONN_RTE -4 /* Routing problem. */ -#define ESPCONN_INPROGRESS -5 /* Operation in progress */ - -#define ESPCONN_ABRT -8 /* Connection aborted. */ -#define ESPCONN_RST -9 /* Connection reset. */ -#define ESPCONN_CLSD -10 /* Connection closed. */ -#define ESPCONN_CONN -11 /* Not connected. */ - -#define ESPCONN_ARG -12 /* Illegal argument. */ -#define ESPCONN_ISCONN -15 /* Already connected. */ - -/** Protocol family and type of the espconn */ -enum espconn_type { - ESPCONN_INVALID = 0, - /* ESPCONN_TCP Group */ - ESPCONN_TCP = 0x10, - /* ESPCONN_UDP Group */ - ESPCONN_UDP = 0x20, -}; - -/** Current state of the espconn. Non-TCP espconn are always in state ESPCONN_NONE! */ -enum espconn_state { - ESPCONN_NONE, - ESPCONN_WAIT, - ESPCONN_LISTEN, - ESPCONN_CONNECT, - ESPCONN_WRITE, - ESPCONN_READ, - ESPCONN_CLOSE -}; - -typedef struct _esp_tcp { - int remote_port; - int local_port; - uint8 local_ip[4]; - uint8 remote_ip[4]; - espconn_connect_callback connect_callback; - espconn_reconnect_callback reconnect_callback; - espconn_connect_callback disconnect_callback; -} esp_tcp; - -typedef struct _esp_udp { - int remote_port; - int local_port; - uint8 local_ip[4]; - uint8 remote_ip[4]; -} esp_udp; - -typedef struct _remot_info{ - enum espconn_state state; - int remote_port; - uint8 remote_ip[4]; -}remot_info; - -/** A callback prototype to inform about events for a espconn */ -typedef void (* espconn_recv_callback)(void *arg, char *pdata, unsigned short len); -typedef void (* espconn_sent_callback)(void *arg); - -/** A espconn descriptor */ -struct espconn { - /** type of the espconn (TCP, UDP) */ - enum espconn_type type; - /** current state of the espconn */ - enum espconn_state state; - union { - esp_tcp *tcp; - esp_udp *udp; - } proto; - /** A callback function that is informed about events for this espconn */ - espconn_recv_callback recv_callback; - espconn_sent_callback sent_callback; - uint8 link_cnt; - void *reverse; -}; - -enum espconn_option{ - ESPCONN_REUSEADDR = 1, - ESPCONN_NODELAY, - ESPCONN_END -}; - -/****************************************************************************** - * FunctionName : espconn_connect - * Description : The function given as the connect - * Parameters : espconn -- the espconn used to listen the connection - * Returns : none -*******************************************************************************/ - -sint8 espconn_connect(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_disconnect - * Description : disconnect with host - * Parameters : espconn -- the espconn used to disconnect the connection - * Returns : none -*******************************************************************************/ - -sint8 espconn_disconnect(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_delete - * Description : disconnect with host - * Parameters : espconn -- the espconn used to disconnect the connection - * Returns : none -*******************************************************************************/ - -sint8 espconn_delete(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_accept - * Description : The function given as the listen - * Parameters : espconn -- the espconn used to listen the connection - * Returns : none -*******************************************************************************/ - -sint8 espconn_accept(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_create - * Description : sent data for client or server - * Parameters : espconn -- espconn to the data transmission - * Returns : result -*******************************************************************************/ - -sint8 espconn_create(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_tcp_get_max_con - * Description : get the number of simulatenously active TCP connections - * Parameters : none - * Returns : none -*******************************************************************************/ - -uint8 espconn_tcp_get_max_con(void); - -/****************************************************************************** - * FunctionName : espconn_tcp_set_max_con - * Description : set the number of simulatenously active TCP connections - * Parameters : num -- total number - * Returns : none -*******************************************************************************/ - -sint8 espconn_tcp_set_max_con(uint8 num); - -/****************************************************************************** - * FunctionName : espconn_tcp_get_max_con_allow - * Description : get the count of simulatenously active connections on the server - * Parameters : espconn -- espconn to get the count - * Returns : result -*******************************************************************************/ - -sint8 espconn_tcp_get_max_con_allow(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_tcp_set_max_con_allow - * Description : set the count of simulatenously active connections on the server - * Parameters : espconn -- espconn to set the count - * num -- support the connection number - * Returns : result -*******************************************************************************/ - -sint8 espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num); - -/****************************************************************************** - * FunctionName : espconn_regist_time - * Description : used to specify the time that should be called when don't recv data - * Parameters : espconn -- the espconn used to the connection - * interval -- the timer when don't recv data - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag); - -/****************************************************************************** - * FunctionName : espconn_get_connection_info - * Description : used to specify the function that should be called when disconnect - * Parameters : espconn -- espconn to set the err callback - * discon_cb -- err callback function to call when err - * Returns : none -*******************************************************************************/ - -sint8 espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags); - -/****************************************************************************** - * FunctionName : espconn_regist_sentcb - * Description : Used to specify the function that should be called when data - * has been successfully delivered to the remote host. - * Parameters : struct espconn *espconn -- espconn to set the sent callback - * espconn_sent_callback sent_cb -- sent callback function to - * call for this espconn when data is successfully sent - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb); - -/****************************************************************************** - * FunctionName : espconn_sent - * Description : sent data for client or server - * Parameters : espconn -- espconn to set for client or server - * psent -- data to send - * length -- length of data to send - * Returns : none -*******************************************************************************/ - -sint8 espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length); - -/****************************************************************************** - * FunctionName : espconn_regist_connectcb - * Description : used to specify the function that should be called when - * connects to host. - * Parameters : espconn -- espconn to set the connect callback - * connect_cb -- connected callback function to call when connected - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb); - -/****************************************************************************** - * FunctionName : espconn_regist_recvcb - * Description : used to specify the function that should be called when recv - * data from host. - * Parameters : espconn -- espconn to set the recv callback - * recv_cb -- recv callback function to call when recv data - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb); - -/****************************************************************************** - * FunctionName : espconn_regist_reconcb - * Description : used to specify the function that should be called when connection - * because of err disconnect. - * Parameters : espconn -- espconn to set the err callback - * recon_cb -- err callback function to call when err - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb); - -/****************************************************************************** - * FunctionName : espconn_regist_disconcb - * Description : used to specify the function that should be called when disconnect - * Parameters : espconn -- espconn to set the err callback - * discon_cb -- err callback function to call when err - * Returns : none -*******************************************************************************/ - -sint8 espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb); - -/****************************************************************************** - * FunctionName : espconn_port - * Description : access port value for client so that we don't end up bouncing - * all connections at the same time . - * Parameters : none - * Returns : access port value -*******************************************************************************/ - -uint32 espconn_port(void); - -/****************************************************************************** - * FunctionName : espconn_set_opt - * Description : access port value for client so that we don't end up bouncing - * all connections at the same time . - * Parameters : none - * Returns : access port value -*******************************************************************************/ - -sint8 espconn_set_opt(struct espconn *espconn, uint8 opt); - -/****************************************************************************** - * TypedefName : dns_found_callback - * Description : Callback which is invoked when a hostname is found. - * Parameters : name -- pointer to the name that was looked up. - * ipaddr -- pointer to an ip_addr_t containing the IP address of - * the hostname, or NULL if the name could not be found (or on any - * other error). - * callback_arg -- a user-specified callback argument passed to - * dns_gethostbyname -*******************************************************************************/ - -typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); - -/****************************************************************************** - * FunctionName : espconn_gethostbyname - * Description : Resolve a hostname (string) into an IP address. - * Parameters : pespconn -- espconn to resolve a hostname - * hostname -- the hostname that is to be queried - * addr -- pointer to a ip_addr_t where to store the address if - * it is already cached in the dns_table (only valid if ESPCONN_OK - * is returned!) - * found -- a callback function to be called on success, failure - * or timeout (only if ERR_INPROGRESS is returned!) - * Returns : err_t return code - * - ESPCONN_OK if hostname is a valid IP address string or the host - * name is already in the local names table. - * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server - * for resolution if no errors are present. - * - ESPCONN_ARG: dns client not initialized or invalid hostname -*******************************************************************************/ - -err_t espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t *addr, dns_found_callback found); - -/****************************************************************************** - * FunctionName : espconn_encry_connect - * Description : The function given as connection - * Parameters : espconn -- the espconn used to connect with the host - * Returns : none -*******************************************************************************/ - -sint8 espconn_secure_connect(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_encry_disconnect - * Description : The function given as the disconnection - * Parameters : espconn -- the espconn used to disconnect with the host - * Returns : none -*******************************************************************************/ - -sint8 espconn_secure_disconnect(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_encry_sent - * Description : sent data for client or server - * Parameters : espconn -- espconn to set for client or server - * psent -- data to send - * length -- length of data to send - * Returns : none -*******************************************************************************/ - -sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length); - -/****************************************************************************** - * FunctionName : espconn_secure_accept - * Description : The function given as the listen - * Parameters : espconn -- the espconn used to listen the connection - * Returns : none -*******************************************************************************/ - -sint8 espconn_secure_accept(struct espconn *espconn); - -/****************************************************************************** - * FunctionName : espconn_igmp_join - * Description : join a multicast group - * Parameters : host_ip -- the ip address of udp server - * multicast_ip -- multicast ip given by user - * Returns : none -*******************************************************************************/ -sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); - -/****************************************************************************** - * FunctionName : espconn_igmp_leave - * Description : leave a multicast group - * Parameters : host_ip -- the ip address of udp server - * multicast_ip -- multicast ip given by user - * Returns : none -*******************************************************************************/ -sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip); - -/****************************************************************************** - * FunctionName : espconn_recv_hold - * Description : hold tcp receive - * Parameters : espconn -- espconn to hold - * Returns : none -*******************************************************************************/ -sint8 espconn_recv_hold(struct espconn *pespconn); - -/****************************************************************************** - * FunctionName : espconn_recv_unhold - * Description : unhold tcp receive - * Parameters : espconn -- espconn to unhold - * Returns : none -*******************************************************************************/ -sint8 espconn_recv_unhold(struct espconn *pespconn); - -#endif - +#ifndef __ESPCONN_H__ +#define __ESPCONN_H__ + +#include "lwip/ip_addr.h" + +typedef sint8 err_t; + +typedef void *espconn_handle; +typedef void (* espconn_connect_callback)(void *arg); +typedef void (* espconn_reconnect_callback)(void *arg, sint8 err); + +/* Definitions for error constants. */ + +#define ESPCONN_OK 0 /* No error, everything OK. */ +#define ESPCONN_MEM -1 /* Out of memory error. */ +#define ESPCONN_TIMEOUT -3 /* Timeout. */ +#define ESPCONN_RTE -4 /* Routing problem. */ +#define ESPCONN_INPROGRESS -5 /* Operation in progress */ + +#define ESPCONN_ABRT -8 /* Connection aborted. */ +#define ESPCONN_RST -9 /* Connection reset. */ +#define ESPCONN_CLSD -10 /* Connection closed. */ +#define ESPCONN_CONN -11 /* Not connected. */ + +#define ESPCONN_ARG -12 /* Illegal argument. */ +#define ESPCONN_ISCONN -15 /* Already connected. */ + +/** Protocol family and type of the espconn */ +enum espconn_type { + ESPCONN_INVALID = 0, + /* ESPCONN_TCP Group */ + ESPCONN_TCP = 0x10, + /* ESPCONN_UDP Group */ + ESPCONN_UDP = 0x20, +}; + +/** Current state of the espconn. Non-TCP espconn are always in state ESPCONN_NONE! */ +enum espconn_state { + ESPCONN_NONE, + ESPCONN_WAIT, + ESPCONN_LISTEN, + ESPCONN_CONNECT, + ESPCONN_WRITE, + ESPCONN_READ, + ESPCONN_CLOSE +}; + +typedef struct _esp_tcp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; + espconn_connect_callback connect_callback; + espconn_reconnect_callback reconnect_callback; + espconn_connect_callback disconnect_callback; + espconn_connect_callback write_finish_fn; +} esp_tcp; + +typedef struct _esp_udp { + int remote_port; + int local_port; + uint8 local_ip[4]; + uint8 remote_ip[4]; +} esp_udp; + +typedef struct _remot_info{ + enum espconn_state state; + int remote_port; + uint8 remote_ip[4]; +}remot_info; + +/** A callback prototype to inform about events for a espconn */ +typedef void (* espconn_recv_callback)(void *arg, char *pdata, unsigned short len); +typedef void (* espconn_sent_callback)(void *arg); + +/** A espconn descriptor */ +struct espconn { + /** type of the espconn (TCP, UDP) */ + enum espconn_type type; + /** current state of the espconn */ + enum espconn_state state; + union { + esp_tcp *tcp; + esp_udp *udp; + } proto; + /** A callback function that is informed about events for this espconn */ + espconn_recv_callback recv_callback; + espconn_sent_callback sent_callback; + uint8 link_cnt; + void *reverse; +}; + +enum espconn_option{ + ESPCONN_START = 0x00, + ESPCONN_REUSEADDR = 0x01, + ESPCONN_NODELAY = 0x02, + ESPCONN_COPY = 0x04, + ESPCONN_END +}; + +/****************************************************************************** + * FunctionName : espconn_connect + * Description : The function given as the connect + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +sint8 espconn_connect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_disconnect + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +sint8 espconn_disconnect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_delete + * Description : disconnect with host + * Parameters : espconn -- the espconn used to disconnect the connection + * Returns : none +*******************************************************************************/ + +sint8 espconn_delete(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +sint8 espconn_accept(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_create + * Description : sent data for client or server + * Parameters : espconn -- espconn to the data transmission + * Returns : result +*******************************************************************************/ + +sint8 espconn_create(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con + * Description : get the number of simulatenously active TCP connections + * Parameters : none + * Returns : none +*******************************************************************************/ + +uint8 espconn_tcp_get_max_con(void); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con + * Description : set the number of simulatenously active TCP connections + * Parameters : num -- total number + * Returns : none +*******************************************************************************/ + +sint8 espconn_tcp_set_max_con(uint8 num); + +/****************************************************************************** + * FunctionName : espconn_tcp_get_max_con_allow + * Description : get the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to get the count + * Returns : result +*******************************************************************************/ + +sint8 espconn_tcp_get_max_con_allow(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_tcp_set_max_con_allow + * Description : set the count of simulatenously active connections on the server + * Parameters : espconn -- espconn to set the count + * num -- support the connection number + * Returns : result +*******************************************************************************/ + +sint8 espconn_tcp_set_max_con_allow(struct espconn *espconn, uint8 num); + +/****************************************************************************** + * FunctionName : espconn_regist_time + * Description : used to specify the time that should be called when don't recv data + * Parameters : espconn -- the espconn used to the connection + * interval -- the timer when don't recv data + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_time(struct espconn *espconn, uint32 interval, uint8 type_flag); + +/****************************************************************************** + * FunctionName : espconn_get_connection_info + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +sint8 espconn_get_connection_info(struct espconn *pespconn, remot_info **pcon_info, uint8 typeflags); + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : struct espconn *espconn -- espconn to set the sent callback + * espconn_sent_callback sent_cb -- sent callback function to + * call for this espconn when data is successfully sent + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callback sent_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_sentcb + * Description : Used to specify the function that should be called when data + * has been successfully delivered to the remote host. + * Parameters : espconn -- espconn to set the sent callback + * sent_cb -- sent callback function to call for this espconn + * when data is successfully sent + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn); + +/****************************************************************************** + * FunctionName : espconn_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_sent(struct espconn *espconn, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_regist_connectcb + * Description : used to specify the function that should be called when + * connects to host. + * Parameters : espconn -- espconn to set the connect callback + * connect_cb -- connected callback function to call when connected + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_connectcb(struct espconn *espconn, espconn_connect_callback connect_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_recvcb + * Description : used to specify the function that should be called when recv + * data from host. + * Parameters : espconn -- espconn to set the recv callback + * recv_cb -- recv callback function to call when recv data + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_recvcb(struct espconn *espconn, espconn_recv_callback recv_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_reconcb + * Description : used to specify the function that should be called when connection + * because of err disconnect. + * Parameters : espconn -- espconn to set the err callback + * recon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_reconcb(struct espconn *espconn, espconn_reconnect_callback recon_cb); + +/****************************************************************************** + * FunctionName : espconn_regist_disconcb + * Description : used to specify the function that should be called when disconnect + * Parameters : espconn -- espconn to set the err callback + * discon_cb -- err callback function to call when err + * Returns : none +*******************************************************************************/ + +sint8 espconn_regist_disconcb(struct espconn *espconn, espconn_connect_callback discon_cb); + +/****************************************************************************** + * FunctionName : espconn_port + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ + +uint32 espconn_port(void); + +/****************************************************************************** + * FunctionName : espconn_set_opt + * Description : access port value for client so that we don't end up bouncing + * all connections at the same time . + * Parameters : none + * Returns : access port value +*******************************************************************************/ + +sint8 espconn_set_opt(struct espconn *espconn, uint8 opt); + +/****************************************************************************** + * TypedefName : dns_found_callback + * Description : Callback which is invoked when a hostname is found. + * Parameters : name -- pointer to the name that was looked up. + * ipaddr -- pointer to an ip_addr_t containing the IP address of + * the hostname, or NULL if the name could not be found (or on any + * other error). + * callback_arg -- a user-specified callback argument passed to + * dns_gethostbyname +*******************************************************************************/ + +typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg); + +/****************************************************************************** + * FunctionName : espconn_gethostbyname + * Description : Resolve a hostname (string) into an IP address. + * Parameters : pespconn -- espconn to resolve a hostname + * hostname -- the hostname that is to be queried + * addr -- pointer to a ip_addr_t where to store the address if + * it is already cached in the dns_table (only valid if ESPCONN_OK + * is returned!) + * found -- a callback function to be called on success, failure + * or timeout (only if ERR_INPROGRESS is returned!) + * Returns : err_t return code + * - ESPCONN_OK if hostname is a valid IP address string or the host + * name is already in the local names table. + * - ESPCONN_INPROGRESS enqueue a request to be sent to the DNS server + * for resolution if no errors are present. + * - ESPCONN_ARG: dns client not initialized or invalid hostname +*******************************************************************************/ + +err_t espconn_gethostbyname(struct espconn *pespconn, const char *hostname, ip_addr_t *addr, dns_found_callback found); + +/****************************************************************************** + * FunctionName : espconn_encry_connect + * Description : The function given as connection + * Parameters : espconn -- the espconn used to connect with the host + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_connect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_encry_disconnect + * Description : The function given as the disconnection + * Parameters : espconn -- the espconn used to disconnect with the host + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_disconnect(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_encry_sent + * Description : sent data for client or server + * Parameters : espconn -- espconn to set for client or server + * psent -- data to send + * length -- length of data to send + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length); + +/****************************************************************************** + * FunctionName : espconn_secure_accept + * Description : The function given as the listen + * Parameters : espconn -- the espconn used to listen the connection + * Returns : none +*******************************************************************************/ + +sint8 espconn_secure_accept(struct espconn *espconn); + +/****************************************************************************** + * FunctionName : espconn_igmp_join + * Description : join a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +/****************************************************************************** + * FunctionName : espconn_igmp_leave + * Description : leave a multicast group + * Parameters : host_ip -- the ip address of udp server + * multicast_ip -- multicast ip given by user + * Returns : none +*******************************************************************************/ +sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip); + +/****************************************************************************** + * FunctionName : espconn_recv_hold + * Description : hold tcp receive + * Parameters : espconn -- espconn to hold + * Returns : none +*******************************************************************************/ +sint8 espconn_recv_hold(struct espconn *pespconn); + +/****************************************************************************** + * FunctionName : espconn_recv_unhold + * Description : unhold tcp receive + * Parameters : espconn -- espconn to unhold + * Returns : none +*******************************************************************************/ +sint8 espconn_recv_unhold(struct espconn *pespconn); + +#endif + diff --git a/include/user_interface.h b/include/user_interface.h index 1f5a9352..4212ba94 100644 --- a/include/user_interface.h +++ b/include/user_interface.h @@ -89,6 +89,7 @@ bool system_rtc_mem_write(uint8 des_addr, const void *src_addr, uint16 save_size void system_uart_swap(void); uint16 system_adc_read(void); +uint16 system_get_vdd33(void); const char *system_get_sdk_version(void); @@ -97,6 +98,7 @@ const char *system_get_sdk_version(void); #define SOFTAP_MODE 0x02 #define STATIONAP_MODE 0x03 + typedef enum _auth_mode { AUTH_OPEN = 0, AUTH_WEP, @@ -240,6 +242,10 @@ typedef void (* wifi_promiscuous_cb_t)(uint8 *buf, uint16 len); void wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); +#define PHY_MODE_B 0x01 +#define PHY_MODE_G 0x02 +#define PHY_MODE_N 0x03 + enum phy_mode { PHY_MODE_11B = 1, PHY_MODE_11G = 2, diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld index 141cab6d..a36cb31e 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 @@ -71,7 +71,6 @@ SECTIONS _irom0_text_start = ABSOLUTE(.); *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) *(.literal.* .text.*) - *(.rodata2.text) /* put font and progmem data into irom0 */ *(.u8g_progmem.*) diff --git a/lib/libat.a b/lib/libat.a index e1db9fa8..81d93ea8 100644 Binary files a/lib/libat.a and b/lib/libat.a differ diff --git a/lib/libjson.a b/lib/libjson.a index f32c329d..efb36fe4 100644 Binary files a/lib/libjson.a and b/lib/libjson.a differ diff --git a/lib/liblwip.a b/lib/liblwip.a index 6ef795bb..0f90932b 100644 Binary files a/lib/liblwip.a and b/lib/liblwip.a differ diff --git a/lib/libmain.a b/lib/libmain.a index 5d417167..aa5009af 100644 Binary files a/lib/libmain.a and b/lib/libmain.a differ diff --git a/lib/libnet80211.a b/lib/libnet80211.a index 5ae2da5c..94e738cf 100644 Binary files a/lib/libnet80211.a and b/lib/libnet80211.a differ diff --git a/lib/libphy.a b/lib/libphy.a index 1598f206..5702e4ef 100644 Binary files a/lib/libphy.a and b/lib/libphy.a differ diff --git a/lib/libpp.a b/lib/libpp.a index 17466205..daa4054e 100644 Binary files a/lib/libpp.a and b/lib/libpp.a differ diff --git a/lib/libsmartconfig.a b/lib/libsmartconfig.a index f402b5f3..9a8db685 100644 Binary files a/lib/libsmartconfig.a and b/lib/libsmartconfig.a differ diff --git a/lib/libssl.a b/lib/libssl.a index 36b78fe2..d360d36b 100644 Binary files a/lib/libssl.a and b/lib/libssl.a differ diff --git a/lib/libupgrade.a b/lib/libupgrade.a index 051d683b..fd9dbb78 100644 Binary files a/lib/libupgrade.a and b/lib/libupgrade.a differ diff --git a/lib/libwpa.a b/lib/libwpa.a index abd611b4..98ceb381 100644 Binary files a/lib/libwpa.a and b/lib/libwpa.a differ diff --git a/lua_examples/adc_rgb.lua b/lua_examples/adc_rgb.lua new file mode 100644 index 00000000..ff8b70e6 --- /dev/null +++ b/lua_examples/adc_rgb.lua @@ -0,0 +1,43 @@ +-- +-- Light sensor on ADC(0), RGB LED connected to gpio12(6) Green, gpio13(7) Blue & gpio15(8) Red. +-- This works out of the box on the typical ESP8266 evaluation boards with Battery Holder +-- +-- It uses the input from the sensor to drive a "rainbow" effect on the RGB LED +-- Includes a very "pseudoSin" function +-- + +function led(r,Sg,b) + pwm.setduty(8,r) + pwm.setduty(6,g) + pwm.setduty(7,b) +end + +-- this is perhaps the lightest weight sin function in existance +-- Given an integer from 0..128, 0..512 appximating 256 + 256 * sin(idx*Pi/256) +-- This is first order square approximation of sin, it's accurate around 0 and any multiple of 128 (Pi/2), +-- 92% accurate at 64 (Pi/4). +function pseudoSin (idx) + idx = idx % 128 + lookUp = 32 - idx % 64 + val = 256 - (lookUp * lookUp) / 4 + if (idx > 64) then + val = - val; + end + return 256+val +end + +pwm.setup(6,500,512) +pwm.setup(7,500,512) +pwm.setup(8,500,512) +pwm.start(6) +pwm.start(7) +pwm.start(8) + +tmr.alarm(1,20,1,function() + idx = 3 * adc.read(0) / 2 + r = pseudoSin(idx) + g = pseudoSin(idx + 43) + b = pseudoSin(idx + 85) + led(r,g,b) + idx = (idx + 1) % 128 + end) diff --git a/lua_examples/http_server.lua b/lua_examples/http_server.lua new file mode 100644 index 00000000..c9ad275a --- /dev/null +++ b/lua_examples/http_server.lua @@ -0,0 +1,121 @@ +-- +-- Simple NodeMCU web server (done is a not so nodeie fashion :-) +-- +-- Highly modified by Bruce Meacham, based on work by Scott Beasley 2015 +-- Open and free to change and use. Enjoy. [Beasley/Meacham 2015] +-- +-- Meacham Update: I streamlined/improved the parsing to focus on simple HTTP GET request and their simple parameters +-- Also added the code to drive a servo/light. Comment out as you see fit. +-- +-- Usage: +-- Change SSID and SSID_PASSPHRASE for your wifi network +-- Download to NodeMCU +-- node.compile("http_server.lua") +-- dofile("http_server.lc") +-- When the server is esablished it will output the IP address. +-- http://{ip address}/?s0=1200&light=1 +-- s0 is the servo position (actually the PWM hertz), 500 - 2000 are all good values +-- light chanel high(1)/low(0), some evaluation boards have LEDs pre-wired in a "pulled high" confguration, so '0' ground the emitter and turns it on backwards. +-- +-- Add to init.lua if you want it to autoboot. +-- + +-- Your Wifi connection data +local SSID = "YOUR WIFI SSID" +local SSID_PASSWORD = "YOUR SSID PASSPHRASE" + +-- General setup +local pinLight = 2 -- this is GPIO4 +gpio.mode(pinLight,gpio.OUTPUT) +gpio.write(pinLight,gpio.HIGH) + +servo = {} +servo.pin = 4 --this is GPIO2 +servo.value = 1500 +servo.id = "servo" +gpio.mode(servo.pin, gpio.OUTPUT) +gpio.write(servo.pin, gpio.LOW) + +-- This alarm drives the servo +tmr.alarm(0,10,1,function() -- 50Hz + if servo.value then -- generate pulse + gpio.write(servo.pin, gpio.HIGH) + tmr.delay(servo.value) + gpio.write(servo.pin, gpio.LOW) + end +end) + +local function connect (conn, data) + local query_data + + conn:on ("receive", + function (cn, req_data) + params = get_http_req (req_data) + cn:send("HTTP/1.1 200/OK\r\nServer: NodeLuau\r\nContent-Type: text/html\r\n\r\n") + cn:send ("

ESP8266 Servo & Light Server

\r\n") + if (params["light"] ~= nil) then + if ("0" == params["light"]) then + gpio.write(pinLight, gpio.LOW) + else + gpio.write(pinLight, gpio.HIGH) + end + end + + if (params["s0"] ~= nil) then + servo.value = tonumber(params["s0"]); + end + + -- Close the connection for the request + cn:close ( ) + end) +end + +-- Build and return a table of the http request data +function get_http_req (instr) + local t = {} + local str = string.sub(instr, 0, 200) + local v = string.gsub(split(str, ' ')[2], '+', ' ') + parts = split(v, '?') + local params = {} + if (table.maxn(parts) > 1) then + for idx,part in ipairs(split(parts[2], '&')) do + parmPart = split(part, '=') + params[parmPart[1]] = parmPart[2] + end + end + return params +end + +-- Source: http://lua-users.org/wiki/MakingLuaLikePhp +-- Credit: http://richard.warburton.it/ +function split(str, splitOn) + if (splitOn=='') then return false end + local pos,arr = 0,{} + for st,sp in function() return string.find(str,splitOn,pos,true) end do + table.insert(arr,string.sub(str,pos,st-1)) + pos = sp + 1 + end + table.insert(arr,string.sub(str,pos)) + return arr +end + +-- Configure the ESP as a station (client) +wifi.setmode (wifi.STATION) +wifi.sta.config (SSID, SSID_PASSWORD) +wifi.sta.autoconnect (1) + +-- Hang out until we get a wifi connection before the httpd server is started. +tmr.alarm (1, 800, 1, function ( ) + if wifi.sta.getip ( ) == nil then + print ("Waiting for Wifi connection") + else + tmr.stop (1) + print ("Config done, IP is " .. wifi.sta.getip ( )) + end +end) + +-- Create the httpd server +svr = net.createServer (net.TCP, 30) + +-- Server listening on port 80, call connect function if a request is received +svr:listen (80, connect) 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_examples/u8glib/u8g_bitmaps.lua b/lua_examples/u8glib/u8g_bitmaps.lua index 1b388e41..4ac8a60f 100644 --- a/lua_examples/u8glib/u8g_bitmaps.lua +++ b/lua_examples/u8glib/u8g_bitmaps.lua @@ -2,9 +2,9 @@ -- setup I2c and connect display function init_i2c_display() -- SDA and SCL can be assigned freely to available GPIOs - sda = 5 -- GPIO14 - scl = 6 -- GPIO12 - sla = 0x3c + local sda = 5 -- GPIO14 + local scl = 6 -- GPIO12 + local sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end @@ -15,9 +15,9 @@ function init_spi_display() -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) -- CS, D/C, and RES can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 - res = 0 -- GPIO16 + local cs = 8 -- GPIO15, pull-down 10k to GND + local dc = 4 -- GPIO2 + local res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) disp = u8g.ssd1306_128x64_spi(cs, dc, res) diff --git a/lua_examples/u8glib/u8g_graphics_test.lua b/lua_examples/u8glib/u8g_graphics_test.lua index 27c7a346..1985b461 100644 --- a/lua_examples/u8glib/u8g_graphics_test.lua +++ b/lua_examples/u8glib/u8g_graphics_test.lua @@ -2,9 +2,9 @@ -- setup I2c and connect display function init_i2c_display() -- SDA and SCL can be assigned freely to available GPIOs - sda = 5 -- GPIO14 - scl = 6 -- GPIO12 - sla = 0x3c + local sda = 5 -- GPIO14 + local scl = 6 -- GPIO12 + local sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end @@ -15,9 +15,9 @@ function init_spi_display() -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) -- CS, D/C, and RES can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 - res = 0 -- GPIO16 + local cs = 8 -- GPIO15, pull-down 10k to GND + local dc = 4 -- GPIO2 + local res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) disp = u8g.ssd1306_128x64_spi(cs, dc, res) @@ -91,17 +91,6 @@ function ascii_1() end end -function ascii_2() - local x, y, s - disp:drawStr(0, 0, "ASCII page 2") - for y = 0, 5, 1 do - for x = 0, 15, 1 do - s = y*16 + x + 160 - disp:drawStr(x*7, y*10+10, string.char(s)) - end - end -end - function extra_page(a) disp:drawStr(0, 12, "setScale2x2") disp:setScale2x2() @@ -131,8 +120,6 @@ function draw(draw_state) elseif (component == 6) then ascii_1() elseif (component == 7) then - ascii_2() - elseif (component == 8) then extra_page(bit.band(draw_state, 7)) end end diff --git a/lua_examples/u8glib/u8g_rotation.lua b/lua_examples/u8glib/u8g_rotation.lua index 6d81c779..81d81e6f 100644 --- a/lua_examples/u8glib/u8g_rotation.lua +++ b/lua_examples/u8glib/u8g_rotation.lua @@ -2,9 +2,9 @@ -- setup I2c and connect display function init_i2c_display() -- SDA and SCL can be assigned freely to available GPIOs - sda = 5 -- GPIO14 - scl = 6 -- GPIO12 - sla = 0x3c + local sda = 5 -- GPIO14 + local scl = 6 -- GPIO12 + local sla = 0x3c i2c.setup(0, sda, scl, i2c.SLOW) disp = u8g.ssd1306_128x64_i2c(sla) end @@ -15,9 +15,9 @@ function init_spi_display() -- Hardware SPI MOSI = GPIO13 -- Hardware SPI MISO = GPIO12 (not used) -- CS, D/C, and RES can be assigned freely to available GPIOs - cs = 8 -- GPIO15, pull-down 10k to GND - dc = 4 -- GPIO2 - res = 0 -- GPIO16 + local cs = 8 -- GPIO15, pull-down 10k to GND + local dc = 4 -- GPIO2 + local res = 0 -- GPIO16 spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 0) disp = u8g.ssd1306_128x64_spi(cs, dc, res) diff --git a/lua_modules/dht22/README.md b/lua_modules/dht_lib/README.md similarity index 82% rename from lua_modules/dht22/README.md rename to lua_modules/dht_lib/README.md index ab7b30f9..7b36f76c 100644 --- a/lua_modules/dht22/README.md +++ b/lua_modules/dht_lib/README.md @@ -1,6 +1,8 @@ # 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...] @@ -10,14 +12,13 @@ PIN = 4 -- data pin, GPIO2 DHT= require("dht_lib") ---dht.read11(PIN) -DHT.read22(PIN) +DHT.read(PIN) t = DHT.getTemperature() h = DHT.getHumidity() if h == nil then - print("Error reading from DHT11/22") + print("Error reading from DHTxx") else -- temperature in degrees Celsius and Farenheit @@ -41,8 +42,7 @@ PIN = 4 -- data pin, GPIO2 DHT= require("dht_lib") ---dht.read11(PIN) -DHT.read22(PIN) +DHT.read(PIN) t = DHT.getTemperature() h = DHT.getHumidity() @@ -52,11 +52,12 @@ if h == nil then else -- temperature in degrees Celsius and Farenheit -- floating point and integer version: - print("Temperature: "..t.." deg C") + + print("Temperature: "..(t/10).." deg C") print("Temperature: "..(9 * t / 50 + 32).." deg F") -- humidity - print("Humidity: "..h.."%") + print("Humidity: "..(h/10).."%") end -- release module @@ -64,12 +65,10 @@ DHT = nil package.loaded["dht_lib"]=nil ``` ## Functions -### read11 -read11(pin) -Read humidity and temperature from DHT11. -###read22 -read22(pin) -Read humidity and temperature from DHT22/21. + +###read +read(pin) +Read humidity and temperature from DHTxx(11,21,22...). **Parameters:** * pin - ESP8266 pin connect to data pin @@ -88,4 +87,3 @@ Returns the temperature of the last reading. **Returns:** * last temperature reading in(dht22) 0.1ºC (dht11)1ºC * - diff --git a/lua_modules/dht22/dht_lib.lua b/lua_modules/dht_lib/dht_lib.lua similarity index 56% rename from lua_modules/dht22/dht_lib.lua rename to lua_modules/dht_lib/dht_lib.lua index 641f2e17..97823455 100644 --- a/lua_modules/dht22/dht_lib.lua +++ b/lua_modules/dht_lib/dht_lib.lua @@ -8,10 +8,13 @@ -- *************************************************************************** --Support list: ---DHT11 Tested ->read11 ---DHT21 Not Tested->read22 ---DHT22 Tested->read22 +--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 = {} @@ -37,8 +40,6 @@ local function read(pin) bitStream[j] = 0 end - - -- Step 1: send out start signal to DHT22 gpio.mode(pin, gpio.OUTPUT) gpio.write(pin, gpio.HIGH) @@ -57,7 +58,7 @@ local function read(pin) 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 @@ -69,68 +70,79 @@ local function read(pin) while (gpio_read(pin) == 0) do end end end ----------------------------Convert the bitStream into Number through DHT11 Ways-------------------------- -function M.read11(pin) ---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 + +---------------------------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 checksum = 0 - local checksumTest - --DHT data acquired, process. + + 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 - humidity = humidity + 2 ^ (8 - i) - end - end - for i = 1, 8, 1 do -- Byte[2] - if (bitStream[i + 16] > 3) then - temperature = temperature + 2 ^ (8 - i) - end - end - for i = 1, 8, 1 do --Byte[4] - if (bitStream[i + 32] > 3) then - checksum = checksum + 2 ^ (8 - i) + byte_0 = byte_0 + 2 ^ (8 - i) end end - if(checksum ~= humidity+temperature) then - humidity = nil - temperature = nil - end - -end ----------------------------Convert the bitStream into Number through DHT22 Ways-------------------------- -function M.read22( pin ) ---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 - read(pin) - local checksum = 0 - local checksumTest - --DHT data acquired, process. - for i = 1, 16, 1 do - if (bitStream[i] > 3) then - humidity = humidity + 2 ^ (16 - i) + 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, 16, 1 do - if (bitStream[i + 16] > 3) then - temperature = temperature + 2 ^ (16 - i) + + 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 - if (bitStream[i + 32] > 3) then - checksum = checksum + 2 ^ (8 - i) + + 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) @@ -143,9 +155,18 @@ function M.read22( pin ) if (checksumTest - checksum >= 1) or (checksum - checksumTest >= 1) then humidity = nil end -end ----------------------------Check out the data-------------------------- + 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 @@ -153,5 +174,5 @@ end function M.getHumidity() return humidity end - +-------------Return Index------------------------------------ return M diff --git a/lua_modules/lm92/README.md b/lua_modules/lm92/README.md new file mode 100644 index 00000000..261e1ebe --- /dev/null +++ b/lua_modules/lm92/README.md @@ -0,0 +1,98 @@ +# LM92 module +This module adds basic support for the LM92 +-0.33C 12bit+sign temperature sensor. More details in the [datasheet](http://www.ti.com/lit/ds/symlink/lm92.pdf). +Works: +- getting the temperature +- entering the chip's to shutdown mode (350uA -> 5uA power consumption) +- waking up the chip from shutdown + +##Require +```lua +LM92 = require("lm92") +``` +## Release +```lua +LM92 = nil +package.loaded["lm92"]=nil +``` + +##init() +####Description +Setting the i2c pins and address for lm92. + +####Syntax +init(sda, scl, address) + +####Parameters +sda: 1~12, IO index.
+scl: 1~12, IO index.
+address: 0x48~0x4b, i2c address (depends on tha A0~A1 pins) +####Returns +nil + +####Example +```lua +LM92 = require("lm92") +gpio0 = 3 +gpio2 = 4 +sda = gpio0 +scl = gpio2 +addr = 0x48 +LM92.init(sda, scl,addr) +``` +##getTemperature() +####Description +Returns the temperature register's content. + +####Syntax +getTemperature() + +####Parameters +- + +####Returns +Temperature in degree Celsius. + +####Example +```lua +t = LM92.getTemperature() +print("Got temperature: "..t.." C") +``` + +##wakeup() +####Description +Makes the chip exit the low power shutdown mode. + +####Syntax +wakeup() + +####Parameters +- + +####Returns +- + +####Example +```lua +LM92.wakeup() +tmr.delay( 1 * 1000 * 1000 ) +``` + +##shutdown() +####Description +Makes the chip enter the low power shutdown mode. + +####Syntax +shutdown() + +####Parameters +- + +####Returns +- + +####Example +```lua +LM92.shutdown() +``` +#### TODO: +- add full support of the features, including interrupt and critical alert support diff --git a/lua_modules/lm92/lm92.lua b/lua_modules/lm92/lm92.lua new file mode 100644 index 00000000..fcec7098 --- /dev/null +++ b/lua_modules/lm92/lm92.lua @@ -0,0 +1,94 @@ +-- ****************************************************** +-- LM92 module for ESP8266 with nodeMCU +-- +-- Written by Levente Tamas +-- +-- GNU LGPL, see https://www.gnu.org/copyleft/lesser.html +-- ****************************************************** + +-- Module Bits +local moduleName = ... +local M = {} +_G[moduleName] = M + +-- Default ID +local id = 0 + +-- Local vars +local address = 0 + +-- read regs for len number of bytes +-- return table with data +local function read_reg(reg_addr, len) + local ret={} + local c + local x + i2c.start(id) + i2c.address(id, address ,i2c.TRANSMITTER) + i2c.write(id,reg_addr) + i2c.stop(id) + i2c.start(id) + i2c.address(id, address,i2c.RECEIVER) + c=i2c.read(id,len) + for x=1,len,1 do + tc=string.byte(c,x) + table.insert(ret,tc) + end + i2c.stop(id) + return ret +end + +--write reg with data table +local function write_reg(reg_addr, data) + i2c.start(id) + i2c.address(id, address, i2c.TRANSMITTER) + i2c.write(id, reg_addr) + i2c.write(id, data) + i2c.stop(id) +end + +-- initialize i2c +-- d: sda +-- c: scl +-- a: i2c addr 0x48|A1<<1|A0 (A0-A1: chip pins) +function M.init(d,c,a) +if (d ~= nil) and (c ~= nil) and (d >= 0) and (d <= 11) and (c >= 0) and ( c <= 11) and (d ~= l) and (a ~= nil) and (a >= 0x48) and (a <= 0x4b ) then + sda = d + scl = c + address = a + i2c.start(id) + res = i2c.address(id, address, i2c.TRANSMITTER) --verify that the address is valid + i2c.stop(id) + if (res == false) then + print("device not found") + return nil + end + else + print("i2c configuration failed") return nil + end + i2c.setup(id,sda,scl,i2c.SLOW) +end + +-- Return the temperature data +function M.getTemperature() + local temperature + local tmp=read_reg(0x00,2) --read 2 bytes from the temperature register + temperature=bit.rshift(tmp[1]*256+tmp[2],3) --lower 3 bits are status bits + if (temperature>=0x1000) then + temperature= temperature-0x2000 --convert the two's complement + end + return temperature * 0.0625 +end + +-- Put the LM92 into shutdown mode +function M.shutdown() + write_reg(0x01,0x01) +end + +-- Bring the LM92 out of shutdown mode +function M.wakeup() + write_reg(0x01,0x00) +end + + +return M \ No newline at end of file diff --git a/lua_modules/yeelink/Example_for_Yeelink_Lib.lua b/lua_modules/yeelink/Example_for_Yeelink_Lib.lua new file mode 100644 index 00000000..d5cae98c --- /dev/null +++ b/lua_modules/yeelink/Example_for_Yeelink_Lib.lua @@ -0,0 +1,25 @@ +-- *************************************************************************** +-- Example for Yeelink Lib +-- +-- Written by Martin +-- +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** + +wifi.setmode(wifi.STATION) --Step1: Connect to Wifi +wifi.sta.config("SSID","Password") + +dht = require("dht_lib") --Step2: "Require" the libs +yeelink = require("yeelink_lib") + +yeelink.init(23333,23333,"You api-key",function() --Step3: Register the callback function + + print("Yeelink Init OK...") + tmr.alarm(1,60000,1,function() --Step4: Have fun~ (Update your data) + + dht.read(4) + yeelink.update(dht.getTemperature()) + + end) +end) diff --git a/lua_modules/yeelink/yeelink_lib.lua b/lua_modules/yeelink/yeelink_lib.lua new file mode 100644 index 00000000..226e33ce --- /dev/null +++ b/lua_modules/yeelink/yeelink_lib.lua @@ -0,0 +1,134 @@ + +-- *************************************************************************** +-- Yeelink Updata Libiary Version 0.1.2 r1 +-- +-- Written by Martin +-- but based on a script of zhouxu_o from bbs.nodemcu.com +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** +--==========================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)===== + +if wifi.sta.getip() == nil then + print("Please Connect WIFI First") + tmr.alarm(1,1000,1,function () + if wifi.sta.getip() ~= nil then + tmr.stop(1) + sk:dns("api.yeelink.net",function(conn,ip) + dns=ip + print("DNS YEELINK OK... IP: "..dns) + end) + end + end) +end + +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 + tmr.alarm(2,5000,1,function () + if dns == "0.0.0.0" then + print("Waiting for DNS...") + end + end) + return false + else + return dns + end +end +--========Check the DNS Status=========== +--if DNS success, return the address(string) +--if DNS fail(or processing), return nil +-- +-- +--======================================== +function M.getDNS() + + if dns == "0.0.0.0" then + return nil + 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/pre_build/0.9.6/nodemcu_20150216.bin b/pre_build/0.9.6/nodemcu_20150216.bin new file mode 100644 index 00000000..a1a89593 Binary files /dev/null and b/pre_build/0.9.6/nodemcu_20150216.bin differ diff --git a/pre_build/latest/nodemcu_latest.bin b/pre_build/latest/nodemcu_latest.bin index acc14e99..a1a89593 100644 Binary files a/pre_build/latest/nodemcu_latest.bin and b/pre_build/latest/nodemcu_latest.bin differ 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()