diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs index 5f18488a..11153dfd 100644 --- a/.settings/org.eclipse.cdt.core.prefs +++ b/.settings/org.eclipse.cdt.core.prefs @@ -1,6 +1,6 @@ eclipse.preferences.version=1 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/delimiter=; environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/operation=append -environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/value=C\:\\Espressif\\xtensa-lx106-elf\\bin +environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/PATH/value=C\:\\Espressif\\xtensa-lx106-elf\\bin;C\:\\MinGW\\bin;C\:\\MinGW\\msys\\1.0\\bin;C\:\\Python27 environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/append=true environment/project/cdt.managedbuild.toolchain.gnu.cross.base.992255352/appendContributed=true diff --git a/Makefile b/Makefile index 58aaab83..6b4a545b 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,11 @@ ifeq ($(OS),Windows_NT) OBJCOPY = xtensa-lx106-elf-objcopy endif FIRMWAREDIR = ..\\bin\\ - ESPPORT = com1 + ifndef COMPORT + ESPPORT = com1 + else + ESPPORT = $(COMPORT) + endif ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) # ->AMD64 endif @@ -40,7 +44,11 @@ ifeq ($(OS),Windows_NT) else # We are under other system, may be Linux. Assume using gcc. # Can we use -fdata-sections? - ESPPORT = /dev/ttyUSB0 + ifndef COMPORT + ESPPORT = /dev/ttyUSB0 + else + ESPPORT = $(COMPORT) + endif CCFLAGS += -Os -ffunction-sections -fno-jump-tables AR = xtensa-lx106-elf-ar CC = xtensa-lx106-elf-gcc diff --git a/README.md b/README.md index 27cab97e..674c44e4 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,72 @@ # **NodeMcu** # version 0.9.5 ###A lua based firmware for wifi-soc esp8266 -Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=7&t=104)
+Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)
Lua core based on [eLua project](http://www.eluaproject.net/)
File system based on [spiffs](https://github.com/pellepl/spiffs)
Open source development kit for NodeMCU [nodemcu-devkit](https://github.com/nodemcu/nodemcu-devkit)
Flash tool for NodeMCU [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
wiki: [nodemcu wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)
+api: [nodemcu api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en)
home: [nodemcu.com](http://www.nodemcu.com)
-bbs: [中文论坛Chinese bbs](http://bbs.nodemcu.com)
-Tencent QQ group QQ群: 309957875
+bbs: [Chinese bbs](http://bbs.nodemcu.com)
+Tencent QQ group: 309957875
# Summary - Easy to access wireless router - Based on Lua 5.1.4 (without *io, math, debug, os* module.) - Event-Drive programming preferred. -- Build-in file, timer, pwm, i2c, 1-wire, net, gpio, wifi, adc, uart and system api. +- Build-in file, timer, pwm, i2c, spi, 1-wire, net, mqtt, gpio, wifi, adc, uart and system api. - GPIO pin re-mapped, use the index to access gpio, i2c, pwm. # To Do List (pull requests are very welcomed) - fix wifi smart connect -- add spi module -- add mqtt module +- add spi module (done) +- add mqtt module (done) - add coap module +- cross compiler # Change log +2015-01-27
+support floating point LUA.
+use macro LUA_NUMBER_INTEGRAL in user_config.h control this feature.
+LUA_NUMBER_INTEGRAL to disable floating point support,
+// LUA_NUMBER_INTEGRAL to enable floating point support.
+fix tmr.time(). #132
+fix filesystem length. #113
+fix ssl reboots. #134
+build pre_build bin. + +2015-01-26
+applied sdk095_patch1 to sdk 0.9.5.
+added LUA examples and modules [by dvv](https://github.com/dvv).
+added node.readvdd33() API [by alonewolfx2](https://github.com/alonewolfx2).
+build pre_build bin. + +2015-01-24
+migrate to sdk 0.9.5 release.
+tmr.time() now return second(not precise yet).
+build pre_build bin. + +2015-01-23
+merge mqtt branch to master.
+build pre_build bin. + +2015-01-18
+merge mqtt module to [new branch mqtt](https://github.com/nodemcu/nodemcu-firmware/tree/mqtt) from [https://github.com/tuanpmt/esp_mqtt](https://github.com/tuanpmt/esp_mqtt).
+merge spi module from iabdalkader:spi.
+fix #110,set local port to random in client mode.
+modify gpio.read to NOT set pin to input mode automatic.
+add PATH env with C:\MinGW\bin;C:\MinGW\msys\1.0\bin;C:\Python27 in eclipse project. resolve #103. + 2015-01-08
fix net.socket:send() issue when multi sends are called.
*NOTE*: if data length is bigger than 1460, send next packet AFTER "sent" callback is called.
fix file.read() api, take 0xFF as a regular byte, not EOF.
pre_build/latest/nodemcu_512k_latest.bin is removed. use pre_build/latest/nodemcu_latest.bin instead. -2015-01-07
-retrive more ram back.
-add api file.format() to rebuild file system.
-rename "NodeMcu" to "NodeMCU" in firmware.
-add some check for file system op. - -[more change log](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en#change_log)
-[更多变更日志](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_cn#change_log) +[more change log](https://github.com/nodemcu/nodemcu-firmware/wiki)
##GPIO NEW TABLE ( Build 20141219 and later) @@ -75,39 +102,6 @@ add some check for file system op. #### [*] D0(GPIO16) can only be used as gpio read/write. no interrupt supported. no pwm/i2c/ow supported. -##GPIO OLD TABLE (Before build 20141212) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
IO indexESP8266 pinIO indexESP8266 pin
0GPIO128GPIO0
1GPIO139GPIO2
2GPIO1410GPIO4
3GPIO1511GPIO5
4GPIO3
5GPIO1
6GPIO9
7GPIO10
- #Build option ####file ./app/include/user_config.h ```c @@ -132,6 +126,8 @@ add some check for file system op. #define LUA_USE_MODULES_OW #define LUA_USE_MODULES_BIT #endif /* LUA_USE_MODULES */ +... +// LUA_NUMBER_INTEGRAL ``` #Flash the firmware @@ -140,10 +136,8 @@ for most esp8266 modules, just pull GPIO0 down and restart.
You can use the [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher) to burn the firmware. Or, if you build your own bin from source code.
-eagle.app.v6.flash.bin: 0x00000
-eagle.app.v6.irom0text.bin: 0x10000
-esp_init_data_default.bin: 0x7c000
-blank.bin: 0x7e000
+0x00000.bin: 0x00000
+0x10000.bin: 0x10000
*Better run file.format() after flash* @@ -155,11 +149,13 @@ baudrate:9600 ####Connect to your ap ```lua - print(wifi.sta.getip()) - --0.0.0.0 + ip = wifi.sta.getip() + print(ip) + --nil wifi.setmode(wifi.STATION) wifi.sta.config("SSID","password") - print(wifi.sta.getip()) + ip = wifi.sta.getip() + print(ip) --192.168.18.110 ``` @@ -197,6 +193,56 @@ baudrate:9600 end) ``` +####Connect to MQTT Broker + +```lua +-- init mqtt client with keepalive timer 120sec +m = mqtt.Client("clientid", 120, "user", "password") + +-- setup Last Will and Testament (optional) +-- Broker will publish a message with qos = 0, retain = 0, data = "offline" +-- to topic "/lwt" if client don't send keepalive packet +m:lwt("/lwt", "offline", 0, 0) + +m:on("connect", function(con) print ("connected") end) +m:on("offline", function(con) print ("offline") end) + +-- on publish message receive event +m:on("message", function(conn, topic, data) + print(topic .. ":" ) + if data ~= nil then + print(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) + +-- subscribe topic with qos = 0 +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(); +-- you can call m:connect again + +``` + +#### UDP client and server +```lua +-- a udp server +s=net.createServer(net.UDP) +s:on("receive",function(s,c) print(c) end) +s:listen(5683) + +-- a udp client +cu=net.createConnection(net.UDP) +cu:on("receive",function(cu,c) print(c) end) +cu:connect(5683,"192.168.18.101") +cu:send("hello") +``` + ####Do something shining ```lua function led(r,g,b) diff --git a/app/Makefile b/app/Makefile index 753595d6..15ff75c4 100644 --- a/app/Makefile +++ b/app/Makefile @@ -26,11 +26,11 @@ SUBDIRS= \ driver \ lwip \ json \ - ssl \ upgrade \ platform \ libc \ lua \ + mqtt \ smart \ wofs \ modules \ @@ -41,6 +41,7 @@ endif # } PDIR APPDIR = . LDDIR = ../ld +CCFLAGS += -Os TARGET_LDFLAGS = \ -nostdlib \ @@ -48,7 +49,6 @@ TARGET_LDFLAGS = \ --longcalls \ --text-section-literals - ifeq ($(FLAVOR),debug) TARGET_LDFLAGS += -g -O2 endif @@ -72,11 +72,11 @@ COMPONENTS_eagle.app.v6 = \ driver/libdriver.a \ lwip/liblwip.a \ json/libjson.a \ - ssl/libssl.a \ upgrade/libupgrade.a \ platform/libplatform.a \ libc/liblibc.a \ lua/liblua.a \ + mqtt/mqtt.a \ smart/smart.a \ wofs/wofs.a \ spiffs/spiffs.a \ @@ -99,6 +99,8 @@ LINKFLAGS_eagle.app.v6 = \ -lwpa \ -lmain \ -ljson \ + -lsmartconfig \ + -lssl \ $(DEP_LIBS_eagle.app.v6) \ -Wl,--end-group diff --git a/app/driver/i2c_master.c b/app/driver/i2c_master.c index 24128eaa..772be691 100644 --- a/app/driver/i2c_master.c +++ b/app/driver/i2c_master.c @@ -219,6 +219,45 @@ i2c_master_getAck(void) return retVal; } +/****************************************************************************** +* FunctionName : i2c_master_checkAck +* Description : get dev response +* Parameters : NONE +* Returns : true : get ack ; false : get nack +*******************************************************************************/ +bool ICACHE_FLASH_ATTR +i2c_master_checkAck(void) +{ + if(i2c_master_getAck()){ + return FALSE; + }else{ + return TRUE; + } +} + +/****************************************************************************** +* FunctionName : i2c_master_send_ack +* Description : response ack +* Parameters : NONE +* Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_send_ack(void) +{ + i2c_master_setAck(0x0); +} +/****************************************************************************** +* FunctionName : i2c_master_send_nack +* Description : response nack +* Parameters : NONE +* Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR +i2c_master_send_nack(void) +{ + i2c_master_setAck(0x1); +} + /****************************************************************************** * FunctionName : i2c_master_readByte * Description : read Byte from i2c bus diff --git a/app/driver/pwm.c b/app/driver/pwm.c index 7e1728fd..cc147d05 100644 --- a/app/driver/pwm.c +++ b/app/driver/pwm.c @@ -349,11 +349,6 @@ pwm_init(uint16 freq, uint16 *duty) { uint8 i; - ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL); - TM1_EDGE_INT_ENABLE(); - ETS_FRC1_INTR_ENABLE(); - - RTC_CLR_REG_MASK(FRC1_INT_ADDRESS, FRC1_INT_CLR_MASK); RTC_REG_WRITE(FRC1_CTRL_ADDRESS, //FRC2_AUTO_RELOAD| DIVDED_BY_16 | FRC1_ENABLE_TIMER @@ -377,6 +372,10 @@ pwm_init(uint16 freq, uint16 *duty) // pwm_set_freq_duty(freq, duty); pwm_start(); + + ETS_FRC_TIMER1_INTR_ATTACH(pwm_tim1_intr_handler, NULL); + TM1_EDGE_INT_ENABLE(); + ETS_FRC1_INTR_ENABLE(); } bool ICACHE_FLASH_ATTR diff --git a/app/driver/spi.c b/app/driver/spi.c index a45b2d64..12c32b54 100644 --- a/app/driver/spi.c +++ b/app/driver/spi.c @@ -64,12 +64,11 @@ void spi_lcd_9bit_write(uint8 spi_no,uint8 high_bit,uint8 low_8bit) * Description : SPI master initial function for common byte units transmission * Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid *******************************************************************************/ -void spi_master_init(uint8 spi_no) +void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, unsigned databits, uint32_t clock) { uint32 regvalue; if(spi_no>1) return; //handle invalid input number - if(spi_no==SPI){ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); @@ -86,13 +85,33 @@ void spi_master_init(uint8 spi_no) PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2);//configure io to spi mode } - SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_USR_COMMAND|SPI_USR_MOSI); - CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_DOUTDIN|SPI_USR_MOSI); + + //set clock polarity + // TODO: This doesn't work + //if (cpol == 1) { + // SET_PERI_REG_MASK(SPI_CTRL2(spi_no), (SPI_CK_OUT_HIGH_MODE<1) return; //handle invalid input number - if(spi_no>1) return; //handle invalid input number + while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); - while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); - CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI|SPI_USR_MISO); + WRITE_PERI_REG(SPI_W0(HSPI), *data); - //SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1, - // bit15-0 is cmd value. - WRITE_PERI_REG(SPI_USER2(spi_no), - ((7&SPI_USR_COMMAND_BITLEN)< 0.0) + return (x); + //cmemsg(FP_POWO, &y); + //return(HUGE); + } + else + { + //cmemsg(FP_POWN, &x); + x = -x; + } + } + g = frexp(x, &m); + p = 0; + if (g <= a1[8]) + p = 8; + if (g <= a1[p + 4]) + p += 4; + if (g <= a1[p + 2]) + p += 2; + p++; + z = ((g - a1[p]) - a2[p / 2]) / (g + a1[p]); + z += z; + v = z * z; + r = (((p4 * v + p3) * v + p2) * v + p1) * v * z; + r += k * r; + u2 = (r + z * k) + z; + u1 = 0.0625 * (double)(16 * m - p); + y1 = 0.0625 * (double)((int)(16.0 * y)); + y2 = y - y1; + w = u2 * y + u1 * y2; + w1 = 0.0625 * (double)((int)(16.0 * w)); + w2 = w - w1; + w = w1 + u1 * y1; + w1 = 0.0625 * (double)((int)(16.0 * w)); + w2 += (w - w1); + w = 0.0625 * (double)((int)(16.0 * w2)); + iw1 = 16.0 * (w1 + w); + w2 -= w; + while (w2 > 0.0) + { + iw1++; + w2 -= 0.0625; + } + if (iw1 > MAXEXP) + { + //cmemsg(FP_POWO, &y); + return (HUGE); + } + if (iw1 < MINEXP) + { + //cmemsg(FP_POWU, &y); + return (0.0); + } + m = iw1 / 16; + if (iw1 >= 0) + m++; + p = 16 * m - iw1; + z = ((((((q7 * w2 + q6) * w2 + q5) * w2 + q4) * w2 + q3) * w2 + q2) * w2 + q1) * w2; + z = a1[p] + a1[p] * z; + return (ldexp(z, m)); +} + #if 0 #ifndef __math_68881 -double atan(double x){ - return x; +double atan(double x) +{ + return x; } -double cos(double x){ - return x; +double cos(double x) +{ + return x; } -double sin(double x){ - return x; +double sin(double x) +{ + return x; } -double tan(double x){ - return x; +double tan(double x) +{ + return x; } -double tanh(double x){ - return x; +double tanh(double x) +{ + return x; } -double frexp(double x, int *y){ - return x; +double frexp(double x, int *y) +{ + return x; } -double modf(double x, double *y){ - return x; +double modf(double x, double *y) +{ + return x; } -double ceil(double x){ - return x; +double ceil(double x) +{ + return x; } -double fabs(double x){ - return x; +double fabs(double x) +{ + return x; } -double floor(double x){ - return x; +double floor(double x) +{ + return x; } #endif /* ! defined (__math_68881) */ @@ -39,41 +174,53 @@ double floor(double x){ #ifndef _REENT_ONLY #ifndef __math_68881 -double acos(double x){ - return x; +double acos(double x) +{ + return x; } -double asin(double x){ - return x; +double asin(double x) +{ + return x; } -double atan2(double x, double y){ - return x; +double atan2(double x, double y) +{ + return x; } -double cosh(double x){ - return x; +double cosh(double x) +{ + return x; } -double sinh(double x){ - return x; +double sinh(double x) +{ + return x; } -double exp(double x){ - return x; +double exp(double x) +{ + return x; } -double ldexp(double x, int y){ - return x; +double ldexp(double x, int y) +{ + return x; } -double log(double x){ - return x; +double log(double x) +{ + return x; } -double log10(double x){ - return x; +double log10(double x) +{ + return x; } -double pow(double x, double y){ - return x; +double pow(double x, double y) +{ + return x; } -double sqrt(double x){ - return x; +double sqrt(double x) +{ + return x; } -double fmod(double x, double y){ - return x; +double fmod(double x, double y) +{ + return x; } #endif /* ! defined (__math_68881) */ #endif /* ! defined (_REENT_ONLY) */ diff --git a/app/libc/c_math.h b/app/libc/c_math.h index 65e31afe..ef18ee79 100644 --- a/app/libc/c_math.h +++ b/app/libc/c_math.h @@ -1,6 +1,10 @@ #ifndef _C_MATH_H_ #define _C_MATH_H_ #include + +double floor(double); +double pow(double, double); + #if 0 #ifndef HUGE_VAL #define HUGE_VAL (1.0e99) diff --git a/app/libc/c_stdio.c b/app/libc/c_stdio.c index 0d52d29c..43d7bd24 100644 --- a/app/libc/c_stdio.c +++ b/app/libc/c_stdio.c @@ -12,44 +12,469 @@ int c_stderr = 1001; // FILE *c_tmpfile(void){ // } -// int c_putchar(int c){ +// int c_putchar(int c){ // } -// int c_printf(const char *c, ...){ +// int c_printf(const char *c, ...){ // } // int c_sprintf(char *c, const char *s, ...){ // } -// int c_fprintf(FILE *f, const char *s, ...){ +// int c_fprintf(FILE *f, const char *s, ...){ // } -// int c_fscanf(FILE *f, const char *s, ...){ +// int c_fscanf(FILE *f, const char *s, ...){ // } -// int c_fclose(FILE *f){ +// int c_fclose(FILE *f){ // } -// int c_fflush(FILE *f){ +// int c_fflush(FILE *f){ // } -// int c_setvbuf(FILE *f, char *c, int d, size_t t){ +// int c_setvbuf(FILE *f, char *c, int d, size_t t){ // } // void c_clearerr(FILE *f){ // } -// int c_fseek(FILE *f, long l, int d){ +// int c_fseek(FILE *f, long l, int d){ // } // long c_ftell( FILE *f){ // } -// int c_fputs(const char *c, FILE *f){ +// int c_fputs(const char *c, FILE *f){ // } // char *c_fgets(char *c, int d, FILE *f){ // } -// int c_ungetc(int d, FILE *f){ +// int c_ungetc(int d, FILE *f){ // } // size_t c_fread(void *p, size_t _size, size_t _n, FILE *f){ // } // size_t c_fwrite(const void *p, size_t _size, size_t _n, FILE *f){ // } -// int c_feof(FILE *f){ +// int c_feof(FILE *f){ // } -// int c_ferror(FILE *f){ +// int c_ferror(FILE *f){ // } -// int c_getc(FILE *f){ +// int c_getc(FILE *f){ // } + +#if defined( LUA_NUMBER_INTEGRAL ) + +#else + +/* +File: printf.c + +Copyright (c) 2004,2012 Kustaa Nyholm / SpareTimeLabs + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +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. + +Neither the name of the Kustaa Nyholm or SpareTimeLabs nor the names of its +contributors may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. + +---------------------------------------------------------------------- + +This library is realy just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + +void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + +init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything realy) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + +/* +Add lightweight %g support by vowstar, +NodeMCU Team, 26.1.2015 +*/ + +typedef void (*putcf) (void *, char); + +#ifdef PRINTF_LONG_SUPPORT + +static int uli2a(unsigned long int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned long int d = 1; + int len = 1; + while (num / d >= base) + { + d *= base; + len ++; + } + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; + return len; +} + +static int li2a (long num, char *bf) +{ + int len = 0; + if (num < 0) + { + num = -num; + *bf++ = '-'; + len ++; + } + len += uli2a(num, 10, 0, bf); + return len; +} + +#endif + +static int ui2a(unsigned int num, unsigned int base, int uc, char *bf) +{ + int n = 0; + unsigned int d = 1; + int len = 1; + while (num / d >= base) + { + d *= base; + len ++; + } + while (d != 0) + { + int dgt = num / d; + num %= d; + d /= base; + if (n || dgt > 0 || d == 0) + { + *bf++ = dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10); + ++n; + } + } + *bf = 0; + return len; +} + +static int i2a (int num, char *bf) +{ + int len = 0; + if (num < 0) + { + num = -num; + *bf++ = '-'; + len ++; + } + len += ui2a(num, 10, 0, bf); + return len; +} + +// Converts a floating point number to string. +static int d2a(double num, char *bf) +{ + int len = 0; + double ipart = 0; + double fpart = 0; + double absnum = num; + + // Add sign + if (absnum < 0) + { + absnum = -absnum; + *bf++ = '-'; + // len must add 1 when return + // but can't add at here + } + + // Extract integer part + ipart = (int)absnum; + + // Extract floating part + fpart = absnum - (double)ipart; + + // convert integer part to string +#ifdef PRINTF_LONG_SUPPORT + len += li2a(ipart, bf); +#else + len += i2a(ipart, bf); +#endif + +#ifndef EPSILON +#define EPSILON ((double)(0.00000001)) +#endif + if (fpart < EPSILON) + { + // fpart is zero + } + else + { + bf += len; + // add dot + *bf ++ = '.'; + len += 1; + // add zero after dot + while (fpart < 0.1) + { + fpart *= 10; + *bf ++ = '0'; + len += 1; + } + while ((fpart < (double)1.0 / EPSILON) && ((fpart - (int)fpart) > EPSILON)) + { + fpart = fpart * 10; + } +#ifdef PRINTF_LONG_SUPPORT + len += li2a((int)fpart, bf); +#else + len += i2a((int)fpart, bf); +#endif + } +#undef EPSILON + if (num < 0) + { + len ++; + } + return len; +} + +static int a2d(char ch) +{ + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + else return -1; +} + +static char a2i(char ch, char **src, int base, int *nump) +{ + char *p = *src; + int num = 0; + int digit; + while ((digit = a2d(ch)) >= 0) + { + if (digit > base) break; + num = num * base + digit; + ch = *p++; + } + *src = p; + *nump = num; + return ch; +} + +static void putchw(void *putp, putcf putf, int n, char z, char *bf) +{ + char fc = z ? '0' : ' '; + char ch; + char *p = bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp, fc); + while ((ch = *bf++)) + putf(putp, ch); +} + +void c_format(void *putp, putcf putf, char *fmt, va_list va) +{ + char bf[12]; + + char ch; + + + while ((ch = *(fmt++))) + { + if (ch != '%') + putf(putp, ch); + else + { + char lz = 0; +#ifdef PRINTF_LONG_SUPPORT + char lng = 0; +#endif + int w = 0; + ch = *(fmt++); + if (ch == '0') + { + ch = *(fmt++); + lz = 1; + } + if (ch >= '0' && ch <= '9') + { + ch = a2i(ch, &fmt, 10, &w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch == 'l') + { + ch = *(fmt++); + lng = 1; + } +#endif + switch (ch) + { + case 0: + goto abort; + case 'u' : + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 10, 0, bf); + else +#endif + ui2a(va_arg(va, unsigned int), 10, 0, bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'o' : + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 8, 0, bf); + else +#endif + ui2a(va_arg(va, unsigned int), 8, 0, bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'd' : case 'i': + { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int), bf); + else +#endif + i2a(va_arg(va, int), bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int), 16, (ch == 'X'), bf); + else +#endif + ui2a(va_arg(va, unsigned int), 16, (ch == 'X'), bf); + putchw(putp, putf, w, lz, bf); + break; + case 'e': case 'E': case 'f': + case 'g': case 'G': + { + d2a(va_arg(va, double), bf); + putchw(putp, putf, w, lz, bf); + break; + } + case 'c' : + putf(putp, (char)(va_arg(va, int))); + break; + case 's' : + putchw(putp, putf, w, 0, va_arg(va, char *)); + break; + case '%' : + putf(putp, ch); + default: + break; + } + } + } +abort:; +} + + +static void putcp(void *p, char c) +{ + *(*((char **)p))++ = c; +} + + +void c_sprintf(char *s, char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + c_format(&s, putcp, fmt, va); + putcp(&s, 0); + va_end(va); +} + +#endif diff --git a/app/libc/c_stdio.h b/app/libc/c_stdio.h index d8302a38..88c368f6 100644 --- a/app/libc/c_stdio.h +++ b/app/libc/c_stdio.h @@ -47,12 +47,22 @@ 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 + extern void output_redirect(const char *str); #define c_puts output_redirect // #define c_printf os_printf // int c_printf(const char *c, ...); +#if defined( LUA_NUMBER_INTEGRAL ) #define c_sprintf os_sprintf +#else +#include "c_stdarg.h" +void c_sprintf(char* s,char *fmt, ...); +#endif + // #define c_vsprintf ets_vsprintf #define c_printf(...) do { \ unsigned char __print_buf[BUFSIZ]; \ diff --git a/app/libc/c_stdlib.c b/app/libc/c_stdlib.c index 7115914d..fffd47b5 100644 --- a/app/libc/c_stdlib.c +++ b/app/libc/c_stdlib.c @@ -20,27 +20,27 @@ const char *c_getenv(const char *__string){ } // make sure there is enough memory before real malloc, otherwise malloc will panic and reset -void *c_malloc(size_t __size){ - if(__size>system_get_free_heap_size()){ - NODE_ERR("malloc: not enough memory\n"); - return NULL; - } - return (void *)os_malloc(__size); -} +// void *c_malloc(size_t __size){ +// if(__size>system_get_free_heap_size()){ +// NODE_ERR("malloc: not enough memory\n"); +// return NULL; +// } +// return (void *)os_malloc(__size); +// } -void *c_zalloc(size_t __size){ - if(__size>system_get_free_heap_size()){ - NODE_ERR("zalloc: not enough memory\n"); - return NULL; - } - return (void *)os_zalloc(__size); -} +// void *c_zalloc(size_t __size){ +// if(__size>system_get_free_heap_size()){ +// NODE_ERR("zalloc: not enough memory\n"); +// return NULL; +// } +// return (void *)os_zalloc(__size); +// } -void c_free(void *p){ - // NODE_ERR("free1: %d\n", system_get_free_heap_size()); - os_free(p); - // NODE_ERR("-free1: %d\n", system_get_free_heap_size()); -} +// void c_free(void *p){ +// // NODE_ERR("free1: %d\n", system_get_free_heap_size()); +// os_free(p); +// // NODE_ERR("-free1: %d\n", system_get_free_heap_size()); +// } // int c_rand(void){ @@ -50,8 +50,207 @@ void c_free(void *p){ // int c_atoi(const char *__nptr){ // } -// double c_strtod(const char *__n, char **__end_PTR){ -// } +#include <_ansi.h> +//#include +#include +//#include "mprec.h" + +double c_strtod(const char *string, char **endPtr) +{ + int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ + double powersOf10[] = { /* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 + }; + int sign, expSign = FALSE; + double fraction, dblExp, *d; + register const char *p; + register int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const char *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (isspace((unsigned char)(*p))) { + p += 1; + } + if (*p == '-') { + sign = TRUE; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + sign = FALSE; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!isdigit(c)) { + if ((c != '.') || (decPt >= 0)) { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) { + decPt = mantSize; + } else { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) { + fracExp = decPt - 18; + mantSize = 18; + } else { + fracExp = decPt - mantSize; + } + if (mantSize == 0) { + fraction = 0.0; + p = string; + goto done; + } else { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) { + p += 1; + if (*p == '-') { + expSign = TRUE; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + expSign = FALSE; + } + if (!isdigit((unsigned char)(*p))) { + p = pExp; + goto done; + } + while (isdigit((unsigned char)(*p))) { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expSign) { + exp = fracExp - exp; + } else { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) { + expSign = TRUE; + exp = -exp; + } else { + expSign = FALSE; + } + if (exp > maxExponent) { + exp = maxExponent; + // errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + if (exp & 01) { + dblExp *= *d; + } + } + if (expSign) { + fraction /= dblExp; + } else { + fraction *= dblExp; + } + +done: + if (endPtr != NULL) { + *endPtr = (char *) p; + } + + if (sign) { + return -fraction; + } + return fraction; +} + // long c_strtol(const char *__n, char **__end_PTR, int __base){ // } // unsigned long c_strtoul(const char *__n, char **__end_PTR, int __base){ diff --git a/app/libc/c_stdlib.h b/app/libc/c_stdlib.h index c290ee2e..3757c135 100644 --- a/app/libc/c_stdlib.h +++ b/app/libc/c_stdlib.h @@ -36,7 +36,7 @@ #define c_abs abs #define c_atoi atoi -// #define c_strtod strtod +//#define c_strtod strtod #define c_strtol strtol #define c_strtoul strtoul @@ -55,7 +55,7 @@ void c_free(void *); // void c_srand(unsigned int __seed); // int c_atoi(const char *__nptr); -// double c_strtod(const char *__n, char **__end_PTR); +double c_strtod(const char *__n, char **__end_PTR); // // long c_strtol(const char *__n, char **__end_PTR, int __base); // unsigned long c_strtoul(const char *__n, char **__end_PTR, int __base); // // long long c_strtoll(const char *__n, char **__end_PTR, int __base); diff --git a/app/lua/luaconf.h b/app/lua/luaconf.h index 90da40dc..08c50c59 100644 --- a/app/lua/luaconf.h +++ b/app/lua/luaconf.h @@ -601,7 +601,8 @@ extern int readline4lua(const char *prompt, char *buffer, int length); #endif // #if !defined LUA_INTEGRAL_LONGLONG #else #define LUA_NUMBER_SCAN "%lf" -#define LUA_NUMBER_FMT "%.14g" +//#define LUA_NUMBER_FMT "%.14g" +#define LUA_NUMBER_FMT "%g" #endif // #if defined LUA_NUMBER_INTEGRAL #define lua_number2str(s,n) c_sprintf((s), LUA_NUMBER_FMT, (n)) #define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ diff --git a/app/lua/lundump.c b/app/lua/lundump.c index 92aa5b4f..b036e7a5 100644 --- a/app/lua/lundump.c +++ b/app/lua/lundump.c @@ -5,6 +5,7 @@ */ #include "c_string.h" +#include "c_types.h" #define lundump_c #define LUA_CORE @@ -141,13 +142,11 @@ static lua_Number LoadNumber(LoadState* S) LoadVar(S,y); x = (lua_Number)y; } break; -#if 0 case 8: { int64_t y; LoadVar(S,y); x = (lua_Number)y; } break; -#endif default: lua_assert(0); } } diff --git a/app/lua/lvm.c b/app/lua/lvm.c index fe0243e2..06147856 100644 --- a/app/lua/lvm.c +++ b/app/lua/lvm.c @@ -8,6 +8,7 @@ #include "c_stdio.h" #include "c_stdlib.h" #include "c_string.h" +#include "c_math.h" #define lvm_c #define LUA_CORE diff --git a/app/lwip/app/espconn_tcp.c b/app/lwip/app/espconn_tcp.c index 9cc7129d..91843887 100644 --- a/app/lwip/app/espconn_tcp.c +++ b/app/lwip/app/espconn_tcp.c @@ -812,10 +812,6 @@ espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err) remot_info *pinfo = NULL; LWIP_UNUSED_ARG(err); - if(4096>system_get_free_heap_size()){ - return ERR_MEM; - } - paccept = (espconn_msg *)os_zalloc(sizeof(espconn_msg)); tcp_arg(pcb, paccept); tcp_err(pcb, esponn_server_err); diff --git a/app/modules/Makefile b/app/modules/Makefile index 1d9f19d9..fb170792 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -39,6 +39,7 @@ endif INCLUDES := $(INCLUDES) -I $(PDIR)include INCLUDES += -I ./ INCLUDES += -I ../libc +INCLUDES += -I ../mqtt INCLUDES += -I ../lua INCLUDES += -I ../platform INCLUDES += -I ../wofs diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h index d8166643..6ab6ef46 100644 --- a/app/modules/auxmods.h +++ b/app/modules/auxmods.h @@ -61,6 +61,9 @@ LUALIB_API int ( luaopen_i2c )( lua_State *L ); #define AUXLIB_WIFI "wifi" LUALIB_API int ( luaopen_wifi )( lua_State *L ); +#define AUXLIB_MQTT "mqtt" +LUALIB_API int ( luaopen_mqtt )( lua_State *L ); + #define AUXLIB_NODE "node" LUALIB_API int ( luaopen_node )( lua_State *L ); diff --git a/app/modules/modules.h b/app/modules/modules.h index e58ca748..f28775d5 100644 --- a/app/modules/modules.h +++ b/app/modules/modules.h @@ -37,6 +37,14 @@ #define ROM_MODULES_NET #endif +#if defined(LUA_USE_MODULES_MQTT) +#define MODULES_MQTT "mqtt" +#define ROM_MODULES_MQTT \ + _ROM(MODULES_MQTT, luaopen_mqtt, mqtt_map) +#else +#define ROM_MODULES_MQTT +#endif + #if defined(LUA_USE_MODULES_I2C) #define MODULES_I2C "i2c" #define ROM_MODULES_I2C \ @@ -45,6 +53,14 @@ #define ROM_MODULES_I2C #endif +#if defined(LUA_USE_MODULES_SPI) +#define MODULES_SPI "spi" +#define ROM_MODULES_SPI \ + _ROM(MODULES_SPI, luaopen_spi, spi_map) +#else +#define ROM_MODULES_SPI +#endif + #if defined(LUA_USE_MODULES_TMR) #define MODULES_TMR "tmr" #define ROM_MODULES_TMR \ @@ -105,7 +121,9 @@ ROM_MODULES_GPIO \ ROM_MODULES_PWM \ ROM_MODULES_WIFI \ + ROM_MODULES_MQTT \ ROM_MODULES_I2C \ + ROM_MODULES_SPI \ ROM_MODULES_TMR \ ROM_MODULES_NODE \ ROM_MODULES_FILE \ diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c new file mode 100644 index 00000000..332a9e87 --- /dev/null +++ b/app/modules/mqtt.c @@ -0,0 +1,1051 @@ +// Module for mqtt + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +#include "c_string.h" +#include "c_stdlib.h" + +#include "c_types.h" +#include "mem.h" +#include "espconn.h" + +#include "mqtt_msg.h" + +static lua_State *gL = NULL; + +#define MQTT_BUF_SIZE 1024 +#define MQTT_DEFAULT_KEEPALIVE 60 +#define MQTT_MAX_CLIENT_LEN 64 +#define MQTT_MAX_USER_LEN 64 +#define MQTT_MAX_PASS_LEN 64 +#define MQTT_SEND_TIMEOUT 5 + +typedef enum { + MQTT_INIT, + MQTT_CONNECT_SEND, + MQTT_CONNECT_SENDING, + MQTT_DATA +} tConnState; + +typedef struct mqtt_event_data_t +{ + uint8_t type; + const char* topic; + const char* data; + uint16_t topic_length; + uint16_t data_length; + uint16_t data_offset; +} mqtt_event_data_t; + +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; +} mqtt_state_t; + +typedef struct lmqtt_userdata +{ + struct espconn *pesp_conn; + int self_ref; + int cb_connect_ref; + int cb_disconnect_ref; + int cb_message_ref; + int cb_suback_ref; + int cb_puback_ref; + mqtt_state_t mqtt_state; + mqtt_connect_info_t connect_info; + uint32_t keep_alive_tick; + uint32_t send_timeout; + uint8_t secure; + uint8_t connected; + ETSTimer mqttTimer; + tConnState connState; +}lmqtt_userdata; + +static void mqtt_socket_disconnected(void *arg) // tcp only +{ + NODE_DBG("mqtt_socket_disconnected is called.\n"); + 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 + } + 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); +} + +static void deliver_publish(lmqtt_userdata * mud, uint8_t* message, int length) +{ + const char comma[] = ","; + mqtt_event_data_t event_data; + + event_data.topic_length = length; + event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); + + event_data.data_length = length; + event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + + if(mud->cb_message_ref == LUA_NOREF) + 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); + } else { + lua_call(gL, 2, 0); + } +} + +static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) +{ + NODE_DBG("mqtt_socket_received is called.\n"); + + uint8_t msg_type; + uint8_t msg_qos; + uint16_t msg_id; + + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; + if(mud == NULL) + return; + +READPACKET: + if(len > MQTT_BUF_SIZE && len <= 0) + return; + + c_memcpy(mud->mqtt_state.in_buffer, pdata, len); + mud->mqtt_state.outbound_message = NULL; + switch(mud->connState){ + case MQTT_CONNECT_SENDING: + if(mqtt_get_type(mud->mqtt_state.in_buffer) != MQTT_MSG_TYPE_CONNACK){ + NODE_DBG("MQTT: Invalid packet\r\n"); + mud->connState = MQTT_INIT; + if(mud->secure){ + espconn_secure_disconnect(pesp_conn); + } + else { + espconn_disconnect(pesp_conn); + } + } else { + mud->connState = MQTT_DATA; + NODE_DBG("MQTT: Connected\r\n"); + if(mud->cb_connect_ref == LUA_NOREF) + return; + 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; + + 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); + + 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); + 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) + 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); + 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) + NODE_DBG("MQTT: UnSubscribe successful\r\n"); + 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); + 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){ + NODE_DBG("MQTT: Publish with QoS = 1 successful\r\n"); + 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); + } + + break; + case MQTT_MSG_TYPE_PUBREC: + mud->mqtt_state.outbound_message = mqtt_msg_pubrel(&mud->mqtt_state.mqtt_connection, msg_id); + 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); + 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){ + NODE_DBG("MQTT: Publish with QoS = 2 successful\r\n"); + 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); + } + break; + case MQTT_MSG_TYPE_PINGREQ: + mud->mqtt_state.outbound_message = mqtt_msg_pingresp(&mud->mqtt_state.mqtt_connection); + break; + case MQTT_MSG_TYPE_PINGRESP: + // Ignore + break; + } + // NOTE: this is done down here and not in the switch case above + // because the PSOCK_READBUF_LEN() won't work inside a switch + // statement due to the way protothreads resume. + if(msg_type == MQTT_MSG_TYPE_PUBLISH) + { + + len = 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; + + NODE_DBG("Get another published message\r\n"); + goto 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); + else + espconn_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); + mud->mqtt_state.outbound_message = NULL; + } + return; +} + +static void mqtt_socket_sent(void *arg) +{ + // NODE_DBG("mqtt_socket_sent is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; + if(mud == NULL) + return; + if(!mud->connected) + 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) { + 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); + } +} + +static int mqtt_socket_client( lua_State* L ); +static void mqtt_socket_connected(void *arg) +{ + NODE_DBG("mqtt_socket_connected is called.\n"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL) + return; + lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; + if(mud == NULL) + return; + mud->connected = true; + espconn_regist_recvcb(pesp_conn, mqtt_socket_received); + espconn_regist_sentcb(pesp_conn, mqtt_socket_sent); + espconn_regist_disconcb(pesp_conn, mqtt_socket_disconnected); + + // 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); + } + else + { + espconn_sent(pesp_conn, mud->mqtt_state.outbound_message->data, mud->mqtt_state.outbound_message->length); + } + mud->mqtt_state.outbound_message = NULL; + mud->connState = MQTT_CONNECT_SENDING; + return; +} + +void mqtt_socket_timer(void *arg) +{ + 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->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; + } + } + if(mud->send_timeout > 0) + mud->send_timeout --; +} + +// Lua: mqtt.Client(clientid, keepalive, user, pass) +static int mqtt_socket_client( lua_State* L ) +{ + NODE_DBG("mqtt_socket_client is called.\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; + int stack = 1; + unsigned secure = 0; + int top = lua_gettop(L); + + // create a object + mud = (lmqtt_userdata *)lua_newuserdata(L, sizeof(lmqtt_userdata)); + // pre-initialize it, in case of errors + mud->self_ref = LUA_NOREF; + mud->cb_connect_ref = LUA_NOREF; + mud->cb_disconnect_ref = LUA_NOREF; + + mud->cb_message_ref = LUA_NOREF; + mud->cb_suback_ref = LUA_NOREF; + mud->cb_puback_ref = LUA_NOREF; + mud->pesp_conn = NULL; + mud->secure = 0; + + mud->keep_alive_tick = 0; + mud->send_timeout = 0; + mud->connState = MQTT_INIT; + 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)); + + // set its metatable + luaL_getmetatable(L, "mqtt.socket"); + lua_setmetatable(L, -2); + + if( lua_isstring(L,stack) ) // deal with the clientid string + { + clientId = luaL_checklstring( L, stack, &il ); + 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); + stack++; + } + + if(mud->connect_info.keepalive == 0){ + mud->connect_info.keepalive = MQTT_DEFAULT_KEEPALIVE; + return 1; + } + + if(lua_isstring( L, stack )){ + username = luaL_checklstring( L, stack, &il ); + stack++; + } + if(username == NULL) + return 1; + mud->connect_info.username = (uint8_t *)c_zalloc(il + 1); + if(!mud->connect_info.username){ + return luaL_error(L, "not enough memory"); + } + + 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 ); + stack++; + } + if(password == NULL) + return 1; + mud->connect_info.password = (uint8_t *)c_zalloc(il + 1); + if(!mud->connect_info.password){ + return luaL_error(L, "not enough memory"); + } + + c_memcpy(mud->connect_info.password, password, il); + mud->connect_info.password[il] = 0; + + NODE_DBG("MQTT: Init info: %s, %s, %s\r\n", mud->connect_info.client_id, mud->connect_info.username, mud->connect_info.password); + + return 1; +} + +// Lua: mqtt.delete( socket ) +// call close() first +// socket: unref everything +static int mqtt_delete( lua_State* L ) +{ + NODE_DBG("mqtt_delete is called.\n"); + + lmqtt_userdata *mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); + luaL_argcheck(L, mud, 1, "mqtt.socket expected"); + if(mud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + os_timer_disarm(&mud->mqttTimer); + mud->connected = 0; + 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) + c_free(mud->pesp_conn->proto.tcp); + mud->pesp_conn->proto.tcp = NULL; + c_free(mud->pesp_conn); + mud->pesp_conn = NULL; // for socket, it will free this when disconnected + } + + if(mud->connect_info.will_topic){ + c_free(mud->connect_info.client_id); + } + + if(mud->connect_info.will_message){ + c_free(mud->connect_info.will_message); + } + + if(mud->connect_info.client_id) + c_free(mud->connect_info.client_id); + if(mud->connect_info.username) + c_free(mud->connect_info.username); + if(mud->connect_info.password) + c_free(mud->connect_info.password); + if(mud->mqtt_state.in_buffer) + c_free(mud->mqtt_state.in_buffer); + if(mud->mqtt_state.out_buffer) + c_free(mud->mqtt_state.out_buffer); + + // free (unref) callback ref + if(LUA_NOREF!=mud->cb_connect_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref); + mud->cb_connect_ref = LUA_NOREF; + } + if(LUA_NOREF!=mud->cb_disconnect_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref); + mud->cb_disconnect_ref = LUA_NOREF; + } + if(LUA_NOREF!=mud->cb_message_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_message_ref); + mud->cb_message_ref = LUA_NOREF; + } + if(LUA_NOREF!=mud->cb_suback_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_suback_ref); + mud->cb_suback_ref = LUA_NOREF; + } + if(LUA_NOREF!=mud->cb_puback_ref){ + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); + mud->cb_puback_ref = LUA_NOREF; + } + lua_gc(gL, 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); + return 0; +} + +static void socket_connect(struct espconn *pesp_conn) +{ + if(pesp_conn == NULL) + return; + lmqtt_userdata *mud = (lmqtt_userdata *)pesp_conn->reverse; + if(mud == NULL) + return; + + if(mud->secure){ + espconn_secure_connect(pesp_conn); + } + else + { + espconn_connect(pesp_conn); + } + + NODE_DBG("socket_connect is called.\n"); +} + +static void socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg); +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"); + struct espconn *pesp_conn = arg; + if(pesp_conn == NULL){ + NODE_DBG("pesp_conn null.\n"); + return; + } + + if(ipaddr == NULL) + { + dns_reconn_count++; + if( dns_reconn_count >= 5 ){ + NODE_ERR( "DNS Fail!\n" ); + // Note: should delete the pesp_conn or unref self_ref here. + mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing. + return; + } + NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); + host_ip.addr = 0; + espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_found); + return; + } + + // ipaddr->addr is a uint32_t ip + if(ipaddr->addr != 0) + { + dns_reconn_count = 0; + c_memcpy(pesp_conn->proto.tcp->remote_ip, &(ipaddr->addr), 4); + NODE_DBG("TCP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&(ipaddr->addr))); + NODE_DBG("\n"); + socket_connect(pesp_conn); + } +} + +// 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 topic"); + } + + 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) ) +static int mqtt_socket_connect( lua_State* L ) +{ + NODE_DBG("mqtt_socket_connect is called.\n"); + lmqtt_userdata *mud = NULL; + unsigned port = 1883; + size_t il; + ip_addr_t ipaddr; + const char *domain; + int stack = 1; + unsigned secure = 0; + int top = lua_gettop(L); + + mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); + luaL_argcheck(L, mud, stack, "mqtt.socket expected"); + stack++; + if(mud == NULL) + return 0; + + if(mud->pesp_conn) + c_free(mud->pesp_conn); + struct espconn *pesp_conn = NULL; + pesp_conn = mud->pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn)); + if(!pesp_conn) + return luaL_error(L, "not enough memory"); + + pesp_conn->proto.udp = NULL; + pesp_conn->proto.tcp = (esp_tcp *)c_zalloc(sizeof(esp_tcp)); + if(!pesp_conn->proto.tcp){ + c_free(pesp_conn); + pesp_conn = mud->pesp_conn = NULL; + return luaL_error(L, "not enough memory"); + } + // reverse is for the callback function + pesp_conn->reverse = mud; + pesp_conn->type = ESPCONN_TCP; + pesp_conn->state = ESPCONN_NONE; + mud->connected = 0; + + if( (stack<=top) && lua_isstring(L,stack) ) // deal with the domain string + { + domain = luaL_checklstring( L, stack, &il ); + + stack++; + if (domain == NULL) + { + domain = "127.0.0.1"; + } + ipaddr.addr = ipaddr_addr(domain); + c_memcpy(pesp_conn->proto.tcp->remote_ip, &ipaddr.addr, 4); + NODE_DBG("TCP ip is set: "); + NODE_DBG(IPSTR, IP2STR(&ipaddr.addr)); + NODE_DBG("\n"); + } + + if ( (stack<=top) && lua_isnumber(L, stack) ) + { + port = lua_tointeger(L, stack); + stack++; + NODE_DBG("TCP port is set: %d.\n", port); + } + pesp_conn->proto.tcp->remote_port = port; + pesp_conn->proto.tcp->local_port = espconn_port(); + + if ( (stack<=top) && lua_isnumber(L, stack) ) + { + secure = lua_tointeger(L, stack); + stack++; + if ( secure != 0 && secure != 1 ){ + secure = 0; // default to 0 + } + } else { + secure = 0; // default to 0 + } + mud->secure = secure; // save + + // 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 + if(mud->cb_connect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref); + mud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + stack++; + } + + lua_pushvalue(L, 1); // copy userdata to the top of stack + if(mud->self_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, mud->self_ref); + mud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + espconn_regist_connectcb(pesp_conn, mqtt_socket_connected); + espconn_regist_reconcb(pesp_conn, mqtt_socket_reconnected); + + if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0)) + { + host_ip.addr = 0; + dns_reconn_count = 0; + if(ESPCONN_OK == espconn_gethostbyname(pesp_conn, domain, &host_ip, socket_dns_found)){ + socket_dns_found(domain, &host_ip, pesp_conn); // ip is returned in host_ip. + } + } + else + { + 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); + + return 0; +} + +// Lua: mqtt:close() +// client disconnect and unref itself +static int mqtt_socket_close( lua_State* L ) +{ + NODE_DBG("mqtt_socket_close is called.\n"); + int i = 0; + lmqtt_userdata *mud = NULL; + + mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); + luaL_argcheck(L, mud, 1, "mqtt.socket expected"); + if(mud == NULL) + return 0; + + if(mud->pesp_conn == NULL) + return 0; + + // call mqtt_disconnect() + + if(mud->secure){ + if(mud->pesp_conn->proto.tcp->remote_port || mud->pesp_conn->proto.tcp->local_port) + espconn_secure_disconnect(mud->pesp_conn); + } + else + { + if(mud->pesp_conn->proto.tcp->remote_port || mud->pesp_conn->proto.tcp->local_port) + espconn_disconnect(mud->pesp_conn); + } + return 0; +} + +// Lua: mqtt:on( "method", function() ) +static int mqtt_socket_on( lua_State* L ) +{ + NODE_DBG("mqtt_on is called.\n"); + lmqtt_userdata *mud; + size_t sl; + + mud = (lmqtt_userdata *)luaL_checkudata(L, 1, "mqtt.socket"); + luaL_argcheck(L, mud, 1, "mqtt.socket expected"); + if(mud==NULL){ + NODE_DBG("userdata is nil.\n"); + return 0; + } + + const char *method = luaL_checklstring( L, 2, &sl ); + if (method == NULL) + return luaL_error( L, "wrong arg type" ); + + luaL_checkanyfunction(L, 3); + lua_pushvalue(L, 3); // copy argument (func) to the top of stack + + if( sl == 7 && c_strcmp(method, "connect") == 0){ + if(mud->cb_connect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_connect_ref); + mud->cb_connect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if( sl == 7 && c_strcmp(method, "offline") == 0){ + if(mud->cb_disconnect_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_disconnect_ref); + mud->cb_disconnect_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else if( sl == 7 && c_strcmp(method, "message") == 0){ + if(mud->cb_message_ref != LUA_NOREF) + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_message_ref); + mud->cb_message_ref = luaL_ref(L, LUA_REGISTRYINDEX); + }else{ + lua_pop(L, 1); + return luaL_error( L, "method not supported" ); + } + + return 0; +} + +// Lua: mqtt:subscribe(topic, qos, function()) +static int mqtt_socket_subscribe( lua_State* L ) +{ + NODE_DBG("mqtt_socket_subscribe is called.\n"); + uint8_t stack = 1, qos = 0, retain = 0; + const char *topic; + size_t il; + lmqtt_userdata *mud; + + mud = (lmqtt_userdata *)luaL_checkudata(L, stack, "mqtt.socket"); + luaL_argcheck(L, mud, stack, "mqtt.socket expected"); + stack++; + + if(mud->send_timeout != 0) + return luaL_error( L, "sending in process" ); + + if(!mud->connected) + return luaL_error(L, "not connected"); + + topic = luaL_checklstring( L, stack, &il ); + stack++; + if(topic == NULL) + return luaL_error( L, "need topic name" ); + + qos = luaL_checkinteger( L, stack); + stack++; + + mud->mqtt_state.outbound_message = mqtt_msg_subscribe(&mud->mqtt_state.mqtt_connection, + topic, qos, + &mud->mqtt_state.pending_msg_id); + 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){ + 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(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); + + return 0; +} + +// Lua: mqtt:publish( topic, payload, qos, retain, function() ) +static int mqtt_socket_publish( lua_State* L ) +{ + // NODE_DBG("mqtt_publish is called.\n"); + struct espconn *pesp_conn = NULL; + lmqtt_userdata *mud; + size_t l; + uint8_t stack = 1; + 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; + } + + if(mud->pesp_conn == NULL){ + NODE_DBG("mud->pesp_conn is NULL.\n"); + return 0; + } + 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" ); + + const char *payload = luaL_checklstring( L, stack, &l ); + stack ++; + uint8_t qos = luaL_checkinteger( L, stack); + stack ++; + uint8_t retain = luaL_checkinteger( L, stack); + stack ++; + + + mud->mqtt_state.outbound_message = 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; + 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) + luaL_unref(L, LUA_REGISTRYINDEX, mud->cb_puback_ref); + 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; + return 0; +} + +// Module function map +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" + +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( "on" ), LFUNCVAL ( mqtt_socket_on ) }, + { LSTRKEY( "__gc" ), LFUNCVAL ( mqtt_delete ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + { LSTRKEY( "__index" ), LROVAL ( mqtt_socket_map ) }, +#endif + { LNILKEY, LNILVAL } +}; + +const LUA_REG_TYPE mqtt_map[] = +{ + { LSTRKEY( "Client" ), LFUNCVAL ( mqtt_socket_client ) }, +#if LUA_OPTIMIZE_MEMORY > 0 + + { LSTRKEY( "__metatable" ), LROVAL( mqtt_map ) }, +#endif + { LNILKEY, LNILVAL } +}; + +LUALIB_API int ICACHE_FLASH_ATTR luaopen_mqtt( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + luaL_rometatable(L, "mqtt.socket", (void *)mqtt_socket_map); // create metatable for mqtt.socket + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + int n; + luaL_register( L, AUXLIB_MQTT, mqtt_map ); + + // Set it as its own metatable + lua_pushvalue( L, -1 ); + lua_setmetatable( L, -2 ); + + // Module constants + // MOD_REG_NUMBER( L, "TCP", TCP ); + + // create metatable + luaL_newmetatable(L, "mqtt.socket"); + // metatable.__index = metatable + lua_pushliteral(L, "__index"); + lua_pushvalue(L,-2); + lua_rawset(L,-3); + // Setup the methods inside metatable + luaL_register( L, NULL, mqtt_socket_map ); + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} diff --git a/app/modules/net.c b/app/modules/net.c index dba4fc7e..92514c9b 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -672,16 +672,20 @@ static int net_start( lua_State* L, const char* mt ) { if(isserver) pesp_conn->proto.tcp->local_port = port; - else + else{ pesp_conn->proto.tcp->remote_port = port; + pesp_conn->proto.tcp->local_port = espconn_port(); + } NODE_DBG("TCP port is set: %d.\n", port); } else if (pesp_conn->type == ESPCONN_UDP) { if(isserver) pesp_conn->proto.udp->local_port = port; - else + else{ pesp_conn->proto.udp->remote_port = port; + pesp_conn->proto.udp->local_port = espconn_port(); + } NODE_DBG("UDP port is set: %d.\n", port); } diff --git a/app/modules/node.c b/app/modules/node.c index 929aedd2..11b7b6b6 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -11,7 +11,8 @@ #include "romfs.h" #include "c_string.h" #include "driver/uart.h" -#include "spi_flash.h" +//#include "spi_flash.h" +#include "user_interface.h" #include "flash_api.h" // Lua: restart() @@ -21,18 +22,47 @@ static int node_restart( lua_State* L ) return 0; } -// Lua: dsleep( us ) +// Lua: dsleep( us, option ) static int node_deepsleep( lua_State* L ) { - s32 us; - us = luaL_checkinteger( L, 1 ); - if ( us <= 0 ) - return luaL_error( L, "wrong arg range" ); - system_deep_sleep( us ); + s32 us, option; + //us = luaL_checkinteger( L, 1 ); + // Set deleep option, skip if nil + if ( lua_isnumber(L, 2) ) + { + option = lua_tointeger(L, 2); + if ( option < 0 || option > 4) + return luaL_error( L, "wrong arg range" ); + else + deep_sleep_set_option( option ); + } + // Set deleep time, skip if nil + if ( lua_isnumber(L, 1) ) + { + us = lua_tointeger(L, 1); + // if ( us <= 0 ) + if ( us < 0 ) + return luaL_error( L, "wrong arg range" ); + else + system_deep_sleep( us ); + } return 0; } +// Lua: dsleep_set_options +// Combined to dsleep( us, option ) +// static int node_deepsleep_setoption( lua_State* L ) +// { +// s32 option; +// option = luaL_checkinteger( L, 1 ); +// if ( option < 0 || option > 4) +// return luaL_error( L, "wrong arg range" ); +// else +// deep_sleep_set_option( option ); +// return 0; +// } // Lua: info() + static int node_info( lua_State* L ) { lua_pushinteger(L, NODE_VERSION_MAJOR); @@ -53,6 +83,13 @@ static int node_chipid( lua_State* L ) lua_pushinteger(L, id); return 1; } +// Lua: readvdd33() +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 ) @@ -287,6 +324,9 @@ const LUA_REG_TYPE node_map[] = { LSTRKEY( "led" ), LFUNCVAL( node_led ) }, { LSTRKEY( "input" ), LFUNCVAL( node_input ) }, { LSTRKEY( "output" ), LFUNCVAL( node_output ) }, + { LSTRKEY( "readvdd33" ), LFUNCVAL( node_readvdd33) }, +// Combined to dsleep(us, option) +// { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) }, #if LUA_OPTIMIZE_MEMORY > 0 #endif diff --git a/app/modules/spi.c b/app/modules/spi.c new file mode 100644 index 00000000..90d9468c --- /dev/null +++ b/app/modules/spi.c @@ -0,0 +1,166 @@ +// Module for interfacing with the SPI interface + +//#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" + +// Lua: = spi.setup( id, mode, cpol, cpha, databits, clock ) +static int spi_setup( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + unsigned mode = luaL_checkinteger( L, 2 ); + unsigned cpol = luaL_checkinteger( L, 3 ); + unsigned cpha = luaL_checkinteger( L, 4 ); + unsigned databits = luaL_checkinteger( L, 5 ); + uint32_t clock = luaL_checkinteger( L, 6 ); + + MOD_CHECK_ID( spi, id ); + + if (mode != PLATFORM_SPI_SLAVE && mode != PLATFORM_SPI_MASTER) { + return luaL_error( L, "wrong arg type" ); + } + + if (cpol != PLATFORM_SPI_CPOL_LOW && cpol != PLATFORM_SPI_CPOL_HIGH) { + return luaL_error( L, "wrong arg type" ); + } + + if (cpha != PLATFORM_SPI_CPHA_LOW && cpha != PLATFORM_SPI_CPHA_HIGH) { + return luaL_error( L, "wrong arg type" ); + } + + if (databits != PLATFORM_SPI_DATABITS_8 && databits != PLATFORM_SPI_DATABITS_16) { + return luaL_error( L, "wrong arg type" ); + } + + u32 res = platform_spi_setup(id, mode, cpol, cpha, databits, clock); + lua_pushinteger( L, res ); + return 1; +} + +// Lua: wrote = spi.send( id, data1, [data2], ..., [datan] ) +// data can be either a string, a table or an 8-bit number +static int spi_send( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + const char *pdata; + size_t datalen, i; + int numdata; + u32 wrote = 0; + unsigned argn; + + MOD_CHECK_ID( spi, id ); + if( lua_gettop( L ) < 2 ) + return luaL_error( L, "wrong arg type" ); + + for( argn = 2; argn <= lua_gettop( L ); argn ++ ) + { + // lua_isnumber() would silently convert a string of digits to an integer + // whereas here strings are handled separately. + if( lua_type( L, argn ) == LUA_TNUMBER ) + { + numdata = ( int )luaL_checkinteger( L, argn ); + if( numdata < 0 || numdata > 255 ) + return luaL_error( L, "wrong arg range" ); + platform_spi_send_recv( id, numdata ); + wrote ++; + } + else if( lua_istable( L, argn ) ) + { + datalen = lua_objlen( L, argn ); + for( i = 0; i < datalen; i ++ ) + { + lua_rawgeti( L, argn, i + 1 ); + numdata = ( int )luaL_checkinteger( L, -1 ); + lua_pop( L, 1 ); + if( numdata < 0 || numdata > 255 ) + return luaL_error( L, "wrong arg range" ); + platform_spi_send_recv( id, numdata ); + } + wrote += i; + if( i < datalen ) + break; + } + else + { + pdata = luaL_checklstring( L, argn, &datalen ); + for( i = 0; i < datalen; i ++ ) + platform_spi_send_recv( id, pdata[ i ] ); + wrote += i; + if( i < datalen ) + break; + } + } + + lua_pushinteger( L, wrote ); + return 1; +} + +// Lua: read = spi.recv( id, size ) +static int spi_recv( lua_State *L ) +{ + unsigned id = luaL_checkinteger( L, 1 ); + u32 size = ( u32 )luaL_checkinteger( L, 2 ), i; + + luaL_Buffer b; + spi_data_type data; + + MOD_CHECK_ID( spi, id ); + if (size == 0) { + return 0; + } + + luaL_buffinit( L, &b ); + for (i=0; i 0 + { LSTRKEY( "MASTER" ), LNUMVAL( PLATFORM_SPI_MASTER ) }, + { LSTRKEY( "SLAVE" ), LNUMVAL( PLATFORM_SPI_SLAVE) }, + { LSTRKEY( "CPHA_LOW" ), LNUMVAL( PLATFORM_SPI_CPHA_LOW) }, + { LSTRKEY( "CPHA_HIGH" ), LNUMVAL( PLATFORM_SPI_CPHA_HIGH) }, + { LSTRKEY( "CPOL_LOW" ), LNUMVAL( PLATFORM_SPI_CPOL_LOW) }, + { LSTRKEY( "CPOL_HIGH" ), LNUMVAL( PLATFORM_SPI_CPOL_HIGH) }, + { LSTRKEY( "DATABITS_8" ), LNUMVAL( PLATFORM_SPI_DATABITS_8) }, + { LSTRKEY( "DATABITS_16" ), LNUMVAL( PLATFORM_SPI_DATABITS_16) }, +#endif // #if LUA_OPTIMIZE_MEMORY > 0 + { LNILKEY, LNILVAL } +}; + +LUALIB_API int luaopen_spi( lua_State *L ) +{ +#if LUA_OPTIMIZE_MEMORY > 0 + return 0; +#else // #if LUA_OPTIMIZE_MEMORY > 0 + luaL_register( L, AUXLIB_SPI, spi_map ); + + // Add constants + MOD_REG_NUMBER( L, "MASTER", PLATFORM_SPI_MASTER); + MOD_REG_NUMBER( L, "SLAVE", PLATFORM_SPI_SLAVE); + MOD_REG_NUMBER( L, "CPHA_LOW" , PLATFORM_SPI_CPHA_LOW); + MOD_REG_NUMBER( L, "CPHA_HIGH", PLATFORM_SPI_CPHA_HIGH); + MOD_REG_NUMBER( L, "CPOL_LOW" , PLATFORM_SPI_CPOL_LOW); + MOD_REG_NUMBER( L, "CPOL_HIGH", PLATFORM_SPI_CPOL_HIGH); + MOD_REG_NUMBER( L, "DATABITS_8" , PLATFORM_SPI_DATABITS_8); + MOD_REG_NUMBER( L, "DATABITS_16" , PLATFORM_SPI_DATABITS_16); + + return 1; +#endif // #if LUA_OPTIMIZE_MEMORY > 0 +} + diff --git a/app/modules/tmr.c b/app/modules/tmr.c index 7d67d6be..bd68503e 100644 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -147,14 +147,26 @@ static int tmr_wdclr( lua_State* L ) return 0; } -// Lua: time() , return rtc time in us +static os_timer_t rtc_timer_updator; +static uint64_t cur_count = 0; +static uint64_t rtc_us = 0; +void rtc_timer_update_cb(void *arg){ + uint64_t t = (uint64_t)system_get_rtc_time(); + uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count); + // NODE_ERR("%x\n",t); + cur_count = t; + unsigned c = system_rtc_clock_cali_proc(); + uint64_t itg = c >> 12; + uint64_t dec = c & 0xFFF; + rtc_us += (delta*itg + ((delta*dec)>>12)); + // TODO: store rtc_us to rtc memory. +} +// Lua: time() , return rtc time in second static int tmr_time( lua_State* L ) { - unsigned t = 0xFFFFFFFF & system_get_rtc_time(); - unsigned c = 0xFFFFFFFF & system_rtc_clock_cali_proc(); - lua_pushinteger( L, t ); - lua_pushinteger( L, c ); - return 2; + uint64_t local = rtc_us; + lua_pushinteger( L, ((uint32_t)(local/1000000)) & 0x7FFFFFFF ); + return 1; } // Module function map @@ -182,6 +194,10 @@ LUALIB_API int luaopen_tmr( lua_State *L ) os_timer_setfn(&(alarm_timer[i]), (os_timer_func_t *)(alarm_timer_cb[i]), L); } + os_timer_disarm(&rtc_timer_updator); + os_timer_setfn(&rtc_timer_updator, (os_timer_func_t *)(rtc_timer_update_cb), NULL); + os_timer_arm(&rtc_timer_updator, 500, 1); + #if LUA_OPTIMIZE_MEMORY > 0 return 0; #else // #if LUA_OPTIMIZE_MEMORY > 0 diff --git a/app/modules/wifi.c b/app/modules/wifi.c index e1bf6ea4..4acdd304 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -12,6 +12,7 @@ #include "c_types.h" #include "user_interface.h" #include "smart.h" +#include "smartconfig.h" static int wifi_smart_succeed = LUA_NOREF; @@ -19,6 +20,13 @@ static void wifi_smart_succeed_cb(void *arg){ NODE_DBG("wifi_smart_succeed_cb is called.\n"); if( !arg ) return; +#if 0 + struct station_config *sta_conf = arg; + wifi_station_set_config(sta_conf); + wifi_station_disconnect(); + wifi_station_connect(); + smartconfig_stop(); +#endif if(wifi_smart_succeed == LUA_NOREF) return; lua_State* L = (lua_State *)arg; @@ -81,6 +89,7 @@ static void wifi_scan_done(void *arg, STATUS status) } // Lua: smart(channel, function succeed_cb) +// Lua: smart(type, function succeed_cb) static int wifi_start_smart( lua_State* L ) { unsigned channel; @@ -109,13 +118,15 @@ static int wifi_start_smart( lua_State* L ) }else{ smart_begin(channel, (smart_succeed )wifi_smart_succeed_cb, L); } + // smartconfig_start(0, wifi_smart_succeed_cb); return 0; } -// Lua: exit_smart(channel) +// Lua: exit_smart() static int wifi_exit_smart( lua_State* L ) { smart_end(); + // smartconfig_stop(); if(wifi_smart_succeed != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed); wifi_smart_succeed = LUA_NOREF; diff --git a/app/mqtt/Makefile b/app/mqtt/Makefile new file mode 100644 index 00000000..3fd35717 --- /dev/null +++ b/app/mqtt/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 = mqtt.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/mqtt/mqtt_msg.c b/app/mqtt/mqtt_msg.c new file mode 100644 index 00000000..5e49bfb1 --- /dev/null +++ b/app/mqtt/mqtt_msg.c @@ -0,0 +1,457 @@ +/* +* Copyright (c) 2014, Stephen Robinson +* 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 its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 +#include "mqtt_msg.h" + +#define MQTT_MAX_FIXED_HEADER_SIZE 3 + +enum mqtt_connect_flag +{ + MQTT_CONNECT_FLAG_USERNAME = 1 << 7, + MQTT_CONNECT_FLAG_PASSWORD = 1 << 6, + MQTT_CONNECT_FLAG_WILL_RETAIN = 1 << 5, + MQTT_CONNECT_FLAG_WILL = 1 << 2, + MQTT_CONNECT_FLAG_CLEAN_SESSION = 1 << 1 +}; + +struct __attribute((__packed__)) mqtt_connect_variable_header +{ + uint8_t lengthMsb; + uint8_t lengthLsb; + uint8_t magic[6]; + uint8_t version; + uint8_t flags; + uint8_t keepaliveMsb; + uint8_t keepaliveLsb; +}; + +static int append_string(mqtt_connection_t* connection, const char* string, int len) +{ + if(connection->message.length + len + 2 > connection->buffer_length) + return -1; + + connection->buffer[connection->message.length++] = len >> 8; + connection->buffer[connection->message.length++] = len & 0xff; + memcpy(connection->buffer + connection->message.length, string, len); + connection->message.length += len; + + return len + 2; +} + +static uint16_t append_message_id(mqtt_connection_t* connection, uint16_t message_id) +{ + // If message_id is zero then we should assign one, otherwise + // we'll use the one supplied by the caller + while(message_id == 0) + message_id = ++connection->message_id; + + if(connection->message.length + 2 > connection->buffer_length) + return 0; + + connection->buffer[connection->message.length++] = message_id >> 8; + connection->buffer[connection->message.length++] = message_id & 0xff; + + return message_id; +} + +static int init_message(mqtt_connection_t* connection) +{ + connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; + return MQTT_MAX_FIXED_HEADER_SIZE; +} + +static mqtt_message_t* fail_message(mqtt_connection_t* connection) +{ + connection->message.data = connection->buffer; + connection->message.length = 0; + return &connection->message; +} + +static mqtt_message_t* fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) +{ + int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; + + if(remaining_length > 127) + { + connection->buffer[0] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[1] = 0x80 | (remaining_length % 128); + connection->buffer[2] = remaining_length / 128; + connection->message.length = remaining_length + 3; + connection->message.data = connection->buffer; + } + else + { + connection->buffer[1] = ((type & 0x0f) << 4) | ((dup & 1) << 3) | ((qos & 3) << 1) | (retain & 1); + connection->buffer[2] = remaining_length; + connection->message.length = remaining_length + 2; + connection->message.data = connection->buffer + 1; + } + + return &connection->message; +} + +void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) +{ + memset(connection, 0, sizeof(connection)); + connection->buffer = buffer; + connection->buffer_length = buffer_length; +} + +int mqtt_get_total_length(uint8_t* buffer, uint16_t length) +{ + int i; + int totlen = 0; + + for(i = 1; i < length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + return totlen; +} + +const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for(i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i -1)); + if((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if(i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if(i + topiclen > *length) + return NULL; + + *length = topiclen; + return (const char*)(buffer + i); +} + +const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) +{ + int i; + int totlen = 0; + int topiclen; + + for(i = 1; i < *length; ++i) + { + totlen += (buffer[i] & 0x7f) << (7 * (i - 1)); + if((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + totlen += i; + + if(i + 2 >= *length) + return NULL; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if(i + topiclen >= *length){ + *length = 0; + return NULL; + } + i += topiclen; + + if(mqtt_get_qos(buffer) > 0) + { + if(i + 2 >= *length) + return NULL; + i += 2; + } + + if(totlen < i) + return NULL; + + if(totlen <= *length) + *length = totlen - i; + else + *length = *length - i; + return (const char*)(buffer + i); +} + +uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length) +{ + if(length < 1) + return 0; + + switch(mqtt_get_type(buffer)) + { + case MQTT_MSG_TYPE_PUBLISH: + { + int i; + int topiclen; + + for(i = 1; i < length; ++i) + { + if((buffer[i] & 0x80) == 0) + { + ++i; + break; + } + } + + if(i + 2 >= length) + return 0; + topiclen = buffer[i++] << 8; + topiclen |= buffer[i++]; + + if(i + topiclen >= length) + return 0; + i += topiclen; + + if(mqtt_get_qos(buffer) > 0) + { + if(i + 2 >= length) + return 0; + //i += 2; + } else { + return 0; + } + + return (buffer[i] << 8) | buffer[i + 1]; + } + case MQTT_MSG_TYPE_PUBACK: + case MQTT_MSG_TYPE_PUBREC: + case MQTT_MSG_TYPE_PUBREL: + case MQTT_MSG_TYPE_PUBCOMP: + case MQTT_MSG_TYPE_SUBACK: + case MQTT_MSG_TYPE_UNSUBACK: + case MQTT_MSG_TYPE_SUBSCRIBE: + { + // This requires the remaining length to be encoded in 1 byte, + // which it should be. + if(length >= 4 && (buffer[1] & 0x80) == 0) + return (buffer[2] << 8) | buffer[3]; + else + return 0; + } + + default: + return 0; + } +} + +mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) +{ + struct mqtt_connect_variable_header* variable_header; + + init_message(connection); + + if(connection->message.length + sizeof(*variable_header) > connection->buffer_length) + return fail_message(connection); + variable_header = (void*)(connection->buffer + connection->message.length); + connection->message.length += sizeof(*variable_header); + + variable_header->lengthMsb = 0; + variable_header->lengthLsb = 6; + memcpy(variable_header->magic, "MQIsdp", 6); + variable_header->version = 3; + variable_header->flags = 0; + variable_header->keepaliveMsb = info->keepalive >> 8; + variable_header->keepaliveLsb = info->keepalive & 0xff; + + if(info->clean_session) + variable_header->flags |= MQTT_CONNECT_FLAG_CLEAN_SESSION; + + if(info->client_id != NULL && info->client_id[0] != '\0') + { + if(append_string(connection, info->client_id, strlen(info->client_id)) < 0) + return fail_message(connection); + } + else + return fail_message(connection); + + if(info->will_topic != NULL && info->will_topic[0] != '\0') + { + if(append_string(connection, info->will_topic, strlen(info->will_topic)) < 0) + return fail_message(connection); + + if(append_string(connection, info->will_message, strlen(info->will_message)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_WILL; + if(info->will_retain) + variable_header->flags |= MQTT_CONNECT_FLAG_WILL_RETAIN; + variable_header->flags |= (info->will_qos & 3) << 4; + } + + if(info->username != NULL && info->username[0] != '\0') + { + if(append_string(connection, info->username, strlen(info->username)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_USERNAME; + } + + if(info->password != NULL && info->password[0] != '\0') + { + if(append_string(connection, info->password, strlen(info->password)) < 0) + return fail_message(connection); + + variable_header->flags |= MQTT_CONNECT_FLAG_PASSWORD; + } + + return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) +{ + init_message(connection); + + if(topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if(append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if(qos > 0) + { + if((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + } + else + *message_id = 0; + + if(connection->message.length + data_length > connection->buffer_length) + return fail_message(connection); + 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); +} + +mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if(append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if(append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if(append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); +} + +mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) +{ + init_message(connection); + if(append_message_id(connection, message_id) == 0) + return fail_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) +{ + init_message(connection); + + if(topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if(append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + if(connection->message.length + 1 > connection->buffer_length) + return fail_message(connection); + connection->buffer[connection->message.length++] = qos; + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) +{ + init_message(connection); + + if(topic == NULL || topic[0] == '\0') + return fail_message(connection); + + if((*message_id = append_message_id(connection, 0)) == 0) + return fail_message(connection); + + if(append_string(connection, topic, strlen(topic)) < 0) + return fail_message(connection); + + return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); +} + +mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); +} + +mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection) +{ + init_message(connection); + return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); +} diff --git a/app/mqtt/mqtt_msg.h b/app/mqtt/mqtt_msg.h new file mode 100644 index 00000000..225ba642 --- /dev/null +++ b/app/mqtt/mqtt_msg.h @@ -0,0 +1,129 @@ +/* + * File: mqtt_msg.h + * Author: Minh Tuan + * + * Created on July 12, 2014, 1:05 PM + */ + +#ifndef MQTT_MSG_H +#define MQTT_MSG_H +#include "c_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* +* Copyright (c) 2014, Stephen Robinson +* 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 its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. +* +*/ +/* 7 6 5 4 3 2 1 0*/ +/*| --- Message Type---- | DUP Flag | QoS Level | Retain | +/* Remaining Length */ + + +enum mqtt_message_type +{ + MQTT_MSG_TYPE_CONNECT = 1, + MQTT_MSG_TYPE_CONNACK = 2, + MQTT_MSG_TYPE_PUBLISH = 3, + MQTT_MSG_TYPE_PUBACK = 4, + MQTT_MSG_TYPE_PUBREC = 5, + MQTT_MSG_TYPE_PUBREL = 6, + MQTT_MSG_TYPE_PUBCOMP = 7, + MQTT_MSG_TYPE_SUBSCRIBE = 8, + MQTT_MSG_TYPE_SUBACK = 9, + MQTT_MSG_TYPE_UNSUBSCRIBE = 10, + MQTT_MSG_TYPE_UNSUBACK = 11, + MQTT_MSG_TYPE_PINGREQ = 12, + MQTT_MSG_TYPE_PINGRESP = 13, + MQTT_MSG_TYPE_DISCONNECT = 14 +}; + +typedef struct mqtt_message +{ + uint8_t* data; + uint16_t length; + +} mqtt_message_t; + +typedef struct mqtt_connection +{ + mqtt_message_t message; + + uint16_t message_id; + uint8_t* buffer; + uint16_t buffer_length; + +} mqtt_connection_t; + +typedef struct mqtt_connect_info +{ + char* client_id; + char* username; + char* password; + char* will_topic; + char* will_message; + int keepalive; + int will_qos; + int will_retain; + int clean_session; + +} mqtt_connect_info_t; + + +static inline int mqtt_get_type(uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } +static inline int mqtt_get_dup(uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } +static inline int mqtt_get_qos(uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } +static inline int mqtt_get_retain(uint8_t* buffer) { return (buffer[0] & 0x01); } + +void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); +int mqtt_get_total_length(uint8_t* buffer, uint16_t length); +const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); +const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); +uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); + +mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); +mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); +mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id); +mqtt_message_t* mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id); +mqtt_message_t* mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id); +mqtt_message_t* mqtt_msg_pingreq(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_pingresp(mqtt_connection_t* connection); +mqtt_message_t* mqtt_msg_disconnect(mqtt_connection_t* connection); + + +#ifdef __cplusplus +} +#endif + +#endif /* MQTT_MSG_H */ + diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h index 46689510..6e98e18a 100644 --- a/app/platform/cpu_esp8266.h +++ b/app/platform/cpu_esp8266.h @@ -8,7 +8,7 @@ #include "flash_api.h" // Number of resources (0 if not available/not implemented) #define NUM_GPIO GPIO_PIN_NUM -#define NUM_SPI 1 +#define NUM_SPI 2 #define NUM_UART 1 #define NUM_PWM GPIO_PIN_NUM #define NUM_ADC 1 diff --git a/app/platform/platform.c b/app/platform/platform.c index 9db3f9f6..8480a3bb 100644 --- a/app/platform/platform.c +++ b/app/platform/platform.c @@ -129,11 +129,11 @@ int platform_gpio_read( unsigned pin ) return -1; if(pin == 0){ - gpio16_input_conf(); + // gpio16_input_conf(); return 0x1 & gpio16_input_get(); } - GPIO_DIS_OUTPUT(pin_num[pin]); + // GPIO_DIS_OUTPUT(pin_num[pin]); return 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[pin])); } @@ -435,6 +435,20 @@ int platform_i2c_recv_byte( unsigned id, int ack ){ return r; } +// ***************************************************************************** +// SPI platform interface +uint32_t platform_spi_setup( unsigned id, int mode, unsigned cpol, unsigned cpha, unsigned databits, uint32_t clock) +{ + spi_master_init(id, cpol, cpha, databits, clock); + return 1; +} + +spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data ) +{ + spi_mast_byte_write(id, &data); + return data; +} + // **************************************************************************** // Flash access functions diff --git a/app/platform/platform.h b/app/platform/platform.h index 286dab2d..78452e3f 100644 --- a/app/platform/platform.h +++ b/app/platform/platform.h @@ -84,13 +84,23 @@ int platform_can_recv( unsigned id, uint32_t *canid, uint8_t *idtype, uint8_t *l // SPI enable/disable #define PLATFORM_SPI_ENABLE 1 #define PLATFORM_SPI_DISABLE 0 +// SPI clock phase +#define PLATFORM_SPI_CPHA_LOW 0 +#define PLATFORM_SPI_CPHA_HIGH 1 +// SPI clock polarity +#define PLATFORM_SPI_CPOL_LOW 0 +#define PLATFORM_SPI_CPOL_HIGH 1 +// SPI databits +#define PLATFORM_SPI_DATABITS_8 8 +#define PLATFORM_SPI_DATABITS_16 16 + // Data types typedef uint32_t spi_data_type; // The platform SPI functions int platform_spi_exists( unsigned id ); -uint32_t platform_spi_setup( unsigned id, int mode, uint32_t clock, unsigned cpol, unsigned cpha, unsigned databits ); +uint32_t platform_spi_setup( unsigned id, int mode, unsigned cpol, unsigned cpha, unsigned databits, uint32_t clock); spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data ); void platform_spi_select( unsigned id, int is_select ); diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index dc913754..4795ae5a 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -45,6 +45,8 @@ The small 4KB sectors allow for greater flexibility in applications th void spiffs_mount() { spiffs_config cfg; cfg.phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ); + cfg.phys_addr += 0x3000; + cfg.phys_addr &= 0xFFFFC000; // align to 4 sector. cfg.phys_size = INTERNAL_FLASH_SIZE - ( ( u32_t )cfg.phys_addr - INTERNAL_FLASH_START_ADDRESS ); cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE; // let us not complicate things @@ -74,6 +76,8 @@ int myspiffs_format( void ) SPIFFS_unmount(&fs); u32_t sect_first, sect_last; sect_first = ( u32_t )platform_flash_get_first_free_block_address( NULL ); + sect_first += 0x3000; + sect_first &= 0xFFFFC000; // align to 4 sector. sect_first = platform_flash_get_sector_of_address(sect_first); sect_last = INTERNAL_FLASH_SIZE + INTERNAL_FLASH_START_ADDRESS - 4; sect_last = platform_flash_get_sector_of_address(sect_last); diff --git a/app/upgrade/upgrade.c b/app/upgrade/upgrade.c index 321a7328..8edc8486 100644 --- a/app/upgrade/upgrade.c +++ b/app/upgrade/upgrade.c @@ -222,7 +222,7 @@ upgrade_connect_cb(void *arg) espconn_regist_sentcb(pespconn, upgrade_datasent); if (pbuf != NULL) { - UPGRADE_DBG(pbuf); + UPGRADE_DBG("%s\n", pbuf); #ifdef UPGRADE_SSL_ENABLE espconn_secure_sent(pespconn, pbuf, os_strlen(pbuf)); #else diff --git a/app/user/user_main.c b/app/user/user_main.c index 77c7939e..7d470e2c 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -50,30 +50,8 @@ extern void spiffs_mount(); // extern uint16_t flash_get_sec_num(); -/****************************************************************************** - * FunctionName : user_init - * Description : entry of user application, init user function here - * Parameters : none - * Returns : none -*******************************************************************************/ -void user_init(void) +void nodemcu_init(void) { - // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); - // system_print_meminfo(); - // os_printf("Heap size::%d.\n",system_get_free_heap_size()); - // os_delay_us(50*1000); // delay 50ms before init uart - -#ifdef DEVELOP_VERSION - uart_init(BIT_RATE_74880, BIT_RATE_74880); -#else - uart_init(BIT_RATE_9600, BIT_RATE_9600); -#endif - // uart_init(BIT_RATE_115200, BIT_RATE_115200); - - #ifndef NODE_DEBUG - system_set_os_print(0); - #endif - NODE_ERR("\n"); // Initialize platform first for lua modules. if( platform_init() != PLATFORM_OK ) @@ -85,9 +63,9 @@ void user_init(void) if( !flash_init_data_written() ){ NODE_ERR("Restore init data.\n"); - // Flash init data at FLASHSIZE - 0x04000 Byte. + // Flash init data at FLASHSIZE - 0x04000 Byte. flash_init_data_default(); - // Flash blank data at FLASHSIZE - 0x02000 Byte. + // Flash blank data at FLASHSIZE - 0x02000 Byte. flash_init_data_blank(); } @@ -119,3 +97,30 @@ void user_init(void) task_init(); system_os_post(USER_TASK_PRIO_0,SIG_LUA,'s'); } + +/****************************************************************************** + * FunctionName : user_init + * Description : entry of user application, init user function here + * Parameters : none + * Returns : none +*******************************************************************************/ +void user_init(void) +{ + // NODE_DBG("SDK version:%s\n", system_get_sdk_version()); + // system_print_meminfo(); + // os_printf("Heap size::%d.\n",system_get_free_heap_size()); + // os_delay_us(50*1000); // delay 50ms before init uart + +#ifdef DEVELOP_VERSION + uart_init(BIT_RATE_74880, BIT_RATE_74880); +#else + uart_init(BIT_RATE_9600, BIT_RATE_9600); +#endif + // uart_init(BIT_RATE_115200, BIT_RATE_115200); + + #ifndef NODE_DEBUG + system_set_os_print(0); + #endif + + system_init_done_cb(nodemcu_init); +} diff --git a/examples/fragment.lua b/examples/fragment.lua index 196232c1..b4fb10dd 100644 --- a/examples/fragment.lua +++ b/examples/fragment.lua @@ -25,15 +25,19 @@ ss=net.createServer(net.TCP) ss:listen(80,function(c) end) s=net.createServer(net.TCP) s:listen(80,function(c) c:on("receive",function(s,c) print(c) end) end) -s=net.createServer(net.UDP) s:listen(5683,function(c) c:on("receive",function(s,c) print(c) end) end) +s=net.createServer(net.UDP) +s:on("receive",function(s,c) print(c) end) +s:listen(5683) + +su=net.createConnection(net.UDP) +su:on("receive",function(su,c) print(c) end) +su:connect(5683,"192.168.18.101") +su:send("hello") mm=node.list() for k, v in pairs(mm) do print('file:'..k..' len:'..v) end for k,v in pairs(d) do print("n:"..k..", s:"..v) end -su=net.createConnection(net.UDP) -su:on("receive",function(su,c) print(c) end) -su:connect(5683,"192.168.0.66") -su:send("/v1/id") + gpio.mode(0,gpio.INT) gpio.trig(0,"down",function(l) print("level="..l) end) @@ -57,8 +61,6 @@ su:send("hello world") s=net.createServer(net.TCP) s:listen(8008,function(c) c:on("receive",function(s,c) print(c) pcall(loadstring(c)) end) end) -s=net.createServer(net.UDP) s:listen(8888,function(c) c:on("receive",function(s,c) print(c) pcall(loadstring(c)) end) end) - s=net.createServer(net.TCP) s:listen(8008,function(c) con_std = c function s_output(str) if(con_std~=nil) then con_std:send(str) end end node.output(s_output, 0) c:on("receive",function(c,l) node.input(l) end) c:on("disconnection",function(c) con_std = nil node.output(nil) end) end) @@ -313,3 +315,27 @@ uart.on("data", 0 ,function(input) if input=="q" then uart.on("data") else print uart.on("data","\r",function(input) if input=="quit" then uart.on("data") else print(input) end end, 1) for k, v in pairs(file.list()) do print('file:'..k..' len:'..v) end + +m=mqtt.Client() +m:connect("192.168.18.101",1883) +m:subscribe("/topic",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("/topic","hello",0,0) + +uart.setup(0,9600,8,0,1,0) +sv=net.createServer(net.TCP, 60) +global_c = nil +sv:listen(9999, function(c) + if global_c~=nil then + global_c:close() + end + global_c=c + c:on("receive",function(sck,pl) uart.write(0,pl) end) +end) + +uart.on("data",4, function(data) + if global_c~=nil then + global_c:send(data) + end +end, 0) + diff --git a/include/at_custom.h b/include/at_custom.h new file mode 100644 index 00000000..a9b05c4c --- /dev/null +++ b/include/at_custom.h @@ -0,0 +1,85 @@ + +/* + * custom_at.h + * + * This file is part of Espressif's AT+ command set program. + * Copyright (C) 2013 - 2016, Espressif Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef CUSTOM_AT_H_ +#define CUSTOM_AT_H_ + +#include "c_types.h" + +typedef struct +{ + char *at_cmdName; + int8_t at_cmdLen; + void (*at_testCmd)(uint8_t id); + void (*at_queryCmd)(uint8_t id); + void (*at_setupCmd)(uint8_t id, char *pPara); + void (*at_exeCmd)(uint8_t id); +}at_funcationType; + +/** + * @brief Response "OK" to uart. + * @param None + * @retval None + */ +void at_response_ok(void); +/** + * @brief Response "ERROR" to uart. + * @param events: no used + * @retval None + */ +void at_response_error(void); +/** + * @brief Task of process command or txdata. + * @param custom_at_cmd_array: the array of at cmd that custom defined + * cmd_num : the num of at cmd that custom defined + * @retval None + */ +void at_cmd_array_regist(at_funcationType *custom_at_cmd_array,uint32 cmd_num); +/** + * @brief get digit form at cmd line.the maybe alter pSrc + * @param p_src: at cmd line string + * result:the buffer to be placed result + * err : err num + * @retval TRUE: + * FALSE: + */ +bool at_get_next_int_dec(char **p_src,int*result,int* err); +/** + * @brief get string form at cmd line.the maybe alter pSrc + * @param p_dest: the buffer to be placed result + * p_src: at cmd line string + * max_len :max len of string excepted to get + * @retval None + */ +int32 at_data_str_copy(char *p_dest, char **p_src, int32 max_len); + +/** + * @brief initialize at module + * @param None + * @retval None + */ +void at_init(void); +/** + * @brief print string to at port + * @param string + * @retval None + */ +void at_port_print(const char *str); +#endif diff --git a/include/c_types.h b/include/c_types.h index 88822345..55bffd46 100644 --- a/include/c_types.h +++ b/include/c_types.h @@ -16,6 +16,7 @@ typedef unsigned long uint32_t; typedef signed long sint32_t; typedef signed long int32_t; typedef signed long long sint64_t; +typedef signed long long int64_t; typedef unsigned long long uint64_t; typedef unsigned long long u_int64_t; typedef float real32_t; @@ -78,6 +79,7 @@ typedef enum { #ifdef ICACHE_FLASH #define ICACHE_FLASH_ATTR __attribute__((section(".irom0.text"))) +#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text"))) #else #define ICACHE_FLASH_ATTR #endif /* ICACHE_FLASH */ diff --git a/include/espconn.h b/include/espconn.h index 3fc3cb7e..2af5cf77 100644 --- a/include/espconn.h +++ b/include/espconn.h @@ -91,6 +91,7 @@ struct espconn { enum espconn_option{ ESPCONN_REUSEADDR = 1, + ESPCONN_NODELAY, ESPCONN_END }; @@ -371,5 +372,21 @@ sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip); *******************************************************************************/ 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/ip_addr.h b/include/ip_addr.h index bd757a49..728a75c4 100644 --- a/include/ip_addr.h +++ b/include/ip_addr.h @@ -52,6 +52,11 @@ struct ip_info { #define ip4_addr3_16(ipaddr) ((uint16)ip4_addr3(ipaddr)) #define ip4_addr4_16(ipaddr) ((uint16)ip4_addr4(ipaddr)) + +/** 255.255.255.255 */ +#define IPADDR_NONE ((uint32)0xffffffffUL) +/** 0.0.0.0 */ +#define IPADDR_ANY ((uint32)0x00000000UL) uint32 ipaddr_addr(const char *cp); #define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \ diff --git a/include/osapi.h b/include/osapi.h index 055085f0..5fbdf0da 100644 --- a/include/osapi.h +++ b/include/osapi.h @@ -44,5 +44,14 @@ #define os_sprintf ets_sprintf #define os_update_cpu_frequency ets_update_cpu_frequency +#ifdef USE_OPTIMIZE_PRINTF +#define os_printf(fmt, ...) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \ + os_printf_plus(flash_str, ##__VA_ARGS__); \ + } while(0) +#else +#define os_printf os_printf_plus +#endif + #endif diff --git a/include/ping.h b/include/ping.h new file mode 100644 index 00000000..4ecd032b --- /dev/null +++ b/include/ping.h @@ -0,0 +1,32 @@ +#ifndef __PING_H__ +#define __PING_H__ + + +typedef void (* ping_recv_function)(void* arg, void *pdata); +typedef void (* ping_sent_function)(void* arg, void *pdata); + +struct ping_option{ + uint32 count; + uint32 ip; + uint32 coarse_time; + ping_recv_function recv_function; + ping_sent_function sent_function; + void* reverse; +}; + +struct ping_resp{ + uint32 total_count; + uint32 resp_time; + uint32 seqno; + uint32 timeout_count; + uint32 bytes; + uint32 total_bytes; + uint32 total_time; + sint8 ping_err; +}; + +bool ping_start(struct ping_option *ping_opt); +bool ping_regist_recv(struct ping_option *ping_opt, ping_recv_function ping_recv); +bool ping_regist_sent(struct ping_option *ping_opt, ping_sent_function ping_sent); + +#endif /* __PING_H__ */ diff --git a/include/smartconfig.h b/include/smartconfig.h new file mode 100644 index 00000000..02a1e317 --- /dev/null +++ b/include/smartconfig.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 -2018 Espressif System + * + */ + +#ifndef __SMARTCONFIG_H__ +#define __SMARTCONFIG_H__ + +typedef void (*sc_callback_t)(void *data); + +typedef enum { + SC_STATUS_FIND_CHANNEL = 0, + SC_STATUS_GETTING_SSID_PSWD, + SC_STATUS_GOT_SSID_PSWD, + SC_STATUS_LINK, +} sc_status; + +typedef enum { + SC_TYPE_ESPTOUCH = 0, + SC_TYPE_AIRKISS, +} sc_type; + +sc_status smartconfig_get_status(void); +const char *smartconfig_get_version(void); +bool smartconfig_start(sc_type type, sc_callback_t cb); +bool smartconfig_stop(void); + +#endif diff --git a/include/spi_flash.h b/include/spi_flash.h index 5918c6b3..12dd6e17 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -24,8 +24,6 @@ typedef struct{ #define SPI_FLASH_SEC_SIZE 4096 uint32 spi_flash_get_id(void); -SpiFlashOpResult spi_flash_read_status(uint32 *status); -SpiFlashOpResult spi_flash_write_status(uint32 status_value); SpiFlashOpResult spi_flash_erase_sector(uint16 sec); SpiFlashOpResult spi_flash_write(uint32 des_addr, uint32 *src_addr, uint32 size); SpiFlashOpResult spi_flash_read(uint32 src_addr, uint32 *des_addr, uint32 size); diff --git a/include/user_interface.h b/include/user_interface.h index 1306dfce..1f5a9352 100644 --- a/include/user_interface.h +++ b/include/user_interface.h @@ -1,252 +1,261 @@ -/* - * Copyright (C) 2013 -2014 Espressif System - * - */ - -#ifndef __USER_INTERFACE_H__ -#define __USER_INTERFACE_H__ - -#include "os_type.h" -#ifdef LWIP_OPEN_SRC -#include "lwip/ip_addr.h" -#else -#include "ip_addr.h" -#endif - -#include "queue.h" -#include "user_config.h" -#include "spi_flash.h" - -#ifndef MAC2STR -#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] -#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" -#endif - -enum rst_reason { - DEFAULT_RST_FLAG = 0, - WDT_RST_FLAG = 1, - EXP_RST_FLAG = 2 -}; - -struct rst_info{ - uint32 flag; - uint32 exccause; - uint32 epc1; - uint32 epc2; - uint32 epc3; - uint32 excvaddr; - uint32 depc; -}; - -#define UPGRADE_FW_BIN1 0x00 -#define UPGRADE_FW_BIN2 0x01 - -void system_restore(void); -void system_restart(void); -void system_deep_sleep(uint32 time_in_us); -uint8 system_upgrade_userbin_check(void); -void system_upgrade_reboot(void); -uint8 system_upgrade_flag_check(); -void system_upgrade_flag_set(uint8 flag); -void system_timer_reinit(void); -uint32 system_get_time(void); - -/* user task's prio must be 0/1/2 !!!*/ -enum { - USER_TASK_PRIO_0 = 0, - USER_TASK_PRIO_1, - USER_TASK_PRIO_2, - USER_TASK_PRIO_MAX -}; - -void system_os_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen); -void system_os_post(uint8 prio, os_signal_t sig, os_param_t par); - -void system_print_meminfo(void); -uint32 system_get_free_heap_size(void); - -void system_set_os_print(uint8 onoff); - -uint64 system_mktime(uint32 year, uint32 mon, uint32 day, uint32 hour, uint32 min, uint32 sec); - -uint32 system_get_chip_id(void); - -typedef void (* init_done_cb_t)(void); - -void system_init_done_cb(init_done_cb_t cb); - -uint32 system_rtc_clock_cali_proc(void); -uint32 system_get_rtc_time(void); - -bool system_rtc_mem_read(uint8 src_addr, void *des_addr, uint16 load_size); -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); - -const char *system_get_sdk_version(void); - -#define NULL_MODE 0x00 -#define STATION_MODE 0x01 -#define SOFTAP_MODE 0x02 -#define STATIONAP_MODE 0x03 - -uint8 wifi_get_opmode(void); -bool wifi_set_opmode(uint8 opmode); - -struct bss_info { - STAILQ_ENTRY(bss_info) next; - - uint8 bssid[6]; - uint8 ssid[32]; - uint8 channel; - sint8 rssi; - uint8 authmode; - uint8 is_hidden; -}; - -typedef struct _scaninfo { - STAILQ_HEAD(, bss_info) *pbss; - struct espconn *pespconn; - uint8 totalpage; - uint8 pagenum; - uint8 page_sn; - uint8 data_cnt; -} scaninfo; - -typedef void (* scan_done_cb_t)(void *arg, STATUS status); - -struct station_config { - uint8 ssid[32]; - uint8 password[64]; - uint8 bssid_set; - uint8 bssid[6]; -}; - -bool wifi_station_get_config(struct station_config *config); -bool wifi_station_set_config(struct station_config *config); - -bool wifi_station_connect(void); -bool wifi_station_disconnect(void); - -struct scan_config { - uint8 *ssid; - uint8 *bssid; - uint8 channel; - uint8 show_hidden; -}; - -bool wifi_station_scan(struct scan_config *config, scan_done_cb_t cb); - -uint8 wifi_station_get_auto_connect(void); -bool wifi_station_set_auto_connect(uint8 set); - -enum { - STATION_IDLE = 0, - STATION_CONNECTING, - STATION_WRONG_PASSWORD, - STATION_NO_AP_FOUND, - STATION_CONNECT_FAIL, - STATION_GOT_IP -}; - -enum dhcp_status { - DHCP_STOPPED, - DHCP_STARTED -}; - -uint8 wifi_station_get_connect_status(void); - -uint8 wifi_station_get_current_ap_id(void); -bool wifi_station_ap_change(uint8 current_ap_id); -bool wifi_station_ap_number_set(uint8 ap_number); - -bool wifi_station_dhcpc_start(void); -bool wifi_station_dhcpc_stop(void); -enum dhcp_status wifi_station_dhcpc_status(void); - -typedef enum _auth_mode { - AUTH_OPEN = 0, - AUTH_WEP, - AUTH_WPA_PSK, - AUTH_WPA2_PSK, - AUTH_WPA_WPA2_PSK -} AUTH_MODE; - -struct softap_config { - uint8 ssid[32]; - uint8 password[64]; - uint8 ssid_len; - uint8 channel; - uint8 authmode; - uint8 ssid_hidden; - uint8 max_connection; -}; - -bool wifi_softap_get_config(struct softap_config *config); -bool wifi_softap_set_config(struct softap_config *config); - -struct station_info { - STAILQ_ENTRY(station_info) next; - - uint8 bssid[6]; - struct ip_addr ip; -}; - -struct dhcps_lease { - uint32 start_ip; - uint32 end_ip; -}; - -struct station_info * wifi_softap_get_station_info(void); -void wifi_softap_free_station_info(void); -uint8 wifi_station_get_ap_info(struct station_config config[]); - -bool wifi_softap_dhcps_start(void); -bool wifi_softap_dhcps_stop(void); -bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); -enum dhcp_status wifi_softap_dhcps_status(void); - -#define STATION_IF 0x00 -#define SOFTAP_IF 0x01 - -bool wifi_get_ip_info(uint8 if_index, struct ip_info *info); -bool wifi_set_ip_info(uint8 if_index, struct ip_info *info); -bool wifi_get_macaddr(uint8 if_index, uint8 *macaddr); -bool wifi_set_macaddr(uint8 if_index, uint8 *macaddr); - -uint8 wifi_get_channel(void); -bool wifi_set_channel(uint8 channel); - -void wifi_status_led_install(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func); - -/** Get the absolute difference between 2 u32_t values (correcting overflows) - * 'a' is expected to be 'higher' (without overflow) than 'b'. */ -#define ESP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1))) - -void wifi_promiscuous_enable(uint8 promiscuous); - -typedef void (* wifi_promiscuous_cb_t)(uint8 *buf, uint16 len); - -void wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); - -enum phy_mode { - PHY_MODE_11B = 1, - PHY_MODE_11G = 2, - PHY_MODE_11N = 3 -}; - -enum phy_mode wifi_get_phy_mode(void); -bool wifi_set_phy_mode(enum phy_mode mode); - -enum sleep_type { - NONE_SLEEP_T = 0, - LIGHT_SLEEP_T, - MODEM_SLEEP_T -}; - -bool wifi_set_sleep_type(enum sleep_type type); -enum sleep_type wifi_get_sleep_type(void); - -#endif +/* + * Copyright (C) 2013 -2014 Espressif System + * + */ + +#ifndef __USER_INTERFACE_H__ +#define __USER_INTERFACE_H__ + +#include "os_type.h" +#ifdef LWIP_OPEN_SRC +#include "lwip/ip_addr.h" +#else +#include "ip_addr.h" +#endif + +#include "queue.h" +#include "user_config.h" +#include "spi_flash.h" + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +enum rst_reason { + DEFAULT_RST_FLAG = 0, + WDT_RST_FLAG = 1, + EXP_RST_FLAG = 2 +}; + +struct rst_info{ + uint32 flag; + uint32 exccause; + uint32 epc1; + uint32 epc2; + uint32 epc3; + uint32 excvaddr; + uint32 depc; +}; + +#define UPGRADE_FW_BIN1 0x00 +#define UPGRADE_FW_BIN2 0x01 + +void system_restore(void); +void system_restart(void); + +bool system_deep_sleep_set_option(uint8 option); +void system_deep_sleep(uint32 time_in_us); + +uint8 system_upgrade_userbin_check(void); +void system_upgrade_reboot(void); +uint8 system_upgrade_flag_check(); +void system_upgrade_flag_set(uint8 flag); + +void system_timer_reinit(void); +uint32 system_get_time(void); + +/* user task's prio must be 0/1/2 !!!*/ +enum { + USER_TASK_PRIO_0 = 0, + USER_TASK_PRIO_1, + USER_TASK_PRIO_2, + USER_TASK_PRIO_MAX +}; + +bool system_os_task(os_task_t task, uint8 prio, os_event_t *queue, uint8 qlen); +bool system_os_post(uint8 prio, os_signal_t sig, os_param_t par); + +void system_print_meminfo(void); +uint32 system_get_free_heap_size(void); + +void system_set_os_print(uint8 onoff); +uint8 system_get_os_print(); + +uint64 system_mktime(uint32 year, uint32 mon, uint32 day, uint32 hour, uint32 min, uint32 sec); + +uint32 system_get_chip_id(void); + +typedef void (* init_done_cb_t)(void); + +void system_init_done_cb(init_done_cb_t cb); + +uint32 system_rtc_clock_cali_proc(void); +uint32 system_get_rtc_time(void); + +bool system_rtc_mem_read(uint8 src_addr, void *des_addr, uint16 load_size); +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); + +const char *system_get_sdk_version(void); + +#define NULL_MODE 0x00 +#define STATION_MODE 0x01 +#define SOFTAP_MODE 0x02 +#define STATIONAP_MODE 0x03 + +typedef enum _auth_mode { + AUTH_OPEN = 0, + AUTH_WEP, + AUTH_WPA_PSK, + AUTH_WPA2_PSK, + AUTH_WPA_WPA2_PSK, + AUTH_MAX +} AUTH_MODE; + +uint8 wifi_get_opmode(void); +bool wifi_set_opmode(uint8 opmode); + +struct bss_info { + STAILQ_ENTRY(bss_info) next; + + uint8 bssid[6]; + uint8 ssid[32]; + uint8 channel; + sint8 rssi; + AUTH_MODE authmode; + uint8 is_hidden; +}; + +typedef struct _scaninfo { + STAILQ_HEAD(, bss_info) *pbss; + struct espconn *pespconn; + uint8 totalpage; + uint8 pagenum; + uint8 page_sn; + uint8 data_cnt; +} scaninfo; + +typedef void (* scan_done_cb_t)(void *arg, STATUS status); + +struct station_config { + uint8 ssid[32]; + uint8 password[64]; + uint8 bssid_set; // Note: If bssid_set is 1, station will just connect to the router + // with both ssid[] and bssid[] matched. Please check about this. + uint8 bssid[6]; +}; + +bool wifi_station_get_config(struct station_config *config); +bool wifi_station_set_config(struct station_config *config); + +bool wifi_station_connect(void); +bool wifi_station_disconnect(void); + +struct scan_config { + uint8 *ssid; // Note: ssid == NULL, don't filter ssid. + uint8 *bssid; // Note: bssid == NULL, don't filter bssid. + uint8 channel; // Note: channel == 0, scan all channels, otherwise scan set channel. + uint8 show_hidden; // Note: show_hidden == 1, can get hidden ssid routers' info. +}; + +bool wifi_station_scan(struct scan_config *config, scan_done_cb_t cb); + +uint8 wifi_station_get_auto_connect(void); +bool wifi_station_set_auto_connect(uint8 set); + +enum { + STATION_IDLE = 0, + STATION_CONNECTING, + STATION_WRONG_PASSWORD, + STATION_NO_AP_FOUND, + STATION_CONNECT_FAIL, + STATION_GOT_IP +}; + +enum dhcp_status { + DHCP_STOPPED, + DHCP_STARTED +}; + +uint8 wifi_station_get_connect_status(void); + +uint8 wifi_station_get_current_ap_id(void); +bool wifi_station_ap_change(uint8 current_ap_id); +bool wifi_station_ap_number_set(uint8 ap_number); + +bool wifi_station_dhcpc_start(void); +bool wifi_station_dhcpc_stop(void); +enum dhcp_status wifi_station_dhcpc_status(void); + +struct softap_config { + uint8 ssid[32]; + uint8 password[64]; + uint8 ssid_len; // Note: Recommend to set it according to your ssid + uint8 channel; // Note: support 1 ~ 13 + AUTH_MODE authmode; // Note: Don't support AUTH_WEP in softAP mode. + uint8 ssid_hidden; // Note: default 0 + uint8 max_connection; // Note: default 4, max 4 + uint16 beacon_interval; // Note: support 100 ~ 60000 ms, default 100 +}; + +bool wifi_softap_get_config(struct softap_config *config); +bool wifi_softap_set_config(struct softap_config *config); + +struct station_info { + STAILQ_ENTRY(station_info) next; + + uint8 bssid[6]; + struct ip_addr ip; +}; + +struct dhcps_lease { + uint32 start_ip; + uint32 end_ip; +}; + +struct station_info * wifi_softap_get_station_info(void); +void wifi_softap_free_station_info(void); +uint8 wifi_station_get_ap_info(struct station_config config[]); + +bool wifi_softap_dhcps_start(void); +bool wifi_softap_dhcps_stop(void); +bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); +enum dhcp_status wifi_softap_dhcps_status(void); + +#define STATION_IF 0x00 +#define SOFTAP_IF 0x01 + +bool wifi_get_ip_info(uint8 if_index, struct ip_info *info); +bool wifi_set_ip_info(uint8 if_index, struct ip_info *info); +bool wifi_get_macaddr(uint8 if_index, uint8 *macaddr); +bool wifi_set_macaddr(uint8 if_index, uint8 *macaddr); + +uint8 wifi_get_channel(void); +bool wifi_set_channel(uint8 channel); + +void wifi_status_led_install(uint8 gpio_id, uint32 gpio_name, uint8 gpio_func); +void wifi_status_led_uninstall(); + +/** Get the absolute difference between 2 u32_t values (correcting overflows) + * 'a' is expected to be 'higher' (without overflow) than 'b'. */ +#define ESP_U32_DIFF(a, b) (((a) >= (b)) ? ((a) - (b)) : (((a) + ((b) ^ 0xFFFFFFFF) + 1))) + +void wifi_promiscuous_enable(uint8 promiscuous); + +typedef void (* wifi_promiscuous_cb_t)(uint8 *buf, uint16 len); + +void wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb); + +enum phy_mode { + PHY_MODE_11B = 1, + PHY_MODE_11G = 2, + PHY_MODE_11N = 3 +}; + +enum phy_mode wifi_get_phy_mode(void); +bool wifi_set_phy_mode(enum phy_mode mode); + +enum sleep_type { + NONE_SLEEP_T = 0, + LIGHT_SLEEP_T, + MODEM_SLEEP_T +}; + +bool wifi_set_sleep_type(enum sleep_type type); +enum sleep_type wifi_get_sleep_type(void); + +#endif diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld index 608fafbd..3234dc02 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 = 0x50000 + irom0_0_seg : org = 0x40210000, len = 0x51000 } PHDRS diff --git a/ld/eagle.rom.addr.v6.ld b/ld/eagle.rom.addr.v6.ld index 0ca83963..076a240d 100644 --- a/ld/eagle.rom.addr.v6.ld +++ b/ld/eagle.rom.addr.v6.ld @@ -18,6 +18,7 @@ PROVIDE ( SHA1Transform = 0x4000a364 ); PROVIDE ( SHA1Update = 0x4000b5a8 ); PROVIDE ( SPI_read_status = 0x400043c8 ); PROVIDE ( SPI_write_status = 0x40004400 ); +PROVIDE ( SPI_write_enable = 0x4000443c ); PROVIDE ( Wait_SPI_Idle = 0x4000448c ); PROVIDE ( SPIEraseArea = 0x40004b44 ); PROVIDE ( SPIEraseBlock = 0x400049b4 ); diff --git a/lib/libat.a b/lib/libat.a new file mode 100644 index 00000000..e1db9fa8 Binary files /dev/null and b/lib/libat.a differ diff --git a/lib/libjson.a b/lib/libjson.a index a9492522..f32c329d 100644 Binary files a/lib/libjson.a and b/lib/libjson.a differ diff --git a/lib/liblwip.a b/lib/liblwip.a index 04dd604a..6ef795bb 100644 Binary files a/lib/liblwip.a and b/lib/liblwip.a differ diff --git a/lib/libmain.a b/lib/libmain.a index 271a35fd..b0b90f98 100644 Binary files a/lib/libmain.a and b/lib/libmain.a differ diff --git a/lib/libnet80211.a b/lib/libnet80211.a index 7bcb84f0..5ae2da5c 100644 Binary files a/lib/libnet80211.a and b/lib/libnet80211.a differ diff --git a/lib/libphy.a b/lib/libphy.a index 5679e419..1598f206 100644 Binary files a/lib/libphy.a and b/lib/libphy.a differ diff --git a/lib/libpp.a b/lib/libpp.a index bf1a397d..17466205 100644 Binary files a/lib/libpp.a and b/lib/libpp.a differ diff --git a/lib/libsmartconfig.a b/lib/libsmartconfig.a new file mode 100644 index 00000000..f402b5f3 Binary files /dev/null and b/lib/libsmartconfig.a differ diff --git a/lib/libssl.a b/lib/libssl.a index 3fd56320..36b78fe2 100644 Binary files a/lib/libssl.a and b/lib/libssl.a differ diff --git a/lib/libupgrade.a b/lib/libupgrade.a index a4206383..051d683b 100644 Binary files a/lib/libupgrade.a and b/lib/libupgrade.a differ diff --git a/lib/libwpa.a b/lib/libwpa.a index 85f334ef..abd611b4 100644 Binary files a/lib/libwpa.a and b/lib/libwpa.a differ diff --git a/lua_examples/irsend.lua b/lua_examples/irsend.lua new file mode 100644 index 00000000..4ccd2ebb --- /dev/null +++ b/lua_examples/irsend.lua @@ -0,0 +1,74 @@ +------------------------------------------------------------------------------ +-- IR send module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +-- +-- Example: +-- require("irsend").nec(4, 0x00ff00ff) +------------------------------------------------------------------------------ +local M +do + -- const + local NEC_PULSE_US = 1000000 / 38000 + local NEC_HDR_MARK = 9000 + local NEC_HDR_SPACE = 4500 + local NEC_BIT_MARK = 560 + local NEC_ONE_SPACE = 1600 + local NEC_ZERO_SPACE = 560 + local NEC_RPT_SPACE = 2250 + -- cache + local gpio, bit = gpio, bit + local mode, write = gpio.mode, gpio.write + local waitus = tmr.delay + local isset = bit.isset + -- NB: poorman 38kHz PWM with 1/3 duty. Touch with care! ) + local carrier = function(pin, c) + c = c / NEC_PULSE_US + while c > 0 do + write(pin, 1) + write(pin, 0) + c = c + 0 + c = c + 0 + c = c + 0 + c = c + 0 + c = c + 0 + c = c + 0 + c = c * 1 + c = c * 1 + c = c * 1 + c = c - 1 + end + end + -- tsop signal simulator + local pull = function(pin, c) + write(pin, 0) + waitus(c) + write(pin, 1) + end + -- NB: tsop mode allows to directly connect pin + -- inplace of TSOP input + local nec = function(pin, code, tsop) + local pulse = tsop and pull or carrier + -- setup transmitter + mode(pin, 1) + write(pin, tsop and 1 or 0) + -- header + pulse(pin, NEC_HDR_MARK) + waitus(NEC_HDR_SPACE) + -- sequence, lsb first + for i = 0, 31 do + pulse(pin, NEC_BIT_MARK) + waitus(isset(code, i) and NEC_ONE_SPACE or NEC_ZERO_SPACE) + end + -- trailer + pulse(pin, NEC_BIT_MARK) + -- done transmitter + --mode(pin, 0, tsop and 1 or 0) + end + -- expose + M = { + nec = nec, + } +end +return M diff --git a/lua_examples/tcp2uart.lua b/lua_examples/tcp2uart.lua new file mode 100644 index 00000000..638a2d77 --- /dev/null +++ b/lua_examples/tcp2uart.lua @@ -0,0 +1,16 @@ +uart.setup(0,9600,8,0,1,0) +sv=net.createServer(net.TCP, 60) +global_c = nil +sv:listen(9999, function(c) + if global_c~=nil then + global_c:close() + end + global_c=c + c:on("receive",function(sck,pl) uart.write(0,pl) end) +end) + +uart.on("data",4, function(data) + if global_c~=nil then + global_c:send(data) + end +end, 0) diff --git a/lua_examples/yet-another-dht22.lua b/lua_examples/yet-another-dht22.lua new file mode 100644 index 00000000..1bf65458 --- /dev/null +++ b/lua_examples/yet-another-dht22.lua @@ -0,0 +1,84 @@ +------------------------------------------------------------------------------ +-- DHT11/22 query module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +-- +-- Example: +-- print("DHT11", require("dht22").read(4)) +-- print("DHT22", require("dht22").read(4, true)) +-- NB: the very first read sometimes fails +------------------------------------------------------------------------------ +local M +do + -- cache + local gpio = gpio + local val = gpio.read + local waitus = tmr.delay + -- + local read = function(pin, dht22) + -- wait for pin value + local w = function(v) + local c = 255 + while c > 0 and val(pin) ~= v do c = c - 1 end + return c + end + -- NB: we preallocate incoming data buffer + -- or precise timing in reader gets broken + local b = { 0, 0, 0, 0, 0 } + + -- kick the device + gpio.mode(pin, gpio.INPUT, gpio.PULLUP) + gpio.write(pin, 1) + waitus(10) + gpio.mode(pin, gpio.OUTPUT) + gpio.write(pin, 0) + waitus(20000) + gpio.write(pin, 1) + gpio.mode(pin, gpio.INPUT, gpio.PULLUP) + -- wait for device presense + if w(0) == 0 or w(1) == 0 or w(0) == 0 then + return nil, 0 + end + -- receive 5 octets of data, msb first + for i = 1, 5 do + local x = 0 + for j = 1, 8 do + x = x + x + if w(1) == 0 then return nil, 1 end + -- 70us for 1, 27 us for 0 + waitus(30) + if val(pin) == 1 then + x = x + 1 + if w(0) == 0 then return nil, 2 end + end + end + b[i] = x + end + -- check crc. NB: calculating in receiver loop breaks timings + local crc = 0 + for i = 1, 4 do + crc = (crc + b[i]) % 256 + end + if crc ~= b[5] then return nil, 3 end + -- convert + local t, h + -- DHT22: values in tenths of unit, temperature can be negative + if dht22 then + h = b[1] * 256 + b[2] + t = b[3] * 256 + b[4] + if t > 0x8000 then t = -(t - 0x8000) end + -- DHT11: no negative temperatures, only integers + -- NB: return in 0.1 Celsius + else + h = 10 * b[1] + t = 10 * b[3] + end + return t, h + end + -- expose interface + M = { + read = read, + } +end +return M diff --git a/lua_examples/yet-another-ds18b20.lua b/lua_examples/yet-another-ds18b20.lua new file mode 100644 index 00000000..edcbe2d1 --- /dev/null +++ b/lua_examples/yet-another-ds18b20.lua @@ -0,0 +1,65 @@ +------------------------------------------------------------------------------ +-- DS18B20 query module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +-- +-- Example: +-- for k, v in pairs(require("ds18b20").read(4)) do print(k, v) end +------------------------------------------------------------------------------ +local M +do + local format_addr = function(a) + return ("%02x-%02x%02x%02x%02x%02x%02x"):format( + a:byte(1), + a:byte(7), a:byte(6), a:byte(5), + a:byte(4), a:byte(3), a:byte(2) + ) + end + local read = function(pin, delay) + local ow = require("ow") + -- get list of relevant devices + local d = { } + ow.setup(pin) + ow.reset_search(pin) + while true do + tmr.wdclr() + local a = ow.search(pin) + if not a then break end + if ow.crc8(a) == 0 and + (a:byte(1) == 0x10 or a:byte(1) == 0x28) + then + d[#d + 1] = a + end + end + -- conversion command for all + ow.reset(pin) + ow.skip(pin) + ow.write(pin, 0x44, 1) + -- wait a bit + tmr.delay(delay or 100000) + -- iterate over devices + local r = { } + for i = 1, #d do + tmr.wdclr() + -- read rom command + ow.reset(pin) + ow.select(pin, d[i]) + ow.write(pin, 0xBE, 1) + -- read data + local x = ow.read_bytes(pin, 9) + if ow.crc8(x) == 0 then + local t = (x:byte(1) + x:byte(2) * 256) * 625 + -- NB: temperature in Celcius * 10^4 + r[format_addr(d[i])] = t + d[i] = nil + end + end + return r + end + -- expose + M = { + read = read, + } +end +return M diff --git a/lua_modules/bh1750/bh1750.lua b/lua_modules/bh1750/bh1750.lua new file mode 100644 index 00000000..99131131 --- /dev/null +++ b/lua_modules/bh1750/bh1750.lua @@ -0,0 +1,54 @@ +-- *************************************************************************** +-- BH1750 module for ESP8266 with nodeMCU +-- BH1750 compatible tested 2015-1-22 +-- +-- Written by xiaohu +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** +local moduleName = ... + local M = {} + _G[moduleName] = M + --I2C slave address of GY-30 + local GY_30_address = 0X23 + -- i2c interface ID + local id = 0 + --LUX + local l + --CMD + local CMD = 0x10 + local init = false + function M.init(sda, scl) + i2c.setup(id, sda, scl, i2c.SLOW) + --print("i2c ok..") + init = true + end + local function read_data(ADDR, commands, length) + i2c.start(id) + i2c.address(id, ADDR, i2c.TRANSMITTER) + i2c.write(id, commands) + i2c.stop(id) + i2c.start(id) + i2c.address(id, ADDR,i2c.RECEIVER) + tmr.delay(200000) + c = i2c.read(id, length) + i2c.stop(id) + return c + end + local function read_lux() + dataT = read_data(GY_30_address, CMD, 2) + UT = string.byte(dataT, 1) * 256 + string.byte(dataT, 2) + l = (UT*1000/12) + return(l) + end + function M.read() + if (not init) then + print("init() must be called before read.") + else + read_lux() + end + end + function M.getlux() + return l + end + return M diff --git a/lua_modules/bh1750/bh1750_CN.md b/lua_modules/bh1750/bh1750_CN.md new file mode 100644 index 00000000..42815985 --- /dev/null +++ b/lua_modules/bh1750/bh1750_CN.md @@ -0,0 +1,104 @@ +# BH1750 模块 + +##引用 +```lua +bh1750 = require("bh1750") +``` +## 释放 +```lua +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +##init() +####描述 +设置BH1750所在的I2C引脚
+ +####语法 +init(sda, scl) + +####参数 +sda: 1~12, IO index.
+scl: 1~12, IO index.
+ +####返回值 +nil + +####示例 +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####参见 +**-** []() + + +##read() +####描述 +从bh1750中读取光线传感器数据(Lux勒克斯).
+ +####语法 +read() + +####参数 +nil.
+ +####返回值 +nil.
+ +####示例 +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) +bh1750.read() + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####参见 +**-** []() + + +##getlux() +####描述 +从BH1750中提取数据.
+ +####语法 +getlux() + +####参数 +nil.
+ +####返回值 +l: 整数,Lux计数 +####示例 +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) +bh1750.read() +l = bh1750.getlux() +print("lux: "..(l / 100).."."..(l % 100).." lx") + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####参见 +**-** []() diff --git a/lua_modules/bh1750/bh1750_EN.md b/lua_modules/bh1750/bh1750_EN.md new file mode 100644 index 00000000..5e2a5ece --- /dev/null +++ b/lua_modules/bh1750/bh1750_EN.md @@ -0,0 +1,105 @@ +# bh1750 Module + +##Require +```lua +bh1750 = require("bh1750") +``` +## Release +```lua +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +##init() +####Description +Setting the I2C pin of bh1750.
+ +####Syntax +init(sda, scl) + +####Parameters +sda: 1~12, IO index.
+scl: 1~12, IO index.
+ +####Returns +nil + +####Example +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####See also +**-** []() + + +##read() +####Description +Read Lux data from bh1750.
+ +####Syntax +read() + +####Parameters +nil.
+ +####Returns +nil.
+ +####Example +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) +bh1750.read() + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####See also +**-** []() + + +##getlux() +####Description +Get lux from bh1750.
+ +####Syntax +getlux() + +####Parameters +nil.
+ +####Returns +l: Integer, getlux from bh1750. + +####Example +```lua +SDA_PIN = 6 -- sda pin, GPIO12 +SCL_PIN = 5 -- scl pin, GPIO14 + +bh1750 = require("bh1750") +bh1750.init(SDA_PIN, SCL_PIN) +bh1750.read() +l = bh1750.getlux() +print("lux: "..(l / 100).."."..(l % 100).." lx") + +-- release module +bh1750 = nil +package.loaded["bh1750"]=nil +``` + +####See also +**-** []() diff --git a/lua_modules/bh1750/bh1750_Example1.lua b/lua_modules/bh1750/bh1750_Example1.lua new file mode 100644 index 00000000..911aa8fc --- /dev/null +++ b/lua_modules/bh1750/bh1750_Example1.lua @@ -0,0 +1,24 @@ +-- *************************************************************************** +-- BH1750 Example Program for ESP8266 with nodeMCU +-- BH1750 compatible tested 2015-1-30 +-- +-- Written by xiaohu +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** +tmr.alarm(0, 10000, 1, function() + + SDA_PIN = 6 -- sda pin, GPIO12 + SCL_PIN = 5 -- scl pin, GPIO14 + + bh1750 = require("bh1750") + bh1750.init(SDA_PIN, SCL_PIN) + bh1750.read(OSS) + l = bh1750.getlux() + print("lux: "..(l / 100).."."..(l % 100).." lx") + + -- release module + bh1750 = nil + package.loaded["bh1750"]=nil + +end) diff --git a/lua_modules/bh1750/bh1750_Example2.lua b/lua_modules/bh1750/bh1750_Example2.lua new file mode 100644 index 00000000..48acceac --- /dev/null +++ b/lua_modules/bh1750/bh1750_Example2.lua @@ -0,0 +1,51 @@ +-- *************************************************************************** +-- BH1750 Example Program for ESP8266 with nodeMCU +-- BH1750 compatible tested 2015-1-30 +-- +-- Written by xiaohu +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** +--Updata to Lelian + +--Ps 需要改动的地方LW_GATEWAY(乐联的设备标示),USERKEY(乐联userkey) +--Ps You nees to rewrite the LW_GATEWAY(Lelian's Device ID),USERKEY(Lelian's userkey) + +tmr.alarm(0, 60000, 1, function() + SDA_PIN = 6 -- sda pin, GPIO12 + SCL_PIN = 5 -- scl pin, GPIO14 + + BH1750 = require("BH1750") + BH1750.init(SDA_PIN, SCL_PIN) + BH1750.read(OSS) + l = BH1750.getlux() + + --定义数据变量格式 Define the veriables formate + PostData = "[{\"Name\":\"T\",\"Value\":\"" ..(l / 100).."."..(l % 100).."\"}]" + --创建一个TCP连接 Create a TCP Connection + socket=net.createConnection(net.TCP, 0) + --域名解析IP地址并赋值 DNS...it + socket:dns("www.lewei50.com", function(conn, ip) + ServerIP = ip + print("Connection IP:" .. ServerIP) + end) + +--开始连接服务器 Connect the sever +socket:connect(80, ServerIP) + socket:on("connection", function(sck) end) + +--HTTP请求头定义 HTTP Head +socket:send("POST /api/V1/gateway/UpdateSensors/LW_GATEWAY HTTP/1.1\r\n" .. + "Host: www.lewei50.com\r\n" .. + "Content-Length: " .. string.len(PostData) .. "\r\n" .. + "userkey: USERKEY\r\n\r\n" .. + PostData .. "\r\n") + +--HTTP响应内容 Print the HTTP response +socket:on("receive", function(sck, response) + print(response) + end) + end) + + + diff --git a/lua_modules/bmp085/bmp085.EN.md b/lua_modules/bmp085/bmp085.EN.md new file mode 100644 index 00000000..4811429e --- /dev/null +++ b/lua_modules/bmp085/bmp085.EN.md @@ -0,0 +1,166 @@ +# BMP085 module + +##Require +```lua +bmp085 = require("bmp085") +``` +## Release +```lua +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +##init() +####Description +Setting the i2c pin of bmp085.
+ +####Syntax +init(sda, scl) + +####Parameters +sda: 1~12, IO index.
+scl: 1~12, IO index.
+ +####Returns +nil + +####Example +```lua +bmp085 = require("bmp085") +gpio5 = 1 +gpio4 = 2 +sda = gpio5 +scl = gpio4 +bmp085.init(sda, scl) +-- Don't forget to release it after use +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +####See also +**-** []() + + +##getUP() +####Description +Get calibrated data of pressure from bmp085.
+ +####Syntax +getUP(oss) + +####Parameters +oss: Over sampling setting, which is 0,1,2,3. Default value is 0.
+ +####Returns +p: Integer, calibrated data of pressure from bmp085. + +####Example +```lua +bmp085 = require("bmp085") +sda = 1 +scl = 2 +bmp085.init(sda, scl) +p = bmp085.getUP(oss) +print(p) +-- Don't forget to release it after use +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +####See also +**-** []() + + +##getUP_raw() +####Description +Get raw data of pressure from bmp085.
+ +####Syntax +getUP_raw(oss) + +####Parameters +oss: Over sampling setting, which is 0,1,2,3. Default value is 0.
+ +####Returns +up_raw: Integer, raw data of pressure from bmp085. + +####Example +```lua +bmp085 = require("bmp085") +sda = 1 +scl = 2 +bmp085.init(sda, scl) +up = bmp085.getUP_raw(oss) +print(up) +-- Don't forget to release it after use +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +####See also +**-** []() + + +##getUT() +####Description +Get temperature from bmp085.
+ +####Syntax +getUT(num_10x) + +####Parameters +num_10x: num_10x: bool value, if true, return number of 0.1 centi-degree. Default value is false, which return a string , eg: 16.7.
+ +####Returns +t: Integer or String, if num_10x is true, return number of 0.1 centi-degree, otherwise return a string.The temperature from bmp085.
+ +####Example +```lua +bmp085 = require("bmp085") +sda = 1 +scl = 2 +bmp085.init(sda, scl) +-- Get string of temperature. +p = bmp085.getUT(false) +print(p) +-- Get number of temperature. +p = bmp085.getUT(true) +print(p) +-- Don't forget to release it after use +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +####See also +**-** []() + + +##getAL() +####Description +Get estimated data of altitude from bmp085.
+ +####Syntax +getAL(oss) + +####Parameters +oss: over sampling setting, which is 0,1,2,3. Default value is 0.
+ +####Returns +e: Integer, estimated data of altitude. Altitudi can be calculated by pressure refer to sea level pressure, which is 101325. Pressure changes 100pa corresponds to 8.43m at sea level
+ +####Example +```lua +bmp085 = require("bmp085") +sda = 1 +scl = 2 +bmp085.init(sda, scl) +-- Get string of temperature. +e = bmp085.getAL() +print(p) +-- Don't forget to release it after use +bmp085 = nil +package.loaded["bmp085"]=nil +``` + +####See also +**-** []() diff --git a/lua_modules/bmp085.lua b/lua_modules/bmp085/bmp085.lua similarity index 100% rename from lua_modules/bmp085.lua rename to lua_modules/bmp085/bmp085.lua diff --git a/lua_modules/dht22/README.md b/lua_modules/dht22/README.md new file mode 100644 index 00000000..99ea9ff2 --- /dev/null +++ b/lua_modules/dht22/README.md @@ -0,0 +1,52 @@ +# DHT22 module + +This module is compatible with DHT22 and DHT21. +No need to use a resistor to connect the pin data of DHT22 to ESP8266. + +## Example +```lua +PIN = 4 -- data pin, GPIO2 + +dht22 = require("dht22") +dht22.read(PIN) +t = dht22.getTemperature() +h = dht22.getHumidity() + +if h == -1 then + print("Error reading from DHT22") +else + -- temperature in degrees Celsius and Farenheit + print("Temperature: "..(t / 10).."."..(t % 10).." deg C") + print("Temperature: "..(9 * t / 50 + 32).."."..(9 * t / 5 % 10).." deg F") + + -- humidity + print("Humidity: "..(h/10).."."..(h%10).."%") +end + +-- release module +dht22 = nil +package.loaded["dht22"]=nil +``` +## Functions +### read +read(pin) +Read humidity and temperature from DHT22. + +**Parameters:** + +* pin - ESP8266 pin connect to data pin in DHT22 + +### getHumidity +getHumidity() +Returns the humidity of the last reading. + +**Returns:** +* last humidity reading in per thousand + +### getTemperature +getTemperature() +Returns the temperature of the last reading. + +**Returns:** +* last temperature reading in 0.1ºC + diff --git a/lua_modules/dht22/dht22.lua b/lua_modules/dht22/dht22.lua new file mode 100644 index 00000000..d2374050 --- /dev/null +++ b/lua_modules/dht22/dht22.lua @@ -0,0 +1,100 @@ +-- *************************************************************************** +-- DHT22 module for ESP8266 with nodeMCU +-- +-- Written by Javier Yanez +-- but based on a script of Pigs Fly from ESP8266.com forum +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** + +local moduleName = ... +local M = {} +_G[moduleName] = M + +local humidity +local temperature + +function M.read(pin) + local checksum + local checksumTest + humidity = 0 + temperature = 0 + checksum = 0 + + -- Use Markus Gritsch trick to speed up read/write on GPIO + local gpio_read = gpio.read + + local bitStream = {} + for j = 1, 40, 1 do + bitStream[j] = 0 + end + local bitlength = 0 + + -- Step 1: send out start signal to DHT22 + gpio.mode(pin, gpio.OUTPUT) + gpio.write(pin, gpio.HIGH) + tmr.delay(100) + gpio.write(pin, gpio.LOW) + tmr.delay(20000) + gpio.write(pin, gpio.HIGH) + gpio.mode(pin, gpio.INPUT) + + -- Step 2: DHT22 send response signal + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0 ) do end + local c=0 + while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0 ) do end + c=0 + while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end + + -- Step 3: DHT22 send data + for j = 1, 40, 1 do + while (gpio_read(pin) == 1 and bitlength < 10 ) do + bitlength = bitlength + 1 + end + bitStream[j] = bitlength + bitlength = 0 + -- bus will always let up eventually, don't bother with timeout + while (gpio_read(pin) == 0) do end + end + + --DHT data acquired, process. + for i = 1, 16, 1 do + if (bitStream[i] > 4) then + humidity = humidity + 2 ^ (16 - i) + end + end + for i = 1, 16, 1 do + if (bitStream[i + 16] > 4) then + temperature = temperature + 2 ^ (16 - i) + end + end + for i = 1, 8, 1 do + if (bitStream[i + 32] > 4) then + checksum = checksum + 2 ^ (8 - i) + end + end + + checksumTest=((humidity / 256) + (humidity % 256) + (temperature / 256) + (temperature % 256)) % 256 + + if temperature > 0x8000 then + -- convert to negative format + temperature = -(temperature - 0x8000) + end + + if checksum ~= checksumTest then + humidity = -1 + end +end + +function M.getTemperature() + return temperature +end + +function M.getHumidity() + return humidity +end + +return M diff --git a/lua_modules/ds18b20/ds18b20.CN.md b/lua_modules/ds18b20/ds18b20.ZH.md similarity index 100% rename from lua_modules/ds18b20/ds18b20.CN.md rename to lua_modules/ds18b20/ds18b20.ZH.md diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index 3857c941..bc1b1c99 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -35,27 +35,27 @@ setfenv(1,M) C = 0 F = 1 K = 2 -function setup(dq) - pin = dq - if(pin == nil) then - pin = defaultPin - end - ow.setup(pin) +function setup(dq) + pin = dq + if(pin == nil) then + pin = defaultPin + end + ow.setup(pin) end function addrs() - setup(pin) - tbl = {} - ow.reset_search(pin) - repeat - addr = ow.search(pin) - if(addr ~= nil) then - table.insert(tbl, addr) - end - tmr.wdclr() - until (addr == nil) - ow.reset_search(pin) - return tbl + setup(pin) + tbl = {} + ow.reset_search(pin) + repeat + addr = ow.search(pin) + if(addr ~= nil) then + table.insert(tbl, addr) + end + tmr.wdclr() + until (addr == nil) + ow.reset_search(pin) + return tbl end function readNumber(addr, unit) @@ -64,71 +64,71 @@ function readNumber(addr, unit) flag = false if(addr == nil) then ow.reset_search(pin) - count = 0 + count = 0 repeat - count = count + 1 - addr = ow.search(pin) - tmr.wdclr() - until((addr ~= nil) or (count > 100)) - ow.reset_search(pin) - end + count = count + 1 + addr = ow.search(pin) + tmr.wdclr() + until((addr ~= nil) or (count > 100)) + ow.reset_search(pin) + end if(addr == nil) then - return result + return result end crc = ow.crc8(string.sub(addr,1,7)) if (crc == addr:byte(8)) then if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then - -- print("Device is a DS18S20 family device.") - ow.reset(pin) - ow.select(pin, addr) - ow.write(pin, 0x44, 1) - -- tmr.delay(1000000) - present = ow.reset(pin) - ow.select(pin, addr) - ow.write(pin,0xBE,1) - -- print("P="..present) - data = nil - data = string.char(ow.read(pin)) - for i = 1, 8 do - data = data .. string.char(ow.read(pin)) + -- print("Device is a DS18S20 family device.") + ow.reset(pin) + ow.select(pin, addr) + ow.write(pin, 0x44, 1) + -- tmr.delay(1000000) + present = ow.reset(pin) + ow.select(pin, addr) + ow.write(pin,0xBE,1) + -- print("P="..present) + data = nil + data = string.char(ow.read(pin)) + for i = 1, 8 do + data = data .. string.char(ow.read(pin)) + end + -- print(data:byte(1,9)) + crc = ow.crc8(string.sub(data,1,8)) + -- print("CRC="..crc) + if (crc == data:byte(9)) then + if(unit == nil or unit == C) then + t = (data:byte(1) + data:byte(2) * 256) * 625 + elseif(unit == F) then + t = (data:byte(1) + data:byte(2) * 256) * 1125 + 320000 + elseif(unit == K) then + t = (data:byte(1) + data:byte(2) * 256) * 625 + 2731500 + else + return nil end - -- print(data:byte(1,9)) - crc = ow.crc8(string.sub(data,1,8)) - -- print("CRC="..crc) - if (crc == data:byte(9)) then - if(unit == nil or unit == C) then - t = (data:byte(1) + data:byte(2) * 256) * 625 - elseif(unit == F) then - t = (data:byte(1) + data:byte(2) * 256) * 1125 + 320000 - elseif(unit == K) then - t = (data:byte(1) + data:byte(2) * 256) * 625 + 2731500 - else - return nil - end - t1 = t / 10000 - t2 = t % 10000 - -- print("Temperature="..t1.."."..t2.." Centigrade") - -- result = t1.."."..t2 - return t1, t2 - end - tmr.wdclr() + t1 = t / 10000 + t2 = t % 10000 + -- print("Temperature="..t1.."."..t2.." Centigrade") + -- result = t1.."."..t2 + return t1, t2 + end + tmr.wdclr() else - -- print("Device family is not recognized.") + -- print("Device family is not recognized.") end else - -- print("CRC is not valid!") + -- print("CRC is not valid!") end return result end function read(addr, unit) - t1, t2 = readNumber(addr, unit) - if((t1 == nil ) or (t2 ==nil)) then - return nil - else - return t1.."."..t2 - end + t1, t2 = readNumber(addr, unit) + if((t1 == nil ) or (t2 ==nil)) then + return nil + else + return t1.."."..t2 + end end -- Return module table -return M \ No newline at end of file +return M diff --git a/lua_modules/ds3231/ds3231-example.lua b/lua_modules/ds3231/ds3231-example.lua new file mode 100644 index 00000000..5f3fb90c --- /dev/null +++ b/lua_modules/ds3231/ds3231-example.lua @@ -0,0 +1,15 @@ +require("ds3231") + +-- ESP-01 GPIO Mapping +gpio0, gpio2 = 3, 4 + +ds3231.init(gpio0, gpio2) + +second, minute, hour, day, date, month, year = ds3231.getTime(); + +-- Get current time +print(string.format("Time & Date: %s:%s:%s %s/%s/%s", hour, minute, second, date, month, year)) + +-- Don't forget to release it after use +ds3231 = nil +package.loaded["ds3231"]=nil diff --git a/lua_modules/ds3231/ds3231-web.lua b/lua_modules/ds3231/ds3231-web.lua new file mode 100644 index 00000000..5a28e51f --- /dev/null +++ b/lua_modules/ds3231/ds3231-web.lua @@ -0,0 +1,54 @@ +require('ds3231') + +port = 80 + +-- ESP-01 GPIO Mapping +gpio0, gpio2 = 3, 4 + +days = { + [1] = "Sunday", + [2] = "Monday", + [3] = "Tuesday", + [4] = "Wednesday", + [5] = "Thursday", + [6] = "Friday", + [7] = "Saturday" +} + +months = { + [1] = "January", + [2] = "Febuary", + [3] = "March", + [4] = "April", + [5] = "May", + [6] = "June", + [7] = "July", + [8] = "August", + [9] = "September", + [10] = "October", + [11] = "November", + [12] = "December" +} + +ds3231.init(gpio0, gpio2) + +srv=net.createServer(net.TCP) +srv:listen(port, + function(conn) + + second, minute, hour, day, date, month, year = ds3231.getTime() + prettyTime = string.format("%s, %s %s %s %s:%s:%s", days[day], date, months[month], year, hour, minute, second) + + conn:send("HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. + "" .. + "" .. + "ESP8266
" .. + "Time and Date: " .. prettyTime .. "
" .. + "Node ChipID : " .. node.chipid() .. "
" .. + "Node MAC : " .. wifi.sta.getmac() .. "
" .. + "Node Heap : " .. node.heap() .. "
" .. + "Timer Ticks : " .. tmr.now() .. "
" .. + "") + conn:on("sent",function(conn) conn:close() end) + end +) \ No newline at end of file diff --git a/lua_modules/ds3231/ds3231.EN.md b/lua_modules/ds3231/ds3231.EN.md new file mode 100644 index 00000000..32b62527 --- /dev/null +++ b/lua_modules/ds3231/ds3231.EN.md @@ -0,0 +1,114 @@ +#DS3231 Module +##Require +```lua +ds3231 = require("ds3231") +``` +## Release +```lua +ds3231 = nil +package.loaded["ds3231"]=nil +``` + +##init() +####Description +Setting the pins of DS3231. + +####Syntax +init(sda, scl) + +####Parameters +sda: 1~10, IO index. +scl: 1~10, IO index. + +####Returns +nil + +####Example +```lua +ds3231 = require("ds3231") +ds3231.init(3, 4) +-- Don't forget to release it after use +ds3231 = nil +package.loaded["ds3231"]=nil +``` + +####See also +**-** []() + + + +## setTime() +####Description +Sets the current date and time. + +####Syntax +setTime(second, minute, hour, day, date, month, year) + +####Parameters +second: 00-59 +minute: 00-59 +hour: 00-23 +day: 1-7 (Sunday = 1, Saturday = 7) +date: 01-31 +month: 01-12 +year: 00-99 + +####Returns +nil + +####Example +```lua +ds3231 = require("ds3231") +ds3231.init(3, 4) + +-- Set date and time to Sunday, January 18th 2015 6:30PM +ds3231.setTime(0, 30, 18, 1, 18, 1, 15); + +-- Don't forget to release it after use +ds3231 = nil +package.loaded["ds3231"]=nil +``` + +####See also +**-** []() + + + +## getTime() +####Description +Get the current date and time. + +####Syntax +getTime() + +####Parameters +nil + +####Returns +second: integer. Second 00-59 +minute: integer. Minute 00-59 +hour: integer. Hour 00-23 +day: integer. Day 1-7 (Sunday = 1, Saturday = 7) +date: integer. Date 01-31 +month: integer. Month 01-12 +year: integer. Year 00-99 + +####Example +```lua +ds3231=require("ds3231") +ds3231.init(3, 4) + +-- Get date and time +second, minute, hour, day, date, month, year = ds3231.getTime(); + +-- Print date and time +print(string.format("Time & Date: %s:%s:%s %s/%s/%s", + hour, minute, second, date, month, year)) + +-- Don't forget to release it after use +ds3231 = nil +package.loaded["ds3231"]=nil + +``` +####See also +**-** []() \ No newline at end of file diff --git a/lua_modules/ds3231/ds3231.lua b/lua_modules/ds3231/ds3231.lua new file mode 100644 index 00000000..48b0fd86 --- /dev/null +++ b/lua_modules/ds3231/ds3231.lua @@ -0,0 +1,75 @@ +-------------------------------------------------------------------------------- +-- DS3231 I2C module for NODEMCU +-- NODEMCU TEAM +-- LICENCE: http://opensource.org/licenses/MIT +-- Tobie Booth +-------------------------------------------------------------------------------- + +local moduleName = ... +local M = {} +_G[moduleName] = M + +-- Default value for i2c communication +local id = 0 + +--device address +local dev_addr = 0x68 + +local function decToBcd(val) + return((val/10*16) + (val%10)) +end + +local function bcdToDec(val) + return((val/16*10) + (val%16)) +end + +-- initialize i2c +--parameters: +--d: sda +--l: scl +function M.init(d, l) + if (d ~= nil) and (l ~= nil) and (d >= 0) and (d <= 11) and (l >= 0) and ( l <= 11) and (d ~= l) then + sda = d + scl = l + else + print("iic config failed!") return nil + end + print("init done") + i2c.setup(id, sda, scl, i2c.SLOW) +end + +--get time from DS3231 +function M.getTime() + i2c.start(id) + i2c.address(id, dev_addr, i2c.TRANSMITTER) + i2c.write(id, 0x00) + i2c.stop(id) + i2c.start(id) + i2c.address(id, dev_addr, i2c.RECEIVER) + local c=i2c.read(id, 7) + i2c.stop(id) + return bcdToDec(tonumber(string.byte(c, 1))), + bcdToDec(tonumber(string.byte(c, 2))), + bcdToDec(tonumber(string.byte(c, 3))), + bcdToDec(tonumber(string.byte(c, 4))), + bcdToDec(tonumber(string.byte(c, 5))), + bcdToDec(tonumber(string.byte(c, 6))), + bcdToDec(tonumber(string.byte(c, 7))) +end + +--set time for DS3231 +function M.setTime(second, minute, hour, day, date, month, year) + i2c.start(id) + i2c.address(id, dev_addr, i2c.TRANSMITTER) + i2c.write(id, 0x00) + i2c.write(id, decToBcd(second)) + i2c.write(id, decToBcd(minute)) + i2c.write(id, decToBcd(hour)) + i2c.write(id, decToBcd(day)) + i2c.write(id, decToBcd(date)) + i2c.write(id, decToBcd(month)) + i2c.write(id, decToBcd(year)) + i2c.stop(id) +end + +return M diff --git a/lua_modules/http/http-example.lua b/lua_modules/http/http-example.lua new file mode 100644 index 00000000..5d3788e8 --- /dev/null +++ b/lua_modules/http/http-example.lua @@ -0,0 +1,39 @@ +------------------------------------------------------------------------------ +-- HTTP server Hello world example +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +------------------------------------------------------------------------------ +require("http").createServer(80, function(req, res) + -- analyse method and url + print("+R", req.method, req.url, node.heap()) + -- setup handler of headers, if any + req.onheader = function(self, name, value) + -- print("+H", name, value) + -- E.g. look for "content-type" header, + -- setup body parser to particular format + -- if name == "content-type" then + -- if value == "application/json" then + -- req.ondata = function(self, chunk) ... end + -- elseif value == "application/x-www-form-urlencoded" then + -- req.ondata = function(self, chunk) ... end + -- end + -- end + end + -- setup handler of body, if any + req.ondata = function(self, chunk) + print("+B", chunk and #chunk, node.heap()) + -- request ended? + if not chunk then + -- reply + --res:finish("") + res:send(nil, 200) + res:send_header("Connection", "close") + res:send("Hello, world!") + res:finish() + end + end + -- or just do something not waiting till body (if any) comes + --res:finish("Hello, world!") + --res:finish("Salut, monde!") +end) diff --git a/lua_modules/http/http.lua b/lua_modules/http/http.lua new file mode 100644 index 00000000..9f1e3f25 --- /dev/null +++ b/lua_modules/http/http.lua @@ -0,0 +1,212 @@ +------------------------------------------------------------------------------ +-- HTTP server module +-- +-- LICENCE: http://opensource.org/licenses/MIT +-- Vladimir Dronnikov +------------------------------------------------------------------------------ +local collectgarbage, tonumber, tostring = collectgarbage, tonumber, tostring + +local http +do + ------------------------------------------------------------------------------ + -- request methods + ------------------------------------------------------------------------------ + local make_req = function(conn, method, url) + local req = { + conn = conn, + method = method, + url = url, + } + -- return setmetatable(req, { + -- }) + return req + end + + ------------------------------------------------------------------------------ + -- response methods + ------------------------------------------------------------------------------ + local send = function(self, data, status) + local c = self.conn + -- TODO: req.send should take care of response headers! + if self.send_header then + c:send("HTTP/1.1 ") + c:send(tostring(status or 200)) + -- TODO: real HTTP status code/name table + c:send(" OK\r\n") + -- we use chunked transfer encoding, to not deal with Content-Length: + -- response header + self:send_header("Transfer-Encoding", "chunked") + -- TODO: send standard response headers, such as Server:, Date: + end + if data then + -- NB: no headers allowed after response body started + if self.send_header then + self.send_header = nil + -- end response headers + c:send("\r\n") + end + -- chunked transfer encoding + c:send(("%X\r\n"):format(#data)) + c:send(data) + c:send("\r\n") + end + end + local send_header = function(self, name, value) + local c = self.conn + -- NB: quite a naive implementation + c:send(name) + c:send(": ") + c:send(value) + c:send("\r\n") + end + -- finalize request, optionally sending data + local finish = function(self, data, status) + local c = self.conn + -- NB: req.send takes care of response headers + if data then + self:send(data, status) + end + -- finalize chunked transfer encoding + c:send("0\r\n\r\n") + -- close connection + c:close() + end + -- + local make_res = function(conn) + local res = { + conn = conn, + } + -- return setmetatable(res, { + -- send_header = send_header, + -- send = send, + -- finish = finish, + -- }) + res.send_header = send_header + res.send = send + res.finish = finish + return res + end + + ------------------------------------------------------------------------------ + -- HTTP parser + ------------------------------------------------------------------------------ + local http_handler = function(handler) + return function(conn) + local req, res + local buf = "" + local method, url + local ondisconnect = function(conn) + collectgarbage("collect") + end + -- header parser + local cnt_len = 0 + local onheader = function(conn, k, v) + -- TODO: look for Content-Type: header + -- to help parse body + -- parse content length to know body length + if k == "content-length" then + cnt_len = tonumber(v) + end + if k == "expect" and v == "100-continue" then + conn:send("HTTP/1.1 100 Continue\r\n") + end + -- delegate to request object + if req and req.onheader then + req:onheader(k, v) + end + end + -- body data handler + local body_len = 0 + local ondata = function(conn, chunk) + -- NB: do not reset node in case of lengthy requests + tmr.wdclr() + -- feed request data to request handler + if not req or not req.ondata then return end + req:ondata(chunk) + -- NB: once length of seen chunks equals Content-Length: + -- onend(conn) is called + body_len = body_len + #chunk + -- print("-B", #chunk, body_len, cnt_len, node.heap()) + if body_len >= cnt_len then + req:ondata() + end + end + local onreceive = function(conn, chunk) + -- merge chunks in buffer + if buf then + buf = buf .. chunk + else + buf = chunk + end + -- consume buffer line by line + while #buf > 0 do + -- extract line + local e = buf:find("\r\n", 1, true) + if not e then break end + local line = buf:sub(1, e - 1) + buf = buf:sub(e + 2) + -- method, url? + if not method then + local i + -- NB: just version 1.1 assumed + _, i, method, url = line:find("^([A-Z]+) (.-) HTTP/1.1$") + if method then + -- make request and response objects + req = make_req(conn, method, url) + res = make_res(conn) + end + -- header line? + elseif #line > 0 then + -- parse header + local _, _, k, v = line:find("^([%w-]+):%s*(.+)") + -- header seems ok? + if k then + k = k:lower() + onheader(conn, k, v) + end + -- headers end + else + -- spawn request handler + -- NB: do not reset in case of lengthy requests + tmr.wdclr() + handler(req, res) + tmr.wdclr() + -- NB: we feed the rest of the buffer as starting chunk of body + ondata(conn, buf) + -- buffer no longer needed + buf = nil + -- NB: we explicitly reassign receive handler so that + -- next received chunks go directly to body handler + conn:on("receive", ondata) + -- parser done + break + end + end + end + conn:on("receive", onreceive) + conn:on("disconnection", ondisconnect) + end + end + + ------------------------------------------------------------------------------ + -- HTTP server + ------------------------------------------------------------------------------ + local srv + local createServer = function(port, handler) + -- NB: only one server at a time + if srv then srv:close() end + srv = net.createServer(net.TCP, 15) + -- listen + srv:listen(port, http_handler(handler)) + return srv + end + + ------------------------------------------------------------------------------ + -- HTTP server methods + ------------------------------------------------------------------------------ + http = { + createServer = createServer, + } +end + +return http diff --git a/lua_modules/si7021/si7021-example.lua b/lua_modules/si7021/si7021-example.lua new file mode 100644 index 00000000..5c6ed6cc --- /dev/null +++ b/lua_modules/si7021/si7021-example.lua @@ -0,0 +1,24 @@ + +tmr.alarm(0, 60000, 1, function() + + SDA_PIN = 6 -- sda pin, GPIO12 + SCL_PIN = 5 -- scl pin, GPIO14 + + si7021 = require("si7021") + si7021.init(SDA_PIN, SCL_PIN) + si7021.read(OSS) + h = si7021.getHumidity() + t = si7021.getTemperature() + + -- pressure in differents units + print("Humidity: "..(h / 100).."."..(h % 100).." %") + + -- temperature in degrees Celsius and Farenheit + print("Temperature: "..(t/100).."."..(t%100).." deg C") + print("Temperature: "..(9 * t / 500 + 32).."."..(9 * t / 50 % 10).." deg F") + + -- release module + si7021 = nil + package.loaded["si7021"]=nil + +end) diff --git a/lua_modules/si7021/si7021-lewei.lua b/lua_modules/si7021/si7021-lewei.lua new file mode 100644 index 00000000..28ac9189 --- /dev/null +++ b/lua_modules/si7021/si7021-lewei.lua @@ -0,0 +1,41 @@ + + --创建一个定时器 + tmr.alarm(0, 60000, 1, function() + + SDA_PIN = 6 -- sda pin, GPIO12 + SCL_PIN = 5 -- scl pin, GPIO14 + + si7021 = require("si7021") + si7021.init(SDA_PIN, SCL_PIN) + si7021.read(OSS) + Hum = si7021.getHumidity() + Temp = si7021.getTemperature() + + --定义数据变量格式 + PostData = "[{\"Name\":\"T\",\"Value\":\"" .. (Temp/100).."."..(Temp%100) .. "\"},{\"Name\":\"H\",\"Value\":\"" .. (Hum/100).."."..(Hum%100) .. "\"}]" + --创建一个TCP连接 + socket=net.createConnection(net.TCP, 0) + --域名解析IP地址并赋值 + socket:dns("www.lewei50.com", function(conn, ip) + ServerIP = ip + print("Connection IP:" .. ServerIP) + end) + --开始连接服务器 + socket:connect(80, ServerIP) + socket:on("connection", function(sck) end) + --HTTP请求头定义 + socket:send("POST /api/V1/gateway/UpdateSensors/yourID HTTP/1.1\r\n" .. + "Host: www.lewei50.com\r\n" .. + "Content-Length: " .. string.len(PostData) .. "\r\n" .. + "userkey: yourKEY\r\n\r\n" .. + PostData .. "\r\n") + --HTTP响应内容 + socket:on("receive", function(sck, response) + print(response) + end) + + -- release module + si7021 = nil + package.loaded["si7021"]=nil + + end) diff --git a/lua_modules/si7021/si7021.EN.md b/lua_modules/si7021/si7021.EN.md new file mode 100644 index 00000000..983bb978 --- /dev/null +++ b/lua_modules/si7021/si7021.EN.md @@ -0,0 +1,131 @@ +# si7021 module + +##Require +```lua +si7021 = require("si7021") +``` +## Release +```lua +si7021 = nil +package.loaded["si7021"]=nil +``` + +##init() +####Description +Setting the i2c pin of si7021.
+ +####Syntax +init(sda, scl) + +####Parameters +sda: 1~12, IO index.
+scl: 1~12, IO index.
+ +####Returns +nil + +####Example +```lua +si7021 = require("si7021") +gpio5 = 1 +gpio4 = 2 +sda = gpio5 +scl = gpio4 +si7021.init(sda, scl) +-- Don't forget to release it after use +si7021 = nil +package.loaded["si7021"]=nil +``` + +####See also +**-** []() + + +##read() +####Description +Read temperature and humidity from si7021.
+ +####Syntax +read() + +####Parameters +nil.
+ +####Returns +nil(Why?).
+ +####Example +```lua +si7021 = require("si7021") +sda = 1 +scl = 2 +si7021.init(sda, scl) +r = si7021.read() +print(r) +-- Don't forget to release it after use +si7021 = nil +package.loaded["si7021"]=nil +``` + +####See also +**-** []() + + +##getHumidity() +####Description +Get humidity from si7021.
+ +####Syntax +getHumidity() + +####Parameters +nil.
+ +####Returns +h: Integer, humidity from si7021. + +####Example +```lua +si7021 = require("si7021") +sda = 1 +scl = 2 +si7021.init(sda, scl) +h = si7021.getHumidity() +print(h) +-- Don't forget to release it after use +si7021 = nil +package.loaded["si7021"]=nil +``` + +####See also +**-** []() + + +##getTemperature() +####Description +Get temperature from si7021.
+ +####Syntax +getTemperature() + +####Parameters +nil.
+ +####Returns +t: Integer, temperature from si7021. + +####Example +```lua +si7021 = require("si7021") +sda = 1 +scl = 2 +si7021.init(sda, scl) +t = si7021.getTemperature() +print(t) +-- Don't forget to release it after use +si7021 = nil +package.loaded["si7021"]=nil +``` + +####See also +**-** []() diff --git a/lua_modules/si7021/si7021.lua b/lua_modules/si7021/si7021.lua new file mode 100644 index 00000000..161e57d2 --- /dev/null +++ b/lua_modules/si7021/si7021.lua @@ -0,0 +1,104 @@ +-- *************************************************************************** +-- SI7021 module for ESP8266 with nodeMCU +-- Si7021 compatible tested 2015-1-22 +-- +-- Written by VIP6 +-- +-- MIT license, http://opensource.org/licenses/MIT +-- *************************************************************************** + +local moduleName = ... +local M = {} +_G[moduleName] = M + +--I2C slave address of Si70xx +local Si7021_ADDR = 0x40 + +--Commands +local CMD_MEASURE_HUMIDITY_HOLD = 0xE5 +local CMD_MEASURE_HUMIDITY_NO_HOLD = 0xF5 +local CMD_MEASURE_TEMPERATURE_HOLD = 0xE3 +local CMD_MEASURE_TEMPERATURE_NO_HOLD = 0xF3 + + +-- temperature and pressure +local t,h + +local init = false + +-- i2c interface ID +local id = 0 + +-- 16-bit two's complement +-- value: 16-bit integer +local function twoCompl(value) + if value > 32767 then value = -(65535 - value + 1) + end + return value +end + + + +-- read data from si7021 +-- ADDR: slave address +-- commands: Commands of si7021 +-- length: bytes to read +local function read_data(ADDR, commands, length) + i2c.start(id) + i2c.address(id, ADDR, i2c.TRANSMITTER) + i2c.write(id, commands) + i2c.stop(id) + i2c.start(id) + i2c.address(id, ADDR,i2c.RECEIVER) + tmr.delay(20000) + c = i2c.read(id, length) + i2c.stop(id) + return c +end + +-- initialize module +-- sda: SDA pin +-- scl SCL pin +function M.init(sda, scl) + i2c.setup(id, sda, scl, i2c.SLOW) + --print("i2c ok..") + init = true +end + +-- read humidity from si7021 +local function read_humi() + dataH = read_data(Si7021_ADDR, CMD_MEASURE_HUMIDITY_HOLD, 2) + UH = string.byte(dataH, 1) * 256 + string.byte(dataH, 2) + h = ((UH*12500+65536/2)/65536 - 600) + return(h) +end + +-- read temperature from si7021 +local function read_temp() + dataT = read_data(Si7021_ADDR, CMD_MEASURE_TEMPERATURE_HOLD, 2) + UT = string.byte(dataT, 1) * 256 + string.byte(dataT, 2) + t = ((UT*17572+65536/2)/65536 - 4685) + return(t) +end + +-- read temperature and humidity from si7021 +function M.read() + if (not init) then + print("init() must be called before read.") + else + read_humi() + read_temp() + end +end; + +-- get humidity +function M.getHumidity() + return h +end + +-- get temperature +function M.getTemperature() + return t +end + +return M diff --git a/pre_build/0.9.5/nodemcu_latest.bin b/pre_build/0.9.5/nodemcu_20150108.bin similarity index 100% rename from pre_build/0.9.5/nodemcu_latest.bin rename to pre_build/0.9.5/nodemcu_20150108.bin diff --git a/pre_build/0.9.5/nodemcu_20150118.bin b/pre_build/0.9.5/nodemcu_20150118.bin new file mode 100644 index 00000000..3fd92e0b Binary files /dev/null and b/pre_build/0.9.5/nodemcu_20150118.bin differ diff --git a/pre_build/0.9.5/nodemcu_20150123.bin b/pre_build/0.9.5/nodemcu_20150123.bin new file mode 100644 index 00000000..05966712 Binary files /dev/null and b/pre_build/0.9.5/nodemcu_20150123.bin differ diff --git a/pre_build/0.9.5/nodemcu_20150126.bin b/pre_build/0.9.5/nodemcu_20150126.bin new file mode 100644 index 00000000..4e3e63a7 Binary files /dev/null and b/pre_build/0.9.5/nodemcu_20150126.bin differ diff --git a/pre_build/latest/nodemcu_latest.bin b/pre_build/latest/nodemcu_latest.bin index f72815c2..17112015 100644 Binary files a/pre_build/latest/nodemcu_latest.bin and b/pre_build/latest/nodemcu_latest.bin differ diff --git a/tools/.gitattributes b/tools/.gitattributes new file mode 100644 index 00000000..b93363f7 --- /dev/null +++ b/tools/.gitattributes @@ -0,0 +1,11 @@ +# Enforce Unix newlines +*.css text eol=lf +*.html text eol=lf +*.js text eol=lf +*.json text eol=lf +*.less text eol=lf +*.md text eol=lf +*.svg text eol=lf +*.yml text eol=lf +*.py text eol=lf +*.sh text eol=lf