From 2c8961d153e8ecc30e1a32fed45944b87b441aef Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Mon, 6 Feb 2017 15:54:20 +0300 Subject: [PATCH 01/73] Add TTL support to net module (#1756) --- app/modules/net.c | 23 +++++++++++++++++++++++ docs/en/modules/net.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/app/modules/net.c b/app/modules/net.c index 4d92479a0c..7203d277f9 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -638,6 +638,27 @@ int net_dns( lua_State *L ) { return 0; } +// Lua: client/socket:ttl([ttl]) +int net_ttl( lua_State *L ) { + lnet_userdata *ud = net_get_udata(L); + if (!ud || ud->type == TYPE_TCP_SERVER) + return luaL_error(L, "invalid user data"); + if (!ud->pcb) + return luaL_error(L, "socket is not open/bound yet"); + int ttl = luaL_optinteger(L, 2, -1); + // Since `ttl` field is part of IP_PCB macro + // (which are at beginning of both udp_pcb/tcp_pcb) + // and PCBs declared as `union` there is safe to + // access ttl field without checking for type. + if (ttl == -1) { + ttl = ud->udp_pcb->ttl; + } else { + ud->udp_pcb->ttl = ttl; + } + lua_pushinteger(L, ttl); + return 1; +} + // Lua: client:getpeer() int net_getpeer( lua_State *L ) { lnet_userdata *ud = net_get_udata(L); @@ -951,6 +972,7 @@ static const LUA_REG_TYPE net_tcpsocket_map[] = { { LSTRKEY( "hold" ), LFUNCVAL( net_hold ) }, { LSTRKEY( "unhold" ), LFUNCVAL( net_unhold ) }, { LSTRKEY( "dns" ), LFUNCVAL( net_dns ) }, + { LSTRKEY( "ttl" ), LFUNCVAL( net_ttl ) }, { LSTRKEY( "getpeer" ), LFUNCVAL( net_getpeer ) }, { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) }, { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) }, @@ -964,6 +986,7 @@ static const LUA_REG_TYPE net_udpsocket_map[] = { { LSTRKEY( "on" ), LFUNCVAL( net_on ) }, { LSTRKEY( "send" ), LFUNCVAL( net_send ) }, { LSTRKEY( "dns" ), LFUNCVAL( net_dns ) }, + { LSTRKEY( "ttl" ), LFUNCVAL( net_ttl ) }, { LSTRKEY( "getaddr" ), LFUNCVAL( net_getaddr ) }, { LSTRKEY( "__gc" ), LFUNCVAL( net_delete ) }, { LSTRKEY( "__index" ), LROVAL( net_udpsocket_map ) }, diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 58a425d0b0..67e0fa117a 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -400,6 +400,29 @@ end) #### See also [`net.socket:on()`](#netsocketon) +## net.socket:ttl() + +Changes or retrieves Time-To-Live value on socket. + +#### Syntax +`ttl([ttl])` + +#### Parameters +- `ttl` (optional) new time-to-live value + +#### Returns +current / new ttl value + +#### Example +```lua +sk = net.createConnection(net.TCP, 0) +sk:connect(80, '192.168.1.1') +sk:ttl(1) -- restrict frames to single subnet +``` + +#### See also +[`net.createConnection()`](#netcreateconnection) + ## net.socket:unhold() Unblock TCP receiving data by revocation of a preceding `hold()`. @@ -492,6 +515,12 @@ Retrieve local port and ip of socket. The syntax and functional identical to [`net.socket:getaddr()`](#netsocketgetaddr). +## net.udpsocket:ttl() + +Changes or retrieves Time-To-Live value on socket. + +The syntax and functional identical to [`net.socket:ttl()`](#netsocketttl). + # net.dns Module ## net.dns.getdnsserver() From 416d53eb39418ebe45415306f88e22228cacdf93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Mon, 6 Feb 2017 13:55:26 +0100 Subject: [PATCH 02/73] Add string support for spi.set_mosi() and spi.get_miso() (#1753) clean-up endianess handling in spi driver --- app/driver/spi.c | 95 +++++++++++++++++++---------- app/include/driver/spi.h | 4 +- app/modules/spi.c | 127 +++++++++++++++++++-------------------- app/platform/platform.c | 28 +-------- app/platform/platform.h | 2 - docs/en/modules/spi.md | 13 +++- 6 files changed, 138 insertions(+), 131 deletions(-) diff --git a/app/driver/spi.c b/app/driver/spi.c index e504d4bb46..41f2544d1e 100644 --- a/app/driver/spi.c +++ b/app/driver/spi.c @@ -19,13 +19,13 @@ void spi_lcd_mode_init(uint8 spi_no) if(spi_no>1) return; //handle invalid input number //bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock //bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock - if(spi_no==SPI){ + if(spi_no==SPI_SPI){ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005); //clear bit9,and bit8 PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode - }else if(spi_no==HSPI){ + }else if(spi_no==SPI_HSPI){ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9 PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode @@ -93,10 +93,10 @@ uint32_t spi_set_clkdiv(uint8 spi_no, uint32_t clock_div) WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK); // 80Mhz speed } - if(spi_no==SPI){ + if(spi_no==SPI_SPI){ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005 | (clock_div <= 1 ? 0x100 : 0)); } - else if(spi_no==HSPI){ + else if(spi_no==SPI_HSPI){ WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105 | (clock_div <= 1 ? 0x200 : 0)); } @@ -144,13 +144,13 @@ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_ spi_set_clkdiv(spi_no, clock_div); - if(spi_no==SPI){ + if(spi_no==SPI_SPI){ PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode } - else if(spi_no==HSPI){ + else if(spi_no==SPI_HSPI){ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode @@ -179,8 +179,16 @@ void spi_mast_byte_order(uint8 spi_no, uint8 order) *******************************************************************************/ void spi_mast_blkset(uint8 spi_no, size_t bitlen, const uint8 *data) { + size_t aligned_len = bitlen >> 3; + while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR); - os_memcpy((void *)SPI_W0(spi_no), (const void *)data, bitlen >> 3); + + if (aligned_len % 4) { + // length for memcpy needs to be aligned to uint32 bounday + // otherwise single byte writes are issued to the register and corrupt data + aligned_len += 4 - (aligned_len % 4); + } + os_memcpy((void *)SPI_W0(spi_no), (const void *)data, aligned_len); } /****************************************************************************** @@ -188,12 +196,29 @@ void spi_mast_blkset(uint8 spi_no, size_t bitlen, const uint8 *data) * Description : Copy a block of data from the MISO FIFO * Parameters : uint8 spi_no - SPI module number, Only "SPI" and "HSPI" are valid * size_t bitlen - number of bits to copy, multiple of 8 - * uint8 *data - pointer to data buffer + * uint8 *data - pointer to data buffer, the buffer must be able to + * accept a multiple of 4*8 bits *******************************************************************************/ void spi_mast_blkget(uint8 spi_no, size_t bitlen, uint8 *data) { + size_t aligned_len = bitlen >> 3; + while(READ_PERI_REG(SPI_CMD(spi_no)) & SPI_USR); - os_memcpy((void *)data, (void *)SPI_W0(spi_no), bitlen >> 3); + + if (aligned_len % 4) { + // length for memcpy needs to be aligned to uint32 bounday + // otherwise single byte reads are issued to the register and corrupt data + aligned_len += 4 - (aligned_len % 4); + } + os_memcpy((void *)data, (void *)SPI_W0(spi_no), aligned_len); +} + +static uint32 swap_endianess(uint32 n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000UL) >> 8) | + ((n & 0xff000000UL) >> 24); } /****************************************************************************** @@ -208,8 +233,8 @@ void spi_mast_blkget(uint8 spi_no, size_t bitlen, uint8 *data) *******************************************************************************/ void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data) { - uint8 wn, shift; spi_buf_t spi_buf; + uint8 wn, shift; if (spi_no > 1) return; // handle invalid input number @@ -226,8 +251,10 @@ void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data) // transfer Wn to buf spi_buf.word[1] = READ_PERI_REG(SPI_W0(spi_no) + wn*4); + spi_buf.word[1] = swap_endianess(spi_buf.word[1]); if (wn < 15) { spi_buf.word[0] = READ_PERI_REG(SPI_W0(spi_no) + (wn+1)*4); + spi_buf.word[0] = swap_endianess(spi_buf.word[0]); } shift = 64 - (offset & 0x1f) - bitlen; @@ -235,9 +262,9 @@ void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data) spi_buf.dword |= (uint64)data << shift; if (wn < 15) { - WRITE_PERI_REG(SPI_W0(spi_no) + (wn+1)*4, spi_buf.word[0]); + WRITE_PERI_REG(SPI_W0(spi_no) + (wn+1)*4, swap_endianess(spi_buf.word[0])); } - WRITE_PERI_REG(SPI_W0(spi_no) + wn*4, spi_buf.word[1]); + WRITE_PERI_REG(SPI_W0(spi_no) + wn*4, swap_endianess(spi_buf.word[1])); return; } @@ -269,8 +296,10 @@ uint32 spi_mast_get_miso(uint8 spi_no, uint16 offset, uint8 bitlen) // transfer Wn to buf spi_buf.word[1] = READ_PERI_REG(SPI_W0(spi_no) + wn*4); + spi_buf.word[1] = swap_endianess(spi_buf.word[1]); if (wn < 15) { spi_buf.word[0] = READ_PERI_REG(SPI_W0(spi_no) + (wn+1)*4); + spi_buf.word[0] = swap_endianess(spi_buf.word[0]); } result = (uint32)(spi_buf.dword >> (64 - ((offset & 0x1f) + bitlen))); @@ -419,12 +448,12 @@ void spi_slave_init(uint8 spi_no) //bit9 should be cleared when HSPI clock doesn't equal CPU clock //bit8 should be cleared when SPI clock doesn't equal CPU clock ////WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9//TEST - if(spi_no==SPI){ + if(spi_no==SPI_SPI){ PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1);//configure io to spi mode - }else if(spi_no==HSPI){ + }else if(spi_no==SPI_HSPI){ PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2);//configure io to spi mode PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2);//configure io to spi mode @@ -511,10 +540,10 @@ void hspi_master_readwrite_repeat(void) uint8 temp; os_timer_disarm(&timer2); - spi_byte_read_espslave(HSPI,&temp); + spi_byte_read_espslave(SPI_HSPI,&temp); temp++; - spi_byte_write_espslave(HSPI,temp); + spi_byte_write_espslave(SPI_HSPI,temp); os_timer_setfn(&timer2, (os_timer_func_t *)hspi_master_readwrite_repeat, NULL); os_timer_arm(&timer2, 500, 0); } @@ -570,23 +599,23 @@ void spi_slave_isr_handler(void *para) if(READ_PERI_REG(0x3ff00020)&BIT4){ //following 3 lines is to clear isr signal - CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI), 0x3ff); + CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI_SPI), 0x3ff); }else if(READ_PERI_REG(0x3ff00020)&BIT7){ //bit7 is for hspi isr, - regvalue=READ_PERI_REG(SPI_SLAVE(HSPI)); - CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI), + regvalue=READ_PERI_REG(SPI_SLAVE(SPI_HSPI)); + CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI_HSPI), SPI_TRANS_DONE_EN| SPI_SLV_WR_STA_DONE_EN| SPI_SLV_RD_STA_DONE_EN| SPI_SLV_WR_BUF_DONE_EN| SPI_SLV_RD_BUF_DONE_EN); - SET_PERI_REG_MASK(SPI_SLAVE(HSPI), SPI_SYNC_RESET); - CLEAR_PERI_REG_MASK(SPI_SLAVE(HSPI), + SET_PERI_REG_MASK(SPI_SLAVE(SPI_HSPI), SPI_SYNC_RESET); + CLEAR_PERI_REG_MASK(SPI_SLAVE(SPI_HSPI), SPI_TRANS_DONE| SPI_SLV_WR_STA_DONE| SPI_SLV_RD_STA_DONE| SPI_SLV_WR_BUF_DONE| SPI_SLV_RD_BUF_DONE); - SET_PERI_REG_MASK(SPI_SLAVE(HSPI), + SET_PERI_REG_MASK(SPI_SLAVE(SPI_HSPI), SPI_TRANS_DONE_EN| SPI_SLV_WR_STA_DONE_EN| SPI_SLV_RD_STA_DONE_EN| @@ -597,7 +626,7 @@ void spi_slave_isr_handler(void *para) GPIO_OUTPUT_SET(0, 0); idx=0; while(idx<8){ - recv_data=READ_PERI_REG(SPI_W0(HSPI)+(idx<<2)); + recv_data=READ_PERI_REG(SPI_W0(SPI_HSPI)+(idx<<2)); spi_data[idx<<2] = recv_data&0xff; spi_data[(idx<<2)+1] = (recv_data>>8)&0xff; spi_data[(idx<<2)+2] = (recv_data>>16)&0xff; @@ -627,15 +656,15 @@ void ICACHE_FLASH_ATTR set_miso_data() { if(GPIO_INPUT_GET(2)==0){ - WRITE_PERI_REG(SPI_W8(HSPI),0x05040302); - WRITE_PERI_REG(SPI_W9(HSPI),0x09080706); - WRITE_PERI_REG(SPI_W10(HSPI),0x0d0c0b0a); - WRITE_PERI_REG(SPI_W11(HSPI),0x11100f0e); - - WRITE_PERI_REG(SPI_W12(HSPI),0x15141312); - WRITE_PERI_REG(SPI_W13(HSPI),0x19181716); - WRITE_PERI_REG(SPI_W14(HSPI),0x1d1c1b1a); - WRITE_PERI_REG(SPI_W15(HSPI),0x21201f1e); + WRITE_PERI_REG(SPI_W8(SPI_HSPI),0x05040302); + WRITE_PERI_REG(SPI_W9(SPI_HSPI),0x09080706); + WRITE_PERI_REG(SPI_W10(SPI_HSPI),0x0d0c0b0a); + WRITE_PERI_REG(SPI_W11(SPI_HSPI),0x11100f0e); + + WRITE_PERI_REG(SPI_W12(SPI_HSPI),0x15141312); + WRITE_PERI_REG(SPI_W13(SPI_HSPI),0x19181716); + WRITE_PERI_REG(SPI_W14(SPI_HSPI),0x1d1c1b1a); + WRITE_PERI_REG(SPI_W15(SPI_HSPI),0x21201f1e); GPIO_OUTPUT_SET(2, 1); } } @@ -697,7 +726,7 @@ void ICACHE_FLASH_ATTR spi_test_init() { os_printf("spi init\n\r"); - spi_slave_init(HSPI); + spi_slave_init(SPI_HSPI); os_printf("gpio init\n\r"); gpio_init(); os_printf("spi task init \n\r"); diff --git a/app/include/driver/spi.h b/app/include/driver/spi.h index ecf4be053f..c37e985e62 100644 --- a/app/include/driver/spi.h +++ b/app/include/driver/spi.h @@ -8,8 +8,8 @@ #include "os_type.h" /*SPI number define*/ -#define SPI 0 -#define HSPI 1 +#define SPI_SPI 0 +#define SPI_HSPI 1 #define SPI_ORDER_LSB 0 #define SPI_ORDER_MSB 1 diff --git a/app/modules/spi.c b/app/modules/spi.c index bf60028a67..fcb64bc505 100644 --- a/app/modules/spi.c +++ b/app/modules/spi.c @@ -4,6 +4,8 @@ #include "lauxlib.h" #include "platform.h" +#include "driver/spi.h" + #define SPI_HALFDUPLEX 0 #define SPI_FULLDUPLEX 1 @@ -196,37 +198,35 @@ static int spi_recv( lua_State *L ) } // Lua: spi.set_mosi( id, offset, bitlen, data1, [data2], ..., [datan] ) +// Lua: spi.set_mosi( id, string ) static int spi_set_mosi( lua_State *L ) { - int id = luaL_checkinteger( L, 1 ); - int offset = luaL_checkinteger( L, 2 ); - int bitlen = luaL_checkinteger( L, 3 ); - int argn; + int id = luaL_checkinteger( L, 1 ); MOD_CHECK_ID( spi, id ); - if (offset < 0 || offset > 511) { - return luaL_error( L, "offset out of range" ); - } + if (lua_type( L, 2 ) == LUA_TSTRING) { + size_t len; + const char *data = luaL_checklstring( L, 2, &len ); + luaL_argcheck( L, 2, len <= 64, "out of range" ); - if (bitlen < 1 || bitlen > 32) { - return luaL_error( L, "bitlen out of range" ); - } + spi_mast_blkset( id, len * 8, data ); - if (lua_gettop( L ) < 4) { - return luaL_error( L, "too few args" ); - } + } else { + int offset = luaL_checkinteger( L, 2 ); + int bitlen = luaL_checkinteger( L, 3 ); - for (argn = 4; argn <= lua_gettop( L ); argn++, offset += bitlen ) - { - u32 data = ( u32 )luaL_checkinteger(L, argn ); + luaL_argcheck( L, 2, offset >= 0 && offset <= 511, "out of range" ); + luaL_argcheck( L, 3, bitlen >= 1 && bitlen <= 32, "out of range" ); - if (offset + bitlen > 512) { - return luaL_error( L, "data range exceeded > 512 bits" ); - } + for (int argn = 4; argn <= lua_gettop( L ); argn++, offset += bitlen ) { + u32 data = ( u32 )luaL_checkinteger(L, argn ); + + if (offset + bitlen > 512) { + return luaL_error( L, "data range exceeded > 512 bits" ); + } - if (PLATFORM_OK != platform_spi_set_mosi( id, offset, bitlen, data )) { - return luaL_error( L, "failed" ); + spi_mast_set_mosi( id, offset, bitlen, data ); } } @@ -234,72 +234,69 @@ static int spi_set_mosi( lua_State *L ) } // Lua: data = spi.get_miso( id, offset, bitlen, num ) +// Lua: string = spi.get_miso( id, len ) static int spi_get_miso( lua_State *L ) { - int id = luaL_checkinteger( L, 1 ); - int offset = luaL_checkinteger( L, 2 ); - int bitlen = luaL_checkinteger( L, 3 ); - int num = luaL_checkinteger( L, 4 ), i; + int id = luaL_checkinteger( L, 1 ); MOD_CHECK_ID( spi, id ); - if (offset < 0 || offset > 511) { - return luaL_error( L, "out of range" ); - } + if (lua_gettop( L ) == 2) { + uint8_t data[64]; + int len = luaL_checkinteger( L, 2 ); - if (bitlen < 1 || bitlen > 32) { - return luaL_error( L, "bitlen out of range" ); - } + luaL_argcheck( L, 2, len >= 1 && len <= 64, "out of range" ); - if (offset + bitlen * num > 512) { - return luaL_error( L, "out of range" ); - } + spi_mast_blkget( id, len * 8, data ); - for (i = 0; i < num; i++) - { - lua_pushinteger( L, platform_spi_get_miso( id, offset + (bitlen * i), bitlen ) ); + lua_pushlstring( L, data, len ); + return 1; + + } else { + int offset = luaL_checkinteger( L, 2 ); + int bitlen = luaL_checkinteger( L, 3 ); + int num = luaL_checkinteger( L, 4 ), i; + + luaL_argcheck( L, 2, offset >= 0 && offset <= 511, "out of range" ); + luaL_argcheck( L, 3, bitlen >= 1 && bitlen <= 32, "out of range" ); + + if (offset + bitlen * num > 512) { + return luaL_error( L, "out of range" ); + } + + for (i = 0; i < num; i++) { + lua_pushinteger( L, spi_mast_get_miso( id, offset + (bitlen * i), bitlen ) ); + } + return num; } - return num; } // Lua: spi.transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, mosi_bitlen, dummy_bitlen, miso_bitlen ) static int spi_transaction( lua_State *L ) { - int id = luaL_checkinteger( L, 1 ); - int cmd_bitlen = luaL_checkinteger( L, 2 ); - u16 cmd_data = ( u16 )luaL_checkinteger( L, 3 ); - int addr_bitlen = luaL_checkinteger( L, 4 ); - u32 addr_data = ( u32 )luaL_checkinteger( L, 5 ); - int mosi_bitlen = luaL_checkinteger( L, 6 ); - int dummy_bitlen = luaL_checkinteger( L, 7 ); - int miso_bitlen = luaL_checkinteger( L, 8 ); + int id = luaL_checkinteger( L, 1 ); MOD_CHECK_ID( spi, id ); - if (cmd_bitlen < 0 || cmd_bitlen > 16) { - return luaL_error( L, "cmd_bitlen out of range" ); - } + int cmd_bitlen = luaL_checkinteger( L, 2 ); + u16 cmd_data = ( u16 )luaL_checkinteger( L, 3 ); + luaL_argcheck( L, 2, cmd_bitlen >= 0 && cmd_bitlen <= 16, "out of range" ); - if (addr_bitlen < 0 || addr_bitlen > 32) { - return luaL_error( L, "addr_bitlen out of range" ); - } + int addr_bitlen = luaL_checkinteger( L, 4 ); + u32 addr_data = ( u32 )luaL_checkinteger( L, 5 ); + luaL_argcheck( L, 4, addr_bitlen >= 0 && addr_bitlen <= 32, "out of range" ); - if (mosi_bitlen < 0 || mosi_bitlen > 512) { - return luaL_error( L, "mosi_bitlen out of range" ); - } + int mosi_bitlen = luaL_checkinteger( L, 6 ); + luaL_argcheck( L, 6, mosi_bitlen >= 0 && mosi_bitlen <= 512, "out of range" ); - if (dummy_bitlen < 0 || dummy_bitlen > 256) { - return luaL_error( L, "dummy_bitlen out of range" ); - } + int dummy_bitlen = luaL_checkinteger( L, 7 ); + luaL_argcheck( L, 7, dummy_bitlen >= 0 && dummy_bitlen <= 256, "out of range" ); - if (miso_bitlen < -512 || miso_bitlen > 512) { - return luaL_error( L, "miso_bitlen out of range" ); - } + int miso_bitlen = luaL_checkinteger( L, 8 ); + luaL_argcheck( L, 8, miso_bitlen >= -512 && miso_bitlen <= 512, "out of range" ); - if (PLATFORM_OK != platform_spi_transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, - mosi_bitlen, dummy_bitlen, miso_bitlen) ) { - return luaL_error( L, "failed" ); - } + spi_mast_transaction( id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, + mosi_bitlen, dummy_bitlen, miso_bitlen ); return 0; } diff --git a/app/platform/platform.c b/app/platform/platform.c index 9c1ee6da5e..74605654c7 100755 --- a/app/platform/platform.c +++ b/app/platform/platform.c @@ -755,6 +755,8 @@ int platform_i2c_recv_byte( unsigned id, int ack ){ uint32_t platform_spi_setup( uint8_t id, int mode, unsigned cpol, unsigned cpha, uint32_t clock_div ) { spi_master_init( id, cpol, cpha, clock_div ); + // all platform functions assume LSB order for MOSI & MISO buffer + spi_mast_byte_order( id, SPI_ORDER_LSB ); return 1; } @@ -779,8 +781,6 @@ spi_data_type platform_spi_send_recv( uint8_t id, uint8_t bitlen, spi_data_type int platform_spi_blkwrite( uint8_t id, size_t len, const uint8_t *data ) { - spi_mast_byte_order( id, SPI_ORDER_LSB ); - while (len > 0) { size_t chunk_len = len > 64 ? 64 : len; @@ -791,8 +791,6 @@ int platform_spi_blkwrite( uint8_t id, size_t len, const uint8_t *data ) len -= chunk_len; } - spi_mast_byte_order( id, SPI_ORDER_MSB ); - return PLATFORM_OK; } @@ -802,8 +800,6 @@ int platform_spi_blkread( uint8_t id, size_t len, uint8_t *data ) os_memset( (void *)mosi_idle, 0xff, len > 64 ? 64 : len ); - spi_mast_byte_order( id, SPI_ORDER_LSB ); - while (len > 0 ) { size_t chunk_len = len > 64 ? 64 : len; @@ -815,29 +811,9 @@ int platform_spi_blkread( uint8_t id, size_t len, uint8_t *data ) len -= chunk_len; } - spi_mast_byte_order( id, SPI_ORDER_MSB ); - return PLATFORM_OK; } -int platform_spi_set_mosi( uint8_t id, uint16_t offset, uint8_t bitlen, spi_data_type data ) -{ - if (offset + bitlen > 512) - return PLATFORM_ERR; - - spi_mast_set_mosi( id, offset, bitlen, data ); - - return PLATFORM_OK; -} - -spi_data_type platform_spi_get_miso( uint8_t id, uint16_t offset, uint8_t bitlen ) -{ - if (offset + bitlen > 512) - return 0; - - return spi_mast_get_miso( id, offset, bitlen ); -} - int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data, uint8_t addr_bitlen, spi_data_type addr_data, uint16_t mosi_bitlen, uint8_t dummy_bitlen, int16_t miso_bitlen ) diff --git a/app/platform/platform.h b/app/platform/platform.h index 1071b0fe21..8861f99a27 100644 --- a/app/platform/platform.h +++ b/app/platform/platform.h @@ -113,8 +113,6 @@ void platform_spi_select( unsigned id, int is_select ); int platform_spi_blkwrite( uint8_t id, size_t len, const uint8_t *data ); int platform_spi_blkread( uint8_t id, size_t len, uint8_t *data ); -int platform_spi_set_mosi( uint8_t id, uint16_t offset, uint8_t bitlen, spi_data_type data ); -spi_data_type platform_spi_get_miso( uint8_t id, uint16_t offset, uint8_t bitlen ); int platform_spi_transaction( uint8_t id, uint8_t cmd_bitlen, spi_data_type cmd_data, uint8_t addr_bitlen, spi_data_type addr_data, uint16_t mosi_bitlen, uint8_t dummy_bitlen, int16_t miso_bitlen ); diff --git a/docs/en/modules/spi.md b/docs/en/modules/spi.md index abe3a98f91..0238facdab 100644 --- a/docs/en/modules/spi.md +++ b/docs/en/modules/spi.md @@ -119,7 +119,10 @@ transactions are initiated with full control over the hardware features. Extract data items from MISO buffer after `spi.transaction()`. #### Syntax -`data1[, data2[, ..., datan]] = spi.get_miso(id, offset, bitlen, num)` +```lua +data1[, data2[, ..., datan]] = spi.get_miso(id, offset, bitlen, num) +string = spi.get_miso(id, num) +``` #### Parameters - `id` SPI ID number: 0 for SPI, 1 for HSPI @@ -128,7 +131,7 @@ Extract data items from MISO buffer after `spi.transaction()`. - `num` number of data items to retrieve ####Returns -`num` data items +`num` data items or `string` #### See also [spi.transaction()](#spitransaction) @@ -137,13 +140,17 @@ Extract data items from MISO buffer after `spi.transaction()`. Insert data items into MOSI buffer for `spi.transaction()`. #### Syntax -`spi.set_mosi(id, offset, bitlen, data1[, data2[, ..., datan]])` +```lua +spi.set_mosi(id, offset, bitlen, data1[, data2[, ..., datan]]) +spi.set_mosi(id, string) +``` ####Parameters - `id` SPI ID number: 0 for SPI, 1 for HSPI - `offset` bit offset into MOSI buffer for inserting data1 and subsequent items - `bitlen` bit length of data1, data2, ... - `data` data items where `bitlen` number of bits are considered for the transaction. +- `string` send data to be copied into MOSI buffer at offset 0, bit length 8 #### Returns `nil` From b9d9f5856a44515bf03cc04801e8d99b79d6bbde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Mon, 6 Feb 2017 22:03:17 +0100 Subject: [PATCH 03/73] Fix cloud builder link --- docs/en/lua-developer-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/lua-developer-faq.md b/docs/en/lua-developer-faq.md index 2e6c2c96c1..eefdcf1c7d 100644 --- a/docs/en/lua-developer-faq.md +++ b/docs/en/lua-developer-faq.md @@ -340,6 +340,6 @@ Of course you should still use functions to structure your code and encapsulate ## Firmware and Lua app development ### How to save memory? -* The NodeMCU development team recommends that you consider using a tailored firmware build, which only includes the modules that you plan to use before developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. If you want an easy-to-use intermediate option then why note try the [cloud based NodeMCU custom build service](http://frightanic.com/NodeMCU-custom-build)?. +* The NodeMCU development team recommends that you consider using a tailored firmware build, which only includes the modules that you plan to use before developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. If you want an easy-to-use intermediate option then why note try the [cloud based NodeMCU custom build service](https://nodemcu-build.com)?. From 2ab28df92acd5e3c77d360e792b80685157396d7 Mon Sep 17 00:00:00 2001 From: Jason Follas Date: Wed, 8 Feb 2017 15:42:29 -0500 Subject: [PATCH 04/73] Support clearing WiFi config (#1761) * Add wifi.sta.clearconfig(). Adjust password validation to match 2.0.0 SDK rules (no min length enforced, i.e. for WEP) * Updat comments about WEP key not having a minimum * Documentation: add note about node.restore() to wifi.sta.clearconfig() docs, and add SDK verbiage to describe what node.restore() impacts. * Normaliz if statements * Convert leading tabs to leading spaces for consistency --- app/modules/wifi.c | 597 ++++++++++++++++++++++++++-------------- docs/en/modules/node.md | 4 +- docs/en/modules/wifi.md | 23 +- 3 files changed, 408 insertions(+), 216 deletions(-) diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 1ac9306f02..7ee0fbfb33 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -110,16 +110,16 @@ static void wifi_scan_done(void *arg, STATUS status) } if(getap_output_format==1) //use new format(BSSID : SSID, RSSI, Authmode, Channel) { - c_sprintf(temp,MACSTR, MAC2STR(bss_link->bssid)); - wifi_add_sprintf_field(L, temp, "%s,%d,%d,%d", - ssid, bss_link->rssi, bss_link->authmode, bss_link->channel); + c_sprintf(temp,MACSTR, MAC2STR(bss_link->bssid)); + wifi_add_sprintf_field(L, temp, "%s,%d,%d,%d", + ssid, bss_link->rssi, bss_link->authmode, bss_link->channel); NODE_DBG(MACSTR" : %s\n",MAC2STR(bss_link->bssid) , temp);//00 00 00 00 00 00 } - else//use old format(SSID : Authmode, RSSI, BSSID, Channel) + else //use old format(SSID : Authmode, RSSI, BSSID, Channel) { - wifi_add_sprintf_field(L, ssid, "%d,%d,"MACSTR",%d", - bss_link->authmode, bss_link->rssi, MAC2STR(bss_link->bssid),bss_link->channel); - NODE_DBG("%s : %s\n", ssid, temp); + wifi_add_sprintf_field(L, ssid, "%d,%d,"MACSTR",%d", + bss_link->authmode, bss_link->rssi, MAC2STR(bss_link->bssid),bss_link->channel); + NODE_DBG("%s : %s\n", ssid, temp); } bss_link = bss_link->next.stqe_next; @@ -130,7 +130,7 @@ static void wifi_scan_done(void *arg, STATUS status) lua_newtable( L ); } lua_call(L, 1, 0); - unregister_lua_cb(L, &wifi_scan_succeed); + unregister_lua_cb(L, &wifi_scan_succeed); } #ifdef WIFI_SMART_ENABLE @@ -144,15 +144,19 @@ static int wifi_start_smart( lua_State* L ) unsigned channel; int stack = 1; - if ( lua_isnumber(L, stack) ){ + if ( lua_isnumber(L, stack) ) + { channel = lua_tointeger(L, stack); stack++; - } else { + } + else + { channel = 6; } // luaL_checkanyfunction(L, stack); - if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ + 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(wifi_smart_succeed != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed); @@ -164,7 +168,9 @@ static int wifi_start_smart( lua_State* L ) if(wifi_smart_succeed == LUA_NOREF){ smart_begin(channel, NULL, NULL); - }else{ + } + else + { smart_begin(channel, (smart_succeed )wifi_smart_succeed_cb, L); } @@ -220,13 +226,25 @@ static int wifi_setmode( lua_State* L ) bool save_to_flash=true; mode = luaL_checkinteger( L, 1 ); luaL_argcheck(L, mode == STATION_MODE || mode == SOFTAP_MODE || mode == STATIONAP_MODE || mode == NULL_MODE, 1, "Invalid mode"); + if(!lua_isnoneornil(L, 2)) { - if(!lua_isboolean(L, 2)) luaL_typerror(L, 2, lua_typename(L, LUA_TBOOLEAN)); + if(!lua_isboolean(L, 2)) + { + luaL_typerror(L, 2, lua_typename(L, LUA_TBOOLEAN)); + } save_to_flash=lua_toboolean(L, 2); } - if(save_to_flash) wifi_set_opmode( (uint8_t)mode); - else wifi_set_opmode_current( (uint8_t)mode); + + if(save_to_flash) + { + wifi_set_opmode( (uint8_t)mode); + } + else + { + wifi_set_opmode_current( (uint8_t)mode); + } + mode = (unsigned)wifi_get_opmode(); lua_pushinteger( L, mode ); return 1; @@ -268,6 +286,7 @@ static int wifi_setphymode( lua_State* L ) if ( mode != PHY_MODE_11B && mode != PHY_MODE_11G && mode != PHY_MODE_11N ) return luaL_error( L, "wrong arg type" ); + wifi_set_phy_mode( (uint8_t)mode); mode = (unsigned)wifi_get_phy_mode(); lua_pushinteger( L, mode ); @@ -290,56 +309,55 @@ static int wifi_sleep(lua_State* L) sint8 wifi_fpm_do_sleep_return_value = 1; if(lua_isnumber(L, 1)) { - if(luaL_checknumber(L, 1) == 0) - { - desired_sleep_state = 0; - } - else if(luaL_checknumber(L, 1) == 1) - { - desired_sleep_state = 1; - } - } - if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1 ) - { - uint8 wifi_current_opmode = wifi_get_opmode(); - if (wifi_current_opmode == 1 || wifi_current_opmode == 3 ) - { - wifi_station_disconnect(); - } - // set WiFi mode to null mode - wifi_set_opmode(NULL_MODE); - // set force sleep type - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep_return_value = wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME); - if (wifi_fpm_do_sleep_return_value == 0) - { - FLAG_wifi_force_sleep_enabled = TRUE; - } - else - { - wifi_fpm_close(); - FLAG_wifi_force_sleep_enabled = FALSE; - } - + if(luaL_checknumber(L, 1) == 0) + { + desired_sleep_state = 0; + } + else if(luaL_checknumber(L, 1) == 1) + { + desired_sleep_state = 1; + } + } + if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1) + { + uint8 wifi_current_opmode = wifi_get_opmode(); + if (wifi_current_opmode == 1 || wifi_current_opmode == 3) + { + wifi_station_disconnect(); + } + // set WiFi mode to null mode + wifi_set_opmode(NULL_MODE); + // set force sleep type + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + wifi_fpm_open(); + wifi_fpm_do_sleep_return_value = wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME); + if (wifi_fpm_do_sleep_return_value == 0) + { + FLAG_wifi_force_sleep_enabled = TRUE; + } + else + { + wifi_fpm_close(); + FLAG_wifi_force_sleep_enabled = FALSE; + } } else if(FLAG_wifi_force_sleep_enabled && desired_sleep_state == 0) { - FLAG_wifi_force_sleep_enabled = FALSE; - // wake up to use WiFi again - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + FLAG_wifi_force_sleep_enabled = FALSE; + // wake up to use WiFi again + wifi_fpm_do_wakeup(); + wifi_fpm_close(); } if (desired_sleep_state == 1 && FLAG_wifi_force_sleep_enabled == FALSE) { - lua_pushnil(L); - lua_pushnumber(L, wifi_fpm_do_sleep_return_value); + lua_pushnil(L); + lua_pushnumber(L, wifi_fpm_do_sleep_return_value); } else { - lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); - lua_pushnil(L); + lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); + lua_pushnil(L); } return 2; } @@ -396,7 +414,9 @@ static int wifi_getip( lua_State* L, uint8_t mode ) if(pTempIp.ip.addr==0){ lua_pushnil(L); return 1; - } else { + } + else + { c_sprintf(temp, "%d.%d.%d.%d", IP2STR(&pTempIp.ip) ); lua_pushstring( L, temp ); c_sprintf(temp, "%d.%d.%d.%d", IP2STR(&pTempIp.netmask) ); @@ -416,8 +436,9 @@ static int wifi_getbroadcast( lua_State* L, uint8_t mode ) if(pTempIp.ip.addr==0){ lua_pushnil(L); return 1; - } else { - + } + else + { struct ip_addr broadcast_address; uint32 subnet_mask32 = pTempIp.netmask.addr & pTempIp.ip.addr; @@ -478,7 +499,7 @@ static int wifi_setip( lua_State* L, uint8_t mode ) return 1; } -// Lua: wifi.sta.getaplist +// Lua: wifi.sta.getapinfo static int wifi_station_get_ap_info4lua( lua_State* L ) { struct station_config config[5]; @@ -506,8 +527,8 @@ static int wifi_station_get_ap_info4lua( lua_State* L ) c_sprintf(debug_temp, " %-6d %-32s ", i, temp); #endif - memset(temp, 0, sizeof(temp)); - if(strlen(config[i].password) >= 8) + memset(temp, 0, sizeof(temp)); + if(strlen(config[i].password) > 0) /* WPA = min 8, WEP = min 5 ASCII characters for a 40-bit key */ { memcpy(temp, config[i].password, sizeof(config[i].password)); lua_pushstring(L, temp); @@ -517,7 +538,7 @@ static int wifi_station_get_ap_info4lua( lua_State* L ) c_sprintf(debug_temp + strlen(debug_temp), "%-64s ", temp); #endif - memset(temp, 0, sizeof(temp)); + memset(temp, 0, sizeof(temp)); if (config[i].bssid_set) { c_sprintf(temp, MACSTR, MAC2STR(config[i].bssid)); @@ -590,8 +611,16 @@ static int wifi_station_getconfig( lua_State* L, bool get_flash_cfg) { struct station_config sta_conf; char temp[sizeof(sta_conf.password)+1]; //max password length + '\0' - if(get_flash_cfg) wifi_station_get_config_default(&sta_conf); - else wifi_station_get_config(&sta_conf); + + if(get_flash_cfg) + { + wifi_station_get_config_default(&sta_conf); + } + else + { + wifi_station_get_config(&sta_conf); + } + if(sta_conf.ssid==0) { lua_pushnil(L); @@ -607,7 +636,7 @@ static int wifi_station_getconfig( lua_State* L, bool get_flash_cfg) lua_pushstring(L, temp); lua_setfield(L, -2, "ssid"); - if(strlen(sta_conf.password) >= 8) + if(strlen(sta_conf.password) > 0) /* WPA = min 8, WEP = min 5 ASCII characters for a 40-bit key */ { memset(temp, 0, sizeof(temp)); memcpy(temp, sta_conf.password, sizeof(sta_conf.password)); @@ -652,6 +681,36 @@ static int wifi_station_getconfig_default(lua_State *L) return wifi_station_getconfig(L, true); } +// Lua: wifi.sta.clearconfig() +static int wifi_station_clear_config ( lua_State* L ) +{ + struct station_config sta_conf; + bool auto_connect=true; + bool save_to_flash=true; + + memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid)); + memset(sta_conf.password, 0, sizeof(sta_conf.password)); + memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid)); + sta_conf.bssid_set=0; + + wifi_station_disconnect(); + + bool config_success; + if(save_to_flash) + { + config_success = wifi_station_set_config(&sta_conf); + } + else + { + config_success = wifi_station_set_config_current(&sta_conf); + } + + wifi_station_set_auto_connect((uint8)0); + + lua_pushboolean(L, config_success); + return 1; +} + // Lua: wifi.sta.config() static int wifi_station_config( lua_State* L ) { @@ -673,12 +732,18 @@ static int wifi_station_config( lua_State* L ) if( lua_isstring(L, -1) ) { const char *ssid = luaL_checklstring( L, -1, &sl ); - luaL_argcheck(L, ((sl>=1 && sl<=sizeof(sta_conf.ssid)) ), 1, "ssid: length:1-32"); + luaL_argcheck(L, ((sl>=0 && sl<=sizeof(sta_conf.ssid)) ), 1, "ssid: length:0-32"); /* Zero-length SSID is valid as a way to clear config */ memcpy(sta_conf.ssid, ssid, sl); } - else return luaL_argerror( L, 1, "ssid:not string" ); + else + { + return luaL_argerror( L, 1, "ssid:not string" ); + } + } + else + { + return luaL_argerror( L, 1, "ssid required" ); } - else return luaL_argerror( L, 1, "ssid required" ); lua_pop(L, 1); lua_getfield(L, 1, "pwd"); @@ -687,10 +752,13 @@ static int wifi_station_config( lua_State* L ) if( lua_isstring(L, -1) ) { const char *pwd = luaL_checklstring( L, -1, &pl ); - luaL_argcheck(L, ((pl>=8 && pl<=sizeof(sta_conf.password)) ), 1, "pwd: length:8-64"); + luaL_argcheck(L, ((pl>=0 && pl<=sizeof(sta_conf.password)) ), 1, "pwd: length:0-64"); /* WPA = min 8, WEP = min 5 ASCII characters for a 40-bit key */ memcpy(sta_conf.password, pwd, pl); } - else return luaL_argerror( L, 1, "pwd:not string" ); + else + { + return luaL_argerror( L, 1, "pwd:not string" ); + } } lua_pop(L, 1); @@ -704,7 +772,10 @@ static int wifi_station_config( lua_State* L ) ets_str2macaddr(sta_conf.bssid, macaddr); sta_conf.bssid_set = 1; } - else return luaL_argerror(L, 1, "bssid:not string"); + else + { + return luaL_argerror(L, 1, "bssid:not string"); + } } lua_pop(L, 1); @@ -715,29 +786,41 @@ static int wifi_station_config( lua_State* L ) { auto_connect=lua_toboolean(L, -1); } - else return luaL_argerror(L, 1, "auto:not boolean"); + else + { + return luaL_argerror(L, 1, "auto:not boolean"); + } } lua_pop(L, 1); lua_getfield(L, 1, "save"); if (!lua_isnil(L, -1)) { - if (lua_isboolean(L, -1)) save_to_flash=lua_toboolean(L, -1); - else return luaL_argerror(L, 1, "save:not boolean"); + if (lua_isboolean(L, -1)) + { + save_to_flash=lua_toboolean(L, -1); + } + else + { + return luaL_argerror(L, 1, "save:not boolean"); + } + } + else + { + save_to_flash=false; } - else save_to_flash=false; lua_pop(L, 1); } - else //to be depreciated + else //to be deprecated { const char *ssid = luaL_checklstring( L, 1, &sl ); - luaL_argcheck(L, ((sl>=1 && sl=0 && sl=8 && pl<=sizeof(sta_conf.password)) ), 2, "length:0 or 8-64"); + luaL_argcheck(L, (pl>=0 && pl<=sizeof(sta_conf.password)), 2, "length:0-64"); /* WPA = min 8, WEP = min 5 ASCII characters for a 40-bit key */ memcpy(sta_conf.password, password, pl); @@ -801,11 +884,20 @@ static int wifi_station_config( lua_State* L ) wifi_station_disconnect(); bool config_success; - if(save_to_flash) config_success = wifi_station_set_config(&sta_conf); - else config_success = wifi_station_set_config_current(&sta_conf); + if(save_to_flash) + { + config_success = wifi_station_set_config(&sta_conf); + } + else + { + config_success = wifi_station_set_config_current(&sta_conf); + } wifi_station_set_auto_connect((uint8)auto_connect); - if(auto_connect) wifi_station_connect(); + if(auto_connect) + { + wifi_station_connect(); + } lua_pushboolean(L, config_success); return 1; @@ -848,122 +940,143 @@ static int wifi_station_listap( lua_State* L ) if (lua_type(L, 1)==LUA_TTABLE) { - char ssid[32]; - char bssid[6]; - uint8 channel=0; - uint8 show_hidden=0; - size_t len; - - lua_getfield(L, 1, "ssid"); - if (!lua_isnil(L, -1)){ /* found? */ - if( lua_isstring(L, -1) ) // deal with the ssid string - { - const char *ssidstr = luaL_checklstring( L, -1, &len ); - if(len>32) - return luaL_error( L, "ssid:<32" ); - c_memset(ssid, 0, 32); - c_memcpy(ssid, ssidstr, len); - scan_cfg.ssid=ssid; - NODE_DBG(scan_cfg.ssid); - NODE_DBG("\n"); - } - else - return luaL_error( L, "wrong arg type" ); - } - else - scan_cfg.ssid=NULL; - - lua_getfield(L, 1, "bssid"); - if (!lua_isnil(L, -1)){ /* found? */ - if( lua_isstring(L, -1) ) // deal with the ssid string - { - const char *macaddr = luaL_checklstring( L, -1, &len ); - luaL_argcheck(L, len==17, 1, INVALID_MAC_STR); - c_memset(bssid, 0, 6); - ets_str2macaddr(bssid, macaddr); - scan_cfg.bssid=bssid; - NODE_DBG(MACSTR, MAC2STR(scan_cfg.bssid)); - NODE_DBG("\n"); - - } - else - return luaL_error( L, "wrong arg type" ); - } - else - scan_cfg.bssid=NULL; - - - lua_getfield(L, 1, "channel"); - if (!lua_isnil(L, -1)){ /* found? */ - if( lua_isnumber(L, -1) ) // deal with the ssid string - { - channel = luaL_checknumber( L, -1); - if(!(channel>=0 && channel<=13)) - return luaL_error( L, "channel: 0 or 1-13" ); - scan_cfg.channel=channel; - NODE_DBG("%d\n", scan_cfg.channel); - } - else - return luaL_error( L, "wrong arg type" ); - } - else - scan_cfg.channel=0; - - lua_getfield(L, 1, "show_hidden"); - if (!lua_isnil(L, -1)){ /* found? */ - if( lua_isnumber(L, -1) ) // deal with the ssid string - { - show_hidden = luaL_checknumber( L, -1); - if(show_hidden!=0 && show_hidden!=1) - return luaL_error( L, "show_hidden: 0 or 1" ); - scan_cfg.show_hidden=show_hidden; - NODE_DBG("%d\n", scan_cfg.show_hidden); - - } - else - return luaL_error( L, "wrong arg type" ); - } - else - scan_cfg.show_hidden=0; - if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) - { - lua_pushnil(L); - lua_insert(L, 2); - } - lua_pop(L, -4); - } - else if (lua_type(L, 1) == LUA_TNUMBER) - { - lua_pushnil(L); - lua_insert(L, 1); - } - else if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) - { - lua_pushnil(L); - lua_insert(L, 1); - lua_pushnil(L); - lua_insert(L, 1); + char ssid[32]; + char bssid[6]; + uint8 channel=0; + uint8 show_hidden=0; + size_t len; + + lua_getfield(L, 1, "ssid"); + if (!lua_isnil(L, -1)) /* found? */ + { + if( lua_isstring(L, -1) ) // deal with the ssid string + { + const char *ssidstr = luaL_checklstring( L, -1, &len ); + if(len>32) + return luaL_error( L, "ssid:<32" ); + c_memset(ssid, 0, 32); + c_memcpy(ssid, ssidstr, len); + scan_cfg.ssid=ssid; + NODE_DBG(scan_cfg.ssid); + NODE_DBG("\n"); + } + else + { + return luaL_error( L, "wrong arg type" ); + } + } + else + { + scan_cfg.ssid=NULL; + } + + lua_getfield(L, 1, "bssid"); + if (!lua_isnil(L, -1)) /* found? */ + { + if( lua_isstring(L, -1) ) // deal with the ssid string + { + const char *macaddr = luaL_checklstring( L, -1, &len ); + luaL_argcheck(L, len==17, 1, INVALID_MAC_STR); + c_memset(bssid, 0, 6); + ets_str2macaddr(bssid, macaddr); + scan_cfg.bssid=bssid; + NODE_DBG(MACSTR, MAC2STR(scan_cfg.bssid)); + NODE_DBG("\n"); + + } + else + { + return luaL_error( L, "wrong arg type" ); + } + } + else + { + scan_cfg.bssid=NULL; + } + + + lua_getfield(L, 1, "channel"); + if (!lua_isnil(L, -1)) /* found? */ + { + if( lua_isnumber(L, -1) ) // deal with the ssid string + { + channel = luaL_checknumber( L, -1); + if(!(channel>=0 && channel<=13)) + return luaL_error( L, "channel: 0 or 1-13" ); + scan_cfg.channel=channel; + NODE_DBG("%d\n", scan_cfg.channel); + } + else + { + return luaL_error( L, "wrong arg type" ); + } + } + else + { + scan_cfg.channel=0; + } + + lua_getfield(L, 1, "show_hidden"); + if (!lua_isnil(L, -1)) /* found? */ + { + if( lua_isnumber(L, -1) ) // deal with the ssid string + { + show_hidden = luaL_checknumber( L, -1); + if(show_hidden!=0 && show_hidden!=1) + return luaL_error( L, "show_hidden: 0 or 1" ); + scan_cfg.show_hidden=show_hidden; + NODE_DBG("%d\n", scan_cfg.show_hidden); + + } + else + { + return luaL_error( L, "wrong arg type" ); + } + } + else + { + scan_cfg.show_hidden=0; + } + + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 2); + } + lua_pop(L, -4); + } + else if (lua_type(L, 1) == LUA_TNUMBER) + { + lua_pushnil(L); + lua_insert(L, 1); + } + else if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 1); + lua_pushnil(L); + lua_insert(L, 1); } else if(lua_isnil(L, 1)) { - if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) - { - lua_pushnil(L); - lua_insert(L, 2); - } + if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) + { + lua_pushnil(L); + lua_insert(L, 2); + } } else { - return luaL_error( L, "wrong arg type" ); + return luaL_error( L, "wrong arg type" ); } if (lua_type(L, 2) == LUA_TNUMBER) //this section changes the output format - { - getap_output_format=luaL_checkinteger( L, 2 ); - if ( getap_output_format != 0 && getap_output_format != 1) - return luaL_error( L, "wrong arg type" ); - } + { + getap_output_format=luaL_checkinteger( L, 2 ); + if (getap_output_format != 0 && getap_output_format != 1) + return luaL_error( L, "wrong arg type" ); + } NODE_DBG("Use alternate output format: %d\n", getap_output_format); if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION) { @@ -972,16 +1085,16 @@ static int wifi_station_listap( lua_State* L ) if (lua_type(L, 1)==LUA_TTABLE) { - wifi_station_scan(&scan_cfg,wifi_scan_done); + wifi_station_scan(&scan_cfg,wifi_scan_done); } else { - wifi_station_scan(NULL,wifi_scan_done); + wifi_station_scan(NULL,wifi_scan_done); } } else { - unregister_lua_cb(L, &wifi_scan_succeed); + unregister_lua_cb(L, &wifi_scan_succeed); } return 0; } @@ -1007,9 +1120,9 @@ static bool wifi_sta_sethostname(const char *hostname, size_t len) for (int i=1; i= 0 && lint < AUTH_MAX), 1, "auth: Range:0-4"); config.authmode = (uint8_t)luaL_checkinteger(L, -1); } - else return luaL_argerror(L, 1, "auth: not number"); + else + { + return luaL_argerror(L, 1, "auth: not number"); + } } lua_pop(L, 1); @@ -1245,8 +1385,10 @@ static int wifi_ap_config( lua_State* L ) luaL_argcheck(L, (lint >= 1 && lint <= 13), 1, "channel: Range:1-13"); config.channel = (uint8_t)lint; } - else luaL_argerror(L, 1, "channel: not number"); - + else + { + luaL_argerror(L, 1, "channel: not number"); + } } else { @@ -1261,13 +1403,23 @@ static int wifi_ap_config( lua_State* L ) Ltype_tmp=lua_type(L, -1); if(Ltype_tmp==LUA_TNUMBER||Ltype_tmp==LUA_TBOOLEAN) { - if(Ltype_tmp==LUA_TNUMBER)lint=luaL_checkinteger(L, -1); - if(Ltype_tmp==LUA_TBOOLEAN)lint=(lua_Number)lua_toboolean(L, -1); + if(Ltype_tmp==LUA_TNUMBER) + { + lint=luaL_checkinteger(L, -1); + } + + if(Ltype_tmp==LUA_TBOOLEAN) + { + lint=(lua_Number)lua_toboolean(L, -1); + } luaL_argcheck(L, (lint == 0 || lint==1), 1, "hidden: 0 or 1"); config.ssid_hidden = (uint8_t)lint; } - else return luaL_argerror(L, 1, "hidden: not boolean"); + else + { + return luaL_argerror(L, 1, "hidden: not boolean"); + } } else { @@ -1286,7 +1438,10 @@ static int wifi_ap_config( lua_State* L ) config.max_connection = (uint8_t)lint; } - else return luaL_argerror(L, 1, "max: not number"); + else + { + return luaL_argerror(L, 1, "max: not number"); + } } else { @@ -1304,7 +1459,10 @@ static int wifi_ap_config( lua_State* L ) luaL_argcheck(L, (lint >= 100 && lint <= 60000), 1, "beacon: 100-60000"); config.beacon_interval = (uint16_t)lint; } - else return luaL_argerror(L, 1, "beacon: not number"); + else + { + return luaL_argerror(L, 1, "beacon: not number"); + } } else { @@ -1320,7 +1478,10 @@ static int wifi_ap_config( lua_State* L ) { save_to_flash=lua_toboolean(L, -1); } - else return luaL_argerror(L, 1, "save: not boolean"); + else + { + return luaL_argerror(L, 1, "save: not boolean"); + } } lua_pop(L, 1); @@ -1342,8 +1503,15 @@ static int wifi_ap_config( lua_State* L ) #endif bool config_success; - if(save_to_flash) config_success = wifi_softap_set_config(&config); - else config_success = wifi_softap_set_config_current(&config); + if(save_to_flash) + { + config_success = wifi_softap_set_config(&config); + } + else + { + config_success = wifi_softap_set_config_current(&config); + } + lua_pushboolean(L, config_success); return 1; } @@ -1433,6 +1601,7 @@ static int wifi_ap_dhcp_stop( lua_State* L ) static const LUA_REG_TYPE wifi_station_map[] = { { LSTRKEY( "autoconnect" ), LFUNCVAL( wifi_station_setauto ) }, { LSTRKEY( "changeap" ), LFUNCVAL( wifi_station_change_ap ) }, + { LSTRKEY( "clearconfig"), LFUNCVAL( wifi_station_clear_config ) }, { LSTRKEY( "config" ), LFUNCVAL( wifi_station_config ) }, { LSTRKEY( "connect" ), LFUNCVAL( wifi_station_connect4lua ) }, { LSTRKEY( "disconnect" ), LFUNCVAL( wifi_station_disconnect4lua ) }, diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index 3454fad2d0..4892e91336 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -280,7 +280,9 @@ none ## node.restore() -Restores system configuration to defaults using the SDK function `system_restore()`, which doesn't document precisely what it erases/restores. +Restores system configuration to defaults using the SDK function `system_restore()`, which is described in the documentation as: + +> Reset default settings of following APIs: `wifi_station_set_auto_connect`, `wifi_set_phy_mode`, `wifi_softap_set_config` related, `wifi_station_set_config` related, `wifi_set_opmode`, and APs’ information recorded by `#define AP_CACHE`. #### Syntax `node.restore()` diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 861db30456..15b7ab18aa 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -270,6 +270,26 @@ wifi.sta.changeap(4) - [`wifi.sta.getapinfo()`](#wifistagetapinfo) - [`wifi.sta.getapindex()`](#wifistagetapindex) +## wifi.sta.clearconfig() + +Clears the currently saved WiFi station configuration, erasing it from the flash. May be useful for certain factory-reset +scenarios when a full [`node.restore()`](node.md#noderestore) is not desired, or to prepare for using +[End-User Setup](enduser-setup) so that the SoftAP is able to lock onto a single hardware radio channel. + +#### Syntax +`wifi.sta.clearconfig()` + +#### Parameters +none + +#### Returns +- `true` Success +- `false` Failure + +#### See also +- [`wifi.sta.config()`](#wifistaconfig) +- [`node.restore()`](node.md#noderestore) + ## wifi.sta.config() Sets the WiFi station configuration. @@ -280,7 +300,7 @@ Sets the WiFi station configuration. #### Parameters - `station_config` table containing configuration data for station - `ssid` string which is less than 32 bytes. - - `pwd` string which is 8-64 or 0 bytes. Empty string indicates an open WiFi access point. + - `pwd` string which is 0-64. Empty string indicates an open WiFi access point. _Note: WPA requires a minimum of 8-characters, but the ESP8266 can also connect to a WEP access point (a 40-bit WEP key can be provided as its corresponding 5-character ASCII string)._ - `auto` defaults to true - `true` to enable auto connect and connect to access point, hence with `auto=true` there's no need to call [`wifi.sta.connect()`](#wifistaconnect) - `false` to disable auto connect and remain disconnected from access point @@ -332,6 +352,7 @@ wifi.sta.config(station_cfg) ``` #### See also +- [`wifi.sta.clearconfig()`](#wifistaclearconfig) - [`wifi.sta.connect()`](#wifistaconnect) - [`wifi.sta.disconnect()`](#wifistadisconnect) - [`wifi.sta.apinfo()`](#wifistaapinfo) From a21c3d3b11524e6104cce36199fe88180762abee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 9 Feb 2017 21:01:47 +0100 Subject: [PATCH 05/73] Fix markdown syntax --- docs/en/modules/rtcfifo.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/en/modules/rtcfifo.md b/docs/en/modules/rtcfifo.md index 489cb57b9b..8e712cf009 100644 --- a/docs/en/modules/rtcfifo.md +++ b/docs/en/modules/rtcfifo.md @@ -4,6 +4,7 @@ | 2015-06-26 | [DiUS](https://github.com/DiUS), [Johny Mattsson](https://github.com/jmattsson), Bernd Meyer | [Johny Mattsson](https://github.com/jmattsson) | [rtcfifo.c](../../../app/modules/rtcfifo.c)| The rtcfifo module implements a first-in,first-out storage intended for sensor readings. As the name suggests, it is backed by the [RTC](https://en.wikipedia.org/wiki/Real-time_clock) user memory and as such survives deep sleep cycles. Conceptually it can be thought of as a cyclic array of `{ timestamp, name, value }` tuples. Internally it uses a space-optimized storage format to allow the greatest number of samples to be kept. This comes with several trade-offs, and as such is not a one-solution-fits-all. Notably: + - Timestamps are stored with second-precision. - Sample frequency must be at least once every 8.5 minutes. This is a side-effect of delta-compression being used for the time stamps. - Values are limited to 16 bits of precision, but have a separate field for storing an E-n multiplier. This allows for high fidelity even when working with very small values. The effective range is thus 1E-7 to 65535. @@ -110,17 +111,13 @@ This function takes an optional configuration table as an argument. The followin ```lua -- Initialize with default values rtcfifo.prepare() -``` -```lua -- Use RTC slots 19 and up for variable storage rtcfifo.prepare({storage_begin=21, storage_end=128}) ``` ####See also -[`rtcfifo.ready()`](#rtcfifoready) - -####See also -[`rtcfifo.prepare()`](#rtcfifoprepare) +- [`rtcfifo.ready()`](#rtcfifoready) +- [`rtcfifo.prepare()`](#rtcfifoprepare) ## rtcfifo.put() From 00bc9403fe210b3b919ae3efa58828ddec11e08b Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Thu, 9 Feb 2017 15:53:12 -0500 Subject: [PATCH 06/73] Fix the adjusting of the time when there is a rollover (#1788) --- app/modules/gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/modules/gpio.c b/app/modules/gpio.c index 0b57c4620b..21f1d2fd05 100644 --- a/app/modules/gpio.c +++ b/app/modules/gpio.c @@ -43,7 +43,8 @@ static void gpio_intr_callback_task (task_param_t param, uint8 priority) // Now must be >= then . Add the missing bits if (then > (now & 0xffffff)) { - then += 0x1000000; + // Now must have rolled over since the interrupt -- back it down + now -= 0x1000000; } then = (then + (now & 0x7f000000)) & 0x7fffffff; From 4dbf979cb45de5318db56cbac7d1746aec6d9424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Fri, 10 Feb 2017 21:48:32 +0100 Subject: [PATCH 07/73] Document HSPI pin functions (#1790) --- docs/en/modules/spi.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/en/modules/spi.md b/docs/en/modules/spi.md index 0238facdab..a2ac4cf7da 100644 --- a/docs/en/modules/spi.md +++ b/docs/en/modules/spi.md @@ -10,6 +10,17 @@ For technical details of the underlying hardware refer to [metalphreak's ESP8266 The ESP hardware provides two SPI busses, with IDs 0, and 1, which map to pins generally labelled SPI and HSPI. If you are using any kind of development board which provides flash, then bus ID 0 (SPI) is almost certainly used for communicating with the flash chip. You probably want to choose bus ID 1 (HSPI) for your communication, as you will have uncontended use of it. +HSPI signals are fixed to the following IO indices and GPIO pins: + +| Signal | IO index | ESP8266 pin | +|-----------|----------|-------------| +| HSPI CLK | 5 | GPIO14 | +| HSPI /CS | 8 | GPIO15 | +| HSPI MOSI | 7 | GPIO13 | +| HSPI MISO | 6 | GPIO12 | + +See also [spi.setup()](#spisetup). + ## High Level Functions The high level functions provide a send & receive API for half- and full-duplex mode. Sent and received data items are restricted to 1 - 32 bit @@ -86,6 +97,8 @@ _, _, x = spi.send(1, 0, {255, 255, 255}) Set up the SPI configuration. Refer to [Serial Peripheral Interface Bus](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase) for details regarding the clock polarity and phase definition. +Calling `spi.setup()` will route the HSPI signals to the related pins, overriding previous configuration and control by the `gpio` module. It is possible to revert any pin back to gpio control if its HSPI functionality is not needed, just set the desired `gpio.mode()` for it. This is recommended especially for the HSPI /CS pin function in case that SPI slave-select is driven from a different pin by `gpio.write()` - the SPI engine would toggle pin 8 otherwise. + #### Syntax `spi.setup(id, mode, cpol, cpha, databits, clock_div[, duplex_mode])` @@ -109,6 +122,13 @@ Refer to [Serial Peripheral Interface Bus](https://en.wikipedia.org/wiki/Serial_ #### Returns Number: 1 +#### Example +```lua +spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8) +-- we won't be using the HSPI /CS line, so disable it again +gpio.mode(8, gpio.INPUT, gpio.PULLUP) +``` + ## Low Level Hardware Functions The low level functions provide a hardware-centric API for application scenarios that need to excercise more complex SPI transactions. The From 4dfa5cd7d61e3f887ac86a0a651ec8846238a9e1 Mon Sep 17 00:00:00 2001 From: FrankX Date: Sat, 11 Feb 2017 22:53:06 +0100 Subject: [PATCH 08/73] Correct BME280 IIR filter setting (#1787) --- app/modules/bme280.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/modules/bme280.c b/app/modules/bme280.c index f9f73b80bb..2450338697 100644 --- a/app/modules/bme280.c +++ b/app/modules/bme280.c @@ -249,8 +249,8 @@ static int bme280_lua_init(lua_State* L) { bme280_ossh = (!lua_isnumber(L, 5))?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 5)&bit3); // 5-th parameter: humidity oversampling - config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 4) // 7-th parameter: inactive duration in normal mode - | ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 1); // 8-th parameter: IIR filter + config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 5) // 7-th parameter: inactive duration in normal mode + | ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 2); // 8-th parameter: IIR filter full_init = !lua_isnumber(L, 9)?1:lua_tointeger(L, 9); // 9-th parameter: init the chip too NODE_DBG("mode: %x\nhumidity oss: %x\nconfig: %x\n", bme280_mode, bme280_ossh, config); @@ -493,4 +493,4 @@ static const LUA_REG_TYPE bme280_map[] = { { LNILKEY, LNILVAL} }; -NODEMCU_MODULE(BME280, "bme280", bme280_map, NULL); \ No newline at end of file +NODEMCU_MODULE(BME280, "bme280", bme280_map, NULL); From b26ed97246e7c9739f9b05e9f62391698afca063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 12 Feb 2017 17:04:37 +0100 Subject: [PATCH 09/73] Improve MQTT client example, fixes #1792 --- docs/en/modules/mqtt.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/en/modules/mqtt.md b/docs/en/modules/mqtt.md index 04e6a4bdc2..6609ac758a 100644 --- a/docs/en/modules/mqtt.md +++ b/docs/en/modules/mqtt.md @@ -48,18 +48,22 @@ m:on("message", function(client, topic, data) end) -- for TLS: m:connect("192.168.11.118", secure-port, 1) -m:connect("192.168.11.118", 1883, 0, function(client) print("connected") end, - function(client, reason) print("failed reason: "..reason) end) - --- Calling subscribe/publish only makes sense once the connection --- was successfully established. In a real-world application you want --- move those into the 'connect' callback or make otherwise sure the --- connection was established. - --- subscribe topic with qos = 0 -m:subscribe("/topic",0, function(client) print("subscribe success") end) --- publish a message with data = hello, QoS = 0, retain = 0 -m:publish("/topic","hello",0,0, function(client) print("sent") end) +m:connect("192.168.11.118", 1883, 0, function(client) + print("connected") + -- Calling subscribe/publish only makes sense once the connection + -- was successfully established. You can do that either here in the + -- 'connect' callback or you need to otherwise make sure the + -- connection was established (e.g. tracking connection status or in + -- m:on("connect", function)). + + -- subscribe topic with qos = 0 + client:subscribe("/topic", 0, function(client) print("subscribe success") end) + -- publish a message with data = hello, QoS = 0, retain = 0 + client:publish("/topic", "hello", 0, 0, function(client) print("sent") end) +end, +function(client, reason) + print("failed reason: " .. reason) +end) m:close(); -- you can call m:connect again From 0349c1e004cda7793e70463db121bde6b37d5e27 Mon Sep 17 00:00:00 2001 From: vsky Date: Sun, 12 Feb 2017 17:08:02 +0100 Subject: [PATCH 10/73] Improve BME280 code samples for negative values (#1794) --- docs/en/modules/bme280.md | 51 ++++++++++++--------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/docs/en/modules/bme280.md b/docs/en/modules/bme280.md index 212139b0f2..b5e67d57fb 100644 --- a/docs/en/modules/bme280.md +++ b/docs/en/modules/bme280.md @@ -137,27 +137,20 @@ QNH = bme280.qfe2qnh(P, alt) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) H, T = bme280.humi() -if T<0 then - print(string.format("T=-%d.%02d", -T/100, -T%100)) -else - print(string.format("T=%d.%02d", T/100, T%100)) -end + +local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T +print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) + D = bme280.dewpoint(H, T) -if D<0 then - print(string.format("dew_point=-%d.%02d", -D/100, -D%100)) -else - print(string.format("dew_point=%d.%02d", D/100, D%100)) -end +local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D +print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100)) -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure P = bme280.baro() curAlt = bme280.altitude(P, QNH) -if curAlt<0 then - print(string.format("altitude=-%d.%02d", -curAlt/100, -curAlt%100)) -else - print(string.format("altitude=%d.%02d", curAlt/100, curAlt%100)) -end +local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt +print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100)) ``` Or simpler and more efficient @@ -167,29 +160,20 @@ alt=320 -- altitude of the measurement place bme280.init(3, 4) T, P, H, QNH = bme280.read(alt) -if T<0 then - print(string.format("T=-%d.%02d", -T/100, -T%100)) -else - print(string.format("T=%d.%02d", T/100, T%100)) -end +local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T +print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) print(string.format("QFE=%d.%03d", P/1000, P%1000)) print(string.format("QNH=%d.%03d", QNH/1000, QNH%1000)) print(string.format("humidity=%d.%03d%%", H/1000, H%1000)) D = bme280.dewpoint(H, T) -if D<0 then - print(string.format("dew_point=-%d.%02d", -D/100, -D%100)) -else - print(string.format("dew_point=%d.%02d", D/100, D%100)) -end +local Dsgn = (D < 0 and -1 or 1); D = Dsgn*D +print(string.format("dew_point=%s%d.%02d", Dsgn<0 and "-" or "", D/100, D%100)) -- altimeter function - calculate altitude based on current sea level pressure (QNH) and measure pressure P = bme280.baro() curAlt = bme280.altitude(P, QNH) -if curAlt<0 then - print(string.format("altitude=-%d.%02d", -curAlt/100, -curAlt%100)) -else - print(string.format("altitude=%d.%02d", curAlt/100, curAlt%100)) -end +local curAltsgn = (curAlt < 0 and -1 or 1); curAlt = curAltsgn*curAlt +print(string.format("altitude=%s%d.%02d", curAltsgn<0 and "-" or "", curAlt/100, curAlt%100)) ``` Use `bme280.init(sda, scl, 1, 3, 0, 3, 0, 4)` for "game mode" - Oversampling settings pressure ×4, temperature ×1, humidity ×0, sensor mode: normal mode, inactive duration = 0.5 ms, IIR filter settings filter coefficient 16. @@ -199,11 +183,8 @@ Example of readout in forced mode (asynchronous) bme280.init(3, 4, nil, nil, nil, 0) -- initialize to sleep mode bme280.startreadout(0, function () T, P = bme280.read() - if T<0 then - print(string.format("T=-%d.%02d", -T/100, -T%100)) - else - print(string.format("T=%d.%02d", T/100, T%100)) - end + local Tsgn = (T < 0 and -1 or 1); T = Tsgn*T + print(string.format("T=%s%d.%02d", Tsgn<0 and "-" or "", T/100, T%100)) end) ``` From b382a42057a264eba55e3781e18143f52e0b426e Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Wed, 22 Feb 2017 11:59:04 -0800 Subject: [PATCH 11/73] Fix debug message for wifi.sta.getrssi() (#1814) --- app/modules/wifi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 7ee0fbfb33..4415e84dc5 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -1168,7 +1168,7 @@ static int wifi_station_status( lua_State* L ) // Lua: wifi.sta.getrssi() static int wifi_station_getrssi( lua_State* L ){ sint8 rssival=wifi_station_get_rssi(); - NODE_DBG("\n\tRSSI is %i\n", rssival); + NODE_DBG("\n\tRSSI is %d\n", rssival); if (rssival<10) { lua_pushinteger(L, rssival); From 8931f09ce4146638609b124407e874578453ddce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Fri, 24 Feb 2017 21:20:09 +0100 Subject: [PATCH 12/73] Fix missing return code for ws2812_init() (#1816) --- app/modules/ws2812.c | 4 +++- docs/en/modules/ws2812.md | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/modules/ws2812.c b/app/modules/ws2812.c index c84c74561f..869cbcb08e 100644 --- a/app/modules/ws2812.c +++ b/app/modules/ws2812.c @@ -27,7 +27,7 @@ typedef struct { // Init UART1 to be able to stream WS2812 data to GPIO2 pin // If DUAL mode is selected, init UART0 to stream to TXD0 as well // You HAVE to redirect LUA's output somewhere else -static void ws2812_init(lua_State* L) { +static int ws2812_init(lua_State* L) { const int mode = luaL_optinteger(L, 1, MODE_SINGLE); luaL_argcheck(L, mode == MODE_SINGLE || mode == MODE_DUAL, 1, "ws2812.SINGLE or ws2812.DUAL expected"); @@ -57,6 +57,8 @@ static void ws2812_init(lua_State* L) { GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, BIT2); // Enable Function 2 for GPIO2 (U1TXD) PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); + + return 0; } // Stream data using UART1 routed to GPIO2 diff --git a/docs/en/modules/ws2812.md b/docs/en/modules/ws2812.md index 3bc0ef8e47..2b2ed56c59 100644 --- a/docs/en/modules/ws2812.md +++ b/docs/en/modules/ws2812.md @@ -15,14 +15,17 @@ through the serial port (it will be reconfigured to support WS2812-like protocol). If you want to keep access to Lua's console, you will have to use an other input channel like a TCP server (see [example](https://github.com/nodemcu/nodemcu-firmware/blob/master/examples/telnet.lua)) -## ws2812.init(mode) +## ws2812.init() Initialize UART1 and GPIO2, should be called once and before write(). Initialize UART0 (TXD0) too if `ws2812.MODE_DUAL` is set. +#### Syntax +`ws2812.init([mode])` + #### Parameters -- `mode` (optional) either `ws2812.MODE_SINGLE` (default if omitted) or `ws2812.MODE_DUAL`. -In `ws2812.MODE_DUAL` mode you will be able to handle two strips in parallel but will lose access -to Lua's serial console as it shares the same UART and PIN. +- `mode` (optional) either `ws2812.MODE_SINGLE` (default if omitted) or `ws2812.MODE_DUAL` + +In `ws2812.MODE_DUAL` mode you will be able to handle two strips in parallel but will lose access to Lua's serial console as it shares the same UART and PIN. #### Returns `nil` From 5feae3fee1debe6aef75b8836fa86f9967037f2f Mon Sep 17 00:00:00 2001 From: vsky Date: Sat, 25 Feb 2017 22:56:45 +0100 Subject: [PATCH 13/73] Re-write DS18B20 asynchronous example (#1820) --- lua_modules/ds18b20/ds18b20-example.lua | 41 +++-- lua_modules/ds18b20/ds18b20-web.lua | 43 +++-- lua_modules/ds18b20/ds18b20.EN.md | 157 +++------------- lua_modules/ds18b20/ds18b20.ZH.md | 160 ----------------- lua_modules/ds18b20/ds18b20.lua | 230 +++++++++++------------- 5 files changed, 176 insertions(+), 455 deletions(-) delete mode 100644 lua_modules/ds18b20/ds18b20.ZH.md diff --git a/lua_modules/ds18b20/ds18b20-example.lua b/lua_modules/ds18b20/ds18b20-example.lua index c349e43ab8..d8ab621be8 100644 --- a/lua_modules/ds18b20/ds18b20-example.lua +++ b/lua_modules/ds18b20/ds18b20-example.lua @@ -1,27 +1,26 @@ +-- encoder module is needed only for debug output; lines can be removed if no +-- debug output is needed and/or encoder module is missing + t = require("ds18b20") +pin = 3 -- gpio0 = 3, gpio2 = 4 --- ESP-01 GPIO Mapping -gpio0 = 3 -gpio2 = 4 +function readout(temp) + for addr, temp in pairs(temp) do + -- print(string.format("Sensor %s: %s 'C", addr, temp)) + print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) -- readable address with base64 encoding is preferred when encoder module is available + end -t.setup(gpio0) -addrs = t.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) + -- Module can be released when it is no longer needed + t = nil + package.loaded["ds18b20"]=nil end --- Just read temperature -print("Temperature: "..t.read().."'C") - --- Get temperature of first detected sensor in Fahrenheit -print("Temperature: "..t.read(nil,t.F).."'F") - --- Query the second detected sensor, get temperature in Kelvin -if (table.getn(addrs) >= 2) then - print("Second sensor: "..t.read(addrs[2],t.K).."'K") +-- t:readTemp(readout) -- default pin value is 3 +t:readTemp(readout, pin) +if t.sens then + print("Total number of DS18B20 sensors: "..table.getn(t.sens)) + for i, s in ipairs(t.sens) do + -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) + print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + end end - --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil diff --git a/lua_modules/ds18b20/ds18b20-web.lua b/lua_modules/ds18b20/ds18b20-web.lua index 7ff819798d..41182dfda8 100644 --- a/lua_modules/ds18b20/ds18b20-web.lua +++ b/lua_modules/ds18b20/ds18b20-web.lua @@ -1,27 +1,36 @@ -require('ds18b20') +t = require('ds18b20') port = 80 +pin = 3 -- gpio0 = 3, gpio2 = 4 +gconn = {} -- global variable for connection --- ESP-01 GPIO Mapping -gpio0, gpio2 = 3, 4 +function readout(temp) + local resp = "HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. + "" .. + "" .. + "ESP8266
" + + for addr, temp in pairs(temp) do + -- resp = resp .. string.format("Sensor %s: %s ℃
", addr, temp) + resp = resp .. string.format("Sensor %s: %s ℃
", encoder.toBase64(addr), temp) -- readable address with base64 encoding is preferred when encoder module is available + end + + resp = resp .. + "Node ChipID: " .. node.chipid() .. "
" .. + "Node MAC: " .. wifi.sta.getmac() .. "
" .. + "Node Heap: " .. node.heap() .. "
" .. + "Timer Ticks: " .. tmr.now() .. "
" .. + "" -ds18b20.setup(gpio0) + gconn:send(resp) + gconn:on("sent",function(conn) conn:close() end) +end srv=net.createServer(net.TCP) srv:listen(port, function(conn) - conn:send("HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. - "" .. - "" .. - "ESP8266
" .. - "Temperature : " .. ds18b20.read() .. "
" .. - "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) + gconn = conn + -- t:readTemp(readout) -- default pin value is 3 + t:readTemp(readout, pin) end ) - - diff --git a/lua_modules/ds18b20/ds18b20.EN.md b/lua_modules/ds18b20/ds18b20.EN.md index bdf69397cf..155bc29d82 100644 --- a/lua_modules/ds18b20/ds18b20.EN.md +++ b/lua_modules/ds18b20/ds18b20.EN.md @@ -8,150 +8,47 @@ ds18b20 = require("ds18b20") ds18b20 = nil package.loaded["ds18b20"]=nil ``` -##Constant -C, F, K - -##setup() -####Description -Setting the pin of DS18B20.
- -####Syntax -setup(pin) - -####Parameters -pin: 1~10, IO index. If parameter is nil, it will use pin 9(GPIO2) automatically.
- -####Returns -nil - -####Example -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` - -####See also -**-** []() - - - -## addrs() -####Description -Return a table contain all of the addresses of DS18B20 on one-wire. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
- -####Syntax -addrs() - -####Parameters -nil -####Returns -addrs: A table contain all of the addresses of DS18B20 on one-wire. Every address is a string. If failed, it will be nil.
-####Example -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) -addrs = ds18b20.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) -end --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` - -####See also -**-** []() +##readTemp() +Scans the bus for DS18B20 sensors, starts a readout (conversion) for all sensors and calls a callback function when all temperatures are available. Powered sensors are read at once first. Parasit-powered sensors are read one by one. The first parasit-powered sensor is read together with all powered sensors. +The module requires `ow` module. - -## readNumber() -####Description -Read the value of temperature. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
+The also module uses `encoder` module for printing debug information with more readable representation of sensor address (`encoder.toBase64()`). ####Syntax -readNumber(addr, unit) +`readTemp(callback, pin)` ####Parameters -addr: string, the address of DS18B20. It will select the first address which be found when this parameter is nil.
-unit: integer, unit conversion. Only Constant is acceptable, such as C(Celsius),F(Fahrenheit) and K(Kelvin). If this parameter is nil, the constant C(Celsius) will be selected automatically.
+- `callback` function that receives all results when all conversions finish. The callback funciton has one parameter - an array addressed by sensor addresses and a value of the temperature (string for integer version). +- `pin` pin of the one-wire bus. If nil, GPIO0 (3) is used. ####Returns -t1: integer. The integer part of the temperature. If it read fails, return nil.
-t2: integer. The fractional part of the temperature. If it read fails, return nil.
+nil ####Example ```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.readNumber(addrs[1],t.C)) -print(t.readNumber(addrs[1],t.F)) -print(t.readNumber(addrs[1],t.K)) --- The second DS18B20 -print(t.readNumber(addrs[2],t.C)) -print(t.readNumber(addrs[2],t.F)) -print(t.readNumber(addrs[2],t.K)) --- Just read -print(t.readNumber()) --- Just read as fahrenheit -print(t.readNumber(nil,t.F)) --- Read as values -t1, t2 = t.readNumber() --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -####See also -**-** []() +t = require("ds18b20") +pin = 3 -- gpio0 = 3, gpio2 = 4 - -## read() -####Description -Read the string of temperature. If the setup(pin) function not executed, the pin 9(GPIO2) will be initialized as one-wire mode automatically.
+function readout(temp) + for addr, temp in pairs(temp) do + print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) + end -####Syntax -read(addr, unit) - -####Parameters -addr: string, the address of DS18B20. It will select the first address which be found when this parameter is nil.
-unit: integer, unit conversion. Only Constant is acceptable, such as C(Celsius),F(Fahrenheit) and K(Kelvin). If this parameter is nil, the constant C(Celsius) will be selected automatically.
- -####Returns -t: string. The string of the temperature. If it read fails, return nil.
+ -- Module can be released when it is no longer needed + t = nil + package.loaded["ds18b20"]=nil +end -####Example -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.read(addrs[1],t.C)) -print(t.read(addrs[1],t.F)) -print(t.read(addrs[1],t.K)) --- The second DS18B20 -print(t.read(addrs[2],t.C)) -print(t.read(addrs[2],t.F)) -print(t.read(addrs[2],t.K)) --- Just read -print(t.read()) --- Just read as centigrade -print(t.read(nil,t.C)) --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil +-- t:readTemp(readout) -- default pin value is 3 +t:readTemp(readout, pin) +if t.sens then + print("Total number of DS18B20 sensors: "..table.getn(t.sens)) + for i, s in ipairs(t.sens) do + -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) + print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + end +end ``` -####See also -**-** []() - diff --git a/lua_modules/ds18b20/ds18b20.ZH.md b/lua_modules/ds18b20/ds18b20.ZH.md deleted file mode 100644 index c6092714ea..0000000000 --- a/lua_modules/ds18b20/ds18b20.ZH.md +++ /dev/null @@ -1,160 +0,0 @@ -#DS18B20 模块 -##引用 -```lua -ds18b20 = require("ds18b20") -``` -#释放 -```lua -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -##常量 -C, F, K - - -##setup() -####描述 -设置DS18B20所在的管脚(pin)。
- -####语法 -setup(pin) - -####参数 -pin: 1~10, IO 编号。如果参数为nil,会自动设定为9(GPIO2).
- -####返回值 -nil - -####示例 -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` - -####参见 -**-** []() - - - -## addrs() -####描述 -返回单总线上所有DS18B20器件的地址列表(table)。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -addrs() - -####参数 -nil -####返回值 -addrs: 返回包含单总线上所有DS18B20器件的地址列表(table)。其中地址是字符串类型(String)。如果失败则返回nil.
- -####示例 -```lua -ds18b20 = require("ds18b20") -ds18b20.setup(9) -addrs = ds18b20.addrs() -if (addrs ~= nil) then - print("Total DS18B20 sensors: "..table.getn(addrs)) -end --- Don't forget to release it after use -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` - -####参见 -**-** []() - - - -## readNumber() -####描述 -读取温度数值。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -readNumber(addr, unit) - -####参数 -addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。
-unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。
- -####返回值 -t1: 数值,温度的整数部分。如果读取失败返回nil.
-t2: 数值,温度的小数部分。如果读取失败返回nil.
- -####示例 -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.readNumber(addrs[1],t.C)) -print(t.readNumber(addrs[1],t.F)) -print(t.readNumber(addrs[1],t.K)) --- The second DS18B20 -print(t.readNumber(addrs[2],t.C)) -print(t.readNumber(addrs[2],t.F)) -print(t.readNumber(addrs[2],t.K)) --- Just read -print(t.readNumber()) --- Just read as fahrenheit -print(t.readNumber(nil,t.F)) --- Read as values -t1, t2 = t.readNumber() --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil - -``` -####参见 -**-** []() - - -## read() -####描述 -读取温度字符串。如果没有执行过setup(pin),则会自动对引脚9(GPIO2)进行单总线模式初始化。
- -####语法 -read(addr, unit) - -####参数 -addr: 字符串, DS18B20地址。 如果该参数为nil,会自动选择第一个发现的地址。
-unit: 单位转换,只接受常量C(摄氏度),F(华氏度), K(开氏度)。如果该参数为nil,会自动选择常量C(摄氏度) 。
- -####返回值 -t: 字符串,表示成字符串形式的温度。如果读取失败返回nil.
- -####示例 -```lua -t=require("ds18b20") -t.setup(9) -addrs=t.addrs() --- Total DS18B20 numbers, assume it is 2 -print(table.getn(addrs)) --- The first DS18B20 -print(t.read(addrs[1],t.C)) -print(t.read(addrs[1],t.F)) -print(t.read(addrs[1],t.K)) --- The second DS18B20 -print(t.read(addrs[2],t.C)) -print(t.read(addrs[2],t.F)) -print(t.read(addrs[2],t.K)) --- Just read -print(t.read()) --- Just read as centigrade -print(t.read(nil,t.C)) --- Don't forget to release it after use -t = nil -ds18b20 = nil -package.loaded["ds18b20"]=nil -``` -####参见 -**-** []() - diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index de2d869b88..7571ad3964 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -1,143 +1,119 @@ -------------------------------------------------------------------------------- -- DS18B20 one wire module for NODEMCU --- NODEMCU TEAM --- LICENCE: http://opensource.org/licenses/MIT --- Vowstar --- 2015/02/14 sza2 Fix for negative values +-- by @voborsky, @devsaurus +-- encoder module is needed only for debug output; lines can be removed if no +-- debug output is needed and/or encoder module is missing +-- +-- by default the module is for integer version, comment integer version and +-- uncomment float version part for float version -------------------------------------------------------------------------------- --- Set module name as parameter of require -local modname = ... -local M = {} -_G[modname] = M --------------------------------------------------------------------------------- --- Local used variables --------------------------------------------------------------------------------- --- DS18B20 dq pin -local pin = nil --- DS18B20 default pin -local defaultPin = 9 --------------------------------------------------------------------------------- --- Local used modules --------------------------------------------------------------------------------- --- Table module -local table = table --- String module -local string = string --- One wire module -local ow = ow --- Timer module -local tmr = tmr --- Limited to local environment -setfenv(1,M) --------------------------------------------------------------------------------- --- Implementation --------------------------------------------------------------------------------- -C = 'C' -F = 'F' -K = 'K' -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) +return({ + pin=3, + sens={}, + temp={}, + + conversion = function(self) + local pin = self.pin + for i,s in ipairs(self.sens) do + if s.status == 0 then + print("starting conversion:", encoder.toBase64(s.addr), s.parasite == 1 and "parasite" or " ") + ow.reset(pin) + ow.select(pin, s.addr) -- select the sensor + ow.write(pin, 0x44, 1) -- and start conversion + s.status = 1 + if s.parasite == 1 then break end -- parasite sensor blocks bus during conversion + end end - tmr.wdclr() - until (addr == nil) - ow.reset_search(pin) - return tbl -end - -function readNumber(addr, unit) - result = nil - setup(pin) - flag = false - if(addr == nil) then - ow.reset_search(pin) - count = 0 - repeat - count = count + 1 - addr = ow.search(pin) - tmr.wdclr() - until((addr ~= nil) or (count > 100)) + tmr.alarm(tmr.create(), 750, tmr.ALARM_SINGLE, function() self:readout() end) + end, + + readTemp = function(self, cb, lpin) + local pin = self.pin + self.cb = cb + self.temp={} + if lpin then pin = lpin end + ow.setup(pin) + + self.sens={} ow.reset_search(pin) - end - if(addr == nil) then - 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)) + -- ow.target_search(pin,0x28) + -- search the first device + local addr = ow.search(pin) + -- and loop through all devices + while addr do + -- search next device + local crc=ow.crc8(string.sub(addr,1,7)) + if (crc==addr:byte(8)) and ((addr:byte(1)==0x10) or (addr:byte(1)==0x28)) then + ow.reset(pin) + ow.select(pin, addr) -- select the found sensor + ow.write(pin, 0xB4, 1) -- Read Power Supply [B4h] + local parasite = (ow.read(pin)==0 and 1 or 0) + table.insert(self.sens,{addr=addr, parasite=parasite, status=0}) + print("contact: ", encoder.toBase64(addr), parasite == 1 and "parasite" or " ") end - -- print(data:byte(1,9)) - crc = ow.crc8(string.sub(data,1,8)) - -- print("CRC="..crc) - if (crc == data:byte(9)) then - t = (data:byte(1) + data:byte(2) * 256) - if (t > 32767) then - t = t - 65536 - end - if (addr:byte(1) == 0x28) then - t = t * 625 -- DS18B20, 4 fractional bits - else - t = t * 5000 -- DS18S20, 1 fractional bit - end + addr = ow.search(pin) + tmr.wdclr() + end + + -- place powered sensors first + table.sort(self.sens, function(a,b) return a.parasite 0x7fff) then t = t - 0x10000 end + if (s.addr:byte(1) == 0x28) then + t = t * 625 -- DS18B20, 4 fractional bits else - return nil + t = t * 5000 -- DS18S20, 1 fractional bit end - t = t / 10000 - return t + + -- integer version + local sgn = t<0 and -1 or 1 + local tA = sgn*t + local tH=tA/10000 + local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0) + + if tH and (tH~=85) then + self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL + print(encoder.toBase64(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) + s.status = 2 + end + -- end integer version + -- -- float version + -- if t and (math.floor(t/10000)~=85) then + -- self.temp[s.addr]=t + -- print(encoder.toBase64(s.addr), t) + -- s.status = 2 + -- end + -- -- end float version end - tmr.wdclr() + next = next or s.status == 0 + end + if next then + node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end) else - -- print("Device family is not recognized.") + self.sens = nil + if self.cb then + node.task.post(node.task.MEDIUM_PRIORITY, function() self.cb(self.temp) end) + end end - else - -- print("CRC is not valid!") - end - return result -end - -function read(addr, unit) - t = readNumber(addr, unit) - if (t == nil) then - return nil - else - return t + end -end +}) --- Return module table -return M From 2168e5185eaf12ef243d9321383767914cdb54ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Wed, 1 Mar 2017 12:41:17 +0100 Subject: [PATCH 14/73] Apply power selection at the end of the write slot while IRQs are masked (#1808) --- app/driver/onewire.c | 44 ++++++++++++++++++++---------------- app/include/driver/onewire.h | 2 +- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/driver/onewire.c b/app/driver/onewire.c index a52572c4f5..7a45b51ad0 100644 --- a/app/driver/onewire.c +++ b/app/driver/onewire.c @@ -122,15 +122,29 @@ uint8_t onewire_reset(uint8_t pin) // Write a bit. Port and bit is used to cut lookup time and provide // more certain timing. // -static void onewire_write_bit(uint8_t pin, uint8_t v) +static void onewire_write_bit(uint8_t pin, uint8_t v, uint8_t power) { if (v & 1) { - onewire_read_bit(pin); + noInterrupts(); + DIRECT_WRITE_LOW(pin); + delayMicroseconds(5); + if (power) { + DIRECT_WRITE_HIGH(pin); + } else { + DIRECT_MODE_INPUT(pin); // drive output high by the pull-up + } + delayMicroseconds(8); + interrupts(); + delayMicroseconds(52); } else { noInterrupts(); DIRECT_WRITE_LOW(pin); delayMicroseconds(65); - DIRECT_MODE_INPUT(pin); // drive output high by the pull-up + if (power) { + DIRECT_WRITE_HIGH(pin); + } else { + DIRECT_MODE_INPUT(pin); // drive output high by the pull-up + } interrupts(); delayMicroseconds(5); } @@ -157,9 +171,10 @@ static uint8_t onewire_read_bit(uint8_t pin) } // -// Write a byte. The writing code uses the active drivers to raise the +// Write a byte. The writing code uses the external pull-up to raise the // pin high, if you need power after the write (e.g. DS18S20 in -// parasite power mode) then set 'power' to 1, otherwise the pin will +// parasite power mode) then set 'power' to 1 and the output driver will +// be activated at the end of the write. Otherwise the pin will // go tri-state at the end of the write to avoid heating in a short or // other mishap. // @@ -167,26 +182,15 @@ void onewire_write(uint8_t pin, uint8_t v, uint8_t power /* = 0 */) { uint8_t bitMask; for (bitMask = 0x01; bitMask; bitMask <<= 1) { - onewire_write_bit(pin, (bitMask & v)?1:0); - } - if ( power ) { - noInterrupts(); - DIRECT_WRITE_HIGH(pin); - - interrupts(); + // send last bit with requested power mode + onewire_write_bit(pin, (bitMask & v)?1:0, bitMask & 0x80 ? power : 0); } } void onewire_write_bytes(uint8_t pin, const uint8_t *buf, uint16_t count, bool power /* = 0 */) { uint16_t i; for (i = 0 ; i < count ; i++) - onewire_write(pin, buf[i], owDefaultPower); - if ( power ) { - noInterrupts(); - DIRECT_WRITE_HIGH(pin); - - interrupts(); - } + onewire_write(pin, buf[i], i < count-1 ? owDefaultPower : power); } // @@ -360,7 +364,7 @@ uint8_t onewire_search(uint8_t pin, uint8_t *newAddr) ROM_NO[pin][rom_byte_number] &= ~rom_byte_mask; // serial number search direction write bit - onewire_write_bit(pin, search_direction); + onewire_write_bit(pin, search_direction, 0); // increment the byte counter id_bit_number // and shift the mask rom_byte_mask diff --git a/app/include/driver/onewire.h b/app/include/driver/onewire.h index 1fcd4cde94..c52492e7c1 100644 --- a/app/include/driver/onewire.h +++ b/app/include/driver/onewire.h @@ -74,7 +74,7 @@ void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count); // Write a bit. The bus is always left powered at the end, see // note in write() about that. -static void onewire_write_bit(uint8_t pin, uint8_t v); +static void onewire_write_bit(uint8_t pin, uint8_t v, uint8_t power); // Read a bit. static uint8_t onewire_read_bit(uint8_t pin); From f577c2c080667c333172f6aa570611f8a668805d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Wed, 1 Mar 2017 12:41:56 +0100 Subject: [PATCH 15/73] Sync uart configuration to Tx FIFO level (#1806) * sync uart configuration to tx fifo level * poll tx fifo empty before disabling interrupts * echo parameter is optional for uart.setup() --- app/driver/uart.c | 22 ++++++++++++++++++++++ docs/en/modules/uart.md | 10 +++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/driver/uart.c b/app/driver/uart.c index 8687712b7f..379d3da01d 100755 --- a/app/driver/uart.c +++ b/app/driver/uart.c @@ -43,6 +43,22 @@ static void (*alt_uart0_tx)(char txchar); LOCAL void ICACHE_RAM_ATTR uart0_rx_intr_handler(void *para); + +/****************************************************************************** + * FunctionName : uart_wait_tx_empty + * Description : Internal used function + * Wait for TX FIFO to become empty. + * Parameters : uart_no, use UART0 or UART1 defined ahead + * Returns : NONE +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +uart_wait_tx_empty(uint8 uart_no) +{ + while ((READ_PERI_REG(UART_STATUS(uart_no)) & (UART_TXFIFO_CNT< 0) + ; +} + + /****************************************************************************** * FunctionName : uart_config * Description : Internal used function @@ -54,6 +70,8 @@ uart0_rx_intr_handler(void *para); LOCAL void ICACHE_FLASH_ATTR uart_config(uint8 uart_no) { + uart_wait_tx_empty(uart_no); + if (uart_no == UART1) { PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); } else { @@ -98,6 +116,8 @@ uart_config(uint8 uart_no) void ICACHE_FLASH_ATTR uart0_alt(uint8 on) { + uart_wait_tx_empty(UART0); + if (on) { PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); @@ -348,6 +368,8 @@ uart_setup(uint8 uart_no) #ifdef BIT_RATE_AUTOBAUD uart_stop_autobaud(); #endif + // poll Tx FIFO empty outside before disabling interrupts + uart_wait_tx_empty(uart_no); ETS_UART_INTR_DISABLE(); uart_config(uart_no); ETS_UART_INTR_ENABLE(); diff --git a/docs/en/modules/uart.md b/docs/en/modules/uart.md index 49c376c90b..1b08cddc7a 100644 --- a/docs/en/modules/uart.md +++ b/docs/en/modules/uart.md @@ -67,10 +67,14 @@ end, 0) ## uart.setup() -(Re-)configures the communication parameters of the UART. +(Re-)configures the communication parameters of the UART. + +!!! note + + Bytes sent to the UART can get lost if this function re-configures the UART while reception is in progress. #### Syntax -`uart.setup(id, baud, databits, parity, stopbits, echo)` +`uart.setup(id, baud, databits, parity, stopbits[, echo])` #### Parameters - `id` always zero, only one uart supported @@ -78,7 +82,7 @@ end, 0) - `databits` one of 5, 6, 7, 8 - `parity` `uart.PARITY_NONE`, `uart.PARITY_ODD`, or `uart.PARITY_EVEN` - `stopbits` `uart.STOPBITS_1`, `uart.STOPBITS_1_5`, or `uart.STOPBITS_2` -- `echo` if 0, disable echo, otherwise enable echo +- `echo` if 0, disable echo, otherwise enable echo (default if omitted) #### Returns configured baud rate (number) From a26969b38857d6699bfa3103190e5a51df43c588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Wed, 1 Mar 2017 12:42:20 +0100 Subject: [PATCH 16/73] Add generic function to print deprecation notes (#1538) --- app/platform/platform.c | 5 +++++ app/platform/platform.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/app/platform/platform.c b/app/platform/platform.c index 74605654c7..8327e8b78e 100755 --- a/app/platform/platform.c +++ b/app/platform/platform.c @@ -916,3 +916,8 @@ uint32_t platform_flash_mapped2phys (uint32_t mapped_addr) uint32_t meg = (b1 << 1) | b0; return mapped_addr - INTERNAL_FLASH_MAPPED_ADDRESS + meg * 0x100000; } + +void* platform_print_deprecation_note( const char *msg, const char *time_frame) +{ + c_printf( "Warning, deprecated API! %s. It will be removed %s. See documentation for details.\n", msg, time_frame ); +} diff --git a/app/platform/platform.h b/app/platform/platform.h index 8861f99a27..a1ab812b96 100644 --- a/app/platform/platform.h +++ b/app/platform/platform.h @@ -302,6 +302,9 @@ int platform_gpio_exists( unsigned id ); int platform_tmr_exists( unsigned id ); // ***************************************************************************** + +void* platform_print_deprecation_note( const char *msg, const char *time_frame); + // Helper macros #define MOD_CHECK_ID( mod, id )\ if( !platform_ ## mod ## _exists( id ) )\ From f5652187a6beb3cd2ca7e802e706f65f4d8fdd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Thu, 2 Mar 2017 04:48:44 +0100 Subject: [PATCH 17/73] use flash size byte to determine the location of the init data for byte 107 (#1827) --- app/modules/adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/modules/adc.c b/app/modules/adc.c index 06068cd8cb..3ccd90aa44 100644 --- a/app/modules/adc.c +++ b/app/modules/adc.c @@ -29,7 +29,7 @@ static int adc_init107( lua_State *L ) { uint8_t byte107 = luaL_checkinteger (L, 1); - uint32 init_sector = flash_safe_get_sec_num () - 4; + uint32 init_sector = flash_rom_get_sec_num () - 4; // Note 32bit alignment so we can safely cast to uint32 for the flash api char init_data[SPI_FLASH_SEC_SIZE] __attribute__((aligned(4))); From d0622c30a066e93ae3d1cb8f33741328512a4b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 5 Mar 2017 17:10:17 +0100 Subject: [PATCH 18/73] Overhaul the tools section, fixes #1779 (#1831) --- docs/en/upload.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/en/upload.md b/docs/en/upload.md index 565a55bb7e..9ff9a3b075 100644 --- a/docs/en/upload.md +++ b/docs/en/upload.md @@ -1,8 +1,13 @@ As with [flashing](flash.md) there are several ways to upload code from your computer to the device. -Note that the NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. ESPlorer will do this automatically when changing the speed in the dropdown list. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps. +!!! note + + The NodeMCU serial interface uses 115'200bps at boot time. To change the speed after booting, issue `uart.setup(0,9600,8,0,1,1)`. If the device panics and resets at any time, errors will be written to the serial interface at 115'200 bps. # Tools +Transferring application code to ESP8266/8285 is an essential task, one that you'll perform quite frequently. Hence, it does make sense to try a few different uploading tools until you find one you feel comfortable with. [https://frightanic.com/iot/tools-ides-nodemcu/](https://frightanic.com/iot/tools-ides-nodemcu/) lists almost a dozen classical uploaders - in addition to IDEs or IDE-like applications which of course transfer code as well. + +The NodeMCU firmware team does not give any recommendations as for which uploader to use nor are there any "NodeMCU approved" tools. The below listed tools are just three, in no particular order, which seem popular and/or reasonably well maintained. ## ESPlorer @@ -12,7 +17,7 @@ Note that the NodeMCU serial interface uses 115'200bps at boot time. To change t Source: [https://github.com/4refr0nt/ESPlorer](https://github.com/4refr0nt/ESPlorer) -Supported platforms: OS X, Linux, Windows, anything that runs Java +Supported platforms: macOS, Linux, Windows, anything that runs Java ## nodemcu-uploader.py @@ -20,23 +25,16 @@ Supported platforms: OS X, Linux, Windows, anything that runs Java Source: [https://github.com/kmpm/nodemcu-uploader](https://github.com/kmpm/nodemcu-uploader) -Supported platforms: OS X, Linux, Windows, anything that runs Python - -## NodeMCU Studio - -> THIS TOOL IS IN REALLY REALLY REALLY REALLY EARLY STAGE!!!!!!!!!!!!!!!!!!!!!!!!!!! - -Source: [https://github.com/nodemcu/nodemcu-studio-csharp](https://github.com/nodemcu/nodemcu-studio-csharp) - -Supported platforms: Windows +Supported platforms: macOS, Linux, Windows, anything that runs Python -## luatool +## NodeMCU-Tool -> Allow easy uploading of any Lua-based script into the ESP8266 flash memory with NodeMcu firmware +> Upload/Download Lua files to your ESP8266 module with NodeMCU firmware. +> Simple. Command Line. Cross-Platform. File Management. NodeMCU. -Source: [https://github.com/4refr0nt/luatool](https://github.com/4refr0nt/luatool) +Source: [https://github.com/andidittrich/NodeMCU-Tool](https://github.com/andidittrich/NodeMCU-Tool) -Supported platforms: OS X, Linux, Windows, anything that runs Python +Supported platforms: macOS, Linux Windows, anything that runs Node.js # init.lua You will see "lua: cannot open init.lua" printed to the serial console when the device boots after it's been freshly flashed. If NodeMCU finds a `init.lua` in the root of the file system it will execute it as part of the boot sequence (standard Lua feature). Hence, your application is initialized and triggered from `init.lua`. Usually you first set up the WiFi connection and only continue once that has been successful. From be2a9df1b0fafa6b8822c24faf2a6ee5f334c676 Mon Sep 17 00:00:00 2001 From: thirschbuechler Date: Mon, 6 Mar 2017 13:14:07 +0100 Subject: [PATCH 19/73] Add modulo and power hints (#1832) --- docs/en/lua-developer-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/lua-developer-faq.md b/docs/en/lua-developer-faq.md index eefdcf1c7d..b0a2994410 100644 --- a/docs/en/lua-developer-faq.md +++ b/docs/en/lua-developer-faq.md @@ -29,7 +29,7 @@ Whilst the Lua standard distribution includes a host stand-alone Lua interpreter The ESP8266 was designed and is fabricated in China by [Espressif Systems](http://espressif.com/new-sdk-release/). Espressif have also developed and released a companion software development kit (SDK) to enable developers to build practical IoT applications for the ESP8266. The SDK is made freely available to developers in the form of binary libraries and SDK documentation. However this is in a *closed format*, with no developer access to the source files, so ESP8266 applications *must* rely solely on the SDK API (and the somewhat Spartan SDK API documentation). -The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without loosing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint. +The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without loosing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint (`modulo` can be done via `%`, `power` via `^`). NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully featured implementation of Lua 5.1 that has been optimized for embedded system development and execution to provide a scripting framework that can be used to deliver useful applications within the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules. On a typical build this approach reduces the RAM footprint by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [Lua Tiny RAM](http://www.eluaproject.net/doc/master/en_arch_ltr.html). From fcc91967f9dc766691c89a260b3dc45bb172636b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Fonseca?= Date: Tue, 7 Mar 2017 06:04:39 +0000 Subject: [PATCH 20/73] Improve wifi documentation - prefer constants to hardcoded values (#1837) --- docs/en/modules/wifi.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 15b7ab18aa..3cca640ebf 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -993,14 +993,14 @@ Gets the current status in station mode. `nil` #### Returns -number: 0~5 - -- 0: STA_IDLE, -- 1: STA_CONNECTING, -- 2: STA_WRONGPWD, -- 3: STA_APNOTFOUND, -- 4: STA_FAIL, -- 5: STA_GOTIP. +The current state which can be one of the following: + +- `wifi.STA_IDLE` +- `wifi.STA_CONNECTING` +- `wifi.STA_WRONGPWD` +- `wifi.STA_APNOTFOUND` +- `wifi.STA_FAIL` +- `wifi.STA_GOTIP` # wifi.ap Module From 4acabab1cc141fc6dd7e2bd600137e1fc2f16b50 Mon Sep 17 00:00:00 2001 From: Jonathan Karras Date: Mon, 6 Mar 2017 23:06:50 -0700 Subject: [PATCH 21/73] Change address printing to Hex from Base64. (#1840) --- lua_modules/ds18b20/ds18b20-example.lua | 4 +-- lua_modules/ds18b20/ds18b20.lua | 35 ++++++++++++------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lua_modules/ds18b20/ds18b20-example.lua b/lua_modules/ds18b20/ds18b20-example.lua index d8ab621be8..9921829cd8 100644 --- a/lua_modules/ds18b20/ds18b20-example.lua +++ b/lua_modules/ds18b20/ds18b20-example.lua @@ -7,7 +7,7 @@ pin = 3 -- gpio0 = 3, gpio2 = 4 function readout(temp) for addr, temp in pairs(temp) do -- print(string.format("Sensor %s: %s 'C", addr, temp)) - print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) -- readable address with base64 encoding is preferred when encoder module is available + print(string.format("Sensor %s: %s °C", encoder.toHex(addr), temp)) -- readable address with base64 encoding is preferred when encoder module is available end -- Module can be released when it is no longer needed @@ -21,6 +21,6 @@ if t.sens then print("Total number of DS18B20 sensors: "..table.getn(t.sens)) for i, s in ipairs(t.sens) do -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) - print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + print(string.format(" sensor #%d address: %s%s", i, encoder.toHex(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available end end diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index 7571ad3964..dddd9deef7 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -12,12 +12,12 @@ return({ pin=3, sens={}, temp={}, - + conversion = function(self) local pin = self.pin for i,s in ipairs(self.sens) do if s.status == 0 then - print("starting conversion:", encoder.toBase64(s.addr), s.parasite == 1 and "parasite" or " ") + print("starting conversion:", encoder.toHex(s.addr), s.parasite == 1 and "parasite" or " ") ow.reset(pin) ow.select(pin, s.addr) -- select the sensor ow.write(pin, 0x44, 1) -- and start conversion @@ -27,14 +27,14 @@ return({ end tmr.alarm(tmr.create(), 750, tmr.ALARM_SINGLE, function() self:readout() end) end, - + readTemp = function(self, cb, lpin) local pin = self.pin self.cb = cb self.temp={} if lpin then pin = lpin end ow.setup(pin) - + self.sens={} ow.reset_search(pin) -- ow.target_search(pin,0x28) @@ -50,25 +50,25 @@ return({ ow.write(pin, 0xB4, 1) -- Read Power Supply [B4h] local parasite = (ow.read(pin)==0 and 1 or 0) table.insert(self.sens,{addr=addr, parasite=parasite, status=0}) - print("contact: ", encoder.toBase64(addr), parasite == 1 and "parasite" or " ") + print("contact: ", encoder.toHex(addr), parasite == 1 and "parasite" or " ") end addr = ow.search(pin) tmr.wdclr() end - + -- place powered sensors first table.sort(self.sens, function(a,b) return a.parasite= 5 and 1 or 0) - + if tH and (tH~=85) then self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL - print(encoder.toBase64(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) + print(encoder.toHex(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) s.status = 2 end -- end integer version -- -- float version -- if t and (math.floor(t/10000)~=85) then -- self.temp[s.addr]=t - -- print(encoder.toBase64(s.addr), t) + -- print(encoder.toHex(s.addr), t) -- s.status = 2 -- end -- -- end float version end next = next or s.status == 0 end - if next then + if next then node.task.post(node.task.MEDIUM_PRIORITY, function() self:conversion() end) else self.sens = nil - if self.cb then - node.task.post(node.task.MEDIUM_PRIORITY, function() self.cb(self.temp) end) + if self.cb then + node.task.post(node.task.MEDIUM_PRIORITY, function() self.cb(self.temp) end) end end - + end }) - From 466c03d90fb0618488e9e790200777ed52c83ad2 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 7 Mar 2017 22:50:32 +0300 Subject: [PATCH 22/73] Deprecation messages for convenient net.create(Server/Connection) calls (#1844) --- app/modules/net.c | 11 +++++++++-- docs/en/modules/net.md | 12 ++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/modules/net.c b/app/modules/net.c index 7203d277f9..4a3c65ccfd 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -297,7 +297,10 @@ int net_createServer( lua_State *L ) { type = luaL_optlong(L, 1, TYPE_TCP); timeout = luaL_optlong(L, 2, 30); - if (type == TYPE_UDP) return net_createUDPSocket( L ); + if (type == TYPE_UDP) { + platform_print_deprecation_note("net.createServer with net.UDP type", "in next version"); + return net_createUDPSocket( L ); + } if (type != TYPE_TCP) return luaL_error(L, "invalid type"); lnet_userdata *u = net_create(L, TYPE_TCP_SERVER); @@ -312,9 +315,13 @@ int net_createConnection( lua_State *L ) { type = luaL_optlong(L, 1, TYPE_TCP); secure = luaL_optlong(L, 2, 0); - if (type == TYPE_UDP) return net_createUDPSocket( L ); + if (type == TYPE_UDP) { + platform_print_deprecation_note("net.createConnection with net.UDP type", "in next version"); + return net_createUDPSocket( L ); + } if (type != TYPE_TCP) return luaL_error(L, "invalid type"); if (secure) { + platform_print_deprecation_note("net.createConnection with secure flag", "in next version"); #ifdef TLS_MODULE_PRESENT return tls_socket_create( L ); #else diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 67e0fa117a..ec95d6bd65 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -13,11 +13,11 @@ Constants to be used in other functions: `net.TCP`, `net.UDP` Creates a client. #### Syntax -`net.createConnection(type, secure)` +`net.createConnection([type[, secure]])` #### Parameters -- `type` `net.TCP` or `net.UDP`. UDP connections chained to [net.createUDPSocket()](#netcreateudpsocket) -- `secure` 1 for encrypted, 0 for plain. Secure connections chained to [tls.createConnection()](tls.md#tlscreateconnection) +- `type` `net.TCP` (default) or `net.UDP` +- `secure` 1 for encrypted, 0 for plain (default) !!! attention This will change in upcoming releases so that `net.createConnection` will always create an unencrypted TCP connection. @@ -44,11 +44,11 @@ net.createConnection(net.TCP, 0) Creates a server. #### Syntax -`net.createServer(type, timeout)` +`net.createServer([type[, timeout]])` #### Parameters -- `type` `net.TCP` or `net.UDP`. UDP connections chained to [net.createUDPSocket()](#netcreateudpsocket) -- `timeout` for a TCP server timeout is 1~28'800 seconds (for an inactive client to be disconnected) +- `type` `net.TCP` (default) or `net.UDP` +- `timeout` for a TCP server timeout is 1~28'800 seconds, 30 sec by default (for an inactive client to be disconnected) !!! attention The `type` parameter will be removed in upcoming releases so that `net.createServer` will always create a TCP-based server. For UDP use [net.createUDPSocket()](#netcreateudpsocket) instead. From b4319bdb4b5c5b23335e27bd306c19f48f339b02 Mon Sep 17 00:00:00 2001 From: FrankX Date: Thu, 9 Mar 2017 07:20:01 +0100 Subject: [PATCH 23/73] Add driver for XPT2046 touch controller (#1848) --- app/include/user_modules.h | 1 + app/modules/xpt2046.c | 217 +++++++++++++++++++++++++++++++++++++ docs/en/modules/xpt2046.md | 131 ++++++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 350 insertions(+) create mode 100644 app/modules/xpt2046.c create mode 100644 docs/en/modules/xpt2046.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index cf4f5d5697..2d2c19ad1c 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -71,6 +71,7 @@ //#define LUA_USE_MODULES_WPS //#define LUA_USE_MODULES_WS2801 //#define LUA_USE_MODULES_WS2812 +//#define LUA_USE_MODULES_XPT2046 #endif /* LUA_CROSS_COMPILER */ #endif /* __USER_MODULES_H__ */ diff --git a/app/modules/xpt2046.c b/app/modules/xpt2046.c new file mode 100644 index 0000000000..5d00f9544a --- /dev/null +++ b/app/modules/xpt2046.c @@ -0,0 +1,217 @@ +// Module for xpt2046 +// by Starofall, F.J. Exoo +// used source code from: +// - https://github.com/spapadim/XPT2046/ +// - https://github.com/PaulStoffregen/XPT2046_Touchscreen/ + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" + +// Hardware specific values +static const uint16_t CAL_MARGIN = 0; // Set to 0: up to the application +static const uint8_t CTRL_LO_DFR = 0b0011; +static const uint8_t CTRL_LO_SER = 0b0100; +static const uint8_t CTRL_HI_X = 0b1001 << 4; +static const uint8_t CTRL_HI_Y = 0b1101 << 4; +static const uint16_t ADC_MAX = 0x0fff; // 12 bits + +// Runtime variables +static uint16_t _width, _height; +static uint8_t _cs_pin, _irq_pin; +static int32_t _cal_dx, _cal_dy, _cal_dvi, _cal_dvj; +static uint16_t _cal_vi1, _cal_vj1; + +// Average pair with least distance between each +static int16_t besttwoavg( int16_t x , int16_t y , int16_t z ) { + int16_t da, db, dc; + int16_t reta = 0; + + if ( x > y ) da = x - y; else da = y - x; + if ( x > z ) db = x - z; else db = z - x; + if ( z > y ) dc = z - y; else dc = y - z; + + if ( da <= db && da <= dc ) reta = (x + y) >> 1; + else if ( db <= da && db <= dc ) reta = (x + z) >> 1; + else reta = (y + z) >> 1; + + return reta; +} + +// Checks if the irq_pin is down +static int isTouching() { + return (platform_gpio_read(_irq_pin) == 0); +} + +// transfer 16 bits from the touch display - returns the recived uint16_t +static uint16_t transfer16(uint16_t _data) { + union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } t; + t.val = _data; + t.msb = platform_spi_send_recv(1, 8, t.msb); + t.lsb = platform_spi_send_recv(1, 8, t.lsb); + return t.val; +} + +// reads the value from the touch panel +static uint16_t _readLoop(uint8_t ctrl, uint8_t max_samples) { + uint16_t prev = 0xffff, cur = 0xffff; + uint8_t i = 0; + do { + prev = cur; + cur = platform_spi_send_recv(1, 8 , 0); + cur = (cur << 4) | (platform_spi_send_recv(1, 8 , ctrl) >> 4); // 16 clocks -> 12-bits (zero-padded at end) + } while ((prev != cur) && (++i < max_samples)); + return cur; +} + +// Returns the raw position information +static void getRaw(uint16_t *vi, uint16_t *vj) { + // Implementation based on TI Technical Note http://www.ti.com/lit/an/sbaa036/sbaa036.pdf + + // Disable interrupt: reading position generates false interrupt + ETS_GPIO_INTR_DISABLE(); + + platform_gpio_write(_cs_pin, PLATFORM_GPIO_LOW); + platform_spi_send_recv(1, 8 , CTRL_HI_X | CTRL_LO_DFR); // Send first control int + *vi = _readLoop(CTRL_HI_X | CTRL_LO_DFR, 255); + *vj = _readLoop(CTRL_HI_Y | CTRL_LO_DFR, 255); + + // Turn off ADC by issuing one more read (throwaway) + // This needs to be done, because PD=0b11 (needed for MODE_DFR) will disable PENIRQ + platform_spi_send_recv(1, 8 , 0); // Maintain 16-clocks/conversion; _readLoop always ends after issuing a control int + platform_spi_send_recv(1, 8 , CTRL_HI_Y | CTRL_LO_SER); + transfer16(0); // Flush last read, just to be sure + + platform_gpio_write(_cs_pin, PLATFORM_GPIO_HIGH); + + // Clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[_irq_pin])); + // Enable interrupt again + ETS_GPIO_INTR_ENABLE(); +} + +// sets the calibration of the display +static void setCalibration (uint16_t vi1, uint16_t vj1, uint16_t vi2, uint16_t vj2) { + _cal_dx = _width - 2*CAL_MARGIN; + _cal_dy = _height - 2*CAL_MARGIN; + + _cal_vi1 = (int32_t)vi1; + _cal_vj1 = (int32_t)vj1; + _cal_dvi = (int32_t)vi2 - vi1; + _cal_dvj = (int32_t)vj2 - vj1; +} + +// returns the position on the screen by also applying the calibration +static void getPosition (uint16_t *x, uint16_t *y) { + if (isTouching() == 0) { + *x = *y = 0xffff; + return; + } + uint16_t vi, vj; + + getRaw(&vi, &vj); + + // Map to (un-rotated) display coordinates + *x = (uint16_t)(_cal_dx * (vj - _cal_vj1) / _cal_dvj + CAL_MARGIN); + if (*x > 0x7fff) *x = 0; + *y = (uint16_t)(_cal_dy * (vi - _cal_vi1) / _cal_dvi + CAL_MARGIN); + if (*y > 0x7fff) *y = 0; +} + + +// Lua: xpt2046.init(cspin, irqpin, height, width) +static int xpt2046_init( lua_State* L ) { + _cs_pin = luaL_checkinteger( L, 1 ); + _irq_pin = luaL_checkinteger( L, 2 ); + _height = luaL_checkinteger( L, 3 ); + _width = luaL_checkinteger( L, 4 ); + // set pins correct + platform_gpio_mode(_cs_pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT ); + + setCalibration( + /*vi1=*/((int32_t)CAL_MARGIN) * ADC_MAX / _width, + /*vj1=*/((int32_t)CAL_MARGIN) * ADC_MAX / _height, + /*vi2=*/((int32_t)_width - CAL_MARGIN) * ADC_MAX / _width, + /*vj2=*/((int32_t)_height - CAL_MARGIN) * ADC_MAX / _height + ); + + // assume spi was inited before with a clockDiv of >=16 + // as higher spi clock speed produced inaccurate results + + // do first powerdown + platform_gpio_write(_cs_pin, PLATFORM_GPIO_LOW); + + // Issue a throw-away read, with power-down enabled (PD{1,0} == 0b00) + // Otherwise, ADC is disabled + platform_spi_send_recv(1, 8, CTRL_HI_Y | CTRL_LO_SER); + transfer16(0); // Flush, just to be sure + + platform_gpio_write(_cs_pin, PLATFORM_GPIO_HIGH); + return 0; +} + +// Lua: xpt2046.isTouched() +static int xpt2046_isTouched( lua_State* L ) { + lua_pushboolean( L, isTouching()); + return 1; +} + +// Lua: xpt2046.setCalibration(a,b,c,d) +static int xpt2046_setCalibration( lua_State* L ) { + int32_t a = luaL_checkinteger( L, 1 ); + int32_t b = luaL_checkinteger( L, 2 ); + int32_t c = luaL_checkinteger( L, 3 ); + int32_t d = luaL_checkinteger( L, 4 ); + setCalibration(a,b,c,d); + return 0; +} + +// Lua: xpt2046.xpt2046_getRaw() +static int xpt2046_getRaw( lua_State* L ) { + uint16_t x, y; + getRaw(&x, &y); + lua_pushinteger( L, x); + lua_pushinteger( L, y); + return 2; +} + +// Lua: xpt2046.xpt2046_getPosition() +static int xpt2046_getPosition( lua_State* L ) { + uint16_t x, y; + getPosition(&x, &y); + lua_pushinteger( L, x); + lua_pushinteger( L, y); + return 2; +} + + +// Lua: xpt2046.xpt2046_getPositionAvg() +static int xpt2046_getPositionAvg( lua_State* L ) { + // Run three times + uint16_t x1, y1, x2, y2, x3, y3; + getPosition(&x1, &y1); + getPosition(&x2, &y2); + getPosition(&x3, &y3); + + // Average the two best results + int16_t x = besttwoavg(x1,x2,x3); + int16_t y = besttwoavg(y1,y2,y3); + + lua_pushinteger( L, x); + lua_pushinteger( L, y); + return 2; +} + +// Module function map +static const LUA_REG_TYPE xpt2046_map[] = { + { LSTRKEY( "isTouched"), LFUNCVAL(xpt2046_isTouched) }, + { LSTRKEY( "getRaw" ), LFUNCVAL(xpt2046_getRaw) }, + { LSTRKEY( "getPosition"), LFUNCVAL(xpt2046_getPosition)}, + { LSTRKEY( "getPositionAvg"), LFUNCVAL(xpt2046_getPositionAvg)}, + { LSTRKEY( "setCalibration"), LFUNCVAL(xpt2046_setCalibration)}, + { LSTRKEY( "init" ), LFUNCVAL(xpt2046_init) }, + { LNILKEY, LNILVAL } +}; + + +NODEMCU_MODULE(XPT2046, "xpt2046", xpt2046_map, NULL); diff --git a/docs/en/modules/xpt2046.md b/docs/en/modules/xpt2046.md new file mode 100644 index 0000000000..7ee4dd7c39 --- /dev/null +++ b/docs/en/modules/xpt2046.md @@ -0,0 +1,131 @@ +# XPT2046 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-03-09| [Starofall](https://github.com/nodemcu/nodemcu-firmware/pull/1242)/[Frank Exoo](https://github.com/FrankX0) | [Frank Exoo](https://github.com/FrankX0) | [xpt2046.c](../../../app/modules/xpt2046.c)| + +XPT2046 is a touch controller used by several cheap displays - often in combination with the ILI9341 display controller. +The module is built based on the libraries of [spapadim](https://github.com/spapadim/XPT2046/) and [PaulStoffregen](https://github.com/PaulStoffregen/XPT2046_Touchscreen). + + +## xpt2046.init() +Initiates the XPT2046 module to read touch values from the display. It is required to call [`spi.setup()`](spi.md#spisetup) before calling `xpt2046.init` (see example). +As the ucg lib also requires [`spi.setup()`](spi.md#spisetup) to be called before it is important to only call it once in total and to activate `spi.FULLDUPLEX`. +The `clock_div` used in [`spi.setup()`](spi.md#spisetup) should be 16 or higher, as lower values might produces inaccurate results. + +#### Syntax +`xpt2046.init(cs_pin, irq_pin, height, width)` + +#### Parameters +- `cs_pin` GPIO pin for cs +- `irq_pin` GPIO pin for irq +- `height` display height in pixel +- `width` display width in pixel + +#### Returns +`nil` + +#### Example +```lua +-- Setup spi with `clock_div` of 16 and spi.FULLDUPLEX +spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 16,spi.FULLDUPLEX) +-- SETTING UP DISPLAY (using ucg module) +local disp = ucg.ili9341_18x240x320_hw_spi(8, 4, 0) +disp:begin(0) +-- SETTING UP TOUCH +xpt2046.init(2,1,320,240) +xpt2046.setCalibration(198, 1776, 1762, 273) +``` + + +## xpt2046.setCalibration() +Sets the calibration of the display. Calibration values can be optained by using [`xpt2046.getRaw()`](#xpt2046getraw) and read the values in the edges. + +#### Syntax +`xpt2046.setCalibration(x1, y1, x2, y2)` + +#### Parameters +- `x1` raw x value at top left +- `y1` raw y value at top left +- `x2` raw x value at bottom right +- `y2` raw y value at bottom right + +#### Returns +`nil` + + +## xpt2046.isTouched() +Checks if the touch panel is touched. + +#### Syntax +`xpt2046.isTouched()` + +#### Returns +`true` if the display is touched, else `false` + +#### Example +```lua +if(xpt2046.isTouched()) then + local x, y = xpt2046.getPosition() + print(x .. "-" .. y) +end +``` + + +## xpt2046.getPosition() +Returns the position the display is touched using the calibration values and given width and height. +Can be used in an interrupt pin callback to return the coordinates when the touch screen is touched. + +#### Syntax +`xpt2046.getPosition()` + +#### Returns +returns both the x and the y position. + +#### Example +```lua +-- Setup spi with `clock_div` of 16 and spi.FULLDUPLEX +spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 16,spi.FULLDUPLEX) +-- SETTING UP TOUCH +cs_pin = 2 -- GPIO4 +irq_pin = 3 -- GPIO0 +height = 240 +width = 320 +xpt2046.init(cs_pin, irq_pin, width, height) +xpt2046.setCalibration(198, 1776, 1762, 273) +gpio.mode(irq_pin,gpio.INT,gpio.PULLUP) +gpio.trig(irq_pin, "down", function() + print(xpt2046.getPosition()) +end) +``` + + +## xpt2046.getPositionAvg() +To create better measurements this function reads the position three times and averages the two positions with the least distance. + +#### Syntax +`xpt2046.getPositionAvg()` + +#### Returns +returns both the x and the y position. + +#### Example +```lua +local x, y = xpt2046.getPositionAvg() +print(x .. "-" .. y) +``` + + +## xpt2046.getRaw() +Reads the raw value from the display. Useful for debugging and custom conversions. + +#### Syntax +`xpt2046.getRaw()` + +#### Returns +returns both the x and the y position as a raw value. + +#### Example +```lua +local rawX, rawY = xpt2046.getRaw() +print(rawX .. "-" .. rawY) +``` diff --git a/mkdocs.yml b/mkdocs.yml index d76220de13..d2753d2161 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -89,5 +89,6 @@ pages: - 'wps': 'en/modules/wps.md' - 'ws2801': 'en/modules/ws2801.md' - 'ws2812': 'en/modules/ws2812.md' + - 'xpt2046': 'en/modules/xpt2046.md' #- Deutsch: # - Home: 'de/index.md' From e8d5a05952af9224d4b25036ae80e01eb5388095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 9 Mar 2017 22:19:18 +0100 Subject: [PATCH 24/73] Document that the socket receive event is fired for every frame, fixes #1849 --- docs/en/modules/net.md | 16 ++++++++++++++++ docs/en/modules/tls.md | 2 ++ 2 files changed, 18 insertions(+) diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index ec95d6bd65..69a9ac0959 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -324,6 +324,22 @@ srv:on("connection", function(sck, c) end) srv:connect(80,"httpbin.org") ``` +!!! note + The `receive` event is fired for every network frame! Hence, if the data sent to the device exceeds 1460 bytes (derived from [Ethernet frame size](https://en.wikipedia.org/wiki/Ethernet_frame)) it will fire more than once. There may be other situations where incoming data is split across multiple frames (e.g. HTTP POST with `multipart/form-data`). You need to manually buffer the data and find means to determine if all data was received. + +```lua +local buffer = nil + +srv:on("receive", function(sck, c) + if buffer == nil then + buffer = c + else + buffer = buffer .. c + end +end) +-- throttling could be implemented using socket:hold() +-- example: https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/pcm/play_network.lua#L83 +``` #### See also - [`net.createServer()`](#netcreateserver) diff --git a/docs/en/modules/tls.md b/docs/en/modules/tls.md index 814bba8253..3d28d1b343 100644 --- a/docs/en/modules/tls.md +++ b/docs/en/modules/tls.md @@ -148,6 +148,8 @@ srv:on("connection", function(sck, c) end) srv:connect(443,"google.com") ``` +!!! note + The `receive` event is fired for every network frame! See details at [net.socket:on()](net.md#netsocketon). #### See also - [`tls.createConnection()`](#tlscreateconnection) From f562ef8fc195dfd2aa4ff329c5dd50e651b3d1ac Mon Sep 17 00:00:00 2001 From: Jonathan Karras Date: Fri, 10 Mar 2017 14:07:29 -0700 Subject: [PATCH 25/73] Grammar and spelling fixes (#1852) --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- CONTRIBUTING.md | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index baaafcba79..978c298dce 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,4 +7,4 @@ Make sure all boxes are checked (add x inside the brackets) when you submit your - [ ] I have thoroughly tested my contribution. - [ ] The code changes are reflected in the documentation at `docs/en/*`. -\ \ No newline at end of file +\ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6659cfef5c..5222dfc83a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ The following is a set of guidelines for contributing to NodeMCU on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. -It is appreciated if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasing that the you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. +It is appreciated if you raise an issue _before_ you start changing NodeMCU, discussing the proposed change; emphasizing that you are proposing to develop the patch yourself, and outlining the strategy for implementation. This type of discussion is what we should be doing on the issues list and it is better to do this before or in parallel to developing the patch rather than having "you should have done it this way" type of feedback on the PR itself. ### Table Of Contents * [General remarks](#general-remarks) @@ -16,20 +16,20 @@ It is appreciated if you raise an issue _before_ you start changing NodeMCU, dis * [Commit messages](#commit-messages) * [For collaborators](#for-collaborators) * [Handling releases](#handling-releases) - + ## General remarks We are a friendly and welcoming community and look forward to your contributions. Once your contribution is integrated into this repository we feel responsible for it. Therefore, be prepared for constructive feedback. Before we merge anything we need to ensure that it fits in and is consistent with the rest of NodeMCU. -If you made something really cool but won't spend time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issues we'll take a look at it anyway. +If you made something really cool but won't spend the time to integrate it into this upstream project please still share it in your fork on GitHub. If you mention it in an issue we'll take a look at it anyway. ## Development environment setup -Use the platform and tools you feel most comfortable with. There are no constraints imposed by this project. You have (at least) two options to set up the toolchain to build the NodeMCU firmware: +Use the platform and tools you feel most comfortable with. There are no constraints imposed by this project. You have (at least) two options to set up the toolchain to build the NodeMCU firmware: - [Full-fledged Linux environment](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain), either physical or virtual. -- [Docker image](https://hub.docker.com/r/marcelstoer/nodemcu-build/) which allows to run the build inside the container as if you were running a build script on your local machine. +- [Docker image](https://hub.docker.com/r/marcelstoer/nodemcu-build/) which allows running the build inside the container as if you were running a build script on your local machine. ## Writing Documentation The NodeMCU documentation is maintained within the same repository as the code. The primary reason is to keep the two in sync more easily. It's thus trivial for the NodeMCU team to verify that a PR includes the necessary documentation. Furthermore, the documentation is merged automatically with the code if it moves from branch X to Y. -The documentation consists of a collection of Markdown files (see note on Markdown syntax at end of chapter) stored in the [`/docs`](docs) directory. With every commit a human readable and browsable version is automatically built with [Read the Docs](https://readthedocs.io/) (RTD). The public NodeMCU documentation can be found at [nodemcu.readthedocs.io](http://nodemcu.readthedocs.io/). +The documentation consists of a collection of Markdown files (see note on Markdown syntax at end of chapter) stored in the [`/docs`](docs) directory. With every commit, a human readable and browsable version is automatically built with [Read the Docs](https://readthedocs.io/) (RTD). The public NodeMCU documentation can be found at [nodemcu.readthedocs.io](http://nodemcu.readthedocs.io/). There are essentially only two things to keep in mind if you're contributing a PR: @@ -61,7 +61,7 @@ Avoid intermediate merge commits. [Rebase](https://www.atlassian.com/git/tutoria 1. Think about [squashing (some of) your commits](http://www.andrewconnell.com/blog/squash-multiple-git-commits-into-one). There are [several ways](http://stackoverflow.com/a/5201642/131929) to do this. There's no need to squash everything into a single commit as GitHub offers to do this when we merge your changes. However, you might want to trim your commit history to relevant chunks. 1. Bring your fork up-to-date with the NodeMCU upstream repo ([see below](#keeping-your-fork-in-sync)). Then rebase your branch on `dev` running `git rebase dev`. 1. `git push` -1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. +1. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/) (PR) on GitHub. This is just one way of doing things. If you're proficient in Git matters you're free to choose your own. If you want to read more then the [GitHub chapter in the Git book](http://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project#The-GitHub-Flow) is a way to start. [GitHub's own documentation](https://help.github.com/categories/collaborating/) contains a wealth of information as well. @@ -100,13 +100,13 @@ Don't forget to [reference affected issues](https://help.github.com/articles/clo ## For collaborators ### Handling releases -- Create a [milestone](https://github.com/nodemcu/nodemcu-firmware/milestones) right after you cut a new release. Give it a meaningful name if you already have an idea what the scope of the upcoming release is going to be. Also set the due date to ~2 months in the future. -- Add this milestone to every PR before you merge it. Also add the milestone to PRs you want to see land in this milestone. +- Create a [milestone](https://github.com/nodemcu/nodemcu-firmware/milestones) right after you cut a new release. Give it a meaningful name if you already have an idea what the scope of the upcoming release is going to be. Set the due date to ~2 months in the future. +- Add this milestone to every PR before you merge it. Also, add the milestone to PRs you want to see land in this milestone. - Add notes to the description of the milestone in the course of the ~2 months it lives. -- Be careful and reluctant to merge PRs once we're past the 6-weeks mark of a milestone. Ideally we don't merge anything in the last 2 weeks. +- Be careful and reluctant to merge PRs once we're past the 6-weeks mark of a milestone. Ideally, we don't merge anything in the last 2 weeks. - Cutting a release - - Create a PR for the `master` branch for collaborators to approve. - - Once approved merge it. :exclamation::boom::exclamation: Make sure you do NOT "squash and merge" but make a regular merge commit! - - Fetch the changes into your local clone and create an annotated tag like so: `git tag -a -master_ -m ""`, `git push --tags` - - Create a new [release](https://github.com/nodemcu/nodemcu-firmware/releases) based on the tag you just pushed. The version name is the same as the tag name. - - Write release notes. Mention breaking changes explicitly. Since every PR that went into this release is linked to from the milestone it should be fairly easy to include important changes in the release notes. + - Create a PR for the `master` branch for collaborators to approve. + - Once approved merge it. :exclamation::boom::exclamation: Make sure you do NOT "squash and merge" but make a regular merge commit! + - Fetch the changes into your local clone and create an annotated tag like so: `git tag -a -master_ -m ""`, `git push --tags` + - Create a new [release](https://github.com/nodemcu/nodemcu-firmware/releases) based on the tag you just pushed. The version name is the same as the tag name. + - Write release notes. Mention breaking changes explicitly. Since every PR that went into this release is linked to from the milestone it should be fairly easy to include important changes in the release notes. From c0848ec66f31ca42259851be3fd6227e0a5758fb Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Fri, 10 Mar 2017 16:09:36 -0500 Subject: [PATCH 26/73] Sample code for timezone handling (#1853) --- lua_examples/timezone/README.md | 56 +++++++++++++++++++ lua_examples/timezone/alaska.zone | Bin 0 -> 2358 bytes lua_examples/timezone/central.zone | Bin 0 -> 3543 bytes lua_examples/timezone/eastern.zone | Bin 0 -> 3519 bytes lua_examples/timezone/mountain.zone | Bin 0 -> 2427 bytes lua_examples/timezone/pacific.zone | Bin 0 -> 2819 bytes lua_examples/timezone/tz.lua | 81 ++++++++++++++++++++++++++++ 7 files changed, 137 insertions(+) create mode 100644 lua_examples/timezone/README.md create mode 100644 lua_examples/timezone/alaska.zone create mode 100644 lua_examples/timezone/central.zone create mode 100644 lua_examples/timezone/eastern.zone create mode 100644 lua_examples/timezone/mountain.zone create mode 100644 lua_examples/timezone/pacific.zone create mode 100644 lua_examples/timezone/tz.lua diff --git a/lua_examples/timezone/README.md b/lua_examples/timezone/README.md new file mode 100644 index 0000000000..a57cd38945 --- /dev/null +++ b/lua_examples/timezone/README.md @@ -0,0 +1,56 @@ +# tz module + +This is a simple module that parses timezone files as found on unix systems. It is oriented around converting the current time. It can convert other times, but it is +rather less efficient as it maintains only a single cached entry in memory. + +On my linux system, these files can be found in `/usr/share/zoneinfo`. + + +## tz.setzone() + +This sets the timezone to be used in subsequent conversions + +#### Syntax +`tz.setzone(timezone)` + +#### Parameters +- `timezone` this is the timezone string. It must correspond to a file in the file system which is named timezone.zone. + +#### Returns +true if the zone exists in the file system. + +## tz.getoffset() + +This gets the offset (in seconds) of the time passed as the argument. + +#### Syntax +`tz.getoffset(time)` + +#### Parameters +- `time` the number of seconds since the epoch. This is the same value as used by the `sntp` module. + +#### Returns + +- The number of seconds of offset. West of Greenwich is negative. +- The start time (in epoch seconds) of this offset. +- The end time (in epoch seconds) of this offset. + +#### Example +``` +tz = require('tz') +tz.setzone('eastern') +sntp.sync(nil, function(now) + local tm = rtctime.epoch2cal(now + tz.getoffset(now)) + print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"])) +end) +``` + +## tz.getzones() + +This returns a list of the available timezones in the file system. + +#### Syntax +`tz.getzones()` + +#### Returns +A list of timezones. diff --git a/lua_examples/timezone/alaska.zone b/lua_examples/timezone/alaska.zone new file mode 100644 index 0000000000000000000000000000000000000000..d14735026a09cff0104e479fa6543301d482deed GIT binary patch literal 2358 zcmciCeN2^A0LSr%*9!zjf`LeMd;#Ru0}4V>-YX!W5+Jvm7%3#iuo4W$@Qu=R=2lX+ zIh>7-b8NLqJJYRnExKi`P0TUedKt^qt(h!c>unar?R*dGubS&G&$H+GoZayk+wbdL z*Y;Gt^RE+Oe&OZxnU{Oo$bmxt*!*8Q#+sMQKgKH5)ai}xz*{S%(_iimdTF5y?n!rt zY%SBF&87aZx-=c`N%gz(BDCw34gSd32^~2!?vDELyuR!5h(G%4>oUea7=aq*rcD9 z>p53bRr1&Zec#6}m2xs%-+%1765Yje?(Pva&o@WT-~7E;Q1F*bU3pHV&H7fRXPyx0 zQY&KD^o`D(=nz@MAL{IjFRPq^V>iRe7yS=6B|+#T9$xlKOa6 zklrE-^M6xIT`Oc!a-1l-won#N{wf~$EJ{8&{-r3f==Ri_5!${USh)f*q_)nD|e>Yc~+ zBk!(LHI4gp?cowtSJJB2bf>8L+FY?!(t)(u9> z$1j}~jeBp(Cx!>b`j%0-VPL;#s{TPY??0}d%(wU3I=A`Hf zBZtDfO;z3tW^wSC|Nz2mDcwX^q4z3a43^{(HopYJbHLBUQ)=>cLasnZ>0TYhP}t}brJ)Cz88X$?R{>k$Lo}O9^+H%bv%{ze9F8| z1D93i;#p;GjuT{F&a~eD9#@q4176N+=H;FlFlU+nas=+$@2Nxvhm6i@hKGy~?Etwj zLXH_CFh*pM$S9FvBI85`ij351hKh{UY6gpp78x!wUSz;;9V@3vzj2am>GHztx z$jFhQBV$JfkBr`GhHo|FM*@IE00{vS10)DY6p%0=aX}O(dLF6Hg?dRufSqq*fDCB&bMK zk+33hMFNXN76~m9TO_zhbdm5{O?;66TTO(K5L-=*ksu>cM#7B583{BJX`BvqNN8|a z`0XB7WW*hO9Osi;q`I@mew&=>V4F~yc*odm&$Rahn`#qmlWk$|sjI6g_Ww)wKhfqN cF`e%yqFzkiv#S^9=I6@n>b$(%yj&UeH}EZKga7~l literal 0 HcmV?d00001 diff --git a/lua_examples/timezone/central.zone b/lua_examples/timezone/central.zone new file mode 100644 index 0000000000000000000000000000000000000000..e932c6dcfcabbe83157f011f0a8cc7b61cd46f97 GIT binary patch literal 3543 zcmd7UZFJ9N7{KvsY=#VBB{ZU1nAvD7O=x!3mYIi{$H-GN#qKg=Xl|!9)u|MDDh(NV zY*?O>8Aguk6k}OxjWVL;F7n)e-QT-jIHx+ibnX21`R~8~UhK{Fy%Gi`KhsYBH9qzo z9_Fllc-X2SOvc##`?4(-e*xFMHcQWA??pra!CmgxTNbIlce#Xm!A^r#^5rT7CYCM;~f&K^+=i!xT+C zFNeFHHAng#k>X}wns36k$Wh5PCH@Pgr0i|;t!I-yRxrVQS2|yRzv?YjnxC$Jc=ZKU zmOWS>Pfk!LGCJ!YV>+mxhBnnF`$w4a=mh(=q1y(HQx2e{18}+FB}C`Wac^CtfqpnJSacN!90TDevKTXrErcs%l*; zv~S4Q%J+>xU8C+sRb$;UscGI(ca+W1wMy0-zk-Rn_O{uk&Z^l`cVViz^VL+Tm!4?q zCnrjSf#JqKCS2}%q>;J1Wg}_Wyra6O`gLhk|*nv|0W4+HNymY4(gUQN1B$UdAik^DAPKB zt_~_1q1t3m(!qJ%Roje#I%Lj+Dl{og+D)jg+D8wNhx?pSVS$~bL%T0jN8ctA-uSc$ zFZakJ*AAFY`^x0eV{1)B?seUH`qOX+_ZGvb90D_A2L|?npaQtj_9oW zOuV4_HfgF8`W35$>s~rBY^zE<86X1!4x0gcMFx5{n?X4zWN_&MGbFQ6p30wMhK^bx z!?K5%r{i|(;WN|Kh&DNTWYSeE=J~L5@Ph^#|oB_eAPS&hhgL{=oSCXrQ%tV?8NI_=s-R;SahPh^E6YZO_f$T~$< zDza9Q)rzcFWW^$D7Fo5(x$K|^S;0=bhLKf_tYc&)BWoF1&B%I2Ry4Av zkyVYXYh-03Ya3bJPP@L574Eca99iYaI(OQYj;wWLwIk~tS@Fo4M^-(u?wxk!BWoX7 z{mA-95`bg?Ndb}rBne0skTf89KoWsu0!amu3nUpxHk>vcNIsB+AQ?eYg5(5A3X&Bh zEl6IF#2}eLQiJ3MNe+@7Bt1@>A0$CYhL9AY%MmX~5@WN(kR~Kg42eQAg`^6}6_PBc z%@&d_r_C2b!jOz1DMNCGBn`q!7s= zl0+nnNE(qmG9(hoBtt5ZTrwmR$tFWOoi?9HLY+3FNJ^2MB1uKEili0EE0S0wvka+4 za?6lhB)bghMe@s#V5iM6LyD0cGb90stYrjZLKTDXReO=(hY6&_8=4Lyomn)i0TXL?s}c+qA4eD1Km`+c9})T|e~dH(eZ zv|r)jb;drt4=r96E(88$fA$GSD$7alf^Sm=_O;okfpK9)v{r`&GN89RnmQ> zDe*1UEADvDtf(%~pVx_5S&^kzRSh*?6c5s+YacRS=0)k%3!auWnFH0@Y29UALT6Pr zDooZ#+@`+jb5%ApGOE1QAt|rfr#1y%Hk&HfsjrV8FyEB!)fGF+&F0x_^p+*_&DQL> zdRzYMW_wbO-jP1qd>b=DS9+((&XC@ES5%tpx;9+x4vv@I2YadS-Z8ST{UzC_Zs@AX z$IVaizv-V-D$V}3oArU%5_8bANFNHDWe({X`mk@g{IVrO|5{xrzkT+Ws;(F(M+(!G zF80dNtS8j5ycqdCv4c9E)?7{ukJdE_QRa^xq55P*h&k2bqCVZ|f;oNVCw-=-%ADQ5 zUe{KZnRA<~)cMkVViuOE3$xeB#p&}@U3QUN8a-8APRf-lk3Fld#-z%%4pa1>A#rlO z$w+oy4AH7l{1Iu~ zWt(dJ?%i^0=n{47s<|e}%v4SF+tTdNr#g5`rrcJZuR}hYVM0H8Q8zElFt<;AQs0r4 zVD22*QHLc)nijp9>ANDEnwIS%)!j{M%{>j8s(UU5Nvor^s`Za&rOnnoDtz@1a_^$m zs%?(P-1p`j)h^|jX+O49-ygffJTUMB{b1NK6VY*^j__@l$e;aoF&m~A67AWgQUxxma1!Jrs+1}y6T>gXdW8+tBQ^2WFGETp?Wk9Ha%M%);(*^ zn_f4**1ao#G<|+4)^VlhhYwx(l33C>K~IM1N!$< z1L{glLeyxTP&LaW2KUv8YsZ^`we9tw1%>j|{&?k`HckewZ>N$*d1XjZpn5tkMutwV zQNvm{m*kXPD)~m8JQKS@J#!+&j0msPBX(RcBYh=0bxD-h`y`}6h#J!|XHMK0}h3vH+gs|DL>C?Du$howX0|@ATVOT0`?bGJnVfA~T3g zAu@-^BqFnjOd~Rn$V4JD>9kXc%%#&#CNi7IbRzSKOeiv=$dn>;icBgptH`t>^NLI? zGPB6kB6I7slZ(u*(@rlkzsLk5GmK0zGRMdyBeRT5GcwP}L?bheOf@psPCMDiY&-3A zBlC?+xYN!!GUdpeBa@EIIx_9Zydx9uv@?%PJu>&mOg}RJNCJ=yASpm{fFuFQ z0+I$K4@e@AOdzRn+FT&XK(c|P1IY)H5F{f=N|2l&NkOuLqy@>%ku@`ElxydgupY>I#!AxT2Agro_{6Ot$-Q%;*IBv($GEF@bKwMkJ3&B9TlYsYG(= zw8=!W>9pxY@`)rA$taRiB&SGHk*p$VMe>Ry7RfAt!PJ03%X8>{vAm;#b5+G**avC7#0dgWBX9996Am;*dG9YJz)1D5< z`QWrC1ad|=?J0qr6Ua${oE6Avft(k}iGiFM$f<#x8_3CloE=VkdLZYA)1Dy68RE33 z2y%`fCkb+vAg2j(o**X*a;7v0sOLw+zAjlpvvG&`Yz1^d`MR)1!?bb6-07=JX* ziZRO|P|{j6@nOxzhPsApE1ScuX)UzpvTXFQNdotF-Y4uI`@g<-KVHAx`}gOKYqe)> zhPdW}%{M$;r+IjnU3+u%8~q`w`^7lj^V*MUca2Z)X`U*2^DpS$w!P}3)^I&E?~nSS zs%UXJ)vHGrw2MC@-TF$XS6mr=T8{nT7UO5j<-~_M!gn-V`rnEZVrQ%jXz_`_XC`FO zh8PuGHB(P18CO$t$8<>AunKu8Q-{gpDs2Bj=@{9quDj5q!_OTP5yzYKwD)^N|R~3k;!u#cn>;!RB!U7pRH($*RbI6#$cop;8d>#8$n2H+; z)w53gq-GzysBi8$rRI2N$y;_DP;*!M<-Bzti(50ll(#K=SIm$4K*lF(5$`*%6C4{w z!te>5c+n+pAK0Ul&eW^qt`~I5;Zl{_SgX@sS)|fyn`OqP8ERp{TDhonRAt6LAn(Zd zOf3#clv%NtMb`J>GJEWt$T|6oyz}#Zv82zT@9OUqOWS_dckkLRa_c_P_cZTP_ZIi* z`>MC8ywsO0D|NEdUNNx}XXZFd$h)Pj-xK%E@lq-r}e^W32v_d%>TJ(eOEmg%8 z9$nHgM?JKpNSAs8)rz=e?OOSba*apovdojJ?As*ip8d6O4^ES-e1l?j?}&VOc%P_f z9+Hm?w2I2=BXUjGdhw|9w61DtQI91b(vR1ARCPqVt|=%|HJ7})Ha=O^o^oqXNTl-Y z&(Z6?`&F&)D3?#1o+h63X3M7zjEK6jSXtjTBsOGD$c=SJ#HOh8a&vKq*z6nA-mF8) zJN$*-65X!03>?#2N4@IVt{(l|S+{CvY|`6~=BUQX8ohmIoDu$$7L${i-BZIe^(Idl0#*YMmBLYYWIAVYVfg=h?7&zj91cD>vwq2Y)P5*(|E4iX-#i4PJWjtC(kLSlpj z35gOCCL~TsppZx*p+aJX1Ph545-zKW7ZNazh#?{4h#3+zj;JAFp%{_#pwbng}8xL?ntx7?C(4fkYySgc6A*5=^U!CK67oi6;_JtBEKQ zQjVA+LFI@l5>}45B7xN?B<@Jyk;o&V4-A<61O@-|diAUS zUeow?VVY-pZ%S`^KUsSFja=?DoxisEHu@sKVg3~?Ha{mh7p7#Sq$N4i(^Jw@(j0#Q DJ6nx# literal 0 HcmV?d00001 diff --git a/lua_examples/timezone/pacific.zone b/lua_examples/timezone/pacific.zone new file mode 100644 index 0000000000000000000000000000000000000000..3b7ce1dceebf9fa9db859068da1fa7eba6df1cfa GIT binary patch literal 2819 zcmdVb|4-Fb9LMnsq6kW!Oq2-iq$YxjfTAdt`E7uSpdg7Eeo0`Mh6fbuI0+mF`H%)y5(xkN@&h+5m_ozNkvOky0`N@>o@xc{Csxq zbH@{4zv1xgw}-o``Y1&{I1p;&m1x=i&JFYZhHz;ZaD7?BfWG|g2j<+s9R=1f_R{GK|~{E?d^XQM`%bIG&y`JOXWOXOJH z(l$a}2#nAd8bZ~@b6$OE&kfajxLN;Mc|u*T>5wbS52&jZP13gZpt)95FW2XNX#PsC zmcPfpY;FuMm-fNc>Sm98xz)8?-D-bH-)_lQ9mn(ZKi{S(@4jU1`#4&8wnb^b*SahJ zRo!%_<%3PYf=KB+BiMAA5F~-ImrUTsNfM-fFhRS%*1=bHn{LfD(*48{6;fX*_w27x zJ$8Pk)!UWo-VH|gTwSd0D_o&_<;+r{sad-Bn9-_F)F>U+KhxYF6sE)d6HIv9NO|B) z9~05gTlyZnY5MJHmj}0>GP-n#erWwyrhj&b9aljes&W?{((|fTtNCe7vf$OtX5p}UdAeYwSrk$&dC55@ueDqXB9lzPk$fo( z>}v{lrO4uQKJ(0$JiX+n2(`2{SwCCjRYloRdRaxYT0W|qUQu*N6^FL!lJp&_#1|x` ziS?$m>7uL(t2V3Zj>ziva`Svujl6I?->fOClo$7Fwm{+;Z{-S+x? z?;Agl&sVX|6X5P$-DmfV$yuI^OnaWNCnJkLv>zwy|Nr=%?Qa~OFYMvo%V(e5fbe=G z0!Rpu7$8AFqJV_KY2$zd0*M3?3M3XtFpy{<;XvYn1O$l)5)vdPNKlZdAYnn`f&|8C zBZGv-X=8%~2Z;_69wa_UfRG3wAwpt=1PO@}5+)>0NT85NA)!KI<+Q;}O{Wbf5>F(cNJNp4A~8jRibNF&D-u^Eut;Q)&?2#Q+TbG5b=vSE z@kIiRL>LJ%5@RIDNR*KyW|kw-$0#NKIxk3`>T!;i$@X$Jrq0b~e}F+c_Z83kk*ka0i;0vQQpD3Gym+QC3Z z!)b>D84sr&5M)G;633>h(}9WrFhkU>L64H-6M+>n7o zM$X^QzMTRbT<@OO=c^p#@wjEPD`&Uvzm;>}sA*|- LLLLLL", lens) + + local times = z:read(4 * timecnt) + local typeindex = z:read(timecnt) + local ttinfos = z:read(6 * typecnt) + + z:close() + + local offset = 1 + local tt + for i = 1, timecnt do + tt = struct.unpack(">l", times, (i - 1) * 4 + 1) + if t < tt then + offset = (i - 2) + tend = tt + break + end + tstart = tt + end + + local tindex = struct.unpack("B", typeindex, offset + 1) + toffset = struct.unpack(">l", ttinfos, tindex * 6 + 1) + else + tend = 0x7fffffff + tstart = 0 + end +end + + +function M.getoffset(t) + if t < tstart or t >= tend then + -- Ignore errors + local ok, msg = pcall(function () + load(t) + end) + if not ok then + print (msg) + end + end + + return toffset, tstart, tend +end + +return M From 725feade27813d0f5a323d504cc259bc24ec38e4 Mon Sep 17 00:00:00 2001 From: Jonathan Karras Date: Fri, 10 Mar 2017 14:10:49 -0700 Subject: [PATCH 27/73] Update DS18b20 examples (#1851) - Remove old non-async examples from `lua-examples`. - Rename `ds18b20.EN.md` to `README.md` - Change remaining `toBase64` calls to the more standard `toHex` call. - Fix some spelling and markdown formatting issues in README file. Addresses issue #1841 --- lua_examples/onewire-ds18b20.lua | 76 ------------------- lua_examples/yet-another-ds18b20.lua | 73 ------------------ .../ds18b20/{ds18b20.EN.md => README.md} | 24 +++--- lua_modules/ds18b20/ds18b20-web.lua | 8 +- 4 files changed, 16 insertions(+), 165 deletions(-) delete mode 100644 lua_examples/onewire-ds18b20.lua delete mode 100644 lua_examples/yet-another-ds18b20.lua rename lua_modules/ds18b20/{ds18b20.EN.md => README.md} (72%) diff --git a/lua_examples/onewire-ds18b20.lua b/lua_examples/onewire-ds18b20.lua deleted file mode 100644 index a1a03b2350..0000000000 --- a/lua_examples/onewire-ds18b20.lua +++ /dev/null @@ -1,76 +0,0 @@ ---' --- ds18b20 one wire example for NODEMCU (Integer firmware only) --- NODEMCU TEAM --- LICENCE: http://opensource.org/licenses/MIT --- Vowstar ---' - -pin = 9 -ow.setup(pin) -count = 0 -repeat - count = count + 1 - addr = ow.reset_search(pin) - addr = ow.search(pin) - tmr.wdclr() -until((addr ~= nil) or (count > 100)) -if (addr == nil) then - print("No more addresses.") -else - print(addr:byte(1,8)) - 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.") - repeat - 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 - t = (data:byte(1) + data:byte(2) * 256) - - -- handle negative temperatures - if (t > 0x7fff) then - t = t - 0x10000 - end - - if (addr:byte(1) == 0x28) then - t = t * 625 -- DS18B20, 4 fractional bits - else - t = t * 5000 -- DS18S20, 1 fractional bit - end - - local sign = "" - if (t < 0) then - sign = "-" - t = -1 * t - end - - -- Separate integral and decimal portions, for integer firmware only - local t1 = string.format("%d", t / 10000) - local t2 = string.format("%04u", t % 10000) - local temp = sign .. t1 .. "." .. t2 - print("Temperature= " .. temp .. " Celsius") - end - tmr.wdclr() - until false - else - print("Device family is not recognized.") - end - else - print("CRC is not valid!") - end -end diff --git a/lua_examples/yet-another-ds18b20.lua b/lua_examples/yet-another-ds18b20.lua deleted file mode 100644 index b6c225c5d4..0000000000 --- a/lua_examples/yet-another-ds18b20.lua +++ /dev/null @@ -1,73 +0,0 @@ ------------------------------------------------------------------------------- --- DS18B20 query module --- --- LICENCE: http://opensource.org/licenses/MIT --- Vladimir Dronnikov --- --- Example: --- dofile("ds18b20.lua").read(4, function(r) for k, v in pairs(r) do print(k, v) end end) ------------------------------------------------------------------------------- -local M -do - local bit = bit - 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, cb, 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.alarm(0, delay or 100, 0, function() - -- 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) - -- negatives? - if bit.isset(t, 15) then t = 1 - bit.bxor(t, 0xffff) end - -- NB: temperature in Celsius * 10^4 - t = t * 625 - -- NB: due 850000 means bad pullup. ignore - if t ~= 850000 then - r[format_addr(d[i])] = t - end - d[i] = nil - end - end - cb(r) - end) - end - -- expose - M = { - read = read, - } -end -return M diff --git a/lua_modules/ds18b20/ds18b20.EN.md b/lua_modules/ds18b20/README.md similarity index 72% rename from lua_modules/ds18b20/ds18b20.EN.md rename to lua_modules/ds18b20/README.md index 155bc29d82..0bcd2a4cf2 100644 --- a/lua_modules/ds18b20/ds18b20.EN.md +++ b/lua_modules/ds18b20/README.md @@ -1,5 +1,5 @@ -#DS18B20 Module -##Require +# DS18B20 Module +## Require ```lua ds18b20 = require("ds18b20") ``` @@ -10,31 +10,31 @@ package.loaded["ds18b20"]=nil ``` -##readTemp() -Scans the bus for DS18B20 sensors, starts a readout (conversion) for all sensors and calls a callback function when all temperatures are available. Powered sensors are read at once first. Parasit-powered sensors are read one by one. The first parasit-powered sensor is read together with all powered sensors. +## readTemp() +Scans the bus for DS18B20 sensors, starts a readout (conversion) for all sensors and calls a callback function when all temperatures are available. Powered sensors are read at once first. Parasite-powered sensors are read one by one. The first parasite-powered sensor is read together with all powered sensors. The module requires `ow` module. -The also module uses `encoder` module for printing debug information with more readable representation of sensor address (`encoder.toBase64()`). +The also module uses `encoder` module for printing debug information with more readable representation of sensor address (`encoder.toHex()`). -####Syntax +#### Syntax `readTemp(callback, pin)` -####Parameters -- `callback` function that receives all results when all conversions finish. The callback funciton has one parameter - an array addressed by sensor addresses and a value of the temperature (string for integer version). +#### Parameters +- `callback` function that receives all results when all conversions finish. The callback function has one parameter - an array addressed by sensor addresses and a value of the temperature (string for integer version). - `pin` pin of the one-wire bus. If nil, GPIO0 (3) is used. -####Returns +#### Returns nil -####Example +#### Example ```lua t = require("ds18b20") pin = 3 -- gpio0 = 3, gpio2 = 4 function readout(temp) for addr, temp in pairs(temp) do - print(string.format("Sensor %s: %s 'C", encoder.toBase64(addr), temp)) + print(string.format("Sensor %s: %s 'C", encoder.toHex(addr), temp)) end -- Module can be released when it is no longer needed @@ -48,7 +48,7 @@ if t.sens then print("Total number of DS18B20 sensors: "..table.getn(t.sens)) for i, s in ipairs(t.sens) do -- print(string.format(" sensor #%d address: %s%s", i, s.addr, s.parasite == 1 and " (parasite)" or "")) - print(string.format(" sensor #%d address: %s%s", i, encoder.toBase64(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with base64 encoding is preferred when encoder module is available + print(string.format(" sensor #%d address: %s%s", i, encoder.toHex(s.addr), s.parasite == 1 and " (parasite)" or "")) -- readable address with Hex encoding is preferred when encoder module is available end end ``` diff --git a/lua_modules/ds18b20/ds18b20-web.lua b/lua_modules/ds18b20/ds18b20-web.lua index 41182dfda8..a834709754 100644 --- a/lua_modules/ds18b20/ds18b20-web.lua +++ b/lua_modules/ds18b20/ds18b20-web.lua @@ -8,13 +8,13 @@ function readout(temp) local resp = "HTTP/1.1 200 OK\nContent-Type: text/html\nRefresh: 5\n\n" .. "" .. "" .. - "ESP8266
" - + "ESP8266
" + for addr, temp in pairs(temp) do -- resp = resp .. string.format("Sensor %s: %s ℃
", addr, temp) - resp = resp .. string.format("Sensor %s: %s ℃
", encoder.toBase64(addr), temp) -- readable address with base64 encoding is preferred when encoder module is available + resp = resp .. string.format("Sensor %s: %s ℃
", encoder.toHex(addr), temp) -- readable address with base64 encoding is preferred when encoder module is available end - + resp = resp .. "Node ChipID: " .. node.chipid() .. "
" .. "Node MAC: " .. wifi.sta.getmac() .. "
" .. From 07341e977f9704209a649085475e6c6b8cfc1f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 12 Mar 2017 21:43:46 +0100 Subject: [PATCH 28/73] Transfer build options from README to docs (#1860) * Transfer build options from README to docs, fixes #1830 * reference tls, u8g, ucg module configuration --- README.md | 54 +------------------------- docs/en/build.md | 72 +++++++++++++++++++++++++++++++---- docs/en/extn-developer-faq.md | 3 +- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 1221ce199d..ac8cd0590e 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,9 @@ wifi.sta.config("SSID", "password") # Documentation -The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is mainted in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io). +The entire [NodeMCU documentation](https://nodemcu.readthedocs.io) is maintained right in this repository at [/docs](docs). The fact that the API documentation is maintained in the same repository as the code that *provides* the API ensures consistency between the two. With every commit the documentation is rebuilt by Read the Docs and thus transformed from terse Markdown into a nicely browsable HTML site at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io). - How to [build the firmware](https://nodemcu.readthedocs.io/en/dev/en/build/) -- How to [build the filesystem](https://nodemcu.readthedocs.io/en/dev/en/spiffs/) - How to [flash the firmware](https://nodemcu.readthedocs.io/en/dev/en/flash/) - How to [upload code and NodeMCU IDEs](https://nodemcu.readthedocs.io/en/dev/en/upload/) - API documentation for every module @@ -65,53 +64,4 @@ See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthed # License -[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html) - -# Build Options - -The following sections explain some of the options you have if you want to [build your own NodeMCU firmware](http://nodemcu.readthedocs.io/en/dev/en/build/). - -### Select Modules - -Disable modules you won't be using to reduce firmware size and free up some RAM. The ESP8266 is quite limited in available RAM and running out of memory can cause a system panic. The default configuration is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins. - -Edit `app/include/user_modules.h` and comment-out the `#define` statement for modules you don't need. Example: - -```c -... -#define LUA_USE_MODULES_MQTT -// #define LUA_USE_MODULES_COAP -// #define LUA_USE_MODULES_U8G -... -``` - -### Tag Your Build - -Identify your firmware builds by editing `app/include/user_version.h` - -```c -#define NODE_VERSION "NodeMCU 2.0.0+myname" -#ifndef BUILD_DATE -#define BUILD_DATE "YYYYMMDD" -#endif -``` - -### Set UART Bit Rate - -The initial baud rate at boot time is 115200bps. You can change this by -editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`: - -```c -#define BIT_RATE_DEFAULT BIT_RATE_115200 -``` - -Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause -the firmware to lock onto that baud rate (between 1200 and 230400). - -### Debugging - -To enable runtime debug messages to serial console edit `app/include/user_config.h` - -```c -#define DEVELOP_VERSION -``` +[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html) \ No newline at end of file diff --git a/docs/en/build.md b/docs/en/build.md index eaf144d933..0ce62751fd 100644 --- a/docs/en/build.md +++ b/docs/en/build.md @@ -1,14 +1,72 @@ There are essentially three ways to build your NodeMCU firmware: cloud build service, Docker image, dedicated Linux environment (possibly VM). -**Building manually** +## Tools -Note that the *default configuration in the C header files* (`user_config.h`, `user_modules.h`) is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins. - -## Cloud Build Service +### Cloud Build Service NodeMCU "application developers" just need a ready-made firmware. There's a [cloud build service](http://nodemcu-build.com/) with a nice UI and configuration options for them. -## Docker Image +### Docker Image Occasional NodeMCU firmware hackers don't need full control over the complete tool chain. They might not want to setup a Linux VM with the build environment. Docker to the rescue. Give [Docker NodeMCU build](https://hub.docker.com/r/marcelstoer/nodemcu-build/) a try. -## Linux Build Environment -NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. There is a [post in the esp8266.com Wiki](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain) that describes this. \ No newline at end of file +### Linux Build Environment +NodeMCU firmware developers commit or contribute to the project on GitHub and might want to build their own full fledged build environment with the complete tool chain. There is a [post in the esp8266.com Wiki](http://www.esp8266.com/wiki/doku.php?id=toolchain#how_to_setup_a_vm_to_host_your_toolchain) that describes this. + +## Build Options + +The following sections explain some of the options you have if you want to build your own NodeMCU firmware. + +### Select Modules +Disable modules you won't be using to reduce firmware size and free up some RAM. The ESP8266 is quite limited in available RAM and running out of memory can cause a system panic. The *default configuration* is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only includes general purpose interface modules which require at most two GPIO pins. + +Edit `app/include/user_modules.h` and comment-out the `#define` statement for modules you don't need. Example: + +```c +... +#define LUA_USE_MODULES_MQTT +// #define LUA_USE_MODULES_COAP +// #define LUA_USE_MODULES_U8G +... +``` + +### TLS/SSL Support +To enable TLS support edit `app/include/user_config.h` and uncomment the following flag: + +```c +//#define CLIENT_SSL_ENABLE +``` + +The complete configuration is stored in `app/include/user_mbedtls.h`. This is the file to edit if you build your own firmware and want to change mbed TLS behavior. See the [`tls` documentation](modules/tls.md) for details. + +### Debugging +To enable runtime debug messages to serial console edit `app/include/user_config.h` + +```c +#define DEVELOP_VERSION +``` + +### Set UART Bit Rate +The initial baud rate at boot time is 115200bps. You can change this by +editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`: + +```c +#define BIT_RATE_DEFAULT BIT_RATE_115200 +``` + +Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause +the firmware to lock onto that baud rate (between 1200 and 230400). + +### Tag Your Build +Identify your firmware builds by editing `app/include/user_version.h` + +```c +#define NODE_VERSION "NodeMCU 2.0.0+myname" +#ifndef BUILD_DATE +#define BUILD_DATE "YYYYMMDD" +#endif +``` + +### u8g Module Configuration +Display drivers and embedded fonts are compiled into the firmware image based on the settings in `app/include/u8g_config.h`. See the [`u8g` documentation](modules/u8g.md#displays) for details. + +### ucg Module Configuration +Display drivers and embedded fonts are compiled into the firmware image based on the settings in `app/include/ucg_config.h`. See the [`ucg` documentation](modules/ucg.md#displays) for details. diff --git a/docs/en/extn-developer-faq.md b/docs/en/extn-developer-faq.md index 4cec64f710..67bd9a799a 100644 --- a/docs/en/extn-developer-faq.md +++ b/docs/en/extn-developer-faq.md @@ -1,6 +1,7 @@ # Extension Developer FAQ -**# # # Work in Progress # # #** +## Firmware build options +[Building the firmware → Build Options](build.md#build-options) lists a few of the common parameters to customize your firmware *at build time*. ## How does the non-OS SDK structure execution From 45ae795739a78c5dbffc3b5cfc59b2bd21aeba20 Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Tue, 14 Mar 2017 20:49:41 +1100 Subject: [PATCH 29/73] Extend node.dsleep() to support instant sleep. (#1859) --- app/modules/node.c | 10 +++++++++- docs/en/modules/node.md | 6 +++++- sdk-overrides/include/user_interface.h | 2 ++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/app/modules/node.c b/app/modules/node.c index 57d0320832..2a34111f6a 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -53,6 +53,9 @@ static int node_deepsleep( lua_State* L ) else system_deep_sleep_set_option( option ); } + bool instant = false; + if (lua_isnumber(L, 3)) + instant = lua_tointeger(L, 3); // Set deleep time, skip if nil if ( lua_isnumber(L, 1) ) { @@ -61,7 +64,12 @@ static int node_deepsleep( lua_State* L ) if ( us < 0 ) return luaL_error( L, "wrong arg range" ); else - system_deep_sleep( us ); + { + if (instant) + system_deep_sleep_instant(us); + else + system_deep_sleep( us ); + } } return 0; } diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index 4892e91336..a957936a0d 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -95,7 +95,7 @@ Firmware from before 05 Jan 2016 have a maximum sleeptime of ~35 minutes. This function can only be used in the condition that esp8266 PIN32(RST) and PIN8(XPD_DCDC aka GPIO16) are connected together. Using sleep(0) will set no wake up timer, connect a GPIO to pin RST, the chip will wake up by a falling-edge on pin RST. #### Syntax -`node.dsleep(us, option)` +`node.dsleep(us, option, instant)` #### Parameters - `us` number (integer) or `nil`, sleep time in micro second. If `us == 0`, it will sleep forever. If `us == nil`, will not set sleep time. @@ -107,6 +107,10 @@ Firmware from before 05 Jan 2016 have a maximum sleeptime of ~35 minutes. - 1, RF_CAL after deep-sleep wake up, there will be large current - 2, no RF_CAL after deep-sleep wake up, there will only be small current - 4, disable RF after deep-sleep wake up, just like modem sleep, there will be the smallest current + - `instant` number (integer) or `nil`. If present and non-zero, do not use + the normal grace time before entering deep sleep. This is a largely + undocumented feature, and is only briefly mentioned in Espressif's + [low power solutions](https://espressif.com/sites/default/files/documentation/9b-esp8266_low_power_solutions_en.pdf#page=10) document (chapter 4.5). #### Returns `nil` diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 61ce83568d..246b02b91b 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -12,5 +12,7 @@ enum ext_flash_size_map { FLASH_SIZE_128M_MAP = 9 }; +// Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf +void system_deep_sleep_instant(uint32 time_in_us); #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */ From 25f433a6c6be8efd89ab197c32401441cfca9591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Wed, 15 Mar 2017 23:12:57 +0100 Subject: [PATCH 30/73] typo fixes --- docs/en/modules/ow.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/modules/ow.md b/docs/en/modules/ow.md index 459ba8315f..48187053ef 100644 --- a/docs/en/modules/ow.md +++ b/docs/en/modules/ow.md @@ -137,7 +137,7 @@ Issues a 1-Wire rom select command. Make sure you do the `ow.reset(pin)` first. #### Parameters - `pin` 1~12, I/O index -- `rom` string value, len 8, rom code of the salve device +- `rom` string value, len 8, rom code of the slave device #### Returns `nil` @@ -247,7 +247,7 @@ Writes a byte. If `power` is 1 then the wire is held high at the end for parasit #### Parameters - `pin` 1~12, I/O index -- `v` byte to be written to salve device +- `v` byte to be written to slave device - `power` 1 for wire being held high for parasitically powered devices #### Returns From 46f651cccb15a6a27e044c8afb0eca2e1334e8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Thu, 16 Mar 2017 12:36:09 +0100 Subject: [PATCH 31/73] Fix self.pin when specifying lpin for readTemp() (#1865) --- lua_modules/ds18b20/ds18b20.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index dddd9deef7..90c215109b 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -29,10 +29,10 @@ return({ end, readTemp = function(self, cb, lpin) + if lpin then self.pin = lpin end local pin = self.pin self.cb = cb self.temp={} - if lpin then pin = lpin end ow.setup(pin) self.sens={} From 925991715f2472d66ce73f5e636d72cfa40d9db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Thu, 16 Mar 2017 23:10:04 +0100 Subject: [PATCH 32/73] Fix float version, auto-detect int/float, OO tmr (#1866) --- lua_modules/ds18b20/ds18b20.lua | 39 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/lua_modules/ds18b20/ds18b20.lua b/lua_modules/ds18b20/ds18b20.lua index 90c215109b..7f192346f9 100644 --- a/lua_modules/ds18b20/ds18b20.lua +++ b/lua_modules/ds18b20/ds18b20.lua @@ -25,7 +25,7 @@ return({ if s.parasite == 1 then break end -- parasite sensor blocks bus during conversion end end - tmr.alarm(tmr.create(), 750, tmr.ALARM_SINGLE, function() self:readout() end) + tmr.create():alarm(750, tmr.ALARM_SINGLE, function() self:readout() end) end, readTemp = function(self, cb, lpin) @@ -83,25 +83,28 @@ return({ t = t * 5000 -- DS18S20, 1 fractional bit end - -- integer version - local sgn = t<0 and -1 or 1 - local tA = sgn*t - local tH=tA/10000 - local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0) + if 1/2 == 0 then + -- integer version + local sgn = t<0 and -1 or 1 + local tA = sgn*t + local tH=tA/10000 + local tL=(tA%10000)/1000 + ((tA%1000)/100 >= 5 and 1 or 0) - if tH and (tH~=85) then - self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL - print(encoder.toHex(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) - s.status = 2 + if tH and (tH~=85) then + self.temp[s.addr]=(sgn<0 and "-" or "")..tH.."."..tL + print(encoder.toHex(s.addr),(sgn<0 and "-" or "")..tH.."."..tL) + s.status = 2 + end + -- end integer version + else + -- float version + if t and (math.floor(t/10000)~=85) then + self.temp[s.addr]=t/10000 + print(encoder.toHex(s.addr), t) + s.status = 2 + end + -- end float version end - -- end integer version - -- -- float version - -- if t and (math.floor(t/10000)~=85) then - -- self.temp[s.addr]=t - -- print(encoder.toHex(s.addr), t) - -- s.status = 2 - -- end - -- -- end float version end next = next or s.status == 0 end From 2f00c1d8d9819bed703f61259c484e26268f0f88 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Fri, 17 Mar 2017 09:31:37 +0300 Subject: [PATCH 33/73] TIME_WAIT sockets fixes (#1838) * Enable SO_REUSEADDR for server TCP sockets * Reduce TCP_MSL to 5 sec * Add changes notice for future updates * Move MSL change to lwipiots.h --- app/include/lwipopts.h | 12 +++++++++++- app/modules/net.c | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/include/lwipopts.h b/app/include/lwipopts.h index 23f731adee..3cb93b73df 100755 --- a/app/include/lwipopts.h +++ b/app/include/lwipopts.h @@ -899,6 +899,15 @@ #define TCP_TTL (IP_DEFAULT_TTL) #endif +/** + * TCP_MSL: Maximum segment lifetime + * Override for file + * See https://github.com/nodemcu/nodemcu-firmware/issues/1836 for details + */ +#ifndef TCP_MSL +#define TCP_MSL 5000UL +#endif + /** * TCP_MAXRTX: Maximum number of retransmissions of data segments. */ @@ -1455,7 +1464,8 @@ * SO_REUSE==1: Enable SO_REUSEADDR option. */ #ifndef SO_REUSE -#define SO_REUSE 0 +/* See https://github.com/nodemcu/nodemcu-firmware/issues/1836 for details */ +#define SO_REUSE 1 #endif /** diff --git a/app/modules/net.c b/app/modules/net.c index 4a3c65ccfd..90c25d26fe 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -386,6 +386,7 @@ int net_listen( lua_State *L ) { ud->tcp_pcb = tcp_new(); if (!ud->tcp_pcb) return luaL_error(L, "cannot allocate PCB"); + ud->tcp_pcb->so_options |= SOF_REUSEADDR; err = tcp_bind(ud->tcp_pcb, &addr, port); if (err == ERR_OK) { tcp_arg(ud->tcp_pcb, ud); From d9b3db06f6486505f215efdc04abda2f45a1cdee Mon Sep 17 00:00:00 2001 From: Frank Exoo Date: Sat, 18 Mar 2017 09:45:26 +0100 Subject: [PATCH 34/73] Show IP address after enduser_setup (#1867) --- app/modules/enduser_setup.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c index 20c5cbd65d..2fe519cb1d 100644 --- a/app/modules/enduser_setup.c +++ b/app/modules/enduser_setup.c @@ -144,6 +144,7 @@ static void enduser_setup_ap_stop(void); static void enduser_setup_check_station(void *p); static void enduser_setup_debug(int line, const char *str); +static char ipaddr[16]; #if ENDUSER_SETUP_DEBUG_ENABLE #define ENDUSER_SETUP_DEBUG(str) enduser_setup_debug(__LINE__, str) @@ -285,6 +286,8 @@ static void enduser_setup_check_station(void *p) return; } + c_sprintf (ipaddr, "%d.%d.%d.%d", IP2STR(&ip.ip.addr)); + state->success = 1; state->lastStationStatus = 5; /* We have an IP Address, so the status is 5 (as of SDK 1.5.1) */ state->connecting = 0; @@ -874,7 +877,23 @@ static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client) uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); char json_payload[64]; - c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status); + + struct ip_info ip_info; + + if (curr_status == 5) + { + wifi_get_ip_info(STATION_IF , &ip_info); + /* If IP address not yet available, get now */ + if (strlen(ipaddr) == 0) + { + c_sprintf(ipaddr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); + } + c_sprintf(json_payload, "{\"deviceid\":\"%s\", \"status\":%d}", ipaddr, curr_status); + } + else + { + c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status); + } const char fmt[] = "HTTP/1.1 200 OK\r\n" @@ -1684,6 +1703,8 @@ static int enduser_setup_start(lua_State *L) /* Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation. */ ENDUSER_SETUP_DEBUG("enduser_setup_start"); + ipaddr[0] = '\0'; + if (!do_station_cfg_handle) { do_station_cfg_handle = task_get_id(do_station_cfg); From b6ef1ffee7366f0b8cb650424a2b308d5d283c20 Mon Sep 17 00:00:00 2001 From: thirschbuechler Date: Mon, 20 Mar 2017 21:30:43 +0100 Subject: [PATCH 35/73] Add 7bit address info (#1834) --- docs/en/modules/i2c.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/i2c.md b/docs/en/modules/i2c.md index c7ef9afc22..a81ce37d27 100644 --- a/docs/en/modules/i2c.md +++ b/docs/en/modules/i2c.md @@ -11,7 +11,7 @@ Setup I²C address and read/write mode for the next transfer. #### Parameters - `id` always 0 -- `device_addr` device address +- `device_addr` 7-bit device address, remember that [in I²C `device_addr` represents the upper 7 bits](http://www.nxp.com/documents/user_manual/UM10204.pdf#page=13) followed by a single `direction` bit - `direction` `i2c.TRANSMITTER` for writing mode , `i2c. RECEIVER` for reading mode #### Returns From b09cac058a7b5fae0be85b79bfe8de1a777e2b32 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Tue, 21 Mar 2017 20:24:32 -0400 Subject: [PATCH 36/73] Add support for streaming JSON encoder/decoder (#1755) Replaces the problematic cjson module. --- app/Makefile | 2 - app/cjson/CMakeLists.txt | 76 - app/cjson/LICENSE | 21 - app/cjson/Makefile | 47 - app/cjson/THANKS | 9 - app/cjson/cjson_mem.c | 24 - app/cjson/cjson_mem.h | 9 - app/cjson/devel/json_parser_outline.txt | 50 - app/cjson/dtoa.c | 4359 ----------------------- app/cjson/dtoa_config.h | 74 - app/cjson/fpconv.c | 209 -- app/cjson/fpconv.h | 22 - app/cjson/g_fmt.c | 112 - app/cjson/lua/cjson/util.lua | 271 -- app/cjson/lua/json2lua.lua | 14 - app/cjson/lua/lua2json.lua | 20 - app/cjson/manual.txt | 168 - app/cjson/rfc4627.txt | 563 --- app/cjson/strbuf.c | 253 -- app/cjson/strbuf.h | 155 - app/cjson/tests/README | 4 - app/cjson/tests/bench.lua | 131 - app/cjson/tests/example1.json | 22 - app/cjson/tests/example2.json | 11 - app/cjson/tests/example3.json | 26 - app/cjson/tests/example4.json | 88 - app/cjson/tests/example5.json | 27 - app/cjson/tests/genutf8.pl | 23 - app/cjson/tests/numbers.json | 7 - app/cjson/tests/octets-escaped.dat | 1 - app/cjson/tests/rfc-example1.json | 13 - app/cjson/tests/rfc-example2.json | 22 - app/cjson/tests/test.lua | 425 --- app/cjson/tests/types.json | 1 - app/include/user_modules.h | 2 +- app/modules/Makefile | 2 +- app/modules/cjson.c | 1630 --------- app/modules/sjson.c | 1063 ++++++ app/sjson/jsonsl.c | 1661 +++++++++ app/sjson/jsonsl.h | 995 ++++++ docs/en/modules/cjson.md | 49 +- docs/en/modules/sjson.md | 234 ++ ld/nodemcu.ld | 2 +- lua_examples/mqtt/mqtt_file.lua | 2 +- lua_examples/somfy.lua | 12 +- mkdocs.yml | 1 + 46 files changed, 3965 insertions(+), 8947 deletions(-) delete mode 100644 app/cjson/CMakeLists.txt delete mode 100644 app/cjson/LICENSE delete mode 100644 app/cjson/Makefile delete mode 100644 app/cjson/THANKS delete mode 100644 app/cjson/cjson_mem.c delete mode 100644 app/cjson/cjson_mem.h delete mode 100644 app/cjson/devel/json_parser_outline.txt delete mode 100644 app/cjson/dtoa.c delete mode 100644 app/cjson/dtoa_config.h delete mode 100644 app/cjson/fpconv.c delete mode 100644 app/cjson/fpconv.h delete mode 100644 app/cjson/g_fmt.c delete mode 100644 app/cjson/lua/cjson/util.lua delete mode 100644 app/cjson/lua/json2lua.lua delete mode 100644 app/cjson/lua/lua2json.lua delete mode 100644 app/cjson/manual.txt delete mode 100644 app/cjson/rfc4627.txt delete mode 100644 app/cjson/strbuf.c delete mode 100644 app/cjson/strbuf.h delete mode 100644 app/cjson/tests/README delete mode 100644 app/cjson/tests/bench.lua delete mode 100644 app/cjson/tests/example1.json delete mode 100644 app/cjson/tests/example2.json delete mode 100644 app/cjson/tests/example3.json delete mode 100644 app/cjson/tests/example4.json delete mode 100644 app/cjson/tests/example5.json delete mode 100644 app/cjson/tests/genutf8.pl delete mode 100644 app/cjson/tests/numbers.json delete mode 100644 app/cjson/tests/octets-escaped.dat delete mode 100644 app/cjson/tests/rfc-example1.json delete mode 100644 app/cjson/tests/rfc-example2.json delete mode 100644 app/cjson/tests/test.lua delete mode 100644 app/cjson/tests/types.json delete mode 100644 app/modules/cjson.c create mode 100644 app/modules/sjson.c create mode 100644 app/sjson/jsonsl.c create mode 100644 app/sjson/jsonsl.h create mode 100644 docs/en/modules/sjson.md diff --git a/app/Makefile b/app/Makefile index 26dca44a3a..578b0087e1 100644 --- a/app/Makefile +++ b/app/Makefile @@ -38,7 +38,6 @@ SUBDIRS= \ smart \ modules \ spiffs \ - cjson \ crypto \ dhtlib \ tsl2561 \ @@ -87,7 +86,6 @@ COMPONENTS_eagle.app.v6 = \ smart/smart.a \ spiffs/spiffs.a \ fatfs/libfatfs.a \ - cjson/libcjson.a \ crypto/libcrypto.a \ dhtlib/libdhtlib.a \ tsl2561/tsl2561lib.a \ diff --git a/app/cjson/CMakeLists.txt b/app/cjson/CMakeLists.txt deleted file mode 100644 index c17239b2da..0000000000 --- a/app/cjson/CMakeLists.txt +++ /dev/null @@ -1,76 +0,0 @@ -# If Lua is installed in a non-standard location, please set the LUA_DIR -# environment variable to point to prefix for the install. Eg: -# Unix: export LUA_DIR=/home/user/pkg -# Windows: set LUA_DIR=c:\lua51 - -project(lua-cjson C) -cmake_minimum_required(VERSION 2.6) - -option(USE_INTERNAL_FPCONV "Use internal strtod() / g_fmt() code for performance") -option(MULTIPLE_THREADS "Support multi-threaded apps with internal fpconv - recommended" ON) - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - FORCE) -endif() - -find_package(Lua51 REQUIRED) -include_directories(${LUA_INCLUDE_DIR}) - -if(NOT USE_INTERNAL_FPCONV) - # Use libc number conversion routines (strtod(), sprintf()) - set(FPCONV_SOURCES fpconv.c) -else() - # Use internal number conversion routines - add_definitions(-DUSE_INTERNAL_FPCONV) - set(FPCONV_SOURCES g_fmt.c dtoa.c) - - include(TestBigEndian) - TEST_BIG_ENDIAN(IEEE_BIG_ENDIAN) - if(IEEE_BIG_ENDIAN) - add_definitions(-DIEEE_BIG_ENDIAN) - endif() - - if(MULTIPLE_THREADS) - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads REQUIRED) - if(NOT CMAKE_USE_PTHREADS_INIT) - message(FATAL_ERROR - "Pthreads not found - required by MULTIPLE_THREADS option") - endif() - add_definitions(-DMULTIPLE_THREADS) - endif() -endif() - -# Handle platforms missing isinf() macro (Eg, some Solaris systems). -include(CheckSymbolExists) -CHECK_SYMBOL_EXISTS(isinf math.h HAVE_ISINF) -if(NOT HAVE_ISINF) - add_definitions(-DUSE_INTERNAL_ISINF) -endif() - -set(_MODULE_LINK "${CMAKE_THREAD_LIBS_INIT}") -get_filename_component(_lua_lib_dir ${LUA_LIBRARY} PATH) - -if(APPLE) - set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS - "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") -endif() - -if(WIN32) - # Win32 modules need to be linked to the Lua library. - set(_MODULE_LINK ${LUA_LIBRARY} ${_MODULE_LINK}) - set(_lua_module_dir "${_lua_lib_dir}") - # Windows sprintf()/strtod() handle NaN/inf differently. Not supported. - add_definitions(-DDISABLE_INVALID_NUMBERS) -else() - set(_lua_module_dir "${_lua_lib_dir}/lua/5.1") -endif() - -add_library(cjson MODULE lua_cjson.c strbuf.c ${FPCONV_SOURCES}) -set_target_properties(cjson PROPERTIES PREFIX "") -target_link_libraries(cjson ${_MODULE_LINK}) -install(TARGETS cjson DESTINATION "${_lua_module_dir}") - -# vi:ai et sw=4 ts=4: diff --git a/app/cjson/LICENSE b/app/cjson/LICENSE deleted file mode 100644 index 7f37af3bb3..0000000000 --- a/app/cjson/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (c) 2010-2012 Mark Pulford - 2015 Zeroday Hong nodemcu.com - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app/cjson/Makefile b/app/cjson/Makefile deleted file mode 100644 index c81eb098a2..0000000000 --- a/app/cjson/Makefile +++ /dev/null @@ -1,47 +0,0 @@ - -############################################################# -# 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 = libcjson.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/cjson/THANKS b/app/cjson/THANKS deleted file mode 100644 index 4aade13617..0000000000 --- a/app/cjson/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -The following people have helped with bug reports, testing and/or -suggestions: - -- Louis-Philippe Perron (@loopole) -- Ondřej Jirman -- Steve Donovan -- Zhang "agentzh" Yichun - -Thanks! diff --git a/app/cjson/cjson_mem.c b/app/cjson/cjson_mem.c deleted file mode 100644 index 092cb788fd..0000000000 --- a/app/cjson/cjson_mem.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "cjson_mem.h" -#include "../lua/lauxlib.h" -#include - -static const char errfmt[] = "cjson %salloc: out of mem (%d bytes)"; - -void *cjson_mem_malloc (uint32_t sz) -{ - void *p = (void*)c_malloc (sz); - lua_State *L = lua_getstate(); - if (!p) - luaL_error (L, errfmt, "m", sz); - return p; -} - - -void *cjson_mem_realloc (void *o, uint32_t sz) -{ - void *p = (void*)c_realloc (o, sz); - lua_State *L = lua_getstate(); - if (!p) - luaL_error (L, errfmt, "re", sz); - return p; -} diff --git a/app/cjson/cjson_mem.h b/app/cjson/cjson_mem.h deleted file mode 100644 index ad14e1b280..0000000000 --- a/app/cjson/cjson_mem.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _CJSON_MEM_H_ -#define _CJSON_MEM_H_ - -#include "../lua/lua.h" - -void *cjson_mem_malloc (uint32_t sz); -void *cjson_mem_realloc (void *p, uint32_t sz); - -#endif diff --git a/app/cjson/devel/json_parser_outline.txt b/app/cjson/devel/json_parser_outline.txt deleted file mode 100644 index 01db78d32d..0000000000 --- a/app/cjson/devel/json_parser_outline.txt +++ /dev/null @@ -1,50 +0,0 @@ -parser: - - call parse_value - - next_token - ? nop. - -parse_value: - - next_token - ? call parse_object. - ? call parse_array. - ? push. return. - ? push. return. - ? push. return. - ? push. return. - -parse_object: - - push table - - next_token - ? push. - - next_token - ? nop. - - call parse_value - - set table - - next_token - ? return. - ? loop parse_object. - -parse_array: - - push table - - call parse_value - - table append - - next_token - ? loop parse_array. - ? ] return. - -next_token: - - check next character - ? { return - ? } return - ? [ return - ? ] return - ? , return - ? : return - ? [-0-9] gobble number. return - ? " gobble string. return - ? [ \t\n] eat whitespace. - ? n Check "null". return or - ? t Check "true". return or - ? f Check "false". return or - ? . return - ? \0 return diff --git a/app/cjson/dtoa.c b/app/cjson/dtoa.c deleted file mode 100644 index ff1e744bf2..0000000000 --- a/app/cjson/dtoa.c +++ /dev/null @@ -1,4359 +0,0 @@ -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* Please send bug reports to David M. Gay (dmg at acm dot org, - * with " at " changed at "@" and " dot " changed to "."). */ - -/* On a machine with IEEE extended-precision registers, it is - * necessary to specify double-precision (53-bit) rounding precision - * before invoking strtod or dtoa. If the machine uses (the equivalent - * of) Intel 80x87 arithmetic, the call - * _control87(PC_53, MCW_PC); - * does this with many compilers. Whether this or another call is - * appropriate depends on the compiler; for this to work, it may be - * necessary to #include "float.h" or another system-dependent header - * file. - */ - -/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets errno to ERANGE). With IEEE arithmetic, ties are - * broken by the IEEE round-even rule. Otherwise ties are broken by - * biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE, IBM, or VAX double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_8087 for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_MC68k for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define IBM for IBM mainframe-style floating-point arithmetic. - * #define VAX for VAX-style floating-point arithmetic (D_floating). - * #define No_leftright to omit left-right logic in fast floating-point - * computation of dtoa. This will cause dtoa modes 4 and 5 to be - * treated the same as modes 2 and 3 for some inputs. - * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS - * is also #defined, fegetround() will be queried for the rounding mode. - * Note that both FLT_ROUNDS and fegetround() are specified by the C99 - * standard (and are specified to be consistent, with fesetround() - * affecting the value of FLT_ROUNDS), but that some (Linux) systems - * do not work correctly in this regard, so using fegetround() is more - * portable than using FLT_ROUNDS directly. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and Honor_FLT_ROUNDS is not #defined. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic - * that rounds toward +Infinity. - * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased - * rounding when the underlying floating-point arithmetic uses - * unbiased rounding. This prevent using ordinary floating-point - * arithmetic when the result could be computed with one rounding error. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define NO_LONG_LONG on machines that do not have a "long long" - * integer type (of >= 64 bits). On such machines, you can - * #define Just_16 to store 16 bits per 32-bit Long when doing - * high-precision integer arithmetic. Whether this speeds things - * up or slows things down depends on the machine and the number - * being converted. If long long is available and the name is - * something other than "long long", #define Llong to be the name, - * and if "unsigned Llong" does not work as an unsigned version of - * Llong, #define #ULLong to be the corresponding unsigned type. - * #define KR_headers for old-style C function headers. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. Similarly, if you - * want something other than the system's free() to be called to - * recycle memory acquired from MALLOC, #define FREE to be the - * name of the alternate routine. (FREE or free is only called in - * pathological cases, e.g., in a dtoa call after a dtoa return in - * mode 3 with thousands of digits requested.) - * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making - * memory allocations from a private pool of memory when possible. - * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, - * unless #defined to be a different length. This default length - * suffices to get rid of MALLOC calls except for unusual cases, - * such as decimal-to-binary conversion of a very long string of - * digits. The longest string dtoa can return is about 751 bytes - * long. For conversions by strtod of strings of 800 digits and - * all dtoa conversions in single-threaded executions with 8-byte - * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte - * pointers, PRIVATE_MEM >= 7112 appears adequate. - * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK - * #defined automatically on IEEE systems. On such systems, - * when INFNAN_CHECK is #defined, strtod checks - * for Infinity and NaN (case insensitively). On some systems - * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 - * appropriately -- to the most significant word of a quiet NaN. - * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) - * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, - * strtod also accepts (case insensitively) strings of the form - * NaN(x), where x is a string of hexadecimal digits and spaces; - * if there is only one string of hexadecimal digits, it is taken - * for the 52 fraction bits of the resulting NaN; if there are two - * or more strings of hex digits, the first is for the high 20 bits, - * the second and subsequent for the low 32 bits, with intervening - * white space ignored; but if this results in none of the 52 - * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 - * and NAN_WORD1 are used instead. - * #define MULTIPLE_THREADS if the system offers preemptively scheduled - * multiple threads. In this case, you must provide (or suitably - * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed - * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed - * in pow5mult, ensures lazy evaluation of only one copy of high - * powers of 5; omitting this lock would introduce a small - * probability of wasting memory, but would otherwise be harmless.) - * You must also invoke freedtoa(s) to free the value s returned by - * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. - * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that - * avoids underflows on inputs whose result does not underflow. - * If you #define NO_IEEE_Scale on a machine that uses IEEE-format - * floating-point numbers and flushes underflows to zero rather - * than implementing gradual underflow, then you must also #define - * Sudden_Underflow. - * #define USE_LOCALE to use the current locale's decimal_point value. - * #define SET_INEXACT if IEEE arithmetic is being used and extra - * computation should be done to set the inexact flag when the - * result is inexact and avoid setting inexact when the result - * is exact. In this case, dtoa.c must be compiled in - * an environment, perhaps provided by #include "dtoa.c" in a - * suitable wrapper, that defines two functions, - * int get_inexact(void); - * void clear_inexact(void); - * such that get_inexact() returns a nonzero value if the - * inexact bit is already set, and clear_inexact() sets the - * inexact bit to 0. When SET_INEXACT is #defined, strtod - * also does extra computations to set the underflow and overflow - * flags when appropriate (i.e., when the result is tiny and - * inexact or when it is a numeric value rounded to +-infinity). - * #define NO_ERRNO if strtod should not assign errno = ERANGE when - * the result overflows to +-Infinity or underflows to 0. - * #define NO_HEX_FP to omit recognition of hexadecimal floating-point - * values by strtod. - * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) - * to disable logic for "fast" testing of very long input strings - * to strtod. This testing proceeds by initially truncating the - * input string, then if necessary comparing the whole string with - * a decimal expansion to decide close cases. This logic is only - * used for input more than STRTOD_DIGLIM digits long (default 40). - */ -#if 0 -#include "dtoa_config.h" - -#ifndef Long -#define Long long -#endif -#ifndef ULong -typedef unsigned Long ULong; -#endif - -#ifdef DEBUG -#include "stdio.h" -#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} -#endif - -#include "stdlib.h" -#include "string.h" - -#ifdef USE_LOCALE -#include "locale.h" -#endif - -#ifdef Honor_FLT_ROUNDS -#ifndef Trust_FLT_ROUNDS -#include -#endif -#endif - -#ifdef MALLOC -#ifdef KR_headers -extern char *MALLOC(); -#else -extern void *MALLOC(size_t); -#endif -#else -#define MALLOC malloc -#endif - -#ifndef Omit_Private_Memory -#ifndef PRIVATE_MEM -#define PRIVATE_MEM 2304 -#endif -#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -static double private_mem[PRIVATE_mem], *pmem_next = private_mem; -#endif - -#undef IEEE_Arith -#undef Avoid_Underflow -#ifdef IEEE_MC68k -#define IEEE_Arith -#endif -#ifdef IEEE_8087 -#define IEEE_Arith -#endif - -#ifdef IEEE_Arith -#ifndef NO_INFNAN_CHECK -#undef INFNAN_CHECK -#define INFNAN_CHECK -#endif -#else -#undef INFNAN_CHECK -#define NO_STRTOD_BIGCOMP -#endif - -#include "errno.h" - -#ifdef Bad_float_h - -#ifdef IEEE_Arith -#define DBL_DIG 15 -#define DBL_MAX_10_EXP 308 -#define DBL_MAX_EXP 1024 -#define FLT_RADIX 2 -#endif /*IEEE_Arith*/ - -#ifdef IBM -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 75 -#define DBL_MAX_EXP 63 -#define FLT_RADIX 16 -#define DBL_MAX 7.2370055773322621e+75 -#endif - -#ifdef VAX -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 38 -#define DBL_MAX_EXP 127 -#define FLT_RADIX 2 -#define DBL_MAX 1.7014118346046923e+38 -#endif - -#ifndef LONG_MAX -#define LONG_MAX 2147483647 -#endif - -#else /* ifndef Bad_float_h */ -#include "float.h" -#endif /* Bad_float_h */ - -#ifndef __MATH_H__ -#include "math.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef CONST -#ifdef KR_headers -#define CONST /* blank */ -#else -#define CONST const -#endif -#endif - -#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 -Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. -#endif - -typedef union { double d; ULong L[2]; } U; - -#ifdef IEEE_8087 -#define word0(x) (x)->L[1] -#define word1(x) (x)->L[0] -#else -#define word0(x) (x)->L[0] -#define word1(x) (x)->L[1] -#endif -#define dval(x) (x)->d - -#ifndef STRTOD_DIGLIM -#define STRTOD_DIGLIM 40 -#endif - -#ifdef DIGLIM_DEBUG -extern int strtod_diglim; -#else -#define strtod_diglim STRTOD_DIGLIM -#endif - -/* The following definition of Storeinc is appropriate for MIPS processors. - * An alternative that might be better on some machines is - * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) - */ -#if defined(IEEE_8087) + defined(VAX) -#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ -((unsigned short *)a)[0] = (unsigned short)c, a++) -#else -#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ -((unsigned short *)a)[1] = (unsigned short)c, a++) -#endif - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#ifdef IEEE_Arith -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Nbits 53 -#define Bias 1023 -#define Emax 1023 -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#ifndef NO_IEEE_Scale -#define Avoid_Underflow -#ifdef Flush_Denorm /* debugging option */ -#undef Sudden_Underflow -#endif -#endif - -#ifndef Flt_Rounds -#ifdef FLT_ROUNDS -#define Flt_Rounds FLT_ROUNDS -#else -#define Flt_Rounds 1 -#endif -#endif /*Flt_Rounds*/ - -#ifdef Honor_FLT_ROUNDS -#undef Check_FLT_ROUNDS -#define Check_FLT_ROUNDS -#else -#define Rounding Flt_Rounds -#endif - -#else /* ifndef IEEE_Arith */ -#undef Check_FLT_ROUNDS -#undef Honor_FLT_ROUNDS -#undef SET_INEXACT -#undef Sudden_Underflow -#define Sudden_Underflow -#ifdef IBM -#undef Flt_Rounds -#define Flt_Rounds 0 -#define Exp_shift 24 -#define Exp_shift1 24 -#define Exp_msk1 0x1000000 -#define Exp_msk11 0x1000000 -#define Exp_mask 0x7f000000 -#define P 14 -#define Nbits 56 -#define Bias 65 -#define Emax 248 -#define Emin (-260) -#define Exp_1 0x41000000 -#define Exp_11 0x41000000 -#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ -#define Frac_mask 0xffffff -#define Frac_mask1 0xffffff -#define Bletch 4 -#define Ten_pmax 22 -#define Bndry_mask 0xefffff -#define Bndry_mask1 0xffffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 4 -#define Tiny0 0x100000 -#define Tiny1 0 -#define Quick_max 14 -#define Int_max 15 -#else /* VAX */ -#undef Flt_Rounds -#define Flt_Rounds 1 -#define Exp_shift 23 -#define Exp_shift1 7 -#define Exp_msk1 0x80 -#define Exp_msk11 0x800000 -#define Exp_mask 0x7f80 -#define P 56 -#define Nbits 56 -#define Bias 129 -#define Emax 126 -#define Emin (-129) -#define Exp_1 0x40800000 -#define Exp_11 0x4080 -#define Ebits 8 -#define Frac_mask 0x7fffff -#define Frac_mask1 0xffff007f -#define Ten_pmax 24 -#define Bletch 2 -#define Bndry_mask 0xffff007f -#define Bndry_mask1 0xffff007f -#define LSB 0x10000 -#define Sign_bit 0x8000 -#define Log2P 1 -#define Tiny0 0x80 -#define Tiny1 0 -#define Quick_max 15 -#define Int_max 15 -#endif /* IBM, VAX */ -#endif /* IEEE_Arith */ - -#ifndef IEEE_Arith -#define ROUND_BIASED -#else -#ifdef ROUND_BIASED_without_Round_Up -#undef ROUND_BIASED -#define ROUND_BIASED -#endif -#endif - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -#ifdef KR_headers -extern double rnd_prod(), rnd_quot(); -#else -extern double rnd_prod(double, double), rnd_quot(double, double); -#endif -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef Pack_32 -#define Pack_32 -#endif - -typedef struct BCinfo BCinfo; - struct -BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; }; - -#ifdef KR_headers -#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) -#else -#define FFFFFFFF 0xffffffffUL -#endif - -#ifdef NO_LONG_LONG -#undef ULLong -#ifdef Just_16 -#undef Pack_32 -/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. - * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly - * slower. Hence the default is now to store 32 bits per Long. - */ -#endif -#else /* long long available */ -#ifndef Llong -#define Llong long long -#endif -#ifndef ULLong -#define ULLong unsigned Llong -#endif -#endif /* NO_LONG_LONG */ - -#ifndef MULTIPLE_THREADS -#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ -#define FREE_DTOA_LOCK(n) /*nothing*/ -#endif - -#define Kmax 7 - -#ifdef __cplusplus -extern "C" double fpconv_strtod(const char *s00, char **se); -extern "C" char *dtoa(double d, int mode, int ndigits, - int *decpt, int *sign, char **rve); -#endif - - struct -Bigint { - struct Bigint *next; - int k, maxwds, sign, wds; - ULong x[1]; - }; - - typedef struct Bigint Bigint; - - static Bigint *freelist[Kmax+1]; - - static Bigint * -Balloc -#ifdef KR_headers - (k) int k; -#else - (int k) -#endif -{ - int x; - Bigint *rv; -#ifndef Omit_Private_Memory - unsigned int len; -#endif - - ACQUIRE_DTOA_LOCK(0); - /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ - /* but this case seems very unlikely. */ - if (k <= Kmax && (rv = freelist[k])) - freelist[k] = rv->next; - else { - x = 1 << k; -#ifdef Omit_Private_Memory - rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); -#else - len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) - /sizeof(double); - if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { - rv = (Bigint*)pmem_next; - pmem_next += len; - } - else - rv = (Bigint*)MALLOC(len*sizeof(double)); -#endif - rv->k = k; - rv->maxwds = x; - } - FREE_DTOA_LOCK(0); - rv->sign = rv->wds = 0; - return rv; - } - - static void -Bfree -#ifdef KR_headers - (v) Bigint *v; -#else - (Bigint *v) -#endif -{ - if (v) { - if (v->k > Kmax) -#ifdef FREE - FREE((void*)v); -#else - free((void*)v); -#endif - else { - ACQUIRE_DTOA_LOCK(0); - v->next = freelist[v->k]; - freelist[v->k] = v; - FREE_DTOA_LOCK(0); - } - } - } - -#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ -y->wds*sizeof(Long) + 2*sizeof(int)) - - static Bigint * -multadd -#ifdef KR_headers - (b, m, a) Bigint *b; int m, a; -#else - (Bigint *b, int m, int a) /* multiply by m and add a */ -#endif -{ - int i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; -#else - ULong carry, *x, y; -#ifdef Pack_32 - ULong xi, z; -#endif -#endif - Bigint *b1; - - wds = b->wds; - x = b->x; - i = 0; - carry = a; - do { -#ifdef ULLong - y = *x * (ULLong)m + carry; - carry = y >> 32; - *x++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - xi = *x; - y = (xi & 0xffff) * m + carry; - z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); -#else - y = *x * m + carry; - carry = y >> 16; - *x++ = y & 0xffff; -#endif -#endif - } - while(++i < wds); - if (carry) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = carry; - b->wds = wds; - } - return b; - } - - static Bigint * -s2b -#ifdef KR_headers - (s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9; -#else - (const char *s, int nd0, int nd, ULong y9, int dplen) -#endif -{ - Bigint *b; - int i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; -#ifdef Pack_32 - b = Balloc(k); - b->x[0] = y9; - b->wds = 1; -#else - b = Balloc(k+1); - b->x[0] = y9 & 0xffff; - b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; -#endif - - i = 9; - if (9 < nd0) { - s += 9; - do b = multadd(b, 10, *s++ - '0'); - while(++i < nd0); - s += dplen; - } - else - s += dplen + 9; - for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); - return b; - } - - static int -hi0bits -#ifdef KR_headers - (x) ULong x; -#else - (ULong x) -#endif -{ - int k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; - } - - static int -lo0bits -#ifdef KR_headers - (y) ULong *y; -#else - (ULong *y) -#endif -{ - int k; - ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x) - return 32; - } - *y = x; - return k; - } - - static Bigint * -i2b -#ifdef KR_headers - (i) int i; -#else - (int i) -#endif -{ - Bigint *b; - - b = Balloc(1); - b->x[0] = i; - b->wds = 1; - return b; - } - - static Bigint * -mult -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int k, wa, wb, wc; - ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; - ULong y; -#ifdef ULLong - ULLong carry, z; -#else - ULong carry, z; -#ifdef Pack_32 - ULong z2; -#endif -#endif - - if (a->wds < b->wds) { - c = a; - a = b; - b = c; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - for(x = c->x, xa = x + wc; x < xa; x++) - *x = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef ULLong - for(; xb < xbe; xc0++) { - if ((y = *xb++)) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (ULLong)y + *xc + carry; - carry = z >> 32; - *xc++ = z & FFFFFFFF; - } - while(x < xae); - *xc = carry; - } - } -#else -#ifdef Pack_32 - for(; xb < xbe; xb++, xc0++) { - if (y = *xb & 0xffff) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if (y = *xb >> 16) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#else - for(; xb < xbe; xc0++) { - if (y = *xb++) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * y + *xc + carry; - carry = z >> 16; - *xc++ = z & 0xffff; - } - while(x < xae); - *xc = carry; - } - } -#endif -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; - } - - static Bigint *p5s; - - static Bigint * -pow5mult -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - Bigint *b1, *p5, *p51; - int i; - static int p05[3] = { 5, 25, 125 }; - - if ((i = k & 3)) - b = multadd(b, p05[i-1], 0); - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { - /* first time */ -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p5 = p5s)) { - p5 = p5s = i2b(625); - p5->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p5 = p5s = i2b(625); - p5->next = 0; -#endif - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); - p51->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p51 = p5->next = mult(p5,p5); - p51->next = 0; -#endif - } - p5 = p51; - } - return b; - } - - static Bigint * -lshift -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - int i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - -#ifdef Pack_32 - n = k >> 5; -#else - n = k >> 4; -#endif - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; -#ifdef Pack_32 - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z)) - ++n1; - } -#else - if (k &= 0xf) { - k1 = 16 - k; - z = 0; - do { - *x1++ = *x << k & 0xffff | z; - z = *x++ >> k1; - } - while(x < xe); - if (*x1 = z) - ++n1; - } -#endif - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; - Bfree(b); - return b1; - } - - static int -cmp -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - ULong *xa, *xa0, *xb, *xb0; - int i, j; - - i = a->wds; - j = b->wds; -#ifdef DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; - } - - static Bigint * -diff -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int i, wa, wb; - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef ULLong - ULLong borrow, y; -#else - ULong borrow, y; -#ifdef Pack_32 - ULong z; -#endif -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef ULLong - do { - y = (ULLong)*xa++ - *xb++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } -#else -#ifdef Pack_32 - do { - y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } -#else - do { - y = *xa++ - *xb++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } -#endif -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; - } - - static double -ulp -#ifdef KR_headers - (x) U *x; -#else - (U *x) -#endif -{ - Long L; - U u; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - if (L > 0) { -#endif -#endif -#ifdef IBM - L |= Exp_msk1 >> 4; -#endif - word0(&u) = L; - word1(&u) = 0; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - word0(&u) = 0x80000 >> L; - word1(&u) = 0; - } - else { - word0(&u) = 0; - L -= Exp_shift; - word1(&u) = L >= 31 ? 1 : 1 << 31 - L; - } - } -#endif -#endif - return dval(&u); - } - - static double -b2d -#ifdef KR_headers - (a, e) Bigint *a; int *e; -#else - (Bigint *a, int *e) -#endif -{ - ULong *xa, *xa0, w, y, z; - int k; - U d; -#ifdef VAX - ULong d0, d1; -#else -#define d0 word0(&d) -#define d1 word1(&d) -#endif - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; -#ifdef Pack_32 - if (k < Ebits) { - d0 = Exp_1 | y >> (Ebits - k); - w = xa > xa0 ? *--xa : 0; - d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - d0 = Exp_1 | y << k | z >> (32 - k); - y = xa > xa0 ? *--xa : 0; - d1 = z << k | y >> (32 - k); - } - else { - d0 = Exp_1 | y; - d1 = z; - } -#else - if (k < Ebits + 16) { - z = xa > xa0 ? *--xa : 0; - d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; - w = xa > xa0 ? *--xa : 0; - y = xa > xa0 ? *--xa : 0; - d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - w = xa > xa0 ? *--xa : 0; - k -= Ebits + 16; - d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; - y = xa > xa0 ? *--xa : 0; - d1 = w << k + 16 | y << k; -#endif - ret_d: -#ifdef VAX - word0(&d) = d0 >> 16 | d0 << 16; - word1(&d) = d1 >> 16 | d1 << 16; -#else -#undef d0 -#undef d1 -#endif - return dval(&d); - } - - static Bigint * -d2b -#ifdef KR_headers - (d, e, bits) U *d; int *e, *bits; -#else - (U *d, int *e, int *bits) -#endif -{ - Bigint *b; - int de, k; - ULong *x, y, z; -#ifndef Sudden_Underflow - int i; -#endif -#ifdef VAX - ULong d0, d1; - d0 = word0(d) >> 16 | word0(d) << 16; - d1 = word1(d) >> 16 | word1(d) << 16; -#else -#define d0 word0(d) -#define d1 word1(d) -#endif - -#ifdef Pack_32 - b = Balloc(1); -#else - b = Balloc(2); -#endif - x = b->x; - - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int)(d0 >> Exp_shift); -#ifndef IBM - z |= Exp_msk11; -#endif -#else - if ((de = (int)(d0 >> Exp_shift))) - z |= Exp_msk1; -#endif -#ifdef Pack_32 - if ((y = d1)) { - if ((k = lo0bits(&y))) { - x[0] = y | z << (32 - k); - z >>= k; - } - else - x[0] = y; -#ifndef Sudden_Underflow - i = -#endif - b->wds = (x[1] = z) ? 2 : 1; - } - else { - k = lo0bits(&z); - x[0] = z; -#ifndef Sudden_Underflow - i = -#endif - b->wds = 1; - k += 32; - } -#else - if (y = d1) { - if (k = lo0bits(&y)) - if (k >= 16) { - x[0] = y | z << 32 - k & 0xffff; - x[1] = z >> k - 16 & 0xffff; - x[2] = z >> k; - i = 2; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16 | z << 16 - k & 0xffff; - x[2] = z >> k & 0xffff; - x[3] = z >> k+16; - i = 3; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16; - x[2] = z & 0xffff; - x[3] = z >> 16; - i = 3; - } - } - else { -#ifdef DEBUG - if (!z) - Bug("Zero passed to d2b"); -#endif - k = lo0bits(&z); - if (k >= 16) { - x[0] = z; - i = 0; - } - else { - x[0] = z & 0xffff; - x[1] = z >> 16; - i = 1; - } - k += 32; - } - while(!x[i]) - --i; - b->wds = i + 1; -#endif -#ifndef Sudden_Underflow - if (de) { -#endif -#ifdef IBM - *e = (de - Bias - (P-1) << 2) + k; - *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); -#else - *e = de - Bias - (P-1) + k; - *bits = P - k; -#endif -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; -#ifdef Pack_32 - *bits = 32*i - hi0bits(x[i-1]); -#else - *bits = (i+2)*16 - hi0bits(x[i]); -#endif - } -#endif - return b; - } -#undef d0 -#undef d1 - - static double -ratio -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - U da, db; - int k, ka, kb; - - dval(&da) = b2d(a, &ka); - dval(&db) = b2d(b, &kb); -#ifdef Pack_32 - k = ka - kb + 32*(a->wds - b->wds); -#else - k = ka - kb + 16*(a->wds - b->wds); -#endif -#ifdef IBM - if (k > 0) { - word0(&da) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(&da) *= 1 << k; - } - else { - k = -k; - word0(&db) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(&db) *= 1 << k; - } -#else - if (k > 0) - word0(&da) += k*Exp_msk1; - else { - k = -k; - word0(&db) += k*Exp_msk1; - } -#endif - return dval(&da) / dval(&db); - } - - static CONST double -tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -#ifdef VAX - , 1e23, 1e24 -#endif - }; - - static CONST double -#ifdef IEEE_Arith -bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, -#ifdef Avoid_Underflow - 9007199254740992.*9007199254740992.e-256 - /* = 2^106 * 1e-256 */ -#else - 1e-256 -#endif - }; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -#define Scale_Bit 0x10 -#define n_bigtens 5 -#else -#ifdef IBM -bigtens[] = { 1e16, 1e32, 1e64 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; -#define n_bigtens 3 -#else -bigtens[] = { 1e16, 1e32 }; -static CONST double tinytens[] = { 1e-16, 1e-32 }; -#define n_bigtens 2 -#endif -#endif - -#undef Need_Hexdig -#ifdef INFNAN_CHECK -#ifndef No_Hex_NaN -#define Need_Hexdig -#endif -#endif - -#ifndef Need_Hexdig -#ifndef NO_HEX_FP -#define Need_Hexdig -#endif -#endif - -#ifdef Need_Hexdig /*{*/ -static unsigned char hexdig[256]; - - static void -#ifdef KR_headers -htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; -#else -htinit(unsigned char *h, unsigned char *s, int inc) -#endif -{ - int i, j; - for(i = 0; (j = s[i]) !=0; i++) - h[j] = i + inc; - } - - static void -#ifdef KR_headers -hexdig_init() -#else -hexdig_init(void) -#endif -{ -#define USC (unsigned char *) - htinit(hexdig, USC "0123456789", 0x10); - htinit(hexdig, USC "abcdef", 0x10 + 10); - htinit(hexdig, USC "ABCDEF", 0x10 + 10); - } -#endif /* } Need_Hexdig */ - -#ifdef INFNAN_CHECK - -#ifndef NAN_WORD0 -#define NAN_WORD0 0x7ff80000 -#endif - -#ifndef NAN_WORD1 -#define NAN_WORD1 0 -#endif - - static int -match -#ifdef KR_headers - (sp, t) char **sp, *t; -#else - (const char **sp, const char *t) -#endif -{ - int c, d; - CONST char *s = *sp; - - while((d = *t++)) { - if ((c = *++s) >= 'A' && c <= 'Z') - c += 'a' - 'A'; - if (c != d) - return 0; - } - *sp = s + 1; - return 1; - } - -#ifndef No_Hex_NaN - static void -hexnan -#ifdef KR_headers - (rvp, sp) U *rvp; CONST char **sp; -#else - (U *rvp, const char **sp) -#endif -{ - ULong c, x[2]; - CONST char *s; - int c1, havedig, udx0, xshift; - - if (!hexdig['0']) - hexdig_init(); - x[0] = x[1] = 0; - havedig = xshift = 0; - udx0 = 1; - s = *sp; - /* allow optional initial 0x or 0X */ - while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') - ++s; - if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) - s += 2; - while((c = *(CONST unsigned char*)++s)) { - if ((c1 = hexdig[c])) - c = c1 & 0xf; - else if (c <= ' ') { - if (udx0 && havedig) { - udx0 = 0; - xshift = 1; - } - continue; - } -#ifdef GDTOA_NON_PEDANTIC_NANCHECK - else if (/*(*/ c == ')' && havedig) { - *sp = s + 1; - break; - } - else - return; /* invalid form: don't change *sp */ -#else - else { - do { - if (/*(*/ c == ')') { - *sp = s + 1; - break; - } - } while((c = *++s)); - break; - } -#endif - havedig = 1; - if (xshift) { - xshift = 0; - x[0] = x[1]; - x[1] = 0; - } - if (udx0) - x[0] = (x[0] << 4) | (x[1] >> 28); - x[1] = (x[1] << 4) | c; - } - if ((x[0] &= 0xfffff) || x[1]) { - word0(rvp) = Exp_mask | x[0]; - word1(rvp) = x[1]; - } - } -#endif /*No_Hex_NaN*/ -#endif /* INFNAN_CHECK */ - -#ifdef Pack_32 -#define ULbits 32 -#define kshift 5 -#define kmask 31 -#else -#define ULbits 16 -#define kshift 4 -#define kmask 15 -#endif - -#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/ - static Bigint * -#ifdef KR_headers -increment(b) Bigint *b; -#else -increment(Bigint *b) -#endif -{ - ULong *x, *xe; - Bigint *b1; - - x = b->x; - xe = x + b->wds; - do { - if (*x < (ULong)0xffffffffL) { - ++*x; - return b; - } - *x++ = 0; - } while(x < xe); - { - if (b->wds >= b->maxwds) { - b1 = Balloc(b->k+1); - Bcopy(b1,b); - Bfree(b); - b = b1; - } - b->x[b->wds++] = 1; - } - return b; - } - -#endif /*}*/ - -#ifndef NO_HEX_FP /*{*/ - - static void -#ifdef KR_headers -rshift(b, k) Bigint *b; int k; -#else -rshift(Bigint *b, int k) -#endif -{ - ULong *x, *x1, *xe, y; - int n; - - x = x1 = b->x; - n = k >> kshift; - if (n < b->wds) { - xe = x + b->wds; - x += n; - if (k &= kmask) { - n = 32 - k; - y = *x++ >> k; - while(x < xe) { - *x1++ = (y | (*x << n)) & 0xffffffff; - y = *x++ >> k; - } - if ((*x1 = y) !=0) - x1++; - } - else - while(x < xe) - *x1++ = *x++; - } - if ((b->wds = x1 - b->x) == 0) - b->x[0] = 0; - } - - static ULong -#ifdef KR_headers -any_on(b, k) Bigint *b; int k; -#else -any_on(Bigint *b, int k) -#endif -{ - int n, nwds; - ULong *x, *x0, x1, x2; - - x = b->x; - nwds = b->wds; - n = k >> kshift; - if (n > nwds) - n = nwds; - else if (n < nwds && (k &= kmask)) { - x1 = x2 = x[n]; - x1 >>= k; - x1 <<= k; - if (x1 != x2) - return 1; - } - x0 = x; - x += n; - while(x > x0) - if (*--x) - return 1; - return 0; - } - -enum { /* rounding values: same as FLT_ROUNDS */ - Round_zero = 0, - Round_near = 1, - Round_up = 2, - Round_down = 3 - }; - - void -#ifdef KR_headers -gethex(sp, rvp, rounding, sign) - CONST char **sp; U *rvp; int rounding, sign; -#else -gethex( CONST char **sp, U *rvp, int rounding, int sign) -#endif -{ - Bigint *b; - CONST unsigned char *decpt, *s0, *s, *s1; - Long e, e1; - ULong L, lostbits, *x; - int big, denorm, esign, havedig, k, n, nbits, up, zret; -#ifdef IBM - int j; -#endif - enum { -#ifdef IEEE_Arith /*{{*/ - emax = 0x7fe - Bias - P + 1, - emin = Emin - P + 1 -#else /*}{*/ - emin = Emin - P, -#ifdef VAX - emax = 0x7ff - Bias - P + 1 -#endif -#ifdef IBM - emax = 0x7f - Bias - P -#endif -#endif /*}}*/ - }; -#ifdef USE_LOCALE - int i; -#ifdef NO_LOCALE_CACHE - const unsigned char *decimalpoint = (unsigned char*) - localeconv()->decimal_point; -#else - const unsigned char *decimalpoint; - static unsigned char *decimalpoint_cache; - if (!(s0 = decimalpoint_cache)) { - s0 = (unsigned char*)localeconv()->decimal_point; - if ((decimalpoint_cache = (unsigned char*) - MALLOC(strlen((CONST char*)s0) + 1))) { - strcpy((char*)decimalpoint_cache, (CONST char*)s0); - s0 = decimalpoint_cache; - } - } - decimalpoint = s0; -#endif -#endif - - if (!hexdig['0']) - hexdig_init(); - havedig = 0; - s0 = *(CONST unsigned char **)sp + 2; - while(s0[havedig] == '0') - havedig++; - s0 += havedig; - s = s0; - decpt = 0; - zret = 0; - e = 0; - if (hexdig[*s]) - havedig++; - else { - zret = 1; -#ifdef USE_LOCALE - for(i = 0; decimalpoint[i]; ++i) { - if (s[i] != decimalpoint[i]) - goto pcheck; - } - decpt = s += i; -#else - if (*s != '.') - goto pcheck; - decpt = ++s; -#endif - if (!hexdig[*s]) - goto pcheck; - while(*s == '0') - s++; - if (hexdig[*s]) - zret = 0; - havedig = 1; - s0 = s; - } - while(hexdig[*s]) - s++; -#ifdef USE_LOCALE - if (*s == *decimalpoint && !decpt) { - for(i = 1; decimalpoint[i]; ++i) { - if (s[i] != decimalpoint[i]) - goto pcheck; - } - decpt = s += i; -#else - if (*s == '.' && !decpt) { - decpt = ++s; -#endif - while(hexdig[*s]) - s++; - }/*}*/ - if (decpt) - e = -(((Long)(s-decpt)) << 2); - pcheck: - s1 = s; - big = esign = 0; - switch(*s) { - case 'p': - case 'P': - switch(*++s) { - case '-': - esign = 1; - /* no break */ - case '+': - s++; - } - if ((n = hexdig[*s]) == 0 || n > 0x19) { - s = s1; - break; - } - e1 = n - 0x10; - while((n = hexdig[*++s]) !=0 && n <= 0x19) { - if (e1 & 0xf8000000) - big = 1; - e1 = 10*e1 + n - 0x10; - } - if (esign) - e1 = -e1; - e += e1; - } - *sp = (char*)s; - if (!havedig) - *sp = (char*)s0 - 1; - if (zret) - goto retz1; - if (big) { - if (esign) { -#ifdef IEEE_Arith - switch(rounding) { - case Round_up: - if (sign) - break; - goto ret_tiny; - case Round_down: - if (!sign) - break; - goto ret_tiny; - } -#endif - goto retz; -#ifdef IEEE_Arith - ret_tiny: -#ifndef NO_ERRNO - errno = ERANGE; -#endif - word0(rvp) = 0; - word1(rvp) = 1; - return; -#endif /* IEEE_Arith */ - } - switch(rounding) { - case Round_near: - goto ovfl1; - case Round_up: - if (!sign) - goto ovfl1; - goto ret_big; - case Round_down: - if (sign) - goto ovfl1; - goto ret_big; - } - ret_big: - word0(rvp) = Big0; - word1(rvp) = Big1; - return; - } - n = s1 - s0 - 1; - for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) - k++; - b = Balloc(k); - x = b->x; - n = 0; - L = 0; -#ifdef USE_LOCALE - for(i = 0; decimalpoint[i+1]; ++i); -#endif - while(s1 > s0) { -#ifdef USE_LOCALE - if (*--s1 == decimalpoint[i]) { - s1 -= i; - continue; - } -#else - if (*--s1 == '.') - continue; -#endif - if (n == ULbits) { - *x++ = L; - L = 0; - n = 0; - } - L |= (hexdig[*s1] & 0x0f) << n; - n += 4; - } - *x++ = L; - b->wds = n = x - b->x; - n = ULbits*n - hi0bits(L); - nbits = Nbits; - lostbits = 0; - x = b->x; - if (n > nbits) { - n -= nbits; - if (any_on(b,n)) { - lostbits = 1; - k = n - 1; - if (x[k>>kshift] & 1 << (k & kmask)) { - lostbits = 2; - if (k > 0 && any_on(b,k)) - lostbits = 3; - } - } - rshift(b, n); - e += n; - } - else if (n < nbits) { - n = nbits - n; - b = lshift(b, n); - e -= n; - x = b->x; - } - if (e > Emax) { - ovfl: - Bfree(b); - ovfl1: -#ifndef NO_ERRNO - errno = ERANGE; -#endif - word0(rvp) = Exp_mask; - word1(rvp) = 0; - return; - } - denorm = 0; - if (e < emin) { - denorm = 1; - n = emin - e; - if (n >= nbits) { -#ifdef IEEE_Arith /*{*/ - switch (rounding) { - case Round_near: - if (n == nbits && (n < 2 || any_on(b,n-1))) - goto ret_tiny; - break; - case Round_up: - if (!sign) - goto ret_tiny; - break; - case Round_down: - if (sign) - goto ret_tiny; - } -#endif /* } IEEE_Arith */ - Bfree(b); - retz: -#ifndef NO_ERRNO - errno = ERANGE; -#endif - retz1: - rvp->d = 0.; - return; - } - k = n - 1; - if (lostbits) - lostbits = 1; - else if (k > 0) - lostbits = any_on(b,k); - if (x[k>>kshift] & 1 << (k & kmask)) - lostbits |= 2; - nbits -= n; - rshift(b,n); - e = emin; - } - if (lostbits) { - up = 0; - switch(rounding) { - case Round_zero: - break; - case Round_near: - if (lostbits & 2 - && (lostbits & 1) | (x[0] & 1)) - up = 1; - break; - case Round_up: - up = 1 - sign; - break; - case Round_down: - up = sign; - } - if (up) { - k = b->wds; - b = increment(b); - x = b->x; - if (denorm) { -#if 0 - if (nbits == Nbits - 1 - && x[nbits >> kshift] & 1 << (nbits & kmask)) - denorm = 0; /* not currently used */ -#endif - } - else if (b->wds > k - || ((n = nbits & kmask) !=0 - && hi0bits(x[k-1]) < 32-n)) { - rshift(b,1); - if (++e > Emax) - goto ovfl; - } - } - } -#ifdef IEEE_Arith - if (denorm) - word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; - else - word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); - word1(rvp) = b->x[0]; -#endif -#ifdef IBM - if ((j = e & 3)) { - k = b->x[0] & ((1 << j) - 1); - rshift(b,j); - if (k) { - switch(rounding) { - case Round_up: - if (!sign) - increment(b); - break; - case Round_down: - if (sign) - increment(b); - break; - case Round_near: - j = 1 << (j-1); - if (k & j && ((k & (j-1)) | lostbits)) - increment(b); - } - } - } - e >>= 2; - word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); - word1(rvp) = b->x[0]; -#endif -#ifdef VAX - /* The next two lines ignore swap of low- and high-order 2 bytes. */ - /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ - /* word1(rvp) = b->x[0]; */ - word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); - word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); -#endif - Bfree(b); - } -#endif /*!NO_HEX_FP}*/ - - static int -#ifdef KR_headers -dshift(b, p2) Bigint *b; int p2; -#else -dshift(Bigint *b, int p2) -#endif -{ - int rv = hi0bits(b->x[b->wds-1]) - 4; - if (p2 > 0) - rv -= p2; - return rv & kmask; - } - - static int -quorem -#ifdef KR_headers - (b, S) Bigint *b, *S; -#else - (Bigint *b, Bigint *S) -#endif -{ - int n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; -#else - ULong borrow, carry, y, ys; -#ifdef Pack_32 - ULong si, z, zs; -#endif -#endif - - n = S->wds; -#ifdef DEBUG - /*debug*/ if (b->wds > n) - /*debug*/ Bug("oversize b in quorem"); -#endif - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ -#ifdef DEBUG -#ifdef NO_STRTOD_BIGCOMP - /*debug*/ if (q > 9) -#else - /* An oversized q is possible when quorem is called from bigcomp and */ - /* the input is near, e.g., twice the smallest denormalized number. */ - /*debug*/ if (q > 15) -#endif - /*debug*/ Bug("oversized quotient in quorem"); -#endif - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef ULLong - ys = *sx++ * (ULLong)q + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ * q + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef ULLong - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return q; - } - -#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/ - static double -sulp -#ifdef KR_headers - (x, bc) U *x; BCinfo *bc; -#else - (U *x, BCinfo *bc) -#endif -{ - U u; - double rv; - int i; - - rv = ulp(x); - if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) - return rv; /* Is there an example where i <= 0 ? */ - word0(&u) = Exp_1 + (i << Exp_shift); - word1(&u) = 0; - return rv * u.d; - } -#endif /*}*/ - -#ifndef NO_STRTOD_BIGCOMP - static void -bigcomp -#ifdef KR_headers - (rv, s0, bc) - U *rv; CONST char *s0; BCinfo *bc; -#else - (U *rv, const char *s0, BCinfo *bc) -#endif -{ - Bigint *b, *d; - int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; - - dsign = bc->dsign; - nd = bc->nd; - nd0 = bc->nd0; - p5 = nd + bc->e0 - 1; - speccase = 0; -#ifndef Sudden_Underflow - if (rv->d == 0.) { /* special case: value near underflow-to-zero */ - /* threshold was rounded to zero */ - b = i2b(1); - p2 = Emin - P + 1; - bbits = 1; -#ifdef Avoid_Underflow - word0(rv) = (P+2) << Exp_shift; -#else - word1(rv) = 1; -#endif - i = 0; -#ifdef Honor_FLT_ROUNDS - if (bc->rounding == 1) -#endif - { - speccase = 1; - --p2; - dsign = 0; - goto have_i; - } - } - else -#endif - b = d2b(rv, &p2, &bbits); -#ifdef Avoid_Underflow - p2 -= bc->scale; -#endif - /* floor(log2(rv)) == bbits - 1 + p2 */ - /* Check for denormal case. */ - i = P - bbits; - if (i > (j = P - Emin - 1 + p2)) { -#ifdef Sudden_Underflow - Bfree(b); - b = i2b(1); - p2 = Emin; - i = P - 1; -#ifdef Avoid_Underflow - word0(rv) = (1 + bc->scale) << Exp_shift; -#else - word0(rv) = Exp_msk1; -#endif - word1(rv) = 0; -#else - i = j; -#endif - } -#ifdef Honor_FLT_ROUNDS - if (bc->rounding != 1) { - if (i > 0) - b = lshift(b, i); - if (dsign) - b = increment(b); - } - else -#endif - { - b = lshift(b, ++i); - b->x[0] |= 1; - } -#ifndef Sudden_Underflow - have_i: -#endif - p2 -= p5 + i; - d = i2b(1); - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - */ - if (p5 > 0) - d = pow5mult(d, p5); - else if (p5 < 0) - b = pow5mult(b, -p5); - if (p2 > 0) { - b2 = p2; - d2 = 0; - } - else { - b2 = 0; - d2 = -p2; - } - i = dshift(d, d2); - if ((b2 += i) > 0) - b = lshift(b, b2); - if ((d2 += i) > 0) - d = lshift(d, d2); - - /* Now b/d = exactly half-way between the two floating-point values */ - /* on either side of the input string. Compute first digit of b/d. */ - - if (!(dig = quorem(b,d))) { - b = multadd(b, 10, 0); /* very unlikely */ - dig = quorem(b,d); - } - - /* Compare b/d with s0 */ - - for(i = 0; i < nd0; ) { - if ((dd = s0[i++] - '0' - dig)) - goto ret; - if (!b->x[0] && b->wds == 1) { - if (i < nd) - dd = 1; - goto ret; - } - b = multadd(b, 10, 0); - dig = quorem(b,d); - } - for(j = bc->dp1; i++ < nd;) { - if ((dd = s0[j++] - '0' - dig)) - goto ret; - if (!b->x[0] && b->wds == 1) { - if (i < nd) - dd = 1; - goto ret; - } - b = multadd(b, 10, 0); - dig = quorem(b,d); - } - if (b->x[0] || b->wds > 1) - dd = -1; - ret: - Bfree(b); - Bfree(d); -#ifdef Honor_FLT_ROUNDS - if (bc->rounding != 1) { - if (dd < 0) { - if (bc->rounding == 0) { - if (!dsign) - goto retlow1; - } - else if (dsign) - goto rethi1; - } - else if (dd > 0) { - if (bc->rounding == 0) { - if (dsign) - goto rethi1; - goto ret1; - } - if (!dsign) - goto rethi1; - dval(rv) += 2.*sulp(rv,bc); - } - else { - bc->inexact = 0; - if (dsign) - goto rethi1; - } - } - else -#endif - if (speccase) { - if (dd <= 0) - rv->d = 0.; - } - else if (dd < 0) { - if (!dsign) /* does not happen for round-near */ -retlow1: - dval(rv) -= sulp(rv,bc); - } - else if (dd > 0) { - if (dsign) { - rethi1: - dval(rv) += sulp(rv,bc); - } - } - else { - /* Exact half-way case: apply round-even rule. */ - if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) { - i = 1 - j; - if (i <= 31) { - if (word1(rv) & (0x1 << i)) - goto odd; - } - else if (word0(rv) & (0x1 << (i-32))) - goto odd; - } - else if (word1(rv) & 1) { - odd: - if (dsign) - goto rethi1; - goto retlow1; - } - } - -#ifdef Honor_FLT_ROUNDS - ret1: -#endif - return; - } -#endif /* NO_STRTOD_BIGCOMP */ - - double -fpconv_strtod -#ifdef KR_headers - (s00, se) CONST char *s00; char **se; -#else - (const char *s00, char **se) -#endif -{ - int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; - int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign; - CONST char *s, *s0, *s1; - double aadj, aadj1; - Long L; - U aadj2, adj, rv, rv0; - ULong y, z; - BCinfo bc; - Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; -#ifdef Avoid_Underflow - ULong Lsb, Lsb1; -#endif -#ifdef SET_INEXACT - int oldinexact; -#endif -#ifndef NO_STRTOD_BIGCOMP - int req_bigcomp = 0; -#endif -#ifdef Honor_FLT_ROUNDS /*{*/ -#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ - bc.rounding = Flt_Rounds; -#else /*}{*/ - bc.rounding = 1; - switch(fegetround()) { - case FE_TOWARDZERO: bc.rounding = 0; break; - case FE_UPWARD: bc.rounding = 2; break; - case FE_DOWNWARD: bc.rounding = 3; - } -#endif /*}}*/ -#endif /*}*/ -#ifdef USE_LOCALE - CONST char *s2; -#endif - - sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0; - dval(&rv) = 0.; - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - goto ret0; - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - default: - goto break2; - } - break2: - if (*s == '0') { -#ifndef NO_HEX_FP /*{*/ - switch(s[1]) { - case 'x': - case 'X': -#ifdef Honor_FLT_ROUNDS - gethex(&s, &rv, bc.rounding, sign); -#else - gethex(&s, &rv, 1, sign); -#endif - goto ret; - } -#endif /*}*/ - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; - bc.dp0 = bc.dp1 = s - s0; - for(s1 = s; s1 > s0 && *--s1 == '0'; ) - ++nz1; -#ifdef USE_LOCALE - s1 = localeconv()->decimal_point; - if (c == *s1) { - c = '.'; - if (*++s1) { - s2 = s; - for(;;) { - if (*++s2 != *s1) { - c = 0; - break; - } - if (!*++s1) { - s = s2; - break; - } - } - } - } -#endif - if (c == '.') { - c = *++s; - bc.dp1 = s - s0; - bc.dplen = bc.dp1 - bc.dp0; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - bc.dp0 = s0 - s; - bc.dp1 = bc.dp0 + bc.dplen; - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = nz1 = 0; - } - } - } - dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - goto ret0; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = (int)L; - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) { -#ifdef INFNAN_CHECK - /* Check for Nan and Infinity */ - if (!bc.dplen) - switch(c) { - case 'i': - case 'I': - if (match(&s,"nf")) { - --s; - if (!match(&s,"inity")) - ++s; - word0(&rv) = 0x7ff00000; - word1(&rv) = 0; - goto ret; - } - break; - case 'n': - case 'N': - if (match(&s, "an")) { - word0(&rv) = NAN_WORD0; - word1(&rv) = NAN_WORD1; -#ifndef No_Hex_NaN - if (*s == '(') /*)*/ - hexnan(&rv, &s); -#endif - goto ret; - } - } -#endif /* INFNAN_CHECK */ - ret0: - s = s00; - sign = 0; - } - goto ret; - } - bc.e0 = e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - dval(&rv) = y; - if (k > 9) { -#ifdef SET_INEXACT - if (k > DBL_DIG) - oldinexact = get_inexact(); -#endif - dval(&rv) = tens[k - 9] * dval(&rv) + z; - } - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT -#ifndef Honor_FLT_ROUNDS - && Flt_Rounds == 1 -#endif -#endif - ) { - if (!e) - goto ret; -#ifndef ROUND_BIASED_without_Round_Up - if (e > 0) { - if (e <= Ten_pmax) { -#ifdef VAX - goto vax_ovfl_check; -#else -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv.d = -rv.d; - sign = 0; - } -#endif - /* rv = */ rounded_product(dval(&rv), tens[e]); - goto ret; -#endif - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv.d = -rv.d; - sign = 0; - } -#endif - e -= i; - dval(&rv) *= tens[i]; -#ifdef VAX - /* VAX exponent range is so narrow we must - * worry about overflow here... - */ - vax_ovfl_check: - word0(&rv) -= P*Exp_msk1; - /* rv = */ rounded_product(dval(&rv), tens[e]); - if ((word0(&rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) - goto ovfl; - word0(&rv) += P*Exp_msk1; -#else - /* rv = */ rounded_product(dval(&rv), tens[e]); -#endif - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv.d = -rv.d; - sign = 0; - } -#endif - /* rv = */ rounded_quotient(dval(&rv), tens[-e]); - goto ret; - } -#endif -#endif /* ROUND_BIASED_without_Round_Up */ - } - e1 += nd - k; - -#ifdef IEEE_Arith -#ifdef SET_INEXACT - bc.inexact = 1; - if (k <= DBL_DIG) - oldinexact = get_inexact(); -#endif -#ifdef Avoid_Underflow - bc.scale = 0; -#endif -#ifdef Honor_FLT_ROUNDS - if (bc.rounding >= 2) { - if (sign) - bc.rounding = bc.rounding == 2 ? 0 : 2; - else - if (bc.rounding != 2) - bc.rounding = 0; - } -#endif -#endif /*IEEE_Arith*/ - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15)) - dval(&rv) *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: - /* Can't trust HUGE_VAL */ -#ifdef IEEE_Arith -#ifdef Honor_FLT_ROUNDS - switch(bc.rounding) { - case 0: /* toward 0 */ - case 3: /* toward -infinity */ - word0(&rv) = Big0; - word1(&rv) = Big1; - break; - default: - word0(&rv) = Exp_mask; - word1(&rv) = 0; - } -#else /*Honor_FLT_ROUNDS*/ - word0(&rv) = Exp_mask; - word1(&rv) = 0; -#endif /*Honor_FLT_ROUNDS*/ -#ifdef SET_INEXACT - /* set overflow bit */ - dval(&rv0) = 1e300; - dval(&rv0) *= dval(&rv0); -#endif -#else /*IEEE_Arith*/ - word0(&rv) = Big0; - word1(&rv) = Big1; -#endif /*IEEE_Arith*/ - range_err: - if (bd0) { - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - } -#ifndef NO_ERRNO - errno = ERANGE; -#endif - goto ret; - } - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(&rv) *= bigtens[j]; - /* The last multiplication could overflow. */ - word0(&rv) -= P*Exp_msk1; - dval(&rv) *= bigtens[j]; - if ((z = word0(&rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - word0(&rv) = Big0; - word1(&rv) = Big1; - } - else - word0(&rv) += P*Exp_msk1; - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15)) - dval(&rv) /= tens[i]; - if (e1 >>= 4) { - if (e1 >= 1 << n_bigtens) - goto undfl; -#ifdef Avoid_Underflow - if (e1 & Scale_Bit) - bc.scale = 2*P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - dval(&rv) *= tinytens[j]; - if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; clear j low bits */ - if (j >= 32) { - if (j > 54) - goto undfl; - word1(&rv) = 0; - if (j >= 53) - word0(&rv) = (P+2)*Exp_msk1; - else - word0(&rv) &= 0xffffffff << (j-32); - } - else - word1(&rv) &= 0xffffffff << j; - } -#else - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(&rv) *= tinytens[j]; - /* The last multiplication could underflow. */ - dval(&rv0) = dval(&rv); - dval(&rv) *= tinytens[j]; - if (!dval(&rv)) { - dval(&rv) = 2.*dval(&rv0); - dval(&rv) *= tinytens[j]; -#endif - if (!dval(&rv)) { - undfl: - dval(&rv) = 0.; - goto range_err; - } -#ifndef Avoid_Underflow - word0(&rv) = Tiny0; - word1(&rv) = Tiny1; - /* The refinement below will clean - * this approximation up. - */ - } -#endif - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bc.nd = nd - nz1; -#ifndef NO_STRTOD_BIGCOMP - bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ - /* to silence an erroneous warning about bc.nd0 */ - /* possibly not being initialized. */ - if (nd > strtod_diglim) { - /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ - /* minimum number of decimal digits to distinguish double values */ - /* in IEEE arithmetic. */ - i = j = 18; - if (i > nd0) - j += bc.dplen; - for(;;) { - if (--j < bc.dp1 && j >= bc.dp0) - j = bc.dp0 - 1; - if (s0[j] != '0') - break; - --i; - } - e += nd - i; - nd = i; - if (nd0 > nd) - nd0 = nd; - if (nd < 9) { /* must recompute y */ - y = 0; - for(i = 0; i < nd0; ++i) - y = 10*y + s0[i] - '0'; - for(j = bc.dp1; i < nd; ++i) - y = 10*y + s0[j++] - '0'; - } - } -#endif - bd0 = s2b(s0, nd0, nd, y, bc.dplen); - - for(;;) { - bd = Balloc(bd0->k); - Bcopy(bd, bd0); - bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ - bs = i2b(1); - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Honor_FLT_ROUNDS - if (bc.rounding != 1) - bs2++; -#endif -#ifdef Avoid_Underflow - Lsb = LSB; - Lsb1 = 0; - j = bbe - bc.scale; - i = j + bbbits - 1; /* logb(rv) */ - j = P + 1 - bbbits; - if (i < Emin) { /* denormal */ - i = Emin - i; - j -= i; - if (i < 32) - Lsb <<= i; - else if (i < 52) - Lsb1 = Lsb << (i-32); - else - Lsb1 = Exp_mask; - } -#else /*Avoid_Underflow*/ -#ifdef Sudden_Underflow -#ifdef IBM - j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); -#else - j = P + 1 - bbbits; -#endif -#else /*Sudden_Underflow*/ - j = bbe; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - bb2 += j; - bd2 += j; -#ifdef Avoid_Underflow - bd2 += bc.scale; -#endif - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) - bb = lshift(bb, bb2); - if (bd5 > 0) - bd = pow5mult(bd, bd5); - if (bd2 > 0) - bd = lshift(bd, bd2); - if (bs2 > 0) - bs = lshift(bs, bs2); - delta = diff(bb, bd); - bc.dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); -#ifndef NO_STRTOD_BIGCOMP /*{*/ - if (bc.nd > nd && i <= 0) { - if (bc.dsign) { - /* Must use bigcomp(). */ - req_bigcomp = 1; - break; - } -#ifdef Honor_FLT_ROUNDS - if (bc.rounding != 1) { - if (i < 0) { - req_bigcomp = 1; - break; - } - } - else -#endif - i = -1; /* Discarded digits make delta smaller. */ - } -#endif /*}*/ -#ifdef Honor_FLT_ROUNDS /*{*/ - if (bc.rounding != 1) { - if (i < 0) { - /* Error is less than an ulp */ - if (!delta->x[0] && delta->wds <= 1) { - /* exact */ -#ifdef SET_INEXACT - bc.inexact = 0; -#endif - break; - } - if (bc.rounding) { - if (bc.dsign) { - adj.d = 1.; - goto apply_adj; - } - } - else if (!bc.dsign) { - adj.d = -1.; - if (!word1(&rv) - && !(word0(&rv) & Frac_mask)) { - y = word0(&rv) & Exp_mask; -#ifdef Avoid_Underflow - if (!bc.scale || y > 2*P*Exp_msk1) -#else - if (y) -#endif - { - delta = lshift(delta,Log2P); - if (cmp(delta, bs) <= 0) - adj.d = -0.5; - } - } - apply_adj: -#ifdef Avoid_Underflow /*{*/ - if (bc.scale && (y = word0(&rv) & Exp_mask) - <= 2*P*Exp_msk1) - word0(&adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(&rv) & Exp_mask) <= - P*Exp_msk1) { - word0(&rv) += P*Exp_msk1; - dval(&rv) += adj.d*ulp(dval(&rv)); - word0(&rv) -= P*Exp_msk1; - } - else -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow}*/ - dval(&rv) += adj.d*ulp(&rv); - } - break; - } - adj.d = ratio(delta, bs); - if (adj.d < 1.) - adj.d = 1.; - if (adj.d <= 0x7ffffffe) { - /* adj = rounding ? ceil(adj) : floor(adj); */ - y = adj.d; - if (y != adj.d) { - if (!((bc.rounding>>1) ^ bc.dsign)) - y++; - adj.d = y; - } - } -#ifdef Avoid_Underflow /*{*/ - if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) - word0(&adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { - word0(&rv) += P*Exp_msk1; - adj.d *= ulp(dval(&rv)); - if (bc.dsign) - dval(&rv) += adj.d; - else - dval(&rv) -= adj.d; - word0(&rv) -= P*Exp_msk1; - goto cont; - } -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow}*/ - adj.d *= ulp(&rv); - if (bc.dsign) { - if (word0(&rv) == Big0 && word1(&rv) == Big1) - goto ovfl; - dval(&rv) += adj.d; - } - else - dval(&rv) -= adj.d; - goto cont; - } -#endif /*}Honor_FLT_ROUNDS*/ - - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask -#ifdef IEEE_Arith /*{*/ -#ifdef Avoid_Underflow - || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 -#else - || (word0(&rv) & Exp_mask) <= Exp_msk1 -#endif -#endif /*}*/ - ) { -#ifdef SET_INEXACT - if (!delta->x[0] && delta->wds <= 1) - bc.inexact = 0; -#endif - break; - } - if (!delta->x[0] && delta->wds <= 1) { - /* exact result */ -#ifdef SET_INEXACT - bc.inexact = 0; -#endif - break; - } - delta = lshift(delta,Log2P); - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (bc.dsign) { - if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 - && word1(&rv) == ( -#ifdef Avoid_Underflow - (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) - ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : -#endif - 0xffffffff)) { - /*boundary case -- increment exponent*/ - if (word0(&rv) == Big0 && word1(&rv) == Big1) - goto ovfl; - word0(&rv) = (word0(&rv) & Exp_mask) - + Exp_msk1 -#ifdef IBM - | Exp_msk1 >> 4 -#endif - ; - word1(&rv) = 0; -#ifdef Avoid_Underflow - bc.dsign = 0; -#endif - break; - } - } - else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow /*{{*/ - L = word0(&rv) & Exp_mask; -#ifdef IBM - if (L < Exp_msk1) -#else -#ifdef Avoid_Underflow - if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) -#else - if (L <= Exp_msk1) -#endif /*Avoid_Underflow*/ -#endif /*IBM*/ - { - if (bc.nd >nd) { - bc.uflchk = 1; - break; - } - goto undfl; - } - L -= Exp_msk1; -#else /*Sudden_Underflow}{*/ -#ifdef Avoid_Underflow - if (bc.scale) { - L = word0(&rv) & Exp_mask; - if (L <= (2*P+1)*Exp_msk1) { - if (L > (P+2)*Exp_msk1) - /* round even ==> */ - /* accept rv */ - break; - /* rv = smallest denormal */ - if (bc.nd >nd) { - bc.uflchk = 1; - break; - } - goto undfl; - } - } -#endif /*Avoid_Underflow*/ - L = (word0(&rv) & Exp_mask) - Exp_msk1; -#endif /*Sudden_Underflow}}*/ - word0(&rv) = L | Bndry_mask1; - word1(&rv) = 0xffffffff; -#ifdef IBM - goto cont; -#else -#ifndef NO_STRTOD_BIGCOMP - if (bc.nd > nd) - goto cont; -#endif - break; -#endif - } -#ifndef ROUND_BIASED -#ifdef Avoid_Underflow - if (Lsb1) { - if (!(word0(&rv) & Lsb1)) - break; - } - else if (!(word1(&rv) & Lsb)) - break; -#else - if (!(word1(&rv) & LSB)) - break; -#endif -#endif - if (bc.dsign) -#ifdef Avoid_Underflow - dval(&rv) += sulp(&rv, &bc); -#else - dval(&rv) += ulp(&rv); -#endif -#ifndef ROUND_BIASED - else { -#ifdef Avoid_Underflow - dval(&rv) -= sulp(&rv, &bc); -#else - dval(&rv) -= ulp(&rv); -#endif -#ifndef Sudden_Underflow - if (!dval(&rv)) { - if (bc.nd >nd) { - bc.uflchk = 1; - break; - } - goto undfl; - } -#endif - } -#ifdef Avoid_Underflow - bc.dsign = 1 - bc.dsign; -#endif -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (bc.dsign) - aadj = aadj1 = 1.; - else if (word1(&rv) || word0(&rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (word1(&rv) == Tiny1 && !word0(&rv)) { - if (bc.nd >nd) { - bc.uflchk = 1; - break; - } - goto undfl; - } -#endif - aadj = 1.; - aadj1 = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - aadj1 = -aadj; - } - } - else { - aadj *= 0.5; - aadj1 = bc.dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(bc.rounding) { - case 2: /* towards +infinity */ - aadj1 -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - aadj1 += 0.5; - } -#else - if (Flt_Rounds == 0) - aadj1 += 0.5; -#endif /*Check_FLT_ROUNDS*/ - } - y = word0(&rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - dval(&rv0) = dval(&rv); - word0(&rv) -= P*Exp_msk1; - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - if ((word0(&rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(&rv0) == Big0 && word1(&rv0) == Big1) - goto ovfl; - word0(&rv) = Big0; - word1(&rv) = Big1; - goto cont; - } - else - word0(&rv) += P*Exp_msk1; - } - else { -#ifdef Avoid_Underflow - if (bc.scale && y <= 2*P*Exp_msk1) { - if (aadj <= 0x7fffffff) { - if ((z = aadj) <= 0) - z = 1; - aadj = z; - aadj1 = bc.dsign ? aadj : -aadj; - } - dval(&aadj2) = aadj1; - word0(&aadj2) += (2*P+1)*Exp_msk1 - y; - aadj1 = dval(&aadj2); - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - if (rv.d == 0.) -#ifdef NO_STRTOD_BIGCOMP - goto undfl; -#else - { - if (bc.nd > nd) - bc.dsign = 1; - break; - } -#endif - } - else { - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - } -#else -#ifdef Sudden_Underflow - if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { - dval(&rv0) = dval(&rv); - word0(&rv) += P*Exp_msk1; - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; -#ifdef IBM - if ((word0(&rv) & Exp_mask) < P*Exp_msk1) -#else - if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) -#endif - { - if (word0(&rv0) == Tiny0 - && word1(&rv0) == Tiny1) { - if (bc.nd >nd) { - bc.uflchk = 1; - break; - } - goto undfl; - } - word0(&rv) = Tiny0; - word1(&rv) = Tiny1; - goto cont; - } - else - word0(&rv) -= P*Exp_msk1; - } - else { - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - } -#else /*Sudden_Underflow*/ - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ - if (y <= (P-1)*Exp_msk1 && aadj > 1.) { - aadj1 = (double)(int)(aadj + 0.5); - if (!bc.dsign) - aadj1 = -aadj1; - } - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - } - z = word0(&rv) & Exp_mask; -#ifndef SET_INEXACT - if (bc.nd == nd) { -#ifdef Avoid_Underflow - if (!bc.scale) -#endif - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } - } -#endif - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - } - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); -#ifndef NO_STRTOD_BIGCOMP - if (req_bigcomp) { - bd0 = 0; - bc.e0 += nz1; - bigcomp(&rv, s0, &bc); - y = word0(&rv) & Exp_mask; - if (y == Exp_mask) - goto ovfl; - if (y == 0 && rv.d == 0.) - goto undfl; - } -#endif -#ifdef SET_INEXACT - if (bc.inexact) { - if (!oldinexact) { - word0(&rv0) = Exp_1 + (70 << Exp_shift); - word1(&rv0) = 0; - dval(&rv0) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif -#ifdef Avoid_Underflow - if (bc.scale) { - word0(&rv0) = Exp_1 - 2*P*Exp_msk1; - word1(&rv0) = 0; - dval(&rv) *= dval(&rv0); -#ifndef NO_ERRNO - /* try to avoid the bug of testing an 8087 register value */ -#ifdef IEEE_Arith - if (!(word0(&rv) & Exp_mask)) -#else - if (word0(&rv) == 0 && word1(&rv) == 0) -#endif - errno = ERANGE; -#endif - } -#endif /* Avoid_Underflow */ -#ifdef SET_INEXACT - if (bc.inexact && !(word0(&rv) & Exp_mask)) { - /* set underflow bit */ - dval(&rv0) = 1e-300; - dval(&rv0) *= dval(&rv0); - } -#endif - ret: - if (se) - *se = (char *)s; - return sign ? -dval(&rv) : dval(&rv); - } - -#ifndef MULTIPLE_THREADS - static char *dtoa_result; -#endif - - static char * -#ifdef KR_headers -rv_alloc(i) int i; -#else -rv_alloc(int i) -#endif -{ - int j, k, *r; - - j = sizeof(ULong); - for(k = 0; - sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; - j <<= 1) - k++; - r = (int*)Balloc(k); - *r = k; - return -#ifndef MULTIPLE_THREADS - dtoa_result = -#endif - (char *)(r+1); - } - - static char * -#ifdef KR_headers -nrv_alloc(s, rve, n) char *s, **rve; int n; -#else -nrv_alloc(const char *s, char **rve, int n) -#endif -{ - char *rv, *t; - - t = rv = rv_alloc(n); - while((*t = *s++)) t++; - if (rve) - *rve = t; - return rv; - } - -/* freedtoa(s) must be used to free values s returned by dtoa - * when MULTIPLE_THREADS is #defined. It should be used in all cases, - * but for consistency with earlier versions of dtoa, it is optional - * when MULTIPLE_THREADS is not defined. - */ - - void -#ifdef KR_headers -freedtoa(s) char *s; -#else -freedtoa(char *s) -#endif -{ - Bigint *b = (Bigint *)((int *)s - 1); - b->maxwds = 1 << (b->k = *(int*)b); - Bfree(b); -#ifndef MULTIPLE_THREADS - if (s == dtoa_result) - dtoa_result = 0; -#endif - } - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - - char * -dtoa -#ifdef KR_headers - (dd, mode, ndigits, decpt, sign, rve) - double dd; int mode, ndigits, *decpt, *sign; char **rve; -#else - (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) -#endif -{ - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4,5 ==> similar to 2 and 3, respectively, but (in - round-nearest mode) with the tests of mode 0 to - possibly return a shorter string that rounds to d. - With IEEE arithmetic and compilation with - -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same - as modes 2 and 3 when FLT_ROUNDS != 1. - 6-9 ==> Debugging modes similar to mode - 4: don't try - fast floating-point estimate (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - spec_case, try_quick; - Long L; -#ifndef Sudden_Underflow - int denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mlo, *mhi, *S; - U d2, eps, u; - double ds; - char *s, *s0; -#ifndef No_leftright -#ifdef IEEE_Arith - U eps1; -#endif -#endif -#ifdef SET_INEXACT - int inexact, oldinexact; -#endif -#ifdef Honor_FLT_ROUNDS /*{*/ - int Rounding; -#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ - Rounding = Flt_Rounds; -#else /*}{*/ - Rounding = 1; - switch(fegetround()) { - case FE_TOWARDZERO: Rounding = 0; break; - case FE_UPWARD: Rounding = 2; break; - case FE_DOWNWARD: Rounding = 3; - } -#endif /*}}*/ -#endif /*}*/ - -#ifndef MULTIPLE_THREADS - if (dtoa_result) { - freedtoa(dtoa_result); - dtoa_result = 0; - } -#endif - - u.d = dd; - if (word0(&u) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - word0(&u) &= ~Sign_bit; /* clear sign bit */ - } - else - *sign = 0; - -#if defined(IEEE_Arith) + defined(VAX) -#ifdef IEEE_Arith - if ((word0(&u) & Exp_mask) == Exp_mask) -#else - if (word0(&u) == 0x8000) -#endif - { - /* Infinity or NaN */ - *decpt = 9999; -#ifdef IEEE_Arith - if (!word1(&u) && !(word0(&u) & 0xfffff)) - return nrv_alloc("Infinity", rve, 8); -#endif - return nrv_alloc("NaN", rve, 3); - } -#endif -#ifdef IBM - dval(&u) += 0; /* normalize */ -#endif - if (!dval(&u)) { - *decpt = 1; - return nrv_alloc("0", rve, 1); - } - -#ifdef SET_INEXACT - try_quick = oldinexact = get_inexact(); - inexact = 1; -#endif -#ifdef Honor_FLT_ROUNDS - if (Rounding >= 2) { - if (*sign) - Rounding = Rounding == 2 ? 0 : 2; - else - if (Rounding != 2) - Rounding = 0; - } -#endif - - b = d2b(&u, &be, &bbits); -#ifdef Sudden_Underflow - i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#else - if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { -#endif - dval(&d2) = dval(&u); - word0(&d2) &= Frac_mask1; - word0(&d2) |= Exp_11; -#ifdef IBM - if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) - dval(&d2) /= 1 << j; -#endif - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifdef IBM - i <<= 2; - i += j; -#endif -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) - : word1(&u) << (32 - i); - dval(&d2) = x; - word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (dval(&u) < tens[k]) - k--; - k_check = 0; - } - j = bbits - i - 1; - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - if (mode < 0 || mode > 9) - mode = 0; - -#ifndef SET_INEXACT -#ifdef Check_FLT_ROUNDS - try_quick = Rounding == 1; -#else - try_quick = 1; -#endif -#endif /*SET_INEXACT*/ - - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ - /* silence erroneous "gcc -Wall" warning. */ - switch(mode) { - case 0: - case 1: - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - s = s0 = rv_alloc(i); - -#ifdef Honor_FLT_ROUNDS - if (mode > 1 && Rounding != 1) - leftright = 0; -#endif - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - dval(&d2) = dval(&u); - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - dval(&u) /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - dval(&u) /= ds; - } - else if ((j1 = -k)) { - dval(&u) *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - dval(&u) *= bigtens[i]; - } - } - if (k_check && dval(&u) < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - dval(&u) *= 10.; - ieps++; - } - dval(&eps) = ieps*dval(&u) + 7.; - word0(&eps) -= (P-1)*Exp_msk1; - if (ilim == 0) { - S = mhi = 0; - dval(&u) -= 5.; - if (dval(&u) > dval(&eps)) - goto one_digit; - if (dval(&u) < -dval(&eps)) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); -#ifdef IEEE_Arith - if (k0 < 0 && j1 >= 307) { - eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ - word0(&eps1) -= Exp_msk1 * (Bias+P-1); - dval(&eps1) *= tens[j1 & 0xf]; - for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) - if (j & 1) - dval(&eps1) *= bigtens[i]; - if (eps.d < eps1.d) - eps.d = eps1.d; - } -#endif - for(i = 0;;) { - L = dval(&u); - dval(&u) -= L; - *s++ = '0' + (int)L; - if (1. - dval(&u) < dval(&eps)) - goto bump_up; - if (dval(&u) < dval(&eps)) - goto ret1; - if (++i >= ilim) - break; - dval(&eps) *= 10.; - dval(&u) *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - dval(&eps) *= tens[ilim-1]; - for(i = 1;; i++, dval(&u) *= 10.) { - L = (Long)(dval(&u)); - if (!(dval(&u) -= L)) - ilim = i; - *s++ = '0' + (int)L; - if (i == ilim) { - if (dval(&u) > 0.5 + dval(&eps)) - goto bump_up; - else if (dval(&u) < 0.5 - dval(&eps)) { - while(*--s == '0'); - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = s0; - dval(&u) = dval(&d2); - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || dval(&u) <= 5*ds) - goto no_digits; - goto one_digit; - } - for(i = 1;; i++, dval(&u) *= 10.) { - L = (Long)(dval(&u) / ds); - dval(&u) -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (dval(&u) < 0) { - L--; - dval(&u) += ds; - } -#endif - *s++ = '0' + (int)L; - if (!dval(&u)) { -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - if (i == ilim) { -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(Rounding) { - case 0: goto ret1; - case 2: goto bump_up; - } -#endif - dval(&u) += dval(&u); -#ifdef ROUND_BIASED - if (dval(&u) >= ds) -#else - if (dval(&u) > ds || (dval(&u) == ds && L & 1)) -#endif - { - bump_up: - while(*--s == '9') - if (s == s0) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - } - goto ret1; - } - - m2 = b2; - m5 = b5; - mhi = mlo = 0; - if (leftright) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif -#ifdef IBM - 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); -#else - 1 + P - bbits; -#endif - b2 += i; - s2 += i; - mhi = i2b(1); - } - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); - b = b1; - } - if ((j = b5 - m5)) - b = pow5mult(b, j); - } - else - b = pow5mult(b, b5); - } - S = i2b(1); - if (s5 > 0) - S = pow5mult(S, s5); - - /* Check for special case that d is a normalized power of 2. */ - - spec_case = 0; - if ((mode < 2 || leftright) -#ifdef Honor_FLT_ROUNDS - && Rounding == 1 -#endif - ) { - if (!word1(&u) && !(word0(&u) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(&u) & (Exp_mask & ~Exp_msk1) -#endif - ) { - /* The special case */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ - i = dshift(S, s2); - b2 += i; - m2 += i; - s2 += i; - if (b2 > 0) - b = lshift(b, b2); - if (s2 > 0) - S = lshift(S, s2); - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (leftright) - mhi = multadd(mhi, 10, 0); - ilim = ilim1; - } - } - if (ilim <= 0 && (mode == 3 || mode == 5)) { - if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { - /* no digits, fcvt style */ - no_digits: - k = -1 - ndigits; - goto ret; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) - mhi = lshift(mhi, m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - } - - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); -#ifndef ROUND_BIASED - if (j1 == 0 && mode != 1 && !(word1(&u) & 1) -#ifdef Honor_FLT_ROUNDS - && Rounding >= 1 -#endif - ) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; -#ifdef SET_INEXACT - else if (!b->x[0] && b->wds <= 1) - inexact = 0; -#endif - *s++ = dig; - goto ret; - } -#endif - if (j < 0 || (j == 0 && mode != 1 -#ifndef ROUND_BIASED - && !(word1(&u) & 1) -#endif - )) { - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto accept_dig; - } -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(Rounding) { - case 0: goto accept_dig; - case 2: goto keep_dig; - } -#endif /*Honor_FLT_ROUNDS*/ - if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); -#ifdef ROUND_BIASED - if (j1 >= 0 /*)*/ -#else - if ((j1 > 0 || (j1 == 0 && dig & 1)) -#endif - && dig++ == '9') - goto round_9_up; - } - accept_dig: - *s++ = dig; - goto ret; - } - if (j1 > 0) { -#ifdef Honor_FLT_ROUNDS - if (!Rounding) - goto accept_dig; -#endif - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = dig + 1; - goto ret; - } -#ifdef Honor_FLT_ROUNDS - keep_dig: -#endif - *s++ = dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); - else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); - } - } - } - else - for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto ret; - } - if (i >= ilim) - break; - b = multadd(b, 10, 0); - } - - /* Round off last digit */ - -#ifdef Honor_FLT_ROUNDS - switch(Rounding) { - case 0: goto trimzeros; - case 2: goto roundoff; - } -#endif - b = lshift(b, 1); - j = cmp(b, S); -#ifdef ROUND_BIASED - if (j >= 0) -#else - if (j > 0 || (j == 0 && dig & 1)) -#endif - { - roundoff: - while(*--s == '9') - if (s == s0) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { -#ifdef Honor_FLT_ROUNDS - trimzeros: -#endif - while(*--s == '0'); - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: -#ifdef SET_INEXACT - if (inexact) { - if (!oldinexact) { - word0(&u) = Exp_1 + (70 << Exp_shift); - word1(&u) = 0; - dval(&u) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif - Bfree(b); - *s = 0; - *decpt = k + 1; - if (rve) - *rve = s; - return s0; - } -#ifdef __cplusplus -} -#endif -#endif diff --git a/app/cjson/dtoa_config.h b/app/cjson/dtoa_config.h deleted file mode 100644 index 483cf85ba1..0000000000 --- a/app/cjson/dtoa_config.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _DTOA_CONFIG_H -#define _DTOA_CONFIG_H -#if 0 -#include -#include -#include - -/* Ensure dtoa.c does not USE_LOCALE. Lua CJSON must not use locale - * aware conversion routines. */ -#undef USE_LOCALE - -/* dtoa.c should not touch errno, Lua CJSON does not use it, and it - * may not be threadsafe */ -#define NO_ERRNO - -#define Long int32_t -#define ULong uint32_t -#define Llong int64_t -#define ULLong uint64_t - -#ifdef IEEE_BIG_ENDIAN -#define IEEE_MC68k -#else -#define IEEE_8087 -#endif - -#define MALLOC(n) xmalloc(n) - -static void *xmalloc(size_t size) -{ - void *p; - - p = malloc(size); - if (!p) { - fprintf(stderr, "Out of memory"); - abort(); - } - - return p; -} - -#ifdef MULTIPLE_THREADS - -/* Enable locking to support multi-threaded applications */ - -#include - -static pthread_mutex_t private_dtoa_lock[2] = { - PTHREAD_MUTEX_INITIALIZER, - PTHREAD_MUTEX_INITIALIZER -}; - -#define ACQUIRE_DTOA_LOCK(n) do { \ - int r = pthread_mutex_lock(&private_dtoa_lock[n]); \ - if (r) { \ - fprintf(stderr, "pthread_mutex_lock failed with %d\n", r); \ - abort(); \ - } \ -} while (0) - -#define FREE_DTOA_LOCK(n) do { \ - int r = pthread_mutex_unlock(&private_dtoa_lock[n]); \ - if (r) { \ - fprintf(stderr, "pthread_mutex_unlock failed with %d\n", r);\ - abort(); \ - } \ -} while (0) - -#endif /* MULTIPLE_THREADS */ -#endif -#endif /* _DTOA_CONFIG_H */ - -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/cjson/fpconv.c b/app/cjson/fpconv.c deleted file mode 100644 index f199b45908..0000000000 --- a/app/cjson/fpconv.c +++ /dev/null @@ -1,209 +0,0 @@ -/* fpconv - Floating point conversion routines - * - * Copyright (c) 2011-2012 Mark Pulford - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries - * with locale support will break when the decimal separator is a comma. - * - * fpconv_* will around these issues with a translation buffer if required. - */ - -#include "c_stdio.h" -#include "c_stdlib.h" -// #include -#include "c_string.h" - -#include "fpconv.h" - -#if 0 -/* Lua CJSON assumes the locale is the same for all threads within a - * process and doesn't change after initialisation. - * - * This avoids the need for per thread storage or expensive checks - * for call. */ -static char locale_decimal_point = '.'; - -/* In theory multibyte decimal_points are possible, but - * Lua CJSON only supports UTF-8 and known locales only have - * single byte decimal points ([.,]). - * - * localconv() may not be thread safe (=>crash), and nl_langinfo() is - * not supported on some platforms. Use sprintf() instead - if the - * locale does change, at least Lua CJSON won't crash. */ -static void fpconv_update_locale() -{ - char buf[8]; - - c_sprintf(buf, "%g", 0.5); - - /* Failing this test might imply the platform has a buggy dtoa - * implementation or wide characters */ - if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { - NODE_ERR("Error: wide characters found or printf() bug."); - return; - } - - locale_decimal_point = buf[1]; -} - -/* Check for a valid number character: [-+0-9a-yA-Y.] - * Eg: -0.6e+5, infinity, 0xF0.F0pF0 - * - * Used to find the probable end of a number. It doesn't matter if - * invalid characters are counted - strtod() will find the valid - * number if it exists. The risk is that slightly more memory might - * be allocated before a parse error occurs. */ -static inline int valid_number_character(char ch) -{ - char lower_ch; - - if ('0' <= ch && ch <= '9') - return 1; - if (ch == '-' || ch == '+' || ch == '.') - return 1; - - /* Hex digits, exponent (e), base (p), "infinity",.. */ - lower_ch = ch | 0x20; - if ('a' <= lower_ch && lower_ch <= 'y') - return 1; - - return 0; -} - -/* Calculate the size of the buffer required for a strtod locale - * conversion. */ -static int strtod_buffer_size(const char *s) -{ - const char *p = s; - - while (valid_number_character(*p)) - p++; - - return p - s; -} - -/* Similar to strtod(), but must be passed the current locale's decimal point - * character. Guaranteed to be called at the start of any valid number in a string */ -double fpconv_strtod(const char *nptr, char **endptr) -{ - char localbuf[FPCONV_G_FMT_BUFSIZE]; - char *buf, *endbuf, *dp; - int buflen; - double value; - - /* System strtod() is fine when decimal point is '.' */ - if (locale_decimal_point == '.') - return c_strtod(nptr, endptr); - - buflen = strtod_buffer_size(nptr); - if (!buflen) { - /* No valid characters found, standard strtod() return */ - *endptr = (char *)nptr; - return 0; - } - - /* Duplicate number into buffer */ - if (buflen >= FPCONV_G_FMT_BUFSIZE) { - /* Handle unusually large numbers */ - buf = c_malloc(buflen + 1); - if (!buf) { - NODE_ERR("not enough memory\n"); - return; - } - } else { - /* This is the common case.. */ - buf = localbuf; - } - c_memcpy(buf, nptr, buflen); - buf[buflen] = 0; - - /* Update decimal point character if found */ - dp = c_strchr(buf, '.'); - if (dp) - *dp = locale_decimal_point; - - value = c_strtod(buf, &endbuf); - *endptr = (char *)&nptr[endbuf - buf]; - if (buflen >= FPCONV_G_FMT_BUFSIZE) - c_free(buf); - - return value; -} - -/* "fmt" must point to a buffer of at least 6 characters */ -static void set_number_format(char *fmt, int precision) -{ - int d1, d2, i; - - if(!(1 <= precision && precision <= 14)) return; - - /* Create printf format (%.14g) from precision */ - d1 = precision / 10; - d2 = precision % 10; - fmt[0] = '%'; - fmt[1] = '.'; - i = 2; - if (d1) { - fmt[i++] = '0' + d1; - } - fmt[i++] = '0' + d2; - fmt[i++] = 'g'; - fmt[i] = 0; -} - -/* Assumes there is always at least 32 characters available in the target buffer */ -int fpconv_g_fmt(char *str, double num, int precision) -{ - char buf[FPCONV_G_FMT_BUFSIZE]; - char fmt[6]; - int len; - char *b; - - set_number_format(fmt, precision); - - /* Pass through when decimal point character is dot. */ - if (locale_decimal_point == '.'){ - c_sprintf(str, fmt, num); - return c_strlen(str); - } - - /* snprintf() to a buffer then translate for other decimal point characters */ - c_sprintf(buf, fmt, num); - len = c_strlen(buf); - - /* Copy into target location. Translate decimal point if required */ - b = buf; - do { - *str++ = (*b == locale_decimal_point ? '.' : *b); - } while(*b++); - - return len; -} - -void fpconv_init() -{ - fpconv_update_locale(); -} -#endif -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/cjson/fpconv.h b/app/cjson/fpconv.h deleted file mode 100644 index 012490885d..0000000000 --- a/app/cjson/fpconv.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Lua CJSON floating point conversion routines */ - -/* Buffer required to store the largest string representation of a double. - * - * Longest double printed with %.14g is 21 characters long: - * -1.7976931348623e+308 */ -# define FPCONV_G_FMT_BUFSIZE 32 - -#ifdef USE_INTERNAL_FPCONV -static inline void fpconv_init() -{ - /* Do nothing - not required */ -} -#else -extern inline void fpconv_init(); -#endif - -extern int fpconv_g_fmt(char*, double, int); -extern double fpconv_strtod(const char*, char**); - -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/cjson/g_fmt.c b/app/cjson/g_fmt.c deleted file mode 100644 index 2bf34a83bf..0000000000 --- a/app/cjson/g_fmt.c +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991, 1996 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* g_fmt(buf,x) stores the closest decimal approximation to x in buf; - * it suffices to declare buf - * char buf[32]; - */ -#if 0 -#ifdef __cplusplus -extern "C" { -#endif - extern char *dtoa(double, int, int, int *, int *, char **); - extern int g_fmt(char *, double, int); - extern void freedtoa(char*); -#ifdef __cplusplus - } -#endif - -int -fpconv_g_fmt(char *b, double x, int precision) -{ - register int i, k; - register char *s; - int decpt, j, sign; - char *b0, *s0, *se; - - b0 = b; -#ifdef IGNORE_ZERO_SIGN - if (!x) { - *b++ = '0'; - *b = 0; - goto done; - } -#endif - s = s0 = dtoa(x, 2, precision, &decpt, &sign, &se); - if (sign) - *b++ = '-'; - if (decpt == 9999) /* Infinity or Nan */ { - while((*b++ = *s++)); - /* "b" is used to calculate the return length. Decrement to exclude the - * Null terminator from the length */ - b--; - goto done0; - } - if (decpt <= -4 || decpt > precision) { - *b++ = *s++; - if (*s) { - *b++ = '.'; - while((*b = *s++)) - b++; - } - *b++ = 'e'; - /* sprintf(b, "%+.2d", decpt - 1); */ - if (--decpt < 0) { - *b++ = '-'; - decpt = -decpt; - } - else - *b++ = '+'; - for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); - for(;;) { - i = decpt / k; - *b++ = i + '0'; - if (--j <= 0) - break; - decpt -= i*k; - decpt *= 10; - } - *b = 0; - } - else if (decpt <= 0) { - *b++ = '0'; - *b++ = '.'; - for(; decpt < 0; decpt++) - *b++ = '0'; - while((*b++ = *s++)); - b--; - } - else { - while((*b = *s++)) { - b++; - if (--decpt == 0 && *s) - *b++ = '.'; - } - for(; decpt > 0; decpt--) - *b++ = '0'; - *b = 0; - } - done0: - freedtoa(s0); -#ifdef IGNORE_ZERO_SIGN - done: -#endif - return b - b0; - } -#endif diff --git a/app/cjson/lua/cjson/util.lua b/app/cjson/lua/cjson/util.lua deleted file mode 100644 index 6916dad04b..0000000000 --- a/app/cjson/lua/cjson/util.lua +++ /dev/null @@ -1,271 +0,0 @@ -local json = require "cjson" - --- Various common routines used by the Lua CJSON package --- --- Mark Pulford - --- Determine with a Lua table can be treated as an array. --- Explicitly returns "not an array" for very sparse arrays. --- Returns: --- -1 Not an array --- 0 Empty table --- >0 Highest index in the array -local function is_array(table) - local max = 0 - local count = 0 - for k, v in pairs(table) do - if type(k) == "number" then - if k > max then max = k end - count = count + 1 - else - return -1 - end - end - if max > count * 2 then - return -1 - end - - return max -end - -local serialise_value - -local function serialise_table(value, indent, depth) - local spacing, spacing2, indent2 - if indent then - spacing = "\n" .. indent - spacing2 = spacing .. " " - indent2 = indent .. " " - else - spacing, spacing2, indent2 = " ", " ", false - end - depth = depth + 1 - if depth > 50 then - return "Cannot serialise any further: too many nested tables" - end - - local max = is_array(value) - - local comma = false - local fragment = { "{" .. spacing2 } - if max > 0 then - -- Serialise array - for i = 1, max do - if comma then - table.insert(fragment, "," .. spacing2) - end - table.insert(fragment, serialise_value(value[i], indent2, depth)) - comma = true - end - elseif max < 0 then - -- Serialise table - for k, v in pairs(value) do - if comma then - table.insert(fragment, "," .. spacing2) - end - table.insert(fragment, - ("[%s] = %s"):format(serialise_value(k, indent2, depth), - serialise_value(v, indent2, depth))) - comma = true - end - end - table.insert(fragment, spacing .. "}") - - return table.concat(fragment) -end - -function serialise_value(value, indent, depth) - if indent == nil then indent = "" end - if depth == nil then depth = 0 end - - if value == json.null then - return "json.null" - elseif type(value) == "string" then - return ("%q"):format(value) - elseif type(value) == "nil" or type(value) == "number" or - type(value) == "boolean" then - return tostring(value) - elseif type(value) == "table" then - return serialise_table(value, indent, depth) - else - return "\"<" .. type(value) .. ">\"" - end -end - -local function file_load(filename) - local file - if filename == nil then - file = io.stdin - else - local err - file, err = io.open(filename, "rb") - if file == nil then - error(("Unable to read '%s': %s"):format(filename, err)) - end - end - local data = file:read("*a") - - if filename ~= nil then - file:close() - end - - if data == nil then - error("Failed to read " .. filename) - end - - return data -end - -local function file_save(filename, data) - local file - if filename == nil then - file = io.stdout - else - local err - file, err = io.open(filename, "wb") - if file == nil then - error(("Unable to write '%s': %s"):format(filename, err)) - end - end - file:write(data) - if filename ~= nil then - file:close() - end -end - -local function compare_values(val1, val2) - local type1 = type(val1) - local type2 = type(val2) - if type1 ~= type2 then - return false - end - - -- Check for NaN - if type1 == "number" and val1 ~= val1 and val2 ~= val2 then - return true - end - - if type1 ~= "table" then - return val1 == val2 - end - - -- check_keys stores all the keys that must be checked in val2 - local check_keys = {} - for k, _ in pairs(val1) do - check_keys[k] = true - end - - for k, v in pairs(val2) do - if not check_keys[k] then - return false - end - - if not compare_values(val1[k], val2[k]) then - return false - end - - check_keys[k] = nil - end - for k, _ in pairs(check_keys) do - -- Not the same if any keys from val1 were not found in val2 - return false - end - return true -end - -local test_count_pass = 0 -local test_count_total = 0 - -local function run_test_summary() - return test_count_pass, test_count_total -end - -local function run_test(testname, func, input, should_work, output) - local function status_line(name, status, value) - local statusmap = { [true] = ":success", [false] = ":error" } - if status ~= nil then - name = name .. statusmap[status] - end - print(("[%s] %s"):format(name, serialise_value(value, false))) - end - - local result = { pcall(func, unpack(input)) } - local success = table.remove(result, 1) - - local correct = false - if success == should_work and compare_values(result, output) then - correct = true - test_count_pass = test_count_pass + 1 - end - test_count_total = test_count_total + 1 - - local teststatus = { [true] = "PASS", [false] = "FAIL" } - print(("==> Test [%d] %s: %s"):format(test_count_total, testname, - teststatus[correct])) - - status_line("Input", nil, input) - if not correct then - status_line("Expected", should_work, output) - end - status_line("Received", success, result) - print() - - return correct, result -end - -local function run_test_group(tests) - local function run_helper(name, func, input) - if type(name) == "string" and #name > 0 then - print("==> " .. name) - end - -- Not a protected call, these functions should never generate errors. - func(unpack(input or {})) - print() - end - - for _, v in ipairs(tests) do - -- Run the helper if "should_work" is missing - if v[4] == nil then - run_helper(unpack(v)) - else - run_test(unpack(v)) - end - end -end - --- Run a Lua script in a separate environment -local function run_script(script, env) - local env = env or {} - local func - - -- Use setfenv() if it exists, otherwise assume Lua 5.2 load() exists - if _G.setfenv then - func = loadstring(script) - if func then - setfenv(func, env) - end - else - func = load(script, nil, nil, env) - end - - if func == nil then - error("Invalid syntax.") - end - func() - - return env -end - --- Export functions -return { - serialise_value = serialise_value, - file_load = file_load, - file_save = file_save, - compare_values = compare_values, - run_test_summary = run_test_summary, - run_test = run_test, - run_test_group = run_test_group, - run_script = run_script -} - --- vi:ai et sw=4 ts=4: diff --git a/app/cjson/lua/json2lua.lua b/app/cjson/lua/json2lua.lua deleted file mode 100644 index 014416d244..0000000000 --- a/app/cjson/lua/json2lua.lua +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env lua - --- usage: json2lua.lua [json_file] --- --- Eg: --- echo '[ "testing" ]' | ./json2lua.lua --- ./json2lua.lua test.json - -local json = require "cjson" -local util = require "cjson.util" - -local json_text = util.file_load(arg[1]) -local t = json.decode(json_text) -print(util.serialise_value(t)) diff --git a/app/cjson/lua/lua2json.lua b/app/cjson/lua/lua2json.lua deleted file mode 100644 index aee8869a89..0000000000 --- a/app/cjson/lua/lua2json.lua +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env lua - --- usage: lua2json.lua [lua_file] --- --- Eg: --- echo '{ "testing" }' | ./lua2json.lua --- ./lua2json.lua test.lua - -local json = require "cjson" -local util = require "cjson.util" - -local env = { - json = { null = json.null }, - null = json.null -} - -local t = util.run_script("data = " .. util.file_load(arg[1]), env) -print(json.encode(t.data)) - --- vi:ai et sw=4 ts=4: diff --git a/app/cjson/manual.txt b/app/cjson/manual.txt deleted file mode 100644 index d9975c00ce..0000000000 --- a/app/cjson/manual.txt +++ /dev/null @@ -1,168 +0,0 @@ -= Lua CJSON 2.1devel Manual = -Mark Pulford -:revdate: 1st March 2012 - -Overview --------- - -The Lua CJSON module provides JSON support for Lua. - -*Features*:: -- Fast, standards compliant encoding/parsing routines -- Full support for JSON with UTF-8, including decoding surrogate pairs -- Optional run-time support for common exceptions to the JSON - specification (infinity, NaN,..) -- No dependencies on other libraries - -*Caveats*:: -- UTF-16 and UTF-32 are not supported - -Lua CJSON is covered by the MIT license. Review the file +LICENSE+ for -details. - -API (Functions) ---------------- - -Synopsis -~~~~~~~~ - -[source,lua] ------------- - --- Translate Lua value to/from JSON -text = cjson.encode(value) -value = cjson.decode(text) - - -Module Instantiation -~~~~~~~~~~~~~~~~~~~~ - -decode -~~~~~~ - -[source,lua] ------------- -value = cjson.decode(json_text) ------------- - -+cjson.decode+ will deserialise any UTF-8 JSON string into a Lua value -or table. - -UTF-16 and UTF-32 JSON strings are not supported. - -+cjson.decode+ requires that any NULL (ASCII 0) and double quote (ASCII -34) characters are escaped within strings. All escape codes will be -decoded and other bytes will be passed transparently. UTF-8 characters -are not validated during decoding and should be checked elsewhere if -required. - -JSON +null+ will be converted to a NULL +lightuserdata+ value. This can -be compared with +cjson.null+ for convenience. - -By default, numbers incompatible with the JSON specification (infinity, -NaN, hexadecimal) can be decoded. This default can be changed with -<>. - -.Example: Decoding -[source,lua] -json_text = '[ true, { "foo": "bar" } ]' -value = cjson.decode(json_text) --- Returns: { true, { foo = "bar" } } - -[CAUTION] -Care must be taken after decoding JSON objects with numeric keys. Each -numeric key will be stored as a Lua +string+. Any subsequent code -assuming type +number+ may break. - - -[[encode]] -encode -~~~~~~ - -[source,lua] ------------- -json_text = cjson.encode(value) ------------- - -+cjson.encode+ will serialise a Lua value into a string containing the -JSON representation. - -+cjson.encode+ supports the following types: - -- +boolean+ -- +lightuserdata+ (NULL value only) -- +nil+ -- +number+ -- +string+ -- +table+ - -The remaining Lua types will generate an error: - -- +function+ -- +lightuserdata+ (non-NULL values) -- +thread+ -- +userdata+ - -By default, numbers are encoded with 14 significant digits. Refer to -<> for details. - -Lua CJSON will escape the following characters within each UTF-8 string: - -- Control characters (ASCII 0 - 31) -- Double quote (ASCII 34) -- Forward slash (ASCII 47) -- Blackslash (ASCII 92) -- Delete (ASCII 127) - -All other bytes are passed transparently. - -[CAUTION] -========= -Lua CJSON will successfully encode/decode binary strings, but this is -technically not supported by JSON and may not be compatible with other -JSON libraries. To ensure the output is valid JSON, applications should -ensure all Lua strings passed to +cjson.encode+ are UTF-8. - -Base64 is commonly used to encode binary data as the most efficient -encoding under UTF-8 can only reduce the encoded size by a further -~8%. Lua Base64 routines can be found in the -http://w3.impa.br/%7Ediego/software/luasocket/[LuaSocket] and -http://www.tecgraf.puc-rio.br/%7Elhf/ftp/lua/#lbase64[lbase64] packages. -========= - -Lua CJSON uses a heuristic to determine whether to encode a Lua table as -a JSON array or an object. A Lua table with only positive integer keys -of type +number+ will be encoded as a JSON array. All other tables will -be encoded as a JSON object. - -Lua CJSON does not use metamethods when serialising tables. - -- +rawget+ is used to iterate over Lua arrays -- +next+ is used to iterate over Lua objects - -Lua arrays with missing entries (_sparse arrays_) may optionally be -encoded in several different ways. Refer to -<> for details. - -JSON object keys are always strings. Hence +cjson.encode+ only supports -table keys which are type +number+ or +string+. All other types will -generate an error. - -[NOTE] -Standards compliant JSON must be encapsulated in either an object (+{}+) -or an array (+[]+). If strictly standards compliant JSON is desired, a -table must be passed to +cjson.encode+. - -By default, encoding the following Lua values will generate errors: - -- Numbers incompatible with the JSON specification (infinity, NaN) -- Tables nested more than 1000 levels deep -- Excessively sparse Lua arrays - -.Example: Encoding -[source,lua] -value = { true, { foo = "bar" } } -json_text = cjson.encode(value) --- Returns: '[true,{"foo":"bar"}]' - -// vi:ft=asciidoc tw=72: diff --git a/app/cjson/rfc4627.txt b/app/cjson/rfc4627.txt deleted file mode 100644 index 67b89092e7..0000000000 --- a/app/cjson/rfc4627.txt +++ /dev/null @@ -1,563 +0,0 @@ - - - - - - -Network Working Group D. Crockford -Request for Comments: 4627 JSON.org -Category: Informational July 2006 - - - The application/json Media Type for JavaScript Object Notation (JSON) - -Status of This Memo - - This memo provides information for the Internet community. It does - not specify an Internet standard of any kind. Distribution of this - memo is unlimited. - -Copyright Notice - - Copyright (C) The Internet Society (2006). - -Abstract - - JavaScript Object Notation (JSON) is a lightweight, text-based, - language-independent data interchange format. It was derived from - the ECMAScript Programming Language Standard. JSON defines a small - set of formatting rules for the portable representation of structured - data. - -1. Introduction - - JavaScript Object Notation (JSON) is a text format for the - serialization of structured data. It is derived from the object - literals of JavaScript, as defined in the ECMAScript Programming - Language Standard, Third Edition [ECMA]. - - JSON can represent four primitive types (strings, numbers, booleans, - and null) and two structured types (objects and arrays). - - A string is a sequence of zero or more Unicode characters [UNICODE]. - - An object is an unordered collection of zero or more name/value - pairs, where a name is a string and a value is a string, number, - boolean, null, object, or array. - - An array is an ordered sequence of zero or more values. - - The terms "object" and "array" come from the conventions of - JavaScript. - - JSON's design goals were for it to be minimal, portable, textual, and - a subset of JavaScript. - - - -Crockford Informational [Page 1] - -RFC 4627 JSON July 2006 - - -1.1. Conventions Used in This Document - - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", - "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this - document are to be interpreted as described in [RFC2119]. - - The grammatical rules in this document are to be interpreted as - described in [RFC4234]. - -2. JSON Grammar - - A JSON text is a sequence of tokens. The set of tokens includes six - structural characters, strings, numbers, and three literal names. - - A JSON text is a serialized object or array. - - JSON-text = object / array - - These are the six structural characters: - - begin-array = ws %x5B ws ; [ left square bracket - - begin-object = ws %x7B ws ; { left curly bracket - - end-array = ws %x5D ws ; ] right square bracket - - end-object = ws %x7D ws ; } right curly bracket - - name-separator = ws %x3A ws ; : colon - - value-separator = ws %x2C ws ; , comma - - Insignificant whitespace is allowed before or after any of the six - structural characters. - - ws = *( - %x20 / ; Space - %x09 / ; Horizontal tab - %x0A / ; Line feed or New line - %x0D ; Carriage return - ) - -2.1. Values - - A JSON value MUST be an object, array, number, or string, or one of - the following three literal names: - - false null true - - - -Crockford Informational [Page 2] - -RFC 4627 JSON July 2006 - - - The literal names MUST be lowercase. No other literal names are - allowed. - - value = false / null / true / object / array / number / string - - false = %x66.61.6c.73.65 ; false - - null = %x6e.75.6c.6c ; null - - true = %x74.72.75.65 ; true - -2.2. Objects - - An object structure is represented as a pair of curly brackets - surrounding zero or more name/value pairs (or members). A name is a - string. A single colon comes after each name, separating the name - from the value. A single comma separates a value from a following - name. The names within an object SHOULD be unique. - - object = begin-object [ member *( value-separator member ) ] - end-object - - member = string name-separator value - -2.3. Arrays - - An array structure is represented as square brackets surrounding zero - or more values (or elements). Elements are separated by commas. - - array = begin-array [ value *( value-separator value ) ] end-array - -2.4. Numbers - - The representation of numbers is similar to that used in most - programming languages. A number contains an integer component that - may be prefixed with an optional minus sign, which may be followed by - a fraction part and/or an exponent part. - - Octal and hex forms are not allowed. Leading zeros are not allowed. - - A fraction part is a decimal point followed by one or more digits. - - An exponent part begins with the letter E in upper or lowercase, - which may be followed by a plus or minus sign. The E and optional - sign are followed by one or more digits. - - Numeric values that cannot be represented as sequences of digits - (such as Infinity and NaN) are not permitted. - - - -Crockford Informational [Page 3] - -RFC 4627 JSON July 2006 - - - number = [ minus ] int [ frac ] [ exp ] - - decimal-point = %x2E ; . - - digit1-9 = %x31-39 ; 1-9 - - e = %x65 / %x45 ; e E - - exp = e [ minus / plus ] 1*DIGIT - - frac = decimal-point 1*DIGIT - - int = zero / ( digit1-9 *DIGIT ) - - minus = %x2D ; - - - plus = %x2B ; + - - zero = %x30 ; 0 - -2.5. Strings - - The representation of strings is similar to conventions used in the C - family of programming languages. A string begins and ends with - quotation marks. All Unicode characters may be placed within the - quotation marks except for the characters that must be escaped: - quotation mark, reverse solidus, and the control characters (U+0000 - through U+001F). - - Any character may be escaped. If the character is in the Basic - Multilingual Plane (U+0000 through U+FFFF), then it may be - represented as a six-character sequence: a reverse solidus, followed - by the lowercase letter u, followed by four hexadecimal digits that - encode the character's code point. The hexadecimal letters A though - F can be upper or lowercase. So, for example, a string containing - only a single reverse solidus character may be represented as - "\u005C". - - Alternatively, there are two-character sequence escape - representations of some popular characters. So, for example, a - string containing only a single reverse solidus character may be - represented more compactly as "\\". - - To escape an extended character that is not in the Basic Multilingual - Plane, the character is represented as a twelve-character sequence, - encoding the UTF-16 surrogate pair. So, for example, a string - containing only the G clef character (U+1D11E) may be represented as - "\uD834\uDD1E". - - - -Crockford Informational [Page 4] - -RFC 4627 JSON July 2006 - - - string = quotation-mark *char quotation-mark - - char = unescaped / - escape ( - %x22 / ; " quotation mark U+0022 - %x5C / ; \ reverse solidus U+005C - %x2F / ; / solidus U+002F - %x62 / ; b backspace U+0008 - %x66 / ; f form feed U+000C - %x6E / ; n line feed U+000A - %x72 / ; r carriage return U+000D - %x74 / ; t tab U+0009 - %x75 4HEXDIG ) ; uXXXX U+XXXX - - escape = %x5C ; \ - - quotation-mark = %x22 ; " - - unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - -3. Encoding - - JSON text SHALL be encoded in Unicode. The default encoding is - UTF-8. - - Since the first two characters of a JSON text will always be ASCII - characters [RFC0020], it is possible to determine whether an octet - stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking - at the pattern of nulls in the first four octets. - - 00 00 00 xx UTF-32BE - 00 xx 00 xx UTF-16BE - xx 00 00 00 UTF-32LE - xx 00 xx 00 UTF-16LE - xx xx xx xx UTF-8 - -4. Parsers - - A JSON parser transforms a JSON text into another representation. A - JSON parser MUST accept all texts that conform to the JSON grammar. - A JSON parser MAY accept non-JSON forms or extensions. - - An implementation may set limits on the size of texts that it - accepts. An implementation may set limits on the maximum depth of - nesting. An implementation may set limits on the range of numbers. - An implementation may set limits on the length and character contents - of strings. - - - - -Crockford Informational [Page 5] - -RFC 4627 JSON July 2006 - - -5. Generators - - A JSON generator produces JSON text. The resulting text MUST - strictly conform to the JSON grammar. - -6. IANA Considerations - - The MIME media type for JSON text is application/json. - - Type name: application - - Subtype name: json - - Required parameters: n/a - - Optional parameters: n/a - - Encoding considerations: 8bit if UTF-8; binary if UTF-16 or UTF-32 - - JSON may be represented using UTF-8, UTF-16, or UTF-32. When JSON - is written in UTF-8, JSON is 8bit compatible. When JSON is - written in UTF-16 or UTF-32, the binary content-transfer-encoding - must be used. - - Security considerations: - - Generally there are security issues with scripting languages. JSON - is a subset of JavaScript, but it is a safe subset that excludes - assignment and invocation. - - A JSON text can be safely passed into JavaScript's eval() function - (which compiles and executes a string) if all the characters not - enclosed in strings are in the set of characters that form JSON - tokens. This can be quickly determined in JavaScript with two - regular expressions and calls to the test and replace methods. - - var my_JSON_object = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( - text.replace(/"(\\.|[^"\\])*"/g, ''))) && - eval('(' + text + ')'); - - Interoperability considerations: n/a - - Published specification: RFC 4627 - - - - - - - - -Crockford Informational [Page 6] - -RFC 4627 JSON July 2006 - - - Applications that use this media type: - - JSON has been used to exchange data between applications written - in all of these programming languages: ActionScript, C, C#, - ColdFusion, Common Lisp, E, Erlang, Java, JavaScript, Lua, - Objective CAML, Perl, PHP, Python, Rebol, Ruby, and Scheme. - - Additional information: - - Magic number(s): n/a - File extension(s): .json - Macintosh file type code(s): TEXT - - Person & email address to contact for further information: - Douglas Crockford - douglas@crockford.com - - Intended usage: COMMON - - Restrictions on usage: none - - Author: - Douglas Crockford - douglas@crockford.com - - Change controller: - Douglas Crockford - douglas@crockford.com - -7. Security Considerations - - See Security Considerations in Section 6. - -8. Examples - - This is a JSON object: - - { - "Image": { - "Width": 800, - "Height": 600, - "Title": "View from 15th Floor", - "Thumbnail": { - "Url": "http://www.example.com/image/481989943", - "Height": 125, - "Width": "100" - }, - "IDs": [116, 943, 234, 38793] - - - -Crockford Informational [Page 7] - -RFC 4627 JSON July 2006 - - - } - } - - Its Image member is an object whose Thumbnail member is an object - and whose IDs member is an array of numbers. - - This is a JSON array containing two objects: - - [ - { - "precision": "zip", - "Latitude": 37.7668, - "Longitude": -122.3959, - "Address": "", - "City": "SAN FRANCISCO", - "State": "CA", - "Zip": "94107", - "Country": "US" - }, - { - "precision": "zip", - "Latitude": 37.371991, - "Longitude": -122.026020, - "Address": "", - "City": "SUNNYVALE", - "State": "CA", - "Zip": "94085", - "Country": "US" - } - ] - -9. References - -9.1. Normative References - - [ECMA] European Computer Manufacturers Association, "ECMAScript - Language Specification 3rd Edition", December 1999, - . - - [RFC0020] Cerf, V., "ASCII format for network interchange", RFC 20, - October 1969. - - [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate - Requirement Levels", BCP 14, RFC 2119, March 1997. - - [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax - Specifications: ABNF", RFC 4234, October 2005. - - - -Crockford Informational [Page 8] - -RFC 4627 JSON July 2006 - - - [UNICODE] The Unicode Consortium, "The Unicode Standard Version 4.0", - 2003, . - -Author's Address - - Douglas Crockford - JSON.org - EMail: douglas@crockford.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Crockford Informational [Page 9] - -RFC 4627 JSON July 2006 - - -Full Copyright Statement - - Copyright (C) The Internet Society (2006). - - This document is subject to the rights, licenses and restrictions - contained in BCP 78, and except as set forth therein, the authors - retain all their rights. - - This document and the information contained herein are provided on an - "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS - OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET - ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, - INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE - INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED - WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - -Intellectual Property - - The IETF takes no position regarding the validity or scope of any - Intellectual Property Rights or other rights that might be claimed to - pertain to the implementation or use of the technology described in - this document or the extent to which any license under such rights - might or might not be available; nor does it represent that it has - made any independent effort to identify any such rights. Information - on the procedures with respect to rights in RFC documents can be - found in BCP 78 and BCP 79. - - Copies of IPR disclosures made to the IETF Secretariat and any - assurances of licenses to be made available, or the result of an - attempt made to obtain a general license or permission for the use of - such proprietary rights by implementers or users of this - specification can be obtained from the IETF on-line IPR repository at - http://www.ietf.org/ipr. - - The IETF invites any interested party to bring to its attention any - copyrights, patents or patent applications, or other proprietary - rights that may cover technology that may be required to implement - this standard. Please address the information to the IETF at - ietf-ipr@ietf.org. - -Acknowledgement - - Funding for the RFC Editor function is provided by the IETF - Administrative Support Activity (IASA). - - - - - - - -Crockford Informational [Page 10] - diff --git a/app/cjson/strbuf.c b/app/cjson/strbuf.c deleted file mode 100644 index ee3f2e5b40..0000000000 --- a/app/cjson/strbuf.c +++ /dev/null @@ -1,253 +0,0 @@ -/* strbuf - String buffer routines - * - * Copyright (c) 2010-2012 Mark Pulford - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "c_stdio.h" -#include "c_stdlib.h" -#include "c_stdarg.h" -#include "c_string.h" - -#include "strbuf.h" -#include "cjson_mem.h" - -int strbuf_init(strbuf_t *s, int len) -{ - int size; - - if (len <= 0) - size = STRBUF_DEFAULT_SIZE; - else - size = len + 1; /* \0 terminator */ - - s->buf = NULL; - s->size = size; - s->length = 0; - s->increment = STRBUF_DEFAULT_INCREMENT; - s->dynamic = 0; - s->reallocs = 0; - s->debug = 0; - - s->buf = (char *)cjson_mem_malloc(size); - if (!s->buf){ - NODE_ERR("not enough memory\n"); - return -1; - } - - strbuf_ensure_null(s); - return 0; -} - -strbuf_t *strbuf_new(int len) -{ - strbuf_t *s; - - s = (strbuf_t *)cjson_mem_malloc(sizeof(strbuf_t)); - if (!s){ - NODE_ERR("not enough memory\n"); - return NULL; - } - - strbuf_init(s, len); - - /* Dynamic strbuf allocation / deallocation */ - s->dynamic = 1; - - return s; -} - -int strbuf_set_increment(strbuf_t *s, int increment) -{ - /* Increment > 0: Linear buffer growth rate - * Increment < -1: Exponential buffer growth rate */ - if (increment == 0 || increment == -1){ - NODE_ERR("BUG: Invalid string increment"); - return -1; - } - - s->increment = increment; - return 0; -} - -static inline void debug_stats(strbuf_t *s) -{ - if (s->debug) { - NODE_ERR("strbuf(%lx) reallocs: %d, length: %d, size: %d\n", - (long)s, s->reallocs, s->length, s->size); - } -} - -/* If strbuf_t has not been dynamically allocated, strbuf_free() can - * be called any number of times strbuf_init() */ -void strbuf_free(strbuf_t *s) -{ - debug_stats(s); - - if (s->buf) { - c_free(s->buf); - s->buf = NULL; - } - if (s->dynamic) - c_free(s); -} - -char *strbuf_free_to_string(strbuf_t *s, int *len) -{ - char *buf; - - debug_stats(s); - - strbuf_ensure_null(s); - - buf = s->buf; - if (len) - *len = s->length; - - if (s->dynamic) - c_free(s); - - return buf; -} - -static int calculate_new_size(strbuf_t *s, int len) -{ - int reqsize, newsize; - - if (len <= 0){ - NODE_ERR("BUG: Invalid strbuf length requested"); - return 0; - } - - /* Ensure there is room for optional NULL termination */ - reqsize = len + 1; - - /* If the user has requested to shrink the buffer, do it exactly */ - if (s->size > reqsize) - return reqsize; - - newsize = s->size; - if (s->increment < 0) { - /* Exponential sizing */ - while (newsize < reqsize) - newsize *= -s->increment; - } else { - /* Linear sizing */ - newsize = (((reqsize -1) / s->increment) + 1) * s->increment; - } - - return newsize; -} - - -/* Ensure strbuf can handle a string length bytes long (ignoring NULL - * optional termination). */ -int strbuf_resize(strbuf_t *s, int len) -{ - int newsize; - - newsize = calculate_new_size(s, len); - - if (s->debug > 1) { - NODE_ERR("strbuf(%lx) resize: %d => %d\n", - (long)s, s->size, newsize); - } - - s->buf = (char *)cjson_mem_realloc(s->buf, newsize); - if (!s->buf){ - NODE_ERR("not enough memory"); - return -1; - } - s->size = newsize; - s->reallocs++; - return 0; -} - -void strbuf_append_string(strbuf_t *s, const char *str) -{ - int space, i; - - space = strbuf_empty_length(s); - - for (i = 0; str[i]; i++) { - if (space < 1) { - strbuf_resize(s, s->length + 1); - space = strbuf_empty_length(s); - } - - s->buf[s->length] = str[i]; - s->length++; - space--; - } -} -#if 0 -/* strbuf_append_fmt() should only be used when an upper bound - * is known for the output string. */ -void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) -{ - va_list arg; - int fmt_len; - - strbuf_ensure_empty_length(s, len); - - va_start(arg, fmt); - fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); - va_end(arg); - - if (fmt_len < 0) - die("BUG: Unable to convert number"); /* This should never happen.. */ - - s->length += fmt_len; -} - -/* strbuf_append_fmt_retry() can be used when the there is no known - * upper bound for the output string. */ -void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) -{ - va_list arg; - int fmt_len, try; - int empty_len; - - /* If the first attempt to append fails, resize the buffer appropriately - * and try again */ - for (try = 0; ; try++) { - va_start(arg, fmt); - /* Append the new formatted string */ - /* fmt_len is the length of the string required, excluding the - * trailing NULL */ - empty_len = strbuf_empty_length(s); - /* Add 1 since there is also space to store the terminating NULL. */ - fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); - va_end(arg); - - if (fmt_len <= empty_len) - break; /* SUCCESS */ - if (try > 0) - die("BUG: length of formatted string changed"); - - strbuf_resize(s, s->length + fmt_len); - } - - s->length += fmt_len; -} -#endif -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/cjson/strbuf.h b/app/cjson/strbuf.h deleted file mode 100644 index 38619b73ac..0000000000 --- a/app/cjson/strbuf.h +++ /dev/null @@ -1,155 +0,0 @@ -/* strbuf - String buffer routines - * - * Copyright (c) 2010-2012 Mark Pulford - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "c_stdlib.h" -#include "c_stdarg.h" -#include "user_config.h" - -/* Size: Total bytes allocated to *buf - * Length: String length, excluding optional NULL terminator. - * Increment: Allocation increments when resizing the string buffer. - * Dynamic: True if created via strbuf_new() - */ - -typedef struct { - char *buf; - int size; - int length; - int increment; - int dynamic; - int reallocs; - int debug; -} strbuf_t; - -#ifndef STRBUF_DEFAULT_SIZE -#define STRBUF_DEFAULT_SIZE 1023 -#endif -#ifndef STRBUF_DEFAULT_INCREMENT -#define STRBUF_DEFAULT_INCREMENT -2 -#endif - -/* Initialise */ -extern strbuf_t *strbuf_new(int len); -extern int strbuf_init(strbuf_t *s, int len); -extern int strbuf_set_increment(strbuf_t *s, int increment); - -/* Release */ -extern void strbuf_free(strbuf_t *s); -extern char *strbuf_free_to_string(strbuf_t *s, int *len); - -/* Management */ -extern int strbuf_resize(strbuf_t *s, int len); -static int strbuf_empty_length(strbuf_t *s); -static int strbuf_length(strbuf_t *s); -static char *strbuf_string(strbuf_t *s, int *len); -static void strbuf_ensure_empty_length(strbuf_t *s, int len); -static char *strbuf_empty_ptr(strbuf_t *s); -static void strbuf_extend_length(strbuf_t *s, int len); - -/* Update */ -extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); -extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); -static void strbuf_append_mem(strbuf_t *s, const char *c, int len); -extern void strbuf_append_string(strbuf_t *s, const char *str); -static void strbuf_append_char(strbuf_t *s, const char c); -static void strbuf_ensure_null(strbuf_t *s); - -/* Reset string for before use */ -static inline void strbuf_reset(strbuf_t *s) -{ - s->length = 0; -} - -static inline int strbuf_allocated(strbuf_t *s) -{ - return s->buf != NULL; -} - -/* Return bytes remaining in the string buffer - * Ensure there is space for a NULL terminator. */ -static inline int strbuf_empty_length(strbuf_t *s) -{ - return s->size - s->length - 1; -} - -static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) -{ - if (len > strbuf_empty_length(s)) - strbuf_resize(s, s->length + len); -} - -static inline char *strbuf_empty_ptr(strbuf_t *s) -{ - return s->buf + s->length; -} - -static inline void strbuf_extend_length(strbuf_t *s, int len) -{ - s->length += len; -} - -static inline int strbuf_length(strbuf_t *s) -{ - return s->length; -} - -static inline void strbuf_append_char(strbuf_t *s, const char c) -{ - strbuf_ensure_empty_length(s, 1); - s->buf[s->length++] = c; -} - -static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) -{ - s->buf[s->length++] = c; -} - -static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) -{ - strbuf_ensure_empty_length(s, len); - c_memcpy(s->buf + s->length, c, len); - s->length += len; -} - -static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) -{ - c_memcpy(s->buf + s->length, c, len); - s->length += len; -} - -static inline void strbuf_ensure_null(strbuf_t *s) -{ - s->buf[s->length] = 0; -} - -static inline char *strbuf_string(strbuf_t *s, int *len) -{ - if (len) - *len = s->length; - - return s->buf; -} - -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/cjson/tests/README b/app/cjson/tests/README deleted file mode 100644 index 39e8bd45ac..0000000000 --- a/app/cjson/tests/README +++ /dev/null @@ -1,4 +0,0 @@ -These JSON examples were taken from the JSON website -(http://json.org/example.html) and RFC 4627. - -Used with permission. diff --git a/app/cjson/tests/bench.lua b/app/cjson/tests/bench.lua deleted file mode 100644 index 648020b19c..0000000000 --- a/app/cjson/tests/bench.lua +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env lua - --- This benchmark script measures wall clock time and should be --- run on an unloaded system. --- --- Your Mileage May Vary. --- --- Mark Pulford - -local json_module = os.getenv("JSON_MODULE") or "cjson" - -require "socket" -local json = require(json_module) -local util = require "cjson.util" - -local function find_func(mod, funcnames) - for _, v in ipairs(funcnames) do - if mod[v] then - return mod[v] - end - end - - return nil -end - -local json_encode = find_func(json, { "encode", "Encode", "to_string", "stringify", "json" }) -local json_decode = find_func(json, { "decode", "Decode", "to_value", "parse" }) - -local function average(t) - local total = 0 - for _, v in ipairs(t) do - total = total + v - end - return total / #t -end - -function benchmark(tests, seconds, rep) - local function bench(func, iter) - -- Use socket.gettime() to measure microsecond resolution - -- wall clock time. - local t = socket.gettime() - for i = 1, iter do - func(i) - end - t = socket.gettime() - t - - -- Don't trust any results when the run lasted for less than a - -- millisecond - return nil. - if t < 0.001 then - return nil - end - - return (iter / t) - end - - -- Roughly calculate the number of interations required - -- to obtain a particular time period. - local function calc_iter(func, seconds) - local iter = 1 - local rate - -- Warm up the bench function first. - func() - while not rate do - rate = bench(func, iter) - iter = iter * 10 - end - return math.ceil(seconds * rate) - end - - local test_results = {} - for name, func in pairs(tests) do - -- k(number), v(string) - -- k(string), v(function) - -- k(number), v(function) - if type(func) == "string" then - name = func - func = _G[name] - end - - local iter = calc_iter(func, seconds) - - local result = {} - for i = 1, rep do - result[i] = bench(func, iter) - end - - -- Remove the slowest half (round down) of the result set - table.sort(result) - for i = 1, math.floor(#result / 2) do - table.remove(result, 1) - end - - test_results[name] = average(result) - end - - return test_results -end - -function bench_file(filename) - local data_json = util.file_load(filename) - local data_obj = json_decode(data_json) - - local function test_encode() - json_encode(data_obj) - end - local function test_decode() - json_decode(data_json) - end - - local tests = {} - if json_encode then tests.encode = test_encode end - if json_decode then tests.decode = test_decode end - - return benchmark(tests, 0.1, 5) -end - --- Optionally load any custom configuration required for this module -local success, data = pcall(util.file_load, ("bench-%s.lua"):format(json_module)) -if success then - util.run_script(data, _G) - configure(json) -end - -for i = 1, #arg do - local results = bench_file(arg[i]) - for k, v in pairs(results) do - print(("%s\t%s\t%d"):format(arg[i], k, v)) - end -end - --- vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/example1.json b/app/cjson/tests/example1.json deleted file mode 100644 index 42486cec02..0000000000 --- a/app/cjson/tests/example1.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "glossary": { - "title": "example glossary", - "GlossDiv": { - "title": "S", - "GlossList": { - "GlossEntry": { - "ID": "SGML", - "SortAs": "SGML", - "GlossTerm": "Standard Generalized Mark up Language", - "Acronym": "SGML", - "Abbrev": "ISO 8879:1986", - "GlossDef": { - "para": "A meta-markup language, used to create markup languages such as DocBook.", - "GlossSeeAlso": ["GML", "XML"] - }, - "GlossSee": "markup" - } - } - } - } -} diff --git a/app/cjson/tests/example2.json b/app/cjson/tests/example2.json deleted file mode 100644 index 5600991a4c..0000000000 --- a/app/cjson/tests/example2.json +++ /dev/null @@ -1,11 +0,0 @@ -{"menu": { - "id": "file", - "value": "File", - "popup": { - "menuitem": [ - {"value": "New", "onclick": "CreateNewDoc()"}, - {"value": "Open", "onclick": "OpenDoc()"}, - {"value": "Close", "onclick": "CloseDoc()"} - ] - } -}} diff --git a/app/cjson/tests/example3.json b/app/cjson/tests/example3.json deleted file mode 100644 index d7237a5ae0..0000000000 --- a/app/cjson/tests/example3.json +++ /dev/null @@ -1,26 +0,0 @@ -{"widget": { - "debug": "on", - "window": { - "title": "Sample Konfabulator Widget", - "name": "main_window", - "width": 500, - "height": 500 - }, - "image": { - "src": "Images/Sun.png", - "name": "sun1", - "hOffset": 250, - "vOffset": 250, - "alignment": "center" - }, - "text": { - "data": "Click Here", - "size": 36, - "style": "bold", - "name": "text1", - "hOffset": 250, - "vOffset": 100, - "alignment": "center", - "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" - } -}} diff --git a/app/cjson/tests/example4.json b/app/cjson/tests/example4.json deleted file mode 100644 index d31a395bd4..0000000000 --- a/app/cjson/tests/example4.json +++ /dev/null @@ -1,88 +0,0 @@ -{"web-app": { - "servlet": [ - { - "servlet-name": "cofaxCDS", - "servlet-class": "org.cofax.cds.CDSServlet", - "init-param": { - "configGlossary:installationAt": "Philadelphia, PA", - "configGlossary:adminEmail": "ksm@pobox.com", - "configGlossary:poweredBy": "Cofax", - "configGlossary:poweredByIcon": "/images/cofax.gif", - "configGlossary:staticPath": "/content/static", - "templateProcessorClass": "org.cofax.WysiwygTemplate", - "templateLoaderClass": "org.cofax.FilesTemplateLoader", - "templatePath": "templates", - "templateOverridePath": "", - "defaultListTemplate": "listTemplate.htm", - "defaultFileTemplate": "articleTemplate.htm", - "useJSP": false, - "jspListTemplate": "listTemplate.jsp", - "jspFileTemplate": "articleTemplate.jsp", - "cachePackageTagsTrack": 200, - "cachePackageTagsStore": 200, - "cachePackageTagsRefresh": 60, - "cacheTemplatesTrack": 100, - "cacheTemplatesStore": 50, - "cacheTemplatesRefresh": 15, - "cachePagesTrack": 200, - "cachePagesStore": 100, - "cachePagesRefresh": 10, - "cachePagesDirtyRead": 10, - "searchEngineListTemplate": "forSearchEnginesList.htm", - "searchEngineFileTemplate": "forSearchEngines.htm", - "searchEngineRobotsDb": "WEB-INF/robots.db", - "useDataStore": true, - "dataStoreClass": "org.cofax.SqlDataStore", - "redirectionClass": "org.cofax.SqlRedirection", - "dataStoreName": "cofax", - "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", - "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", - "dataStoreUser": "sa", - "dataStorePassword": "dataStoreTestQuery", - "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", - "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", - "dataStoreInitConns": 10, - "dataStoreMaxConns": 100, - "dataStoreConnUsageLimit": 100, - "dataStoreLogLevel": "debug", - "maxUrlLength": 500}}, - { - "servlet-name": "cofaxEmail", - "servlet-class": "org.cofax.cds.EmailServlet", - "init-param": { - "mailHost": "mail1", - "mailHostOverride": "mail2"}}, - { - "servlet-name": "cofaxAdmin", - "servlet-class": "org.cofax.cds.AdminServlet"}, - - { - "servlet-name": "fileServlet", - "servlet-class": "org.cofax.cds.FileServlet"}, - { - "servlet-name": "cofaxTools", - "servlet-class": "org.cofax.cms.CofaxToolsServlet", - "init-param": { - "templatePath": "toolstemplates/", - "log": 1, - "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", - "logMaxSize": "", - "dataLog": 1, - "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", - "dataLogMaxSize": "", - "removePageCache": "/content/admin/remove?cache=pages&id=", - "removeTemplateCache": "/content/admin/remove?cache=templates&id=", - "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", - "lookInContext": 1, - "adminGroupID": 4, - "betaServer": true}}], - "servlet-mapping": { - "cofaxCDS": "/", - "cofaxEmail": "/cofaxutil/aemail/*", - "cofaxAdmin": "/admin/*", - "fileServlet": "/static/*", - "cofaxTools": "/tools/*"}, - - "taglib": { - "taglib-uri": "cofax.tld", - "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/app/cjson/tests/example5.json b/app/cjson/tests/example5.json deleted file mode 100644 index 49980ca25b..0000000000 --- a/app/cjson/tests/example5.json +++ /dev/null @@ -1,27 +0,0 @@ -{"menu": { - "header": "SVG Viewer", - "items": [ - {"id": "Open"}, - {"id": "OpenNew", "label": "Open New"}, - null, - {"id": "ZoomIn", "label": "Zoom In"}, - {"id": "ZoomOut", "label": "Zoom Out"}, - {"id": "OriginalView", "label": "Original View"}, - null, - {"id": "Quality"}, - {"id": "Pause"}, - {"id": "Mute"}, - null, - {"id": "Find", "label": "Find..."}, - {"id": "FindAgain", "label": "Find Again"}, - {"id": "Copy"}, - {"id": "CopyAgain", "label": "Copy Again"}, - {"id": "CopySVG", "label": "Copy SVG"}, - {"id": "ViewSVG", "label": "View SVG"}, - {"id": "ViewSource", "label": "View Source"}, - {"id": "SaveAs", "label": "Save As"}, - null, - {"id": "Help"}, - {"id": "About", "label": "About Adobe CVG Viewer..."} - ] -}} diff --git a/app/cjson/tests/genutf8.pl b/app/cjson/tests/genutf8.pl deleted file mode 100644 index db661a19db..0000000000 --- a/app/cjson/tests/genutf8.pl +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env perl - -# Create test comparison data using a different UTF-8 implementation. - -# The generated utf8.dat file must have the following MD5 sum: -# cff03b039d850f370a7362f3313e5268 - -use strict; - -# 0xD800 - 0xDFFF are used to encode supplementary codepoints -# 0x10000 - 0x10FFFF are supplementary codepoints -my (@codepoints) = (0 .. 0xD7FF, 0xE000 .. 0x10FFFF); - -my $utf8 = pack("U*", @codepoints); -defined($utf8) or die "Unable create UTF-8 string\n"; - -open(FH, ">:utf8", "utf8.dat") - or die "Unable to open utf8.dat: $!\n"; -print FH $utf8 - or die "Unable to write utf8.dat\n"; -close(FH); - -# vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/numbers.json b/app/cjson/tests/numbers.json deleted file mode 100644 index 4f981ff2e1..0000000000 --- a/app/cjson/tests/numbers.json +++ /dev/null @@ -1,7 +0,0 @@ -[ 0.110001, - 0.12345678910111, - 0.412454033640, - 2.6651441426902, - 2.718281828459, - 3.1415926535898, - 2.1406926327793 ] diff --git a/app/cjson/tests/octets-escaped.dat b/app/cjson/tests/octets-escaped.dat deleted file mode 100644 index ee99a6bf15..0000000000 --- a/app/cjson/tests/octets-escaped.dat +++ /dev/null @@ -1 +0,0 @@ -"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-.\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f" \ No newline at end of file diff --git a/app/cjson/tests/rfc-example1.json b/app/cjson/tests/rfc-example1.json deleted file mode 100644 index 73532fa9d2..0000000000 --- a/app/cjson/tests/rfc-example1.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Image": { - "Width": 800, - "Height": 600, - "Title": "View from 15th Floor", - "Thumbnail": { - "Url": "http://www.example.com/image/481989943", - "Height": 125, - "Width": "100" - }, - "IDs": [116, 943, 234, 38793] - } -} diff --git a/app/cjson/tests/rfc-example2.json b/app/cjson/tests/rfc-example2.json deleted file mode 100644 index 2a0cb681da..0000000000 --- a/app/cjson/tests/rfc-example2.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "precision": "zip", - "Latitude": 37.7668, - "Longitude": -122.3959, - "Address": "", - "City": "SAN FRANCISCO", - "State": "CA", - "Zip": "94107", - "Country": "US" - }, - { - "precision": "zip", - "Latitude": 37.371991, - "Longitude": -122.026020, - "Address": "", - "City": "SUNNYVALE", - "State": "CA", - "Zip": "94085", - "Country": "US" - } -] diff --git a/app/cjson/tests/test.lua b/app/cjson/tests/test.lua deleted file mode 100644 index b8fce84c4a..0000000000 --- a/app/cjson/tests/test.lua +++ /dev/null @@ -1,425 +0,0 @@ -#!/usr/bin/env lua - --- Lua CJSON tests --- --- Mark Pulford --- --- Note: The output of this script is easier to read with "less -S" - -local json = require "cjson" -local json_safe = require "cjson.safe" -local util = require "cjson.util" - -local function gen_raw_octets() - local chars = {} - for i = 0, 255 do chars[i + 1] = string.char(i) end - return table.concat(chars) -end - --- Generate every UTF-16 codepoint, including supplementary codes -local function gen_utf16_escaped() - -- Create raw table escapes - local utf16_escaped = {} - local count = 0 - - local function append_escape(code) - local esc = ('\\u%04X'):format(code) - table.insert(utf16_escaped, esc) - end - - table.insert(utf16_escaped, '"') - for i = 0, 0xD7FF do - append_escape(i) - end - -- Skip 0xD800 - 0xDFFF since they are used to encode supplementary - -- codepoints - for i = 0xE000, 0xFFFF do - append_escape(i) - end - -- Append surrogate pair for each supplementary codepoint - for high = 0xD800, 0xDBFF do - for low = 0xDC00, 0xDFFF do - append_escape(high) - append_escape(low) - end - end - table.insert(utf16_escaped, '"') - - return table.concat(utf16_escaped) -end - -function load_testdata() - local data = {} - - -- Data for 8bit raw <-> escaped octets tests - data.octets_raw = gen_raw_octets() - data.octets_escaped = util.file_load("octets-escaped.dat") - - -- Data for \uXXXX -> UTF-8 test - data.utf16_escaped = gen_utf16_escaped() - - -- Load matching data for utf16_escaped - local utf8_loaded - utf8_loaded, data.utf8_raw = pcall(util.file_load, "utf8.dat") - if not utf8_loaded then - data.utf8_raw = "Failed to load utf8.dat - please run genutf8.pl" - end - - data.table_cycle = {} - data.table_cycle[1] = data.table_cycle - - local big = {} - for i = 1, 1100 do - big = { { 10, false, true, json.null }, "string", a = big } - end - data.deeply_nested_data = big - - return data -end - -function test_decode_cycle(filename) - local obj1 = json.decode(util.file_load(filename)) - local obj2 = json.decode(json.encode(obj1)) - return util.compare_values(obj1, obj2) -end - --- Set up data used in tests -local Inf = math.huge; -local NaN = math.huge * 0; - -local testdata = load_testdata() - -local cjson_tests = { - -- Test API variables - { "Check module name, version", - function () return json._NAME, json._VERSION end, { }, - true, { "cjson", "2.1devel" } }, - - -- Test decoding simple types - { "Decode string", - json.decode, { '"test string"' }, true, { "test string" } }, - { "Decode numbers", - json.decode, { '[ 0.0, -5e3, -1, 0.3e-3, 1023.2, 0e10 ]' }, - true, { { 0.0, -5000, -1, 0.0003, 1023.2, 0 } } }, - { "Decode null", - json.decode, { 'null' }, true, { json.null } }, - { "Decode true", - json.decode, { 'true' }, true, { true } }, - { "Decode false", - json.decode, { 'false' }, true, { false } }, - { "Decode object with numeric keys", - json.decode, { '{ "1": "one", "3": "three" }' }, - true, { { ["1"] = "one", ["3"] = "three" } } }, - { "Decode object with string keys", - json.decode, { '{ "a": "a", "b": "b" }' }, - true, { { a = "a", b = "b" } } }, - { "Decode array", - json.decode, { '[ "one", null, "three" ]' }, - true, { { "one", json.null, "three" } } }, - - -- Test decoding errors - { "Decode UTF-16BE [throw error]", - json.decode, { '\0"\0"' }, - false, { "JSON parser does not support UTF-16 or UTF-32" } }, - { "Decode UTF-16LE [throw error]", - json.decode, { '"\0"\0' }, - false, { "JSON parser does not support UTF-16 or UTF-32" } }, - { "Decode UTF-32BE [throw error]", - json.decode, { '\0\0\0"' }, - false, { "JSON parser does not support UTF-16 or UTF-32" } }, - { "Decode UTF-32LE [throw error]", - json.decode, { '"\0\0\0' }, - false, { "JSON parser does not support UTF-16 or UTF-32" } }, - { "Decode partial JSON [throw error]", - json.decode, { '{ "unexpected eof": ' }, - false, { "Expected value but found T_END at character 21" } }, - { "Decode with extra comma [throw error]", - json.decode, { '{ "extra data": true }, false' }, - false, { "Expected the end but found T_COMMA at character 23" } }, - { "Decode invalid escape code [throw error]", - json.decode, { [[ { "bad escape \q code" } ]] }, - false, { "Expected object key string but found invalid escape code at character 16" } }, - { "Decode invalid unicode escape [throw error]", - json.decode, { [[ { "bad unicode \u0f6 escape" } ]] }, - false, { "Expected object key string but found invalid unicode escape code at character 17" } }, - { "Decode invalid keyword [throw error]", - json.decode, { ' [ "bad barewood", test ] ' }, - false, { "Expected value but found invalid token at character 20" } }, - { "Decode invalid number #1 [throw error]", - json.decode, { '[ -+12 ]' }, - false, { "Expected value but found invalid number at character 3" } }, - { "Decode invalid number #2 [throw error]", - json.decode, { '-v' }, - false, { "Expected value but found invalid number at character 1" } }, - { "Decode invalid number exponent [throw error]", - json.decode, { '[ 0.4eg10 ]' }, - false, { "Expected comma or array end but found invalid token at character 6" } }, - - -- Test decoding nested arrays / objects - { "Set decode_max_depth(5)", - json.decode_max_depth, { 5 }, true, { 5 } }, - { "Decode array at nested limit", - json.decode, { '[[[[[ "nested" ]]]]]' }, - true, { {{{{{ "nested" }}}}} } }, - { "Decode array over nested limit [throw error]", - json.decode, { '[[[[[[ "nested" ]]]]]]' }, - false, { "Found too many nested data structures (6) at character 6" } }, - { "Decode object at nested limit", - json.decode, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' }, - true, { {a={b={c={d={e="nested"}}}}} } }, - { "Decode object over nested limit [throw error]", - json.decode, { '{"a":{"b":{"c":{"d":{"e":{"f":"nested"}}}}}}' }, - false, { "Found too many nested data structures (6) at character 26" } }, - { "Set decode_max_depth(1000)", - json.decode_max_depth, { 1000 }, true, { 1000 } }, - { "Decode deeply nested array [throw error]", - json.decode, { string.rep("[", 1100) .. '1100' .. string.rep("]", 1100)}, - false, { "Found too many nested data structures (1001) at character 1001" } }, - - -- Test encoding nested tables - { "Set encode_max_depth(5)", - json.encode_max_depth, { 5 }, true, { 5 } }, - { "Encode nested table as array at nested limit", - json.encode, { {{{{{"nested"}}}}} }, true, { '[[[[["nested"]]]]]' } }, - { "Encode nested table as array after nested limit [throw error]", - json.encode, { { {{{{{"nested"}}}}} } }, - false, { "Cannot serialise, excessive nesting (6)" } }, - { "Encode nested table as object at nested limit", - json.encode, { {a={b={c={d={e="nested"}}}}} }, - true, { '{"a":{"b":{"c":{"d":{"e":"nested"}}}}}' } }, - { "Encode nested table as object over nested limit [throw error]", - json.encode, { {a={b={c={d={e={f="nested"}}}}}} }, - false, { "Cannot serialise, excessive nesting (6)" } }, - { "Encode table with cycle [throw error]", - json.encode, { testdata.table_cycle }, - false, { "Cannot serialise, excessive nesting (6)" } }, - { "Set encode_max_depth(1000)", - json.encode_max_depth, { 1000 }, true, { 1000 } }, - { "Encode deeply nested data [throw error]", - json.encode, { testdata.deeply_nested_data }, - false, { "Cannot serialise, excessive nesting (1001)" } }, - - -- Test encoding simple types - { "Encode null", - json.encode, { json.null }, true, { 'null' } }, - { "Encode true", - json.encode, { true }, true, { 'true' } }, - { "Encode false", - json.encode, { false }, true, { 'false' } }, - { "Encode empty object", - json.encode, { { } }, true, { '{}' } }, - { "Encode integer", - json.encode, { 10 }, true, { '10' } }, - { "Encode string", - json.encode, { "hello" }, true, { '"hello"' } }, - { "Encode Lua function [throw error]", - json.encode, { function () end }, - false, { "Cannot serialise function: type not supported" } }, - - -- Test decoding invalid numbers - { "Set decode_invalid_numbers(true)", - json.decode_invalid_numbers, { true }, true, { true } }, - { "Decode hexadecimal", - json.decode, { '0x6.ffp1' }, true, { 13.9921875 } }, - { "Decode numbers with leading zero", - json.decode, { '[ 0123, 00.33 ]' }, true, { { 123, 0.33 } } }, - { "Decode +-Inf", - json.decode, { '[ +Inf, Inf, -Inf ]' }, true, { { Inf, Inf, -Inf } } }, - { "Decode +-Infinity", - json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, - true, { { Inf, Inf, -Inf } } }, - { "Decode +-NaN", - json.decode, { '[ +NaN, NaN, -NaN ]' }, true, { { NaN, NaN, NaN } } }, - { "Decode Infrared (not infinity) [throw error]", - json.decode, { 'Infrared' }, - false, { "Expected the end but found invalid token at character 4" } }, - { "Decode Noodle (not NaN) [throw error]", - json.decode, { 'Noodle' }, - false, { "Expected value but found invalid token at character 1" } }, - { "Set decode_invalid_numbers(false)", - json.decode_invalid_numbers, { false }, true, { false } }, - { "Decode hexadecimal [throw error]", - json.decode, { '0x6' }, - false, { "Expected value but found invalid number at character 1" } }, - { "Decode numbers with leading zero [throw error]", - json.decode, { '[ 0123, 00.33 ]' }, - false, { "Expected value but found invalid number at character 3" } }, - { "Decode +-Inf [throw error]", - json.decode, { '[ +Inf, Inf, -Inf ]' }, - false, { "Expected value but found invalid token at character 3" } }, - { "Decode +-Infinity [throw error]", - json.decode, { '[ +Infinity, Infinity, -Infinity ]' }, - false, { "Expected value but found invalid token at character 3" } }, - { "Decode +-NaN [throw error]", - json.decode, { '[ +NaN, NaN, -NaN ]' }, - false, { "Expected value but found invalid token at character 3" } }, - { 'Set decode_invalid_numbers("on")', - json.decode_invalid_numbers, { "on" }, true, { true } }, - - -- Test encoding invalid numbers - { "Set encode_invalid_numbers(false)", - json.encode_invalid_numbers, { false }, true, { false } }, - { "Encode NaN [throw error]", - json.encode, { NaN }, - false, { "Cannot serialise number: must not be NaN or Infinity" } }, - { "Encode Infinity [throw error]", - json.encode, { Inf }, - false, { "Cannot serialise number: must not be NaN or Infinity" } }, - { "Set encode_invalid_numbers(\"null\")", - json.encode_invalid_numbers, { "null" }, true, { "null" } }, - { "Encode NaN as null", - json.encode, { NaN }, true, { "null" } }, - { "Encode Infinity as null", - json.encode, { Inf }, true, { "null" } }, - { "Set encode_invalid_numbers(true)", - json.encode_invalid_numbers, { true }, true, { true } }, - { "Encode NaN", - json.encode, { NaN }, true, { "NaN" } }, - { "Encode +Infinity", - json.encode, { Inf }, true, { "Infinity" } }, - { "Encode -Infinity", - json.encode, { -Inf }, true, { "-Infinity" } }, - { 'Set encode_invalid_numbers("off")', - json.encode_invalid_numbers, { "off" }, true, { false } }, - - -- Test encoding tables - { "Set encode_sparse_array(true, 2, 3)", - json.encode_sparse_array, { true, 2, 3 }, true, { true, 2, 3 } }, - { "Encode sparse table as array #1", - json.encode, { { [3] = "sparse test" } }, - true, { '[null,null,"sparse test"]' } }, - { "Encode sparse table as array #2", - json.encode, { { [1] = "one", [4] = "sparse test" } }, - true, { '["one",null,null,"sparse test"]' } }, - { "Encode sparse array as object", - json.encode, { { [1] = "one", [5] = "sparse test" } }, - true, { '{"1":"one","5":"sparse test"}' } }, - { "Encode table with numeric string key as object", - json.encode, { { ["2"] = "numeric string key test" } }, - true, { '{"2":"numeric string key test"}' } }, - { "Set encode_sparse_array(false)", - json.encode_sparse_array, { false }, true, { false, 2, 3 } }, - { "Encode table with incompatible key [throw error]", - json.encode, { { [false] = "wrong" } }, - false, { "Cannot serialise boolean: table key must be a number or string" } }, - - -- Test escaping - { "Encode all octets (8-bit clean)", - json.encode, { testdata.octets_raw }, true, { testdata.octets_escaped } }, - { "Decode all escaped octets", - json.decode, { testdata.octets_escaped }, true, { testdata.octets_raw } }, - { "Decode single UTF-16 escape", - json.decode, { [["\uF800"]] }, true, { "\239\160\128" } }, - { "Decode all UTF-16 escapes (including surrogate combinations)", - json.decode, { testdata.utf16_escaped }, true, { testdata.utf8_raw } }, - { "Decode swapped surrogate pair [throw error]", - json.decode, { [["\uDC00\uD800"]] }, - false, { "Expected value but found invalid unicode escape code at character 2" } }, - { "Decode duplicate high surrogate [throw error]", - json.decode, { [["\uDB00\uDB00"]] }, - false, { "Expected value but found invalid unicode escape code at character 2" } }, - { "Decode duplicate low surrogate [throw error]", - json.decode, { [["\uDB00\uDB00"]] }, - false, { "Expected value but found invalid unicode escape code at character 2" } }, - { "Decode missing low surrogate [throw error]", - json.decode, { [["\uDB00"]] }, - false, { "Expected value but found invalid unicode escape code at character 2" } }, - { "Decode invalid low surrogate [throw error]", - json.decode, { [["\uDB00\uD"]] }, - false, { "Expected value but found invalid unicode escape code at character 2" } }, - - -- Test locale support - -- - -- The standard Lua interpreter is ANSI C online doesn't support locales - -- by default. Force a known problematic locale to test strtod()/sprintf(). - { "Set locale to cs_CZ (comma separator)", function () - os.setlocale("cs_CZ") - json.new() - end }, - { "Encode number under comma locale", - json.encode, { 1.5 }, true, { '1.5' } }, - { "Decode number in array under comma locale", - json.decode, { '[ 10, "test" ]' }, true, { { 10, "test" } } }, - { "Revert locale to POSIX", function () - os.setlocale("C") - json.new() - end }, - - -- Test encode_keep_buffer() and enable_number_precision() - { "Set encode_keep_buffer(false)", - json.encode_keep_buffer, { false }, true, { false } }, - { "Set encode_number_precision(3)", - json.encode_number_precision, { 3 }, true, { 3 } }, - { "Encode number with precision 3", - json.encode, { 1/3 }, true, { "0.333" } }, - { "Set encode_number_precision(14)", - json.encode_number_precision, { 14 }, true, { 14 } }, - { "Set encode_keep_buffer(true)", - json.encode_keep_buffer, { true }, true, { true } }, - - -- Test config API errors - -- Function is listed as '?' due to pcall - { "Set encode_number_precision(0) [throw error]", - json.encode_number_precision, { 0 }, - false, { "bad argument #1 to '?' (expected integer between 1 and 14)" } }, - { "Set encode_number_precision(\"five\") [throw error]", - json.encode_number_precision, { "five" }, - false, { "bad argument #1 to '?' (number expected, got string)" } }, - { "Set encode_keep_buffer(nil, true) [throw error]", - json.encode_keep_buffer, { nil, true }, - false, { "bad argument #2 to '?' (found too many arguments)" } }, - { "Set encode_max_depth(\"wrong\") [throw error]", - json.encode_max_depth, { "wrong" }, - false, { "bad argument #1 to '?' (number expected, got string)" } }, - { "Set decode_max_depth(0) [throw error]", - json.decode_max_depth, { "0" }, - false, { "bad argument #1 to '?' (expected integer between 1 and 2147483647)" } }, - { "Set encode_invalid_numbers(-2) [throw error]", - json.encode_invalid_numbers, { -2 }, - false, { "bad argument #1 to '?' (invalid option '-2')" } }, - { "Set decode_invalid_numbers(true, false) [throw error]", - json.decode_invalid_numbers, { true, false }, - false, { "bad argument #2 to '?' (found too many arguments)" } }, - { "Set encode_sparse_array(\"not quite on\") [throw error]", - json.encode_sparse_array, { "not quite on" }, - false, { "bad argument #1 to '?' (invalid option 'not quite on')" } }, - - { "Reset Lua CJSON configuration", function () json = json.new() end }, - -- Wrap in a function to ensure the table returned by json.new() is used - { "Check encode_sparse_array()", - function (...) return json.encode_sparse_array(...) end, { }, - true, { false, 2, 10 } }, - - { "Encode (safe) simple value", - json_safe.encode, { true }, - true, { "true" } }, - { "Encode (safe) argument validation [throw error]", - json_safe.encode, { "arg1", "arg2" }, - false, { "bad argument #1 to '?' (expected 1 argument)" } }, - { "Decode (safe) error generation", - json_safe.decode, { "Oops" }, - true, { nil, "Expected value but found invalid token at character 1" } }, - { "Decode (safe) error generation after new()", - function(...) return json_safe.new().decode(...) end, { "Oops" }, - true, { nil, "Expected value but found invalid token at character 1" } }, -} - -print(("==> Testing Lua CJSON version %s\n"):format(json._VERSION)) - -util.run_test_group(cjson_tests) - -for _, filename in ipairs(arg) do - util.run_test("Decode cycle " .. filename, test_decode_cycle, { filename }, - true, { true }) -end - -local pass, total = util.run_test_summary() - -if pass == total then - print("==> Summary: all tests succeeded") -else - print(("==> Summary: %d/%d tests failed"):format(total - pass, total)) - os.exit(1) -end - --- vi:ai et sw=4 ts=4: diff --git a/app/cjson/tests/types.json b/app/cjson/tests/types.json deleted file mode 100644 index c01e7d20ec..0000000000 --- a/app/cjson/tests/types.json +++ /dev/null @@ -1 +0,0 @@ -{ "array": [ 10, true, null ] } diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 2d2c19ad1c..4c1291f90b 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -24,7 +24,6 @@ #define LUA_USE_MODULES_BIT //#define LUA_USE_MODULES_BMP085 //#define LUA_USE_MODULES_BME280 -//#define LUA_USE_MODULES_CJSON //#define LUA_USE_MODULES_COAP //#define LUA_USE_MODULES_CRON //#define LUA_USE_MODULES_CRYPTO @@ -54,6 +53,7 @@ //#define LUA_USE_MODULES_RTCMEM //#define LUA_USE_MODULES_RTCTIME //#define LUA_USE_MODULES_SIGMA_DELTA +//#define LUA_USE_MODULES_SJSON //#define LUA_USE_MODULES_SNTP //#define LUA_USE_MODULES_SOMFY #define LUA_USE_MODULES_SPI diff --git a/app/modules/Makefile b/app/modules/Makefile index 706b3a67f1..87d49b7cb6 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -50,10 +50,10 @@ INCLUDES += -I ../pcm INCLUDES += -I ../platform INCLUDES += -I ../spiffs INCLUDES += -I ../smart -INCLUDES += -I ../cjson INCLUDES += -I ../dhtlib INCLUDES += -I ../fatfs INCLUDES += -I ../http +INCLUDES += -I ../sjson INCLUDES += -I ../websocket PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/modules/cjson.c b/app/modules/cjson.c deleted file mode 100644 index ed7a31eb9b..0000000000 --- a/app/modules/cjson.c +++ /dev/null @@ -1,1630 +0,0 @@ -/* Lua CJSON - JSON support for Lua - * - * Copyright (c) 2010-2012 Mark Pulford - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* Caveats: - * - JSON "null" values are represented as lightuserdata since Lua - * tables cannot contain "nil". Compare with cjson.null. - * - Invalid UTF-8 characters are not detected and will be passed - * untouched. If required, UTF-8 error checking should be done - * outside this library. - * - Javascript comments are not part of the JSON spec, and are not - * currently supported. - * - * Note: Decoding is slower than encoding. Lua spends significant - * time (30%) managing tables when parsing JSON since it is - * difficult to know object/array sizes ahead of time. - */ - -// #include -#include "module.h" -#include "c_string.h" -#include "c_math.h" -#include "c_limits.h" -#include "lauxlib.h" -#include "flash_api.h" -#include "ctype.h" - -#include "strbuf.h" -#include "cjson_mem.h" - -#define FPCONV_G_FMT_BUFSIZE 32 -#define fpconv_strtod c_strtod -#define fpconv_init() ((void)0) - -#ifndef CJSON_MODNAME -#define CJSON_MODNAME "cjson" -#endif - -#ifndef CJSON_VERSION -#define CJSON_VERSION "2.1devel" -#endif - -/* Workaround for Solaris platforms missing isinf() */ -#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) -#define isinf(x) (!isnan(x) && isnan((x) - (x))) -#endif - -#define DEFAULT_SPARSE_CONVERT 0 -#define DEFAULT_SPARSE_RATIO 2 -#define DEFAULT_SPARSE_SAFE 10 -#define DEFAULT_ENCODE_MAX_DEPTH 1000 -#define DEFAULT_DECODE_MAX_DEPTH 1000 -#define DEFAULT_ENCODE_INVALID_NUMBERS 0 -#define DEFAULT_DECODE_INVALID_NUMBERS 1 -#define DEFAULT_ENCODE_KEEP_BUFFER 0 -#define DEFAULT_ENCODE_NUMBER_PRECISION 14 - -#ifdef DISABLE_INVALID_NUMBERS -#undef DEFAULT_DECODE_INVALID_NUMBERS -#define DEFAULT_DECODE_INVALID_NUMBERS 0 -#endif - -typedef enum { - T_OBJ_BEGIN, - T_OBJ_END, - T_ARR_BEGIN, - T_ARR_END, - T_STRING, - T_NUMBER, - T_BOOLEAN, - T_NULL, - T_COLON, - T_COMMA, - T_END, - T_WHITESPACE, - T_ERROR, - T_UNKNOWN -} json_token_type_t; -#if 0 -static const char *json_token_type_name[] = { - "T_OBJ_BEGIN", - "T_OBJ_END", - "T_ARR_BEGIN", - "T_ARR_END", - "T_STRING", - "T_NUMBER", - "T_BOOLEAN", - "T_NULL", - "T_COLON", - "T_COMMA", - "T_END", - "T_WHITESPACE", - "T_ERROR", - "T_UNKNOWN", - NULL -}; -#endif -static const char json_token_type_name[14][16] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = { - {'T','_','O','B','J','_','B','E','G','I','N',0}, - {'T','_','O','B','J','_','E','N','D',0}, - {'T','_','A','R','R','_','B','E','G','I','N',0}, - {'T','_','A','R','R','_','E','N','D',0}, - {'T','_','S','T','R','I','N','G',0}, - {'T','_','N','U','M','B','E','R',0}, - {'T','_','B','O','O','L','E','A','N',0}, - {'T','_','N','U','L','L',0}, - {'T','_','C','O','L','O','N',0}, - {'T','_','C','O','M','M','A',0}, - {'T','_','E','N','D',0}, - {'T','_','W','H','I','T','E','S','P','A','C','E',0}, - {'T','_','E','R','R','O','R',0}, - {'T','_','U','N','K','N','O','W','N',0} -}; - -typedef struct { - // json_token_type_t ch2token[256]; // 256*4 = 1024 byte - // char escape2char[256]; /* Decoding */ - - /* encode_buf is only allocated and used when - * encode_keep_buffer is set */ - strbuf_t encode_buf; - - int encode_sparse_convert; - int encode_sparse_ratio; - int encode_sparse_safe; - int encode_max_depth; - int encode_invalid_numbers; /* 2 => Encode as "null" */ - int encode_number_precision; - int encode_keep_buffer; - - int decode_invalid_numbers; - int decode_max_depth; -} json_config_t; - -typedef struct { - const char *data; - const char *ptr; - strbuf_t *tmp; /* Temporary storage for strings */ - json_config_t *cfg; - int current_depth; -} json_parse_t; - -typedef struct { - json_token_type_t type; - int index; - union { - const char *string; - double number; - int boolean; - } value; - int string_len; -} json_token_t; - -#if 0 -static const char *char2escape[256] = { - "\\u0000", "\\u0001", "\\u0002", "\\u0003", - "\\u0004", "\\u0005", "\\u0006", "\\u0007", - "\\b", "\\t", "\\n", "\\u000b", - "\\f", "\\r", "\\u000e", "\\u000f", - "\\u0010", "\\u0011", "\\u0012", "\\u0013", - "\\u0014", "\\u0015", "\\u0016", "\\u0017", - "\\u0018", "\\u0019", "\\u001a", "\\u001b", - "\\u001c", "\\u001d", "\\u001e", "\\u001f", - NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -}; -#endif - -/* ===== HELPER FUNCTION ===== */ -static const char escape_array[36][8] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = { - {'\\','u','0','0','0','0','\0','\0'}, - {'\\','u','0','0','0','1','\0','\0'}, - {'\\','u','0','0','0','2','\0','\0'}, - {'\\','u','0','0','0','3','\0','\0'}, - {'\\','u','0','0','0','4','\0','\0'}, - {'\\','u','0','0','0','5','\0','\0'}, - {'\\','u','0','0','0','6','\0','\0'}, - {'\\','u','0','0','0','7','\0','\0'}, - {'\\','b','\0','\0','\0','\0','\0','\0'}, - {'\\','t','\0','\0','\0','\0','\0','\0'}, - {'\\','n','\0','\0','\0','\0','\0','\0'}, - {'\\','u','0','0','0','b','\0','\0'}, - {'\\','f','\0','\0','\0','\0','\0','\0'}, - {'\\','r','\0','\0','\0','\0','\0','\0'}, - {'\\','u','0','0','0','e','\0','\0'}, - {'\\','u','0','0','0','f','\0','\0'}, - {'\\','u','0','0','1','0','\0','\0'}, - {'\\','u','0','0','1','1','\0','\0'}, - {'\\','u','0','0','1','2','\0','\0'}, - {'\\','u','0','0','1','3','\0','\0'}, - {'\\','u','0','0','1','4','\0','\0'}, - {'\\','u','0','0','1','5','\0','\0'}, - {'\\','u','0','0','1','6','\0','\0'}, - {'\\','u','0','0','1','7','\0','\0'}, - {'\\','u','0','0','1','8','\0','\0'}, - {'\\','u','0','0','1','9','\0','\0'}, - {'\\','u','0','0','1','a','\0','\0'}, - {'\\','u','0','0','1','b','\0','\0'}, - {'\\','u','0','0','1','c','\0','\0'}, - {'\\','u','0','0','1','d','\0','\0'}, - {'\\','u','0','0','1','e','\0','\0'}, - {'\\','u','0','0','1','f','\0','\0'}, - {'\\','\"','\0','\0','\0','\0','\0','\0'}, - {'\\','/','\0','\0','\0','\0','\0','\0'}, - {'\\','\\','\0','\0','\0','\0','\0','\0'}, - {'\\','u','0','0','7','f','\0','\0'} -}; - -static const char *char2escape(unsigned char c){ - if(c<32) return escape_array[c]; - - switch(c){ - case 34: return escape_array[32]; - case 47: return escape_array[33]; - case 92: return escape_array[34]; - case 127: return escape_array[35]; - default: - return NULL; - } -} - -static json_token_type_t ch2token(unsigned char c){ - switch(c){ - case '{': return T_OBJ_BEGIN; - case '}': return T_OBJ_END; - case '[': return T_ARR_BEGIN; - case ']': return T_ARR_END; - case ',': return T_COMMA; - case ':': return T_COLON; - case '\0': return T_END; - case ' ': return T_WHITESPACE; - case '\t': return T_WHITESPACE; - case '\n': return T_WHITESPACE; - case '\r': return T_WHITESPACE; - - /* Update characters that require further processing */ - case 'f': case 'i': case 'I': case 'n': case 'N': case 't': case '"': case '+': case '-': - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - return T_UNKNOWN; - default: - return T_ERROR; - } -} - -static char escape2char(unsigned char c){ - switch(c){ - case '"': return '"'; - case '\\': return '\\'; - case '/': return '/'; - case 'b': return '\b'; - case 't': return '\t'; - case 'n': return '\n'; - case 'f': return '\f'; - case 'r': return '\r'; - case 'u': return 'u'; - default: - return 0; - } -} - -/* ===== CONFIGURATION ===== */ -#if 0 -static json_config_t *json_fetch_config(lua_State *l) -{ - json_config_t *cfg; - - cfg = lua_touserdata(l, lua_upvalueindex(1)); - if (!cfg) - luaL_error(l, "BUG: Unable to fetch CJSON configuration"); - - return cfg; -} - -/* Ensure the correct number of arguments have been provided. - * Pad with nil to allow other functions to simply check arg[i] - * to find whether an argument was provided */ -static json_config_t *json_arg_init(lua_State *l, int args) -{ - luaL_argcheck(l, lua_gettop(l) <= args, args + 1, - "found too many arguments"); - - while (lua_gettop(l) < args) - lua_pushnil(l); - - return json_fetch_config(l); -} - -/* Process integer options for configuration functions */ -static int json_integer_option(lua_State *l, int optindex, int *setting, - int min, int max) -{ - char errmsg[64]; - int value; - - if (!lua_isnil(l, optindex)) { - value = luaL_checkinteger(l, optindex); - c_sprintf(errmsg, "expected integer between %d and %d", min, max); - luaL_argcheck(l, min <= value && value <= max, 1, errmsg); - *setting = value; - } - - lua_pushinteger(l, *setting); - - return 1; -} - -/* Process enumerated arguments for a configuration function */ -static int json_enum_option(lua_State *l, int optindex, int *setting, - const char **options, int bool_true) -{ - static const char *bool_options[] = { "off", "on", NULL }; - - if (!options) { - options = bool_options; - bool_true = 1; - } - - if (!lua_isnil(l, optindex)) { - if (bool_true && lua_isboolean(l, optindex)) - *setting = lua_toboolean(l, optindex) * bool_true; - else - *setting = luaL_checkoption(l, optindex, NULL, options); - } - - if (bool_true && (*setting == 0 || *setting == bool_true)) - lua_pushboolean(l, *setting); - else - lua_pushstring(l, options[*setting]); - - return 1; -} - -/* Configures handling of extremely sparse arrays: - * convert: Convert extremely sparse arrays into objects? Otherwise error. - * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio - * safe: Always use an array when the max index <= safe */ -static int json_cfg_encode_sparse_array(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 3); - - json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); - json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); - json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); - - return 3; -} - -/* Configures the maximum number of nested arrays/objects allowed when - * encoding */ -static int json_cfg_encode_max_depth(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 1); - - return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); -} - -/* Configures the maximum number of nested arrays/objects allowed when - * encoding */ -static int json_cfg_decode_max_depth(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 1); - - return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); -} - -/* Configures number precision when converting doubles to text */ -static int json_cfg_encode_number_precision(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 1); - - return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14); -} - -/* Configures JSON encoding buffer persistence */ -static int json_cfg_encode_keep_buffer(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 1); - int old_value; - - old_value = cfg->encode_keep_buffer; - - json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); - - /* Init / free the buffer if the setting has changed */ - if (old_value ^ cfg->encode_keep_buffer) { - if (cfg->encode_keep_buffer){ - if(-1==strbuf_init(&cfg->encode_buf, 0)) - return luaL_error(l, "not enough memory"); - } - else - strbuf_free(&cfg->encode_buf); - } - - return 1; -} - -#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) -void json_verify_invalid_number_setting(lua_State *l, int *setting) -{ - if (*setting == 1) { - *setting = 0; - luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); - } -} -#else -#define json_verify_invalid_number_setting(l, s) do { } while(0) -#endif - -static int json_cfg_encode_invalid_numbers(lua_State *l) -{ - static const char *options[] = { "off", "on", "null", NULL }; - json_config_t *cfg = json_arg_init(l, 1); - - json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); - - json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); - - return 1; -} - -static int json_cfg_decode_invalid_numbers(lua_State *l) -{ - json_config_t *cfg = json_arg_init(l, 1); - - json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); - - json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); - - return 1; -} - -static int json_destroy_config(lua_State *l) -{ - json_config_t *cfg; - - cfg = lua_touserdata(l, 1); - if (cfg) - strbuf_free(&cfg->encode_buf); - cfg = NULL; - - return 0; -} - -static void json_create_config(lua_State *l) -{ - json_config_t *cfg; - int i; - - cfg = lua_newuserdata(l, sizeof(*cfg)); - - /* Create GC method to clean up strbuf */ - lua_newtable(l); - lua_pushcfunction(l, json_destroy_config); - lua_setfield(l, -2, "__gc"); - lua_setmetatable(l, -2); - - cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; - cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; - cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; - cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; - cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; - cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; - cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; - cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; - cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; - -#if DEFAULT_ENCODE_KEEP_BUFFER > 0 - strbuf_init(&cfg->encode_buf, 0); -#endif - - /* Decoding init */ - - /* Tag all characters as an error */ - for (i = 0; i < 256; i++) - cfg->ch2token[i] = T_ERROR; - - /* Set tokens that require no further processing */ - cfg->ch2token['{'] = T_OBJ_BEGIN; - cfg->ch2token['}'] = T_OBJ_END; - cfg->ch2token['['] = T_ARR_BEGIN; - cfg->ch2token[']'] = T_ARR_END; - cfg->ch2token[','] = T_COMMA; - cfg->ch2token[':'] = T_COLON; - cfg->ch2token['\0'] = T_END; - cfg->ch2token[' '] = T_WHITESPACE; - cfg->ch2token['\t'] = T_WHITESPACE; - cfg->ch2token['\n'] = T_WHITESPACE; - cfg->ch2token['\r'] = T_WHITESPACE; - - /* Update characters that require further processing */ - cfg->ch2token['f'] = T_UNKNOWN; /* false? */ - cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ - cfg->ch2token['I'] = T_UNKNOWN; - cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ - cfg->ch2token['N'] = T_UNKNOWN; - cfg->ch2token['t'] = T_UNKNOWN; /* true? */ - cfg->ch2token['"'] = T_UNKNOWN; /* string? */ - cfg->ch2token['+'] = T_UNKNOWN; /* number? */ - cfg->ch2token['-'] = T_UNKNOWN; - for (i = 0; i < 10; i++) - cfg->ch2token['0' + i] = T_UNKNOWN; - - /* Lookup table for parsing escape characters */ - for (i = 0; i < 256; i++) - cfg->escape2char[i] = 0; /* String error */ - cfg->escape2char['"'] = '"'; - cfg->escape2char['\\'] = '\\'; - cfg->escape2char['/'] = '/'; - cfg->escape2char['b'] = '\b'; - cfg->escape2char['t'] = '\t'; - cfg->escape2char['n'] = '\n'; - cfg->escape2char['f'] = '\f'; - cfg->escape2char['r'] = '\r'; - cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ -} -#endif - -json_config_t _cfg; - -static json_config_t *json_fetch_config(lua_State *l) -{ - return &_cfg; -} - -static int cfg_init(json_config_t *cfg){ - cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; - cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; - cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; - cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; - cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; - cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; - cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; - cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; - cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; - -#if DEFAULT_ENCODE_KEEP_BUFFER > 0 - if(-1==strbuf_init(&cfg->encode_buf, 0)){ - NODE_ERR("not enough memory\n"); - return -1; - } -#endif - - return 0; -} -/* ===== ENCODING ===== */ - -static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, - const char *reason) -{ - if (!cfg->encode_keep_buffer) - strbuf_free(json); - luaL_error(l, "Cannot serialise %s: %s", - lua_typename(l, lua_type(l, lindex)), reason); -} - -/* json_append_string args: - * - lua_State - * - JSON strbuf - * - String (Lua stack index) - * - * Returns nothing. Doesn't remove string from Lua stack */ -static void json_append_string(lua_State *l, strbuf_t *json, int lindex) -{ - const char *escstr; - int i; - const char *str; - size_t len; - - str = lua_tolstring(l, lindex, &len); - - /* Worst case is len * 6 (all unicode escapes). - * This buffer is reused constantly for small strings - * If there are any excess pages, they won't be hit anyway. - * This gains ~5% speedup. */ - strbuf_ensure_empty_length(json, len * 6 + 2); - - strbuf_append_char_unsafe(json, '\"'); - for (i = 0; i < len; i++) { - escstr = char2escape((unsigned char)str[i]); - if (escstr){ - int i; - char temp[8]; // for now, 8-bytes is enough. - for (i=0; i < 8; ++i) - { - temp[i] = byte_of_aligned_array(escstr, i); - if(temp[i]==0) break; - } - escstr = temp; - strbuf_append_string(json, escstr); - } - else - strbuf_append_char_unsafe(json, str[i]); - } - strbuf_append_char_unsafe(json, '\"'); -} - -/* Find the size of the array on the top of the Lua stack - * -1 object (not a pure array) - * >=0 elements in array - */ -static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) -{ - double k; - int max; - int items; - - max = 0; - items = 0; - - lua_pushnil(l); - /* table, startkey */ - while (lua_next(l, -2) != 0) { - /* table, key, value */ - if (lua_type(l, -2) == LUA_TNUMBER && - (k = lua_tonumber(l, -2))) { - /* Integer >= 1 ? */ - if (floor(k) == k && k >= 1) { - if (k > max) - max = k; - items++; - lua_pop(l, 1); - continue; - } - } - - /* Must not be an array (non integer key) */ - lua_pop(l, 2); - return -1; - } - - /* Encode excessively sparse arrays as objects (if enabled) */ - if (cfg->encode_sparse_ratio > 0 && - max > items * cfg->encode_sparse_ratio && - max > cfg->encode_sparse_safe) { - if (!cfg->encode_sparse_convert) - json_encode_exception(l, cfg, json, -1, "excessively sparse array"); - - return -1; - } - - return max; -} - -static void json_check_encode_depth(lua_State *l, json_config_t *cfg, - int current_depth, strbuf_t *json) -{ - /* Ensure there are enough slots free to traverse a table (key, - * value) and push a string for a potential error message. - * - * Unlike "decode", the key and value are still on the stack when - * lua_checkstack() is called. Hence an extra slot for luaL_error() - * below is required just in case the next check to lua_checkstack() - * fails. - * - * While this won't cause a crash due to the EXTRA_STACK reserve - * slots, it would still be an improper use of the API. */ - if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) - return; - - if (!cfg->encode_keep_buffer) - strbuf_free(json); - - luaL_error(l, "Cannot serialise, excessive nesting (%d)", - current_depth); -} - -static void json_append_data(lua_State *l, json_config_t *cfg, - int current_depth, strbuf_t *json); - -/* json_append_array args: - * - lua_State - * - JSON strbuf - * - Size of passwd Lua array (top of stack) */ -static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, - strbuf_t *json, int array_length) -{ - int comma, i; - - strbuf_append_char(json, '['); - - comma = 0; - for (i = 1; i <= array_length; i++) { - if (comma) - strbuf_append_char(json, ','); - else - comma = 1; - - lua_rawgeti(l, -1, i); - json_append_data(l, cfg, current_depth, json); - lua_pop(l, 1); - } - - strbuf_append_char(json, ']'); -} - -static void json_append_number(lua_State *l, json_config_t *cfg, - strbuf_t *json, int lindex) -{ - double num = lua_tonumber(l, lindex); - int len; - - if (cfg->encode_invalid_numbers == 0) { - /* Prevent encoding invalid numbers */ - if (isinf(num) || isnan(num)) - json_encode_exception(l, cfg, json, lindex, - "must not be NaN or Infinity"); - } else if (cfg->encode_invalid_numbers == 1) { - /* Encode NaN/Infinity separately to ensure Javascript compatible - * values are used. */ - if (isnan(num)) { - strbuf_append_mem(json, "NaN", 3); - return; - } - if (isinf(num)) { - if (num < 0) - strbuf_append_mem(json, "-Infinity", 9); - else - strbuf_append_mem(json, "Infinity", 8); - return; - } - } else { - /* Encode invalid numbers as "null" */ - if (isinf(num) || isnan(num)) { - strbuf_append_mem(json, "null", 4); - return; - } - } - - strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); - // len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); - c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, (LUA_NUMBER)num); - len = c_strlen(strbuf_empty_ptr(json)); - - strbuf_extend_length(json, len); -} - -static void json_append_object(lua_State *l, json_config_t *cfg, - int current_depth, strbuf_t *json) -{ - int comma, keytype; - - /* Object */ - strbuf_append_char(json, '{'); - - lua_pushnil(l); - /* table, startkey */ - comma = 0; - while (lua_next(l, -2) != 0) { - if (comma) - strbuf_append_char(json, ','); - else - comma = 1; - - /* table, key, value */ - keytype = lua_type(l, -2); - if (keytype == LUA_TNUMBER) { - strbuf_append_char(json, '"'); - json_append_number(l, cfg, json, -2); - strbuf_append_mem(json, "\":", 2); - } else if (keytype == LUA_TSTRING) { - json_append_string(l, json, -2); - strbuf_append_char(json, ':'); - } else { - json_encode_exception(l, cfg, json, -2, - "table key must be a number or string"); - /* never returns */ - } - - /* table, key, value */ - json_append_data(l, cfg, current_depth, json); - lua_pop(l, 1); - /* table, key */ - } - - strbuf_append_char(json, '}'); -} - -/* Serialise Lua data into JSON string. */ -static void json_append_data(lua_State *l, json_config_t *cfg, - int current_depth, strbuf_t *json) -{ - int len; - - switch (lua_type(l, -1)) { - case LUA_TSTRING: - json_append_string(l, json, -1); - break; - case LUA_TNUMBER: - json_append_number(l, cfg, json, -1); - break; - case LUA_TBOOLEAN: - if (lua_toboolean(l, -1)) - strbuf_append_mem(json, "true", 4); - else - strbuf_append_mem(json, "false", 5); - break; - case LUA_TTABLE: - current_depth++; - json_check_encode_depth(l, cfg, current_depth, json); - len = lua_array_length(l, cfg, json); - if (len > 0) - json_append_array(l, cfg, current_depth, json, len); - else - json_append_object(l, cfg, current_depth, json); - break; - case LUA_TNIL: - strbuf_append_mem(json, "null", 4); - break; - case LUA_TLIGHTUSERDATA: - if (lua_touserdata(l, -1) == NULL) { - strbuf_append_mem(json, "null", 4); - break; - } - default: - /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, - * and LUA_TLIGHTUSERDATA) cannot be serialised */ - json_encode_exception(l, cfg, json, -1, "type not supported"); - /* never returns */ - } -} - -static int json_encode(lua_State *l) -{ - json_config_t *cfg = json_fetch_config(l); - strbuf_t local_encode_buf; - strbuf_t *encode_buf; - char *json; - int len; - - luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); - - if (!cfg->encode_keep_buffer) { - /* Use private buffer */ - encode_buf = &local_encode_buf; - if(-1==strbuf_init(encode_buf, 0)) - return luaL_error(l, "not enough memory"); - } else { - /* Reuse existing buffer */ - encode_buf = &cfg->encode_buf; - strbuf_reset(encode_buf); - } - - json_append_data(l, cfg, 0, encode_buf); - json = strbuf_string(encode_buf, &len); - - lua_pushlstring(l, json, len); - - if (!cfg->encode_keep_buffer) - strbuf_free(encode_buf); - - return 1; -} - -/* ===== DECODING ===== */ - -static void json_process_value(lua_State *l, json_parse_t *json, - json_token_t *token); - -static int hexdigit2int(char hex) -{ - if ('0' <= hex && hex <= '9') - return hex - '0'; - - /* Force lowercase */ - hex |= 0x20; - if ('a' <= hex && hex <= 'f') - return 10 + hex - 'a'; - - return -1; -} - -static int decode_hex4(const char *hex) -{ - int digit[4]; - int i; - - /* Convert ASCII hex digit to numeric digit - * Note: this returns an error for invalid hex digits, including - * NULL */ - for (i = 0; i < 4; i++) { - digit[i] = hexdigit2int(hex[i]); - if (digit[i] < 0) { - return -1; - } - } - - return (digit[0] << 12) + - (digit[1] << 8) + - (digit[2] << 4) + - digit[3]; -} - -/* Converts a Unicode codepoint to UTF-8. - * Returns UTF-8 string length, and up to 4 bytes in *utf8 */ -static int codepoint_to_utf8(char *utf8, int codepoint) -{ - /* 0xxxxxxx */ - if (codepoint <= 0x7F) { - utf8[0] = codepoint; - return 1; - } - - /* 110xxxxx 10xxxxxx */ - if (codepoint <= 0x7FF) { - utf8[0] = (codepoint >> 6) | 0xC0; - utf8[1] = (codepoint & 0x3F) | 0x80; - return 2; - } - - /* 1110xxxx 10xxxxxx 10xxxxxx */ - if (codepoint <= 0xFFFF) { - utf8[0] = (codepoint >> 12) | 0xE0; - utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; - utf8[2] = (codepoint & 0x3F) | 0x80; - return 3; - } - - /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - if (codepoint <= 0x1FFFFF) { - utf8[0] = (codepoint >> 18) | 0xF0; - utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; - utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; - utf8[3] = (codepoint & 0x3F) | 0x80; - return 4; - } - - return 0; -} - - -/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX - * \u is guaranteed to exist, but the remaining hex characters may be - * missing. - * Translate to UTF-8 and append to temporary token string. - * Must advance index to the next character to be processed. - * Returns: 0 success - * -1 error - */ -static int json_append_unicode_escape(json_parse_t *json) -{ - char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */ - int codepoint; - int surrogate_low; - int len; - int escape_len = 6; - - /* Fetch UTF-16 code unit */ - codepoint = decode_hex4(json->ptr + 2); - if (codepoint < 0) - return -1; - - /* UTF-16 surrogate pairs take the following 2 byte form: - * 11011 x yyyyyyyyyy - * When x = 0: y is the high 10 bits of the codepoint - * x = 1: y is the low 10 bits of the codepoint - * - * Check for a surrogate pair (high or low) */ - if ((codepoint & 0xF800) == 0xD800) { - /* Error if the 1st surrogate is not high */ - if (codepoint & 0x400) - return -1; - - /* Ensure the next code is a unicode escape */ - if (*(json->ptr + escape_len) != '\\' || - *(json->ptr + escape_len + 1) != 'u') { - return -1; - } - - /* Fetch the next codepoint */ - surrogate_low = decode_hex4(json->ptr + 2 + escape_len); - if (surrogate_low < 0) - return -1; - - /* Error if the 2nd code is not a low surrogate */ - if ((surrogate_low & 0xFC00) != 0xDC00) - return -1; - - /* Calculate Unicode codepoint */ - codepoint = (codepoint & 0x3FF) << 10; - surrogate_low &= 0x3FF; - codepoint = (codepoint | surrogate_low) + 0x10000; - escape_len = 12; - } - - /* Convert codepoint to UTF-8 */ - len = codepoint_to_utf8(utf8, codepoint); - if (!len) - return -1; - - /* Append bytes and advance parse index */ - strbuf_append_mem_unsafe(json->tmp, utf8, len); - json->ptr += escape_len; - - return 0; -} - -static void json_set_token_error(json_token_t *token, json_parse_t *json, - const char *errtype) -{ - token->type = T_ERROR; - token->index = json->ptr - json->data; - token->value.string = errtype; -} - -static void json_next_string_token(json_parse_t *json, json_token_t *token) -{ - // char *escape2char = json->cfg->escape2char; - char ch; - - /* Caller must ensure a string is next */ - if(!(*json->ptr == '"')) return; - - /* Skip " */ - json->ptr++; - - /* json->tmp is the temporary strbuf used to accumulate the - * decoded string value. - * json->tmp is sized to handle JSON containing only a string value. - */ - strbuf_reset(json->tmp); - - while ((ch = *json->ptr) != '"') { - if (!ch) { - /* Premature end of the string */ - json_set_token_error(token, json, "unexpected end of string"); - return; - } - - /* Handle escapes */ - if (ch == '\\') { - /* Fetch escape character */ - ch = *(json->ptr + 1); - - /* Translate escape code and append to tmp string */ - ch = escape2char((unsigned char)ch); - if (ch == 'u') { - if (json_append_unicode_escape(json) == 0) - continue; - - json_set_token_error(token, json, - "invalid unicode escape code"); - return; - } - if (!ch) { - json_set_token_error(token, json, "invalid escape code"); - return; - } - - /* Skip '\' */ - json->ptr++; - } - /* Append normal character or translated single character - * Unicode escapes are handled above */ - strbuf_append_char_unsafe(json->tmp, ch); - json->ptr++; - } - json->ptr++; /* Eat final quote (") */ - - strbuf_ensure_null(json->tmp); - - token->type = T_STRING; - token->value.string = strbuf_string(json->tmp, &token->string_len); -} - -/* JSON numbers should take the following form: - * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? - * - * json_next_number_token() uses strtod() which allows other forms: - * - numbers starting with '+' - * - NaN, -NaN, infinity, -infinity - * - hexadecimal numbers - * - numbers with leading zeros - * - * json_is_invalid_number() detects "numbers" which may pass strtod()'s - * error checking, but should not be allowed with strict JSON. - * - * json_is_invalid_number() may pass numbers which cause strtod() - * to generate an error. - */ -static int json_is_invalid_number(json_parse_t *json) -{ - const char *p = json->ptr; - - /* Reject numbers starting with + */ - if (*p == '+') - return 1; - - /* Skip minus sign if it exists */ - if (*p == '-') - p++; - - /* Reject numbers starting with 0x, or leading zeros */ - if (*p == '0') { - int ch2 = *(p + 1); - - if ((ch2 | 0x20) == 'x' || /* Hex */ - ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ - return 1; - - return 0; - } else if (*p <= '9') { - return 0; /* Ordinary number */ - } - - char tmp[4]; // conv to lower. because c_strncasecmp == c_strcmp - int i; - for (i = 0; i < 3; ++i) - { - if(p[i]!=0) - tmp[i] = tolower(p[i]); - else - tmp[i] = 0; - } - tmp[3] = 0; - - /* Reject inf/nan */ - if (!c_strncasecmp(tmp, "inf", 3)) - return 1; - if (!c_strncasecmp(tmp, "nan", 3)) - return 1; - - /* Pass all other numbers which may still be invalid, but - * strtod() will catch them. */ - return 0; -} - -static void json_next_number_token(json_parse_t *json, json_token_t *token) -{ - char *endptr; - - token->type = T_NUMBER; - token->value.number = fpconv_strtod(json->ptr, &endptr); - if (json->ptr == endptr) - json_set_token_error(token, json, "invalid number"); - else - json->ptr = endptr; /* Skip the processed number */ - - return; -} - -/* Fills in the token struct. - * T_STRING will return a pointer to the json_parse_t temporary string - * T_ERROR will leave the json->ptr pointer at the error. - */ -static void json_next_token(json_parse_t *json, json_token_t *token) -{ - // const json_token_type_t *ch2token = json->cfg->ch2token; - int ch; - - /* Eat whitespace. */ - while (1) { - ch = (unsigned char)*(json->ptr); - token->type = ch2token(ch); - if (token->type != T_WHITESPACE) - break; - json->ptr++; - } - - /* Store location of new token. Required when throwing errors - * for unexpected tokens (syntax errors). */ - token->index = json->ptr - json->data; - - /* Don't advance the pointer for an error or the end */ - if (token->type == T_ERROR) { - json_set_token_error(token, json, "invalid token"); - return; - } - - if (token->type == T_END) { - return; - } - - /* Found a known single character token, advance index and return */ - if (token->type != T_UNKNOWN) { - json->ptr++; - return; - } - - /* Process characters which triggered T_UNKNOWN - * - * Must use strncmp() to match the front of the JSON string. - * JSON identifier must be lowercase. - * When strict_numbers if disabled, either case is allowed for - * Infinity/NaN (since we are no longer following the spec..) */ - if (ch == '"') { - json_next_string_token(json, token); - return; - } else if (ch == '-' || ('0' <= ch && ch <= '9')) { - if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) { - json_set_token_error(token, json, "invalid number"); - return; - } - json_next_number_token(json, token); - return; - } else if (!c_strncmp(json->ptr, "true", 4)) { - token->type = T_BOOLEAN; - token->value.boolean = 1; - json->ptr += 4; - return; - } else if (!c_strncmp(json->ptr, "false", 5)) { - token->type = T_BOOLEAN; - token->value.boolean = 0; - json->ptr += 5; - return; - } else if (!c_strncmp(json->ptr, "null", 4)) { - token->type = T_NULL; - json->ptr += 4; - return; - } else if (json->cfg->decode_invalid_numbers && - json_is_invalid_number(json)) { - /* When decode_invalid_numbers is enabled, only attempt to process - * numbers we know are invalid JSON (Inf, NaN, hex) - * This is required to generate an appropriate token error, - * otherwise all bad tokens will register as "invalid number" - */ - json_next_number_token(json, token); - return; - } - - /* Token starts with t/f/n but isn't recognised above. */ - json_set_token_error(token, json, "invalid token"); -} - -/* This function does not return. - * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. - * The only supported exception is the temporary parser string - * json->tmp struct. - * json and token should exist on the stack somewhere. - * luaL_error() will long_jmp and release the stack */ -static void json_throw_parse_error(lua_State *l, json_parse_t *json, - const char *exp, json_token_t *token) -{ - const char *found; - char temp[16]; // for now, 16-bytes is enough. - - strbuf_free(json->tmp); - - if (token->type == T_ERROR) - found = token->value.string; - else - { - found = json_token_type_name[token->type]; - int i; - for (i=0; i < 16; ++i) - { - temp[i] = byte_of_aligned_array(found, i); - if(temp[i]==0) break; - } - found = temp; - } - - /* Note: token->index is 0 based, display starting from 1 */ - luaL_error(l, "Expected %s but found %s at character %d", - exp, found, token->index + 1); -} - -static inline void json_decode_ascend(json_parse_t *json) -{ - json->current_depth--; -} - -static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) -{ - json->current_depth++; - - if (json->current_depth <= json->cfg->decode_max_depth && - lua_checkstack(l, slots)) { - return; - } - - strbuf_free(json->tmp); - luaL_error(l, "Found too many nested data structures (%d) at character %d", - json->current_depth, json->ptr - json->data); -} - -static void json_parse_object_context(lua_State *l, json_parse_t *json) -{ - json_token_t token; - - /* 3 slots required: - * .., table, key, value */ - json_decode_descend(l, json, 3); - - lua_newtable(l); - - json_next_token(json, &token); - - /* Handle empty objects */ - if (token.type == T_OBJ_END) { - json_decode_ascend(json); - return; - } - - while (1) { - if (token.type != T_STRING) - json_throw_parse_error(l, json, "object key string", &token); - - /* Push key */ - lua_pushlstring(l, token.value.string, token.string_len); - - json_next_token(json, &token); - if (token.type != T_COLON) - json_throw_parse_error(l, json, "colon", &token); - - /* Fetch value */ - json_next_token(json, &token); - json_process_value(l, json, &token); - - /* Set key = value */ - lua_rawset(l, -3); - - json_next_token(json, &token); - - if (token.type == T_OBJ_END) { - json_decode_ascend(json); - return; - } - - if (token.type != T_COMMA) - json_throw_parse_error(l, json, "comma or object end", &token); - - json_next_token(json, &token); - } -} - -/* Handle the array context */ -static void json_parse_array_context(lua_State *l, json_parse_t *json) -{ - json_token_t token; - int i; - - /* 2 slots required: - * .., table, value */ - json_decode_descend(l, json, 2); - - lua_newtable(l); - - json_next_token(json, &token); - - /* Handle empty arrays */ - if (token.type == T_ARR_END) { - json_decode_ascend(json); - return; - } - - for (i = 1; ; i++) { - json_process_value(l, json, &token); - lua_rawseti(l, -2, i); /* arr[i] = value */ - - json_next_token(json, &token); - - if (token.type == T_ARR_END) { - json_decode_ascend(json); - return; - } - - if (token.type != T_COMMA) - json_throw_parse_error(l, json, "comma or array end", &token); - - json_next_token(json, &token); - } -} - -/* Handle the "value" context */ -static void json_process_value(lua_State *l, json_parse_t *json, - json_token_t *token) -{ - switch (token->type) { - case T_STRING: - lua_pushlstring(l, token->value.string, token->string_len); - break;; - case T_NUMBER: - lua_pushnumber(l, token->value.number); - break;; - case T_BOOLEAN: - lua_pushboolean(l, token->value.boolean); - break;; - case T_OBJ_BEGIN: - json_parse_object_context(l, json); - break;; - case T_ARR_BEGIN: - json_parse_array_context(l, json); - break;; - case T_NULL: - /* In Lua, setting "t[k] = nil" will delete k from the table. - * Hence a NULL pointer lightuserdata object is used instead */ - lua_pushlightuserdata(l, NULL); - break;; - default: - json_throw_parse_error(l, json, "value", token); - } -} - -static int json_decode(lua_State *l) -{ - json_parse_t json; - json_token_t token; - size_t json_len; - - luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); - - json.cfg = json_fetch_config(l); - json.data = luaL_checklstring(l, 1, &json_len); - json.current_depth = 0; - json.ptr = json.data; - - /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) - * - * CJSON can support any simple data type, hence only the first - * character is guaranteed to be ASCII (at worst: '"'). This is - * still enough to detect whether the wrong encoding is in use. */ - if (json_len >= 2 && (!json.data[0] || !json.data[1])) - luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); - - /* Ensure the temporary buffer can hold the entire string. - * This means we no longer need to do length checks since the decoded - * string must be smaller than the entire json string */ - json.tmp = strbuf_new(json_len); - if(json.tmp == NULL){ - return luaL_error(l, "not enough memory"); - } - - json_next_token(&json, &token); - json_process_value(l, &json, &token); - - /* Ensure there is no more input left */ - json_next_token(&json, &token); - - if (token.type != T_END) - json_throw_parse_error(l, &json, "the end", &token); - - strbuf_free(json.tmp); - - return 1; -} - -/* ===== INITIALISATION ===== */ -#if 0 -#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 -/* Compatibility for Lua 5.1. - * - * luaL_setfuncs() is used to create a module table where the functions have - * json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */ -static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup) -{ - int i; - - luaL_checkstack(l, nup, "too many upvalues"); - for (; reg->name != NULL; reg++) { /* fill the table with given functions */ - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(l, -nup); - lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ - lua_setfield(l, -(nup + 2), reg->name); - } - lua_pop(l, nup); /* remove upvalues */ -} -#endif - -/* Call target function in protected mode with all supplied args. - * Assumes target function only returns a single non-nil value. - * Convert and return thrown errors as: nil, "error message" */ -static int json_protect_conversion(lua_State *l) -{ - int err; - - /* Deliberately throw an error for invalid arguments */ - luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); - - /* pcall() the function stored as upvalue(1) */ - lua_pushvalue(l, lua_upvalueindex(1)); - lua_insert(l, 1); - err = lua_pcall(l, 1, 1, 0); - if (!err) - return 1; - - if (err == LUA_ERRRUN) { - lua_pushnil(l); - lua_insert(l, -2); - return 2; - } - - /* Since we are not using a custom error handler, the only remaining - * errors are memory related */ - return luaL_error(l, "Memory allocation error in CJSON protected call"); -} - -/* Return cjson module table */ -static int lua_cjson_new(lua_State *l) -{ - /* Initialise number conversions */ - fpconv_init(); - - /* cjson module table */ - lua_newtable(l); - - /* Register functions with config data as upvalue */ - json_create_config(l); - luaL_setfuncs(l, reg, 1); - - /* Set cjson.null */ - lua_pushlightuserdata(l, NULL); - lua_setfield(l, -2, "null"); - - /* Set module name / version fields */ - lua_pushliteral(l, CJSON_MODNAME); - lua_setfield(l, -2, "_NAME"); - lua_pushliteral(l, CJSON_VERSION); - lua_setfield(l, -2, "_VERSION"); - - return 1; -} - -/* Return cjson.safe module table */ -static int lua_cjson_safe_new(lua_State *l) -{ - const char *func[] = { "decode", "encode", NULL }; - int i; - - lua_cjson_new(l); - - /* Fix new() method */ - lua_pushcfunction(l, lua_cjson_safe_new); - lua_setfield(l, -2, "new"); - - for (i = 0; func[i]; i++) { - lua_getfield(l, -1, func[i]); - lua_pushcclosure(l, json_protect_conversion, 1); - lua_setfield(l, -2, func[i]); - } - - return 1; -} - -int luaopen_cjson(lua_State *l) -{ - lua_cjson_new(l); - -#ifdef ENABLE_CJSON_GLOBAL - /* Register a global "cjson" table. */ - lua_pushvalue(l, -1); - lua_setglobal(l, CJSON_MODNAME); -#endif - - /* Return cjson table */ - return 1; -} - -int luaopen_cjson_safe(lua_State *l) -{ - lua_cjson_safe_new(l); - - /* Return cjson.safe table */ - return 1; -} -#endif - -// Module function map -static const LUA_REG_TYPE cjson_map[] = { - { LSTRKEY( "encode" ), LFUNCVAL( json_encode ) }, - { LSTRKEY( "decode" ), LFUNCVAL( json_decode ) }, -//{ LSTRKEY( "encode_sparse_array" ), LFUNCVAL( json_cfg_encode_sparse_array ) }, -//{ LSTRKEY( "encode_max_depth" ), LFUNCVAL( json_cfg_encode_max_depth ) }, -//{ LSTRKEY( "decode_max_depth" ), LFUNCVAL( json_cfg_decode_max_depth ) }, -//{ LSTRKEY( "encode_number_precision" ), LFUNCVAL( json_cfg_encode_number_precision ) }, -//{ LSTRKEY( "encode_keep_buffer" ), LFUNCVAL( json_cfg_encode_keep_buffer ) }, -//{ LSTRKEY( "encode_invalid_numbers" ), LFUNCVAL( json_cfg_encode_invalid_numbers ) }, -//{ LSTRKEY( "decode_invalid_numbers" ), LFUNCVAL( json_cfg_decode_invalid_numbers ) }, -//{ LSTRKEY( "new" ), LFUNCVAL( lua_cjson_new ) }, - { LNILKEY, LNILVAL } -}; - -int luaopen_cjson( lua_State *L ) -{ - /* Initialise number conversions */ - // fpconv_init(); // not needed for a specific cpu. - if(-1==cfg_init(&_cfg)){ - return luaL_error(L, "BUG: Unable to init config for cjson");; - } - return 0; -} - -NODEMCU_MODULE(CJSON, "cjson", cjson_map, luaopen_cjson); -/* vi:ai et sw=4 ts=4: - */ diff --git a/app/modules/sjson.c b/app/modules/sjson.c new file mode 100644 index 0000000000..5b4be1268a --- /dev/null +++ b/app/modules/sjson.c @@ -0,0 +1,1063 @@ +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lstring.h" + +#ifndef LOCAL_LUA +#include "module.h" +#include "c_string.h" +#include "c_math.h" +#include "c_limits.h" +#endif + +#define JSONSL_STATE_USER_FIELDS int lua_object_ref; int used_count; +#define JSONSL_NO_JPR + +#include "jsonsl.c" + +#define LUA_SJSONLIBNAME "sjson" + +#define DEFAULT_DEPTH 20 + +#define DBG_PRINTF(...) + +typedef struct { + jsonsl_t jsn; + int result_ref; + int hkey_ref; + int null_ref; + int metatable; + int pos_ref; + uint8_t complete; + const char *error; + lua_State *L; + size_t min_needed; + size_t min_available; + size_t buffer_len; + const char *buffer; // Points into buffer_ref + int buffer_ref; +} JSN_DATA; + +#define get_parent_object_ref() ((state->level == 1) ? data->result_ref : state[-1].lua_object_ref) +#define get_parent_object_used_count_pre_inc() ((state->level == 1) ? 1 : ++state[-1].used_count) + +static const char* get_state_buffer(JSN_DATA *ctx, struct jsonsl_state_st *state) +{ + size_t offset = state->pos_begin - ctx->min_available; + return ctx->buffer + offset; +} + +// The elem data is a ref + +static int error_callback(jsonsl_t jsn, + jsonsl_error_t err, + struct jsonsl_state_st *state, + char *at) +{ + JSN_DATA *data = (JSN_DATA *) jsn->data; + data->error = jsonsl_strerror(err); + + //fprintf(stderr, "Got error at pos %lu: %s\n", jsn->pos, jsonsl_strerror(err)); + return 0; +} + +static void +create_table(JSN_DATA *data) { + lua_newtable(data->L); + if (data->metatable != LUA_NOREF) { + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->metatable); + lua_setmetatable(data->L, -2); + } +} + +static void +create_new_element(jsonsl_t jsn, + jsonsl_action_t action, + struct jsonsl_state_st *state, + const char *buf) +{ + JSN_DATA *data = jsn->data; + + DBG_PRINTF("L%d: new action %d @ %d state->type %s\n", state->level, action, state->pos_begin, jsonsl_strtype(state->type)); + DBG_PRINTF("buf: '%s' ('%.10s')\n", buf, get_state_buffer(data, state)); + + state->lua_object_ref = LUA_NOREF; + + switch(state->type) { + case JSONSL_T_SPECIAL: + case JSONSL_T_STRING: + case JSONSL_T_HKEY: + break; + + case JSONSL_T_LIST: + case JSONSL_T_OBJECT: + create_table(data); + state->lua_object_ref = lua_ref(data->L, 1); + state->used_count = 0; + + lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref()); + if (data->hkey_ref == LUA_NOREF) { + // list, so append + lua_pushnumber(data->L, get_parent_object_used_count_pre_inc()); + DBG_PRINTF("Adding array element\n"); + } else { + // object, so + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref); + lua_unref(data->L, data->hkey_ref); + data->hkey_ref = LUA_NOREF; + DBG_PRINTF("Adding hash element\n"); + } + if (data->pos_ref != LUA_NOREF && state->level > 1) { + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref); + lua_pushnumber(data->L, state->level - 1); + lua_pushvalue(data->L, -3); // get the key + lua_settable(data->L, -3); + lua_pop(data->L, 1); + } + // At this point, the stack: + // top: index/hash key + // : table + + int want_value = 1; + // Invoke the checkpath method if possible + if (data->pos_ref != LUA_NOREF) { + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->metatable); + lua_getfield(data->L, -1, "checkpath"); + if (lua_type(data->L, -1) != LUA_TNIL) { + // Call with the new table and the path as arguments + lua_rawgeti(data->L, LUA_REGISTRYINDEX, state->lua_object_ref); + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref); + lua_call(data->L, 2, 1); + want_value = lua_toboolean(data->L, -1); + } + lua_pop(data->L, 2); // Discard the metatable and either the getfield result or retval + } + + if (want_value) { + lua_rawgeti(data->L, LUA_REGISTRYINDEX, state->lua_object_ref); + lua_settable(data->L, -3); + lua_pop(data->L, 1); // the table + } else { + lua_pop(data->L, 2); // the index and table + } + + break; + + default: + DBG_PRINTF("Unhandled type %c\n", state->type); + luaL_error(data->L, "Unhandled type"); + break; + } + + data->min_needed = state->pos_begin; +} + +static void push_number(JSN_DATA *data, struct jsonsl_state_st *state) { + lua_pushlstring(data->L, get_state_buffer(data, state), state->pos_cur - state->pos_begin); + LUA_NUMBER r = lua_tonumber(data->L, -1); + lua_pop(data->L, 1); + lua_pushnumber(data->L, r); +} + +static int fromhex(char c) { + if (c <= '9') { + return c & 0xf; + } + + return ((c - 'A' + 10) & 0xf); +} + +static void output_utf8(luaL_Buffer *buf, int c) { + char space[4]; + char *b = space; + + if (c<0x80) *b++=c; + else if (c<0x800) *b++=192+c/64, *b++=128+c%64; + else if (c-0xd800u<0x800) *b++ = '?'; + else if (c<0x10000) *b++=224+c/4096, *b++=128+c/64%64, *b++=128+c%64; + else if (c<0x110000) *b++=240+c/262144, *b++=128+c/4096%64, *b++=128+c/64%64, *b++=128+c%64; + else *b++ = '?'; + + luaL_addlstring(buf, space, b - space); +} + +static void push_string(JSN_DATA *data, struct jsonsl_state_st *state) { + luaL_Buffer b; + luaL_buffinit(data->L, &b); + int i; + const char *c = get_state_buffer(data, state) + 1; + for (i = 0; i < state->pos_cur - state->pos_begin - 1; i++) { + int nc = c[i]; + if (nc == '\\') { + i++; + nc = c[i] & 255; + switch (c[i]) { + case 'b': + nc = '\b'; + break; + case 'f': + nc = '\f'; + break; + case 'n': + nc = '\n'; + break; + case 'r': + nc = '\r'; + break; + case 't': + nc = '\t'; + break; + case 'u': + nc = fromhex(c[++i]) << 12; + nc += fromhex(c[++i]) << 8; + nc += fromhex(c[++i]) << 4; + nc += fromhex(c[++i]) ; + output_utf8(&b, nc); + continue; + } + } + luaL_putchar(&b, nc); + } + luaL_pushresult(&b); +} + +static void +cleanup_closing_element(jsonsl_t jsn, + jsonsl_action_t action, + struct jsonsl_state_st *state, + const char *at) +{ + JSN_DATA *data = (JSN_DATA *) jsn->data; + DBG_PRINTF( "L%d: cc action %d state->type %s\n", state->level, action, jsonsl_strtype(state->type)); + DBG_PRINTF( "buf (%d - %d): '%.*s'\n", state->pos_begin, state->pos_cur, state->pos_cur - state->pos_begin, get_state_buffer(data, state)); + DBG_PRINTF( "at: '%s'\n", at); + + switch (state->type) { + case JSONSL_T_HKEY: + push_string(data, state); + data->hkey_ref = lua_ref(data->L, 1); + break; + + case JSONSL_T_STRING: + lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref()); + if (data->hkey_ref == LUA_NOREF) { + // list, so append + lua_pushnumber(data->L, get_parent_object_used_count_pre_inc()); + } else { + // object, so + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref); + lua_unref(data->L, data->hkey_ref); + data->hkey_ref = LUA_NOREF; + } + push_string(data, state); + lua_settable(data->L, -3); + lua_pop(data->L, 1); + break; + + case JSONSL_T_SPECIAL: + DBG_PRINTF("Special flags = 0x%x\n", state->special_flags); + // need to deal with true/false/null + + if (state->special_flags & (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE|JSONSL_SPECIALf_NUMERIC|JSONSL_SPECIALf_NULL)) { + if (state->special_flags & JSONSL_SPECIALf_TRUE) { + lua_pushboolean(data->L, 1); + } else if (state->special_flags & JSONSL_SPECIALf_FALSE) { + lua_pushboolean(data->L, 0); + } else if (state->special_flags & JSONSL_SPECIALf_NULL) { + DBG_PRINTF("Outputting null\n"); + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->null_ref); + } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { + push_number(data, state); + } + + lua_rawgeti(data->L, LUA_REGISTRYINDEX, get_parent_object_ref()); + if (data->hkey_ref == LUA_NOREF) { + // list, so append + lua_pushnumber(data->L, get_parent_object_used_count_pre_inc()); + } else { + // object, so + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->hkey_ref); + lua_unref(data->L, data->hkey_ref); + data->hkey_ref = LUA_NOREF; + } + lua_pushvalue(data->L, -3); + lua_remove(data->L, -4); + lua_settable(data->L, -3); + lua_pop(data->L, 1); + } + break; + case JSONSL_T_OBJECT: + case JSONSL_T_LIST: + lua_unref(data->L, state->lua_object_ref); + state->lua_object_ref = LUA_NOREF; + if (data->pos_ref != LUA_NOREF) { + lua_rawgeti(data->L, LUA_REGISTRYINDEX, data->pos_ref); + lua_pushnumber(data->L, state->level); + lua_pushnil(data->L); + lua_settable(data->L, -3); + lua_pop(data->L, 1); + } + if (state->level == 1) { + data->complete = 1; + } + break; + } +} + +static int sjson_decoder_int(lua_State *L, int argno) { + int nlevels = DEFAULT_DEPTH; + + if (lua_type(L, argno) == LUA_TTABLE) { + lua_getfield(L, argno, "depth"); + nlevels = lua_tointeger(L, argno); + if (nlevels == 0) { + nlevels = DEFAULT_DEPTH; + } + if (nlevels < 4) { + nlevels = 4; + } + if (nlevels > 1000) { + nlevels = 1000; + } + lua_pop(L, 1); + } + + JSN_DATA *data = (JSN_DATA *) lua_newuserdata(L, sizeof(JSN_DATA) + jsonsl_get_size(nlevels)); + // + // Associate its metatable + luaL_getmetatable(L, "sjson.decoder"); + lua_setmetatable(L, -2); + + jsonsl_t jsn = jsonsl_init((jsonsl_t) (data + 1), nlevels); + int i; + + for (i = 0; i < jsn->levels_max; i++) { + jsn->stack[i].lua_object_ref = LUA_NOREF; + } + data->jsn = jsn; + data->result_ref = LUA_NOREF; + + data->null_ref = LUA_REFNIL; + data->metatable = LUA_NOREF; + data->hkey_ref = LUA_NOREF; + data->pos_ref = LUA_NOREF; + data->buffer_ref = LUA_NOREF; + data->complete = 0; + data->error = NULL; + data->L = L; + data->buffer_len = 0; + + data->min_needed = data->min_available = jsn->pos; + + lua_pushlightuserdata(L, 0); + data->null_ref = lua_ref(L, 1); + + // This may throw... + lua_newtable(L); + data->result_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + if (lua_type(L, argno) == LUA_TTABLE) { + luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref); + data->null_ref = LUA_NOREF; + lua_getfield(L, argno, "null"); + data->null_ref = lua_ref(L, 1); + + lua_getfield(L, argno, "metatable"); + lua_pushvalue(L, -1); + data->metatable = lua_ref(L, 1); + + if (lua_type(L, -1) != LUA_TNIL) { + lua_getfield(L, -1, "checkpath"); + if (lua_type(L, -1) != LUA_TNIL) { + lua_newtable(L); + data->pos_ref = lua_ref(L, 1); + } + lua_pop(L, 1); // Throw away the checkpath value + } + lua_pop(L, 1); // Throw away the metatable + } + + jsonsl_enable_all_callbacks(data->jsn); + + jsn->action_callback = NULL; + jsn->action_callback_PUSH = create_new_element; + jsn->action_callback_POP = cleanup_closing_element; + jsn->error_callback = error_callback; + jsn->data = data; + jsn->max_callback_level = nlevels; + + return 1; +} + +static int sjson_decoder(lua_State *L) { + return sjson_decoder_int(L, 1); +} + +static int sjson_decoder_result_int(lua_State *L, JSN_DATA *data) { + if (!data->complete) { + luaL_error(L, "decode not complete"); + } + + lua_rawgeti(L, LUA_REGISTRYINDEX, data->result_ref); + lua_rawgeti(L, -1, 1); + lua_remove(L, -2); + + return 1; +} + +static int sjson_decoder_result(lua_State *L) { + JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, 1, "sjson.decoder"); + + return sjson_decoder_result_int(L, data); +} + + +static void sjson_free_working_data(lua_State *L, JSN_DATA *data) { + jsonsl_t jsn = data->jsn; + int i; + + for (i = 0; i < jsn->levels_max; i++) { + luaL_unref(L, LUA_REGISTRYINDEX, jsn->stack[i].lua_object_ref); + jsn->stack[i].lua_object_ref = LUA_NOREF; + } + + luaL_unref(L, LUA_REGISTRYINDEX, data->metatable); + data->metatable = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, data->hkey_ref); + data->hkey_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref); + data->null_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, data->pos_ref); + data->pos_ref = LUA_NOREF; + luaL_unref(L, LUA_REGISTRYINDEX, data->buffer_ref); + data->buffer_ref = LUA_NOREF; +} + +static int sjson_decoder_write_int(lua_State *L, int udata_pos, int string_pos) { + JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, udata_pos, "sjson.decoder"); + size_t len; + + const char *str = luaL_checklstring(L, string_pos, &len); + + if (data->error) { + luaL_error(L, "JSON parse error: previous call"); + } + + if (!data->complete) { + data->L = L; + + // Merge into any existing buffer and deal with discard + if (data->buffer_ref != LUA_NOREF) { + luaL_Buffer b; + luaL_buffinit(L, &b); + + lua_rawgeti(L, LUA_REGISTRYINDEX, data->buffer_ref); + size_t prev_len; + const char *prev_buffer = luaL_checklstring(L, -1, &prev_len); + lua_pop(L, 1); // But string still referenced so it cannot move + int discard = data->min_needed - data->min_available; + prev_buffer += discard; + prev_len -= discard; + if (prev_len > 0) { + luaL_addlstring(&b, prev_buffer, prev_len); + } + data->min_available += discard; + + luaL_unref(L, LUA_REGISTRYINDEX, data->buffer_ref); + data->buffer_ref = LUA_NOREF; + + lua_pushvalue(L, string_pos); + luaL_addvalue(&b); + luaL_pushresult(&b); + } else { + lua_pushvalue(L, string_pos); + } + + size_t blen; + data->buffer = luaL_checklstring(L, -1, &blen); + data->buffer_len = blen; + data->buffer_ref = lua_ref(L, 1); + + jsonsl_feed(data->jsn, str, len); + + if (data->error) { + luaL_error(L, "JSON parse error: %s", data->error); + } + } + + if (data->complete) { + // We no longer need the buffer + sjson_free_working_data(L, data); + + return sjson_decoder_result_int(L, data); + } + + return 0; +} + +static int sjson_decoder_write(lua_State *L) { + return sjson_decoder_write_int(L, 1, 2); +} + +static int sjson_decode(lua_State *L) { + int push_count = sjson_decoder_int(L, 2); + if (push_count != 1) { + luaL_error(L, "Internal error in sjson.deocder"); + } + + luaL_checkudata(L, -1, "sjson.decoder"); + + push_count = sjson_decoder_write_int(L, -1, 1); + + if (push_count != 1) { + luaL_error(L, "Incomplete JSON object passed to sjson.decode"); + } + + // Now we have two items on the stack -- the udata and the result + lua_remove(L, -2); + + return 1; +} + +static int sjson_decoder_destructor(lua_State *L) { + JSN_DATA *data = (JSN_DATA *)luaL_checkudata(L, 1, "sjson.decoder"); + + sjson_free_working_data(L, data); + + data->jsn = NULL; + + luaL_unref(L, LUA_REGISTRYINDEX, data->result_ref); + data->result_ref = LUA_NOREF; + + DBG_PRINTF("Destructor called\n"); + + return 0; +} +// +//--------------------------------- ENCODER BELOW +// +// +// +//#undef DBG_PRINTF +//#define DBG_PRINTF printf +typedef struct { + int lua_object_ref; + // for arrays + // 0 -> [ + // 1 -> first element + // 2 -> , + // 3 -> second element + // 4 -> ] + // for objects + // 0 -> { firstkey : + // 1 -> first value + // 2 -> , secondkey : + // 3 -> second value + // 4 -> } + short offset; + // -1 for objects + // 0 -> n maximum integer key = n + short size; + int lua_key_ref; +} ENC_DATA_STATE; + +typedef struct { + ENC_DATA_STATE *stack; + int nlevels; + int level; + int current_str_ref; + int null_ref; + int offset; + +} ENC_DATA; + +static int sjson_encoder_get_table_size(lua_State *L, int argno) { + // Returns -1 for object, otherwise the maximum integer key value found. + lua_pushvalue(L, argno); + // stack now contains: -1 => table + lua_pushnil(L); + // stack now contains: -1 => nil; -2 => table + // + int maxkey = 0; + + while (lua_next(L, -2)) { + lua_pop(L, 1); + // stack now contains: -1 => key; -2 => table + if (lua_type(L, -1) == LUA_TNUMBER) { + int val = lua_tointeger(L, -1); + if (val > maxkey) { + maxkey = val; + } else if (val <= 0) { + maxkey = -1; + lua_pop(L, 1); + break; + } + } else { + maxkey = -1; + lua_pop(L, 1); + break; + } + } + + lua_pop(L, 1); + + return maxkey; +} + +static void enc_pop_stack(lua_State *L, ENC_DATA *data) { + if (data->level < 0) { + luaL_error(L, "encoder stack underflow"); + } + ENC_DATA_STATE *state = &data->stack[data->level]; + + lua_unref(L, state->lua_object_ref); + state->lua_object_ref = LUA_NOREF; + lua_unref(L, state->lua_key_ref); + state->lua_key_ref = LUA_REFNIL; + data->level--; +} + +static void enc_push_stack(lua_State *L, ENC_DATA *data, int argno) { + if (++data->level >= data->nlevels) { + luaL_error(L, "encoder stack overflow"); + } + lua_pushvalue(L, argno); + ENC_DATA_STATE *state = &data->stack[data->level]; + state->lua_object_ref = lua_ref(L, 1); + state->size = sjson_encoder_get_table_size(L, argno); + state->offset = 0; // We haven't started on this one yet +} + +static int sjson_encoder(lua_State *L) { + int nlevels = DEFAULT_DEPTH; + int argno = 1; + + // Validate first arg is a table + luaL_checktype(L, argno++, LUA_TTABLE); + + if (lua_type(L, argno) == LUA_TTABLE) { + lua_getfield(L, argno, "depth"); + nlevels = lua_tointeger(L, argno); + if (nlevels == 0) { + nlevels = DEFAULT_DEPTH; + } + if (nlevels < 4) { + nlevels = 4; + } + if (nlevels > 1000) { + nlevels = 1000; + } + lua_pop(L, 1); + } + + ENC_DATA *data = (ENC_DATA *) lua_newuserdata(L, sizeof(ENC_DATA) + nlevels * sizeof(ENC_DATA_STATE)); + + // Associate its metatable + luaL_getmetatable(L, "sjson.encoder"); + lua_setmetatable(L, -2); + + data->nlevels = nlevels; + data->level = -1; + data->stack = (ENC_DATA_STATE *) (data + 1); + data->current_str_ref = LUA_NOREF; + int i; + for (i = 0; i < nlevels; i++) { + data->stack[i].lua_object_ref = LUA_NOREF; + data->stack[i].lua_key_ref = LUA_REFNIL; + } + enc_push_stack(L, data, 1); + + data->null_ref = LUA_REFNIL; + + if (lua_type(L, argno) == LUA_TTABLE) { + luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref); + data->null_ref = LUA_NOREF; + lua_getfield(L, argno, "null"); + data->null_ref = lua_ref(L, 1); + } + + return 1; +} + +static void encode_lua_object(lua_State *L, ENC_DATA *data, int argno, const char *prefix, const char *suffix) { + luaL_Buffer b; + luaL_buffinit(L, &b); + + luaL_addstring(&b, prefix); + + int type = lua_type(L, argno); + + if (type == LUA_TSTRING) { + // Check to see if it is the NULL value + if (data->null_ref != LUA_REFNIL) { + lua_rawgeti(L, LUA_REGISTRYINDEX, data->null_ref); + if (lua_equal(L, -1, -2)) { + type = LUA_TNIL; + } + lua_pop(L, 1); + } + } + + switch (type) { + default: + luaL_error(L, "Cannot encode type %d", type); + break; + + case LUA_TLIGHTUSERDATA: + case LUA_TNIL: + luaL_addstring(&b, "null"); + break; + + case LUA_TBOOLEAN: + luaL_addstring(&b, lua_toboolean(L, argno) ? "true" : "false"); + break; + + case LUA_TNUMBER: + { + lua_pushvalue(L, argno); + size_t len; + const char *str = lua_tolstring(L, -1, &len); + char value[len + 1]; + strcpy(value, str); + lua_pop(L, 1); + luaL_addstring(&b, value); + break; + } + + case LUA_TSTRING: + { + luaL_addchar(&b, '"'); + size_t len; + const char *str = lua_tolstring(L, argno, &len); + while (len > 0) { + if ((*str & 0xff) < 0x20) { + char value[8]; + value[0] = '\\'; + + char *d = value + 1; + + switch(*str) { + case '\f': + *d++ = 'f'; + break; + case '\n': + *d++ = 'n'; + break; + case '\t': + *d++ = 't'; + break; + case '\r': + *d++ = 'r'; + break; + case '\b': + *d++ = 'b'; + break; + + default: + *d++ = 'u'; + *d++ = '0'; + *d++ = '0'; + *d++ = "0123456789abcdef"[(*str >> 4) & 0xf]; + *d++ = "0123456789abcdef"[(*str ) & 0xf]; + break; + + } + *d = '\0'; + luaL_addstring(&b, value); + } else { + luaL_addchar(&b, *str); + } + str++; + len--; + } + luaL_addchar(&b, '"'); + break; + } + } + + luaL_addstring(&b, suffix); + luaL_pushresult(&b); +} + +static int sjson_encoder_next_value_is_table(lua_State *L) { + int count = 10; + + while ((lua_type(L, -1) == LUA_TFUNCTION +#ifdef LUA_TLIGHTFUNCTION + || lua_type(L, -1) == LUA_TLIGHTFUNCTION +#endif + ) && count-- > 0) { + // call it and use the return value + lua_call(L, 0, 1); // Expecting replacement value + } + + return (lua_type(L, -1) == LUA_TTABLE); +} + +static void sjson_encoder_make_next_chunk(lua_State *L, ENC_DATA *data) { + if (data->level < 0) { + return; + } + + luaL_Buffer b; + luaL_buffinit(L, &b); + + // Ending condition + while (data->level >= 0 && !b.lvl) { + ENC_DATA_STATE *state = &data->stack[data->level]; + + int finished = 0; + + if (state->size >= 0) { + if (state->offset == 0) { + // start of object or whatever + luaL_addchar(&b, '['); + } + if (state->offset == state->size << 1) { + luaL_addchar(&b, ']'); + finished = 1; + } else if ((state->offset & 1) == 0) { + if (state->offset > 0) { + luaL_addchar(&b, ','); + } + } else { + // output the value + lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_object_ref); + lua_rawgeti(L, -1, (state->offset >> 1) + 1); + if (sjson_encoder_next_value_is_table(L)) { + enc_push_stack(L, data, -1); + lua_pop(L, 2); + state->offset++; + continue; + } + encode_lua_object(L, data, -1, "", ""); + lua_remove(L, -2); + lua_remove(L, -2); + luaL_addvalue(&b); + } + + state->offset++; + } else { + lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_object_ref); + // stack now contains: -1 => table + lua_rawgeti(L, LUA_REGISTRYINDEX, state->lua_key_ref); + // stack now contains: -1 => nil or key; -2 => table + + if (lua_next(L, -2)) { + // save the key + if (state->offset & 1) { + lua_unref(L, state->lua_key_ref); + state->lua_key_ref = LUA_NOREF; + // Duplicate the key + lua_pushvalue(L, -2); + state->lua_key_ref = lua_ref(L, 1); + } + + if ((state->offset & 1) == 0) { + // copy the key so that lua_tostring does not modify the original + lua_pushvalue(L, -2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + // key + lua_tostring(L, -1); + encode_lua_object(L, data, -1, state->offset ? "," : "{", ":"); + lua_remove(L, -2); + lua_remove(L, -2); + lua_remove(L, -2); + lua_remove(L, -2); + } else { + if (sjson_encoder_next_value_is_table(L)) { + enc_push_stack(L, data, -1); + lua_pop(L, 3); + state->offset++; + continue; + } + encode_lua_object(L, data, -1, "", ""); + lua_remove(L, -2); + lua_remove(L, -2); + lua_remove(L, -2); + } + luaL_addvalue(&b); + } else { + lua_pop(L, 1); + // We have got to the end + luaL_addchar(&b, '}'); + finished = 1; + } + + state->offset++; + } + + if (finished) { + enc_pop_stack(L, data); + } + } + luaL_pushresult(&b); + data->current_str_ref = lua_ref(L, 1); + data->offset = 0; +} + +static int sjson_encoder_read_int(lua_State *L, ENC_DATA *data, int readsize) { + luaL_Buffer b; + luaL_buffinit(L, &b); + + size_t len; + + do { + // Fill the buffer with (up to) readsize characters + if (data->current_str_ref != LUA_NOREF) { + // this is not allowed + lua_rawgeti(L, LUA_REGISTRYINDEX, data->current_str_ref); + const char *str = lua_tolstring(L, -1, &len); + + lua_pop(L, 1); // Note that we still have the string referenced so it can't go away + + int amnt = len - data->offset;; + if (amnt > readsize) { + amnt = readsize; + } + luaL_addlstring(&b, str + data->offset, amnt); + data->offset += amnt; + readsize -= amnt; + + if (data->offset == len) { + lua_unref(L, data->current_str_ref); + data->current_str_ref = LUA_NOREF; + } + } + + if (readsize > 0) { + // Make the next chunk + sjson_encoder_make_next_chunk(L, data); + } + + } while (readsize > 0 && data->current_str_ref != LUA_NOREF); + + luaL_pushresult(&b); + + lua_tolstring(L, -1, &len); + + if (len == 0) { + // we have got to the end + lua_pop(L, 1); + return 0; + } + + return 1; +} + +static int sjson_encoder_read(lua_State *L) { + ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, 1, "sjson.encoder"); + + int readsize = 1024; + if (lua_type(L, 2) == LUA_TNUMBER) { + readsize = lua_tointeger(L, 2); + if (readsize < 1) { + readsize = 1; + } + } + + return sjson_encoder_read_int(L, data, readsize); +} + +static int sjson_encode(lua_State *L) { + sjson_encoder(L); + + ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, -1, "sjson.encoder"); + + int rc = sjson_encoder_read_int(L, data, 1000000); + + lua_remove(L, -(rc + 1)); + + return rc; +} + +static int sjson_encoder_destructor(lua_State *L) { + ENC_DATA *data = (ENC_DATA *)luaL_checkudata(L, 1, "sjson.encoder"); + + int i; + + for (i = 0; i < data->nlevels; i++) { + luaL_unref(L, LUA_REGISTRYINDEX, data->stack[i].lua_object_ref); + luaL_unref(L, LUA_REGISTRYINDEX, data->stack[i].lua_key_ref); + } + + luaL_unref(L, LUA_REGISTRYINDEX, data->null_ref); + luaL_unref(L, LUA_REGISTRYINDEX, data->current_str_ref); + + DBG_PRINTF("Destructor called\n"); + + return 0; +} + +#ifdef LOCAL_LUA +static const luaL_Reg sjson_encoder_map[] = { + { "read", sjson_encoder_read }, + { "__gc", sjson_encoder_destructor }, + { NULL, NULL } +}; + +static const luaL_Reg sjson_decoder_map[] = { + { "write", sjson_decoder_write }, + { "result", sjson_decoder_result }, + { "__gc", sjson_decoder_destructor }, + { NULL, NULL } +}; + +static const luaL_Reg sjsonlib[] = { + { "decode", sjson_decode }, + { "decoder", sjson_decoder }, + { "encode", sjson_encode }, + { "encoder", sjson_encoder }, + {NULL, NULL} +}; +#else +static const LUA_REG_TYPE sjson_encoder_map[] = { + { LSTRKEY( "read" ), LFUNCVAL( sjson_encoder_read ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( sjson_encoder_destructor ) }, + { LSTRKEY( "__index" ), LROVAL( sjson_encoder_map ) }, + { LNILKEY, LNILVAL } +}; + +static const LUA_REG_TYPE sjson_decoder_map[] = { + { LSTRKEY( "write" ), LFUNCVAL( sjson_decoder_write ) }, + { LSTRKEY( "result" ), LFUNCVAL( sjson_decoder_result ) }, + { LSTRKEY( "__gc" ), LFUNCVAL( sjson_decoder_destructor ) }, + { LSTRKEY( "__index" ), LROVAL( sjson_decoder_map ) }, + { LNILKEY, LNILVAL } +}; + +static const LUA_REG_TYPE sjson_map[] = { + { LSTRKEY( "encode" ), LFUNCVAL( sjson_encode ) }, + { LSTRKEY( "decode" ), LFUNCVAL( sjson_decode ) }, + { LSTRKEY( "encoder" ), LFUNCVAL( sjson_encoder ) }, + { LSTRKEY( "decoder" ), LFUNCVAL( sjson_decoder ) }, + { LSTRKEY( "NULL" ), LUDATA( 0 ) }, + { LNILKEY, LNILVAL } +}; +#endif + +LUALIB_API int luaopen_sjson (lua_State *L) { +#ifdef LOCAL_LUA + luaL_register(L, LUA_SJSONLIBNAME, sjsonlib); + lua_getglobal(L, LUA_SJSONLIBNAME); + lua_pushstring(L, "NULL"); + lua_pushlightuserdata(L, 0); + lua_settable(L, -3); + lua_pop(L, 1); + luaL_newmetatable(L, "sjson.encoder"); + luaL_register(L, NULL, sjson_encoder_map); + lua_setfield(L, -1, "__index"); + luaL_newmetatable(L, "sjson.decoder"); + luaL_register(L, NULL, sjson_decoder_map); + lua_setfield(L, -1, "__index"); +#else + luaL_rometatable(L, "sjson.decoder", (void *)sjson_decoder_map); + luaL_rometatable(L, "sjson.encoder", (void *)sjson_encoder_map); +#endif + return 1; +} + +#ifndef LOCAL_LUA +NODEMCU_MODULE(SJSON, "sjson", sjson_map, luaopen_sjson); +#endif diff --git a/app/sjson/jsonsl.c b/app/sjson/jsonsl.c new file mode 100644 index 0000000000..9d2c10727e --- /dev/null +++ b/app/sjson/jsonsl.c @@ -0,0 +1,1661 @@ +/* Copyright (C) 2012-2015 Mark Nunberg. + * + * See included LICENSE file for license details. + */ + +#include "jsonsl.h" +#include +#include +#include + +#ifdef JSONSL_USE_METRICS +#define XMETRICS \ + X(STRINGY_INSIGNIFICANT) \ + X(STRINGY_SLOWPATH) \ + X(ALLOWED_WHITESPACE) \ + X(QUOTE_FASTPATH) \ + X(SPECIAL_FASTPATH) \ + X(SPECIAL_WSPOP) \ + X(SPECIAL_SLOWPATH) \ + X(GENERIC) \ + X(STRUCTURAL_TOKEN) \ + X(SPECIAL_SWITCHFIRST) \ + X(STRINGY_CATCH) \ + X(NUMBER_FASTPATH) \ + X(ESCAPES) \ + X(TOTAL) \ + +struct jsonsl_metrics_st { +#define X(m) \ + unsigned long metric_##m; + XMETRICS +#undef X +}; + +static struct jsonsl_metrics_st GlobalMetrics = { 0 }; +static unsigned long GenericCounter[0x100] = { 0 }; +static unsigned long StringyCatchCounter[0x100] = { 0 }; + +#define INCR_METRIC(m) \ + GlobalMetrics.metric_##m++; + +#define INCR_GENERIC(c) \ + INCR_METRIC(GENERIC); \ + GenericCounter[c]++; \ + +#define INCR_STRINGY_CATCH(c) \ + INCR_METRIC(STRINGY_CATCH); \ + StringyCatchCounter[c]++; + +JSONSL_API +void jsonsl_dump_global_metrics(void) +{ + int ii; + printf("JSONSL Metrics:\n"); +#define X(m) \ + printf("\t%-30s %20lu (%0.2f%%)\n", #m, GlobalMetrics.metric_##m, \ + (float)((float)(GlobalMetrics.metric_##m/(float)GlobalMetrics.metric_TOTAL)) * 100); + XMETRICS +#undef X + printf("Generic Characters:\n"); + for (ii = 0; ii < 0xff; ii++) { + if (GenericCounter[ii]) { + printf("\t[ %c ] %lu\n", ii, GenericCounter[ii]); + } + } + printf("Weird string loop\n"); + for (ii = 0; ii < 0xff; ii++) { + if (StringyCatchCounter[ii]) { + printf("\t[ %c ] %lu\n", ii, StringyCatchCounter[ii]); + } + } +} + +#else +#define INCR_METRIC(m) +#define INCR_GENERIC(c) +#define INCR_STRINGY_CATCH(c) +JSONSL_API +void jsonsl_dump_global_metrics(void) { } +#endif /* JSONSL_USE_METRICS */ + +#define CASE_DIGITS \ +case '1': \ +case '2': \ +case '3': \ +case '4': \ +case '5': \ +case '6': \ +case '7': \ +case '8': \ +case '9': \ +case '0': + +static unsigned extract_special(unsigned); +static int is_special_end(unsigned); +static int is_allowed_whitespace(unsigned); +static int is_allowed_escape(unsigned); +static int is_simple_char(unsigned); +static char get_escape_equiv(unsigned); + +JSONSL_API +size_t jsonsl_get_size(int nlevels) +{ + return sizeof (struct jsonsl_st) + ( (nlevels-1) * sizeof (struct jsonsl_state_st)) ; +} + +JSONSL_API +jsonsl_t jsonsl_init(jsonsl_t jsn, int nlevels) +{ + unsigned int ii; + + memset(jsn, 0, jsonsl_get_size(nlevels)); + jsn->levels_max = nlevels; + jsn->max_callback_level = -1; + jsonsl_reset(jsn); + for (ii = 0; ii < jsn->levels_max; ii++) { + jsn->stack[ii].level = ii; + } + return jsn; +} + +JSONSL_API +jsonsl_t jsonsl_new(int nlevels) +{ + struct jsonsl_st *jsn = (struct jsonsl_st *) + calloc(1, jsonsl_get_size(nlevels)); + + if (jsn) { + jsonsl_init(jsn, nlevels); + } + return jsn; +} + +JSONSL_API +void jsonsl_reset(jsonsl_t jsn) +{ + jsn->tok_last = 0; + jsn->can_insert = 1; + jsn->pos = 0; + jsn->level = 0; + jsn->stopfl = 0; + jsn->in_escape = 0; + jsn->expecting = 0; +} + +JSONSL_API +void jsonsl_destroy(jsonsl_t jsn) +{ + if (jsn) { + free(jsn); + } +} + + +#define FASTPARSE_EXHAUSTED 1 +#define FASTPARSE_BREAK 0 + +/* + * This function is meant to accelerate string parsing, reducing the main loop's + * check if we are indeed a string. + * + * @param jsn the parser + * @param[in,out] bytes_p A pointer to the current buffer (i.e. current position) + * @param[in,out] nbytes_p A pointer to the current size of the buffer + * @return true if all bytes have been exhausted (and thus the main loop can + * return), false if a special character was examined which requires greater + * examination. + */ +static int +jsonsl__str_fastparse(jsonsl_t jsn, + const jsonsl_uchar_t **bytes_p, size_t *nbytes_p) +{ + const jsonsl_uchar_t *bytes = *bytes_p; + const jsonsl_uchar_t *end; + for (end = bytes + *nbytes_p; bytes != end; bytes++) { + if ( +#ifdef JSONSL_USE_WCHAR + *bytes >= 0x100 || +#endif /* JSONSL_USE_WCHAR */ + (is_simple_char(*bytes))) { + INCR_METRIC(TOTAL); + INCR_METRIC(STRINGY_INSIGNIFICANT); + } else { + /* Once we're done here, re-calculate the position variables */ + jsn->pos += (bytes - *bytes_p); + *nbytes_p -= (bytes - *bytes_p); + *bytes_p = bytes; + return FASTPARSE_BREAK; + } + } + + /* Once we're done here, re-calculate the position variables */ + jsn->pos += (bytes - *bytes_p); + return FASTPARSE_EXHAUSTED; +} + +/* Functions exactly like str_fastparse, except it also accepts a 'state' + * argument, since the number's value is updated in the state. */ +static int +jsonsl__num_fastparse(jsonsl_t jsn, + const jsonsl_uchar_t **bytes_p, size_t *nbytes_p, + struct jsonsl_state_st *state) +{ + int exhausted = 1; + size_t nbytes = *nbytes_p; + const jsonsl_uchar_t *bytes = *bytes_p; + + for (; nbytes; nbytes--, bytes++) { + jsonsl_uchar_t c = *bytes; + if (isdigit(c)) { + INCR_METRIC(TOTAL); + INCR_METRIC(NUMBER_FASTPATH); + state->nelem = (state->nelem * 10) + (c - 0x30); + } else { + exhausted = 0; + break; + } + } + jsn->pos += (*nbytes_p - nbytes); + if (exhausted) { + return FASTPARSE_EXHAUSTED; + } + *nbytes_p = nbytes; + *bytes_p = bytes; + return FASTPARSE_BREAK; +} + +JSONSL_API +void +jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) +{ + +#define INVOKE_ERROR(eb) \ + if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \ + goto GT_AGAIN; \ + } \ + return; + +#define STACK_PUSH \ + if (jsn->level >= (levels_max-1)) { \ + jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \ + return; \ + } \ + state = jsn->stack + (++jsn->level); \ + state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \ + state->pos_begin = jsn->pos; + +#define STACK_POP_NOPOS \ + state->pos_cur = jsn->pos; \ + state = jsn->stack + (--jsn->level); + + +#define STACK_POP \ + STACK_POP_NOPOS; \ + state->pos_cur = jsn->pos; + +#define CALLBACK_AND_POP_NOPOS(T) \ + state->pos_cur = jsn->pos; \ + DO_CALLBACK(T, POP); \ + state->nescapes = 0; \ + state = jsn->stack + (--jsn->level); + +#define CALLBACK_AND_POP(T) \ + CALLBACK_AND_POP_NOPOS(T); \ + state->pos_cur = jsn->pos; + +#define SPECIAL_POP \ + CALLBACK_AND_POP(SPECIAL); \ + jsn->expecting = 0; \ + jsn->tok_last = 0; \ + +#define CUR_CHAR (*(jsonsl_uchar_t*)c) + +#define DO_CALLBACK(T, action) \ + if (jsn->call_##T && \ + jsn->max_callback_level > state->level && \ + state->ignore_callback == 0) { \ + \ + if (jsn->action_callback_##action) { \ + jsn->action_callback_##action(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ + } else if (jsn->action_callback) { \ + jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ + } \ + if (jsn->stopfl) { return; } \ + } + + /** + * Verifies that we are able to insert the (non-string) item into a hash. + */ +#define ENSURE_HVAL \ + if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \ + INVOKE_ERROR(HKEY_EXPECTED); \ + } + +#define VERIFY_SPECIAL(lit) \ + if (CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \ + INVOKE_ERROR(SPECIAL_EXPECTED); \ + } + +#define STATE_SPECIAL_LENGTH \ + (state)->nescapes + +#define IS_NORMAL_NUMBER \ + ((state)->special_flags == JSONSL_SPECIALf_UNSIGNED || \ + (state)->special_flags == JSONSL_SPECIALf_SIGNED) + +#define STATE_NUM_LAST jsn->tok_last + +#define CONTINUE_NEXT_CHAR() continue + + const jsonsl_uchar_t *c = (jsonsl_uchar_t*)bytes; + size_t levels_max = jsn->levels_max; + struct jsonsl_state_st *state = jsn->stack + jsn->level; + jsn->base = bytes; + + for (; nbytes; nbytes--, jsn->pos++, c++) { + unsigned state_type; + INCR_METRIC(TOTAL); + + GT_AGAIN: + state_type = state->type; + /* Most common type is typically a string: */ + if (state_type & JSONSL_Tf_STRINGY) { + /* Special escape handling for some stuff */ + if (jsn->in_escape) { + jsn->in_escape = 0; + if (!is_allowed_escape(CUR_CHAR)) { + INVOKE_ERROR(ESCAPE_INVALID); + } else if (CUR_CHAR == 'u') { + DO_CALLBACK(UESCAPE, UESCAPE); + if (jsn->return_UESCAPE) { + return; + } + } + CONTINUE_NEXT_CHAR(); + } + + if (jsonsl__str_fastparse(jsn, &c, &nbytes) == + FASTPARSE_EXHAUSTED) { + /* No need to readjust variables as we've exhausted the iterator */ + return; + } else { + if (CUR_CHAR == '"') { + goto GT_QUOTE; + } else if (CUR_CHAR == '\\') { + goto GT_ESCAPE; + } else { + INVOKE_ERROR(WEIRD_WHITESPACE); + } + } + INCR_METRIC(STRINGY_SLOWPATH); + + } else if (state_type == JSONSL_T_SPECIAL) { + /* Fast track for signed/unsigned */ + if (IS_NORMAL_NUMBER) { + if (jsonsl__num_fastparse(jsn, &c, &nbytes, state) == + FASTPARSE_EXHAUSTED) { + return; + } else { + goto GT_SPECIAL_NUMERIC; + } + } else if (state->special_flags == JSONSL_SPECIALf_DASH) { + if (!isdigit(CUR_CHAR)) { + INVOKE_ERROR(INVALID_NUMBER); + } + + if (CUR_CHAR == '0') { + state->special_flags = JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED; + } else if (isdigit(CUR_CHAR)) { + state->special_flags = JSONSL_SPECIALf_SIGNED; + state->nelem = CUR_CHAR - 0x30; + } else { + INVOKE_ERROR(INVALID_NUMBER); + } + CONTINUE_NEXT_CHAR(); + + } else if (state->special_flags == JSONSL_SPECIALf_ZERO) { + if (isdigit(CUR_CHAR)) { + /* Following a zero! */ + INVOKE_ERROR(INVALID_NUMBER); + } + /* Unset the 'zero' flag: */ + if (state->special_flags & JSONSL_SPECIALf_SIGNED) { + state->special_flags = JSONSL_SPECIALf_SIGNED; + } else { + state->special_flags = JSONSL_SPECIALf_UNSIGNED; + } + goto GT_SPECIAL_NUMERIC; + } + + if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { + GT_SPECIAL_NUMERIC: + switch (CUR_CHAR) { + CASE_DIGITS + STATE_NUM_LAST = '1'; + CONTINUE_NEXT_CHAR(); + + case '.': + if (state->special_flags & JSONSL_SPECIALf_FLOAT) { + INVOKE_ERROR(INVALID_NUMBER); + } + state->special_flags |= JSONSL_SPECIALf_FLOAT; + STATE_NUM_LAST = '.'; + CONTINUE_NEXT_CHAR(); + + case 'e': + case 'E': + if (state->special_flags & JSONSL_SPECIALf_EXPONENT) { + INVOKE_ERROR(INVALID_NUMBER); + } + state->special_flags |= JSONSL_SPECIALf_EXPONENT; + STATE_NUM_LAST = 'e'; + CONTINUE_NEXT_CHAR(); + + case '-': + case '+': + if (STATE_NUM_LAST != 'e') { + INVOKE_ERROR(INVALID_NUMBER); + } + STATE_NUM_LAST = '-'; + CONTINUE_NEXT_CHAR(); + + default: + if (is_special_end(CUR_CHAR)) { + goto GT_SPECIAL_POP; + } + INVOKE_ERROR(INVALID_NUMBER); + break; + } + } + /* else if (!NUMERIC) */ + if (!is_special_end(CUR_CHAR)) { + STATE_SPECIAL_LENGTH++; + + /* Verify TRUE, FALSE, NULL */ + if (state->special_flags == JSONSL_SPECIALf_TRUE) { + VERIFY_SPECIAL("true"); + } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { + VERIFY_SPECIAL("false"); + } else if (state->special_flags == JSONSL_SPECIALf_NULL) { + VERIFY_SPECIAL("null"); + } + INCR_METRIC(SPECIAL_FASTPATH); + CONTINUE_NEXT_CHAR(); + } + + GT_SPECIAL_POP: + jsn->can_insert = 0; + if (IS_NORMAL_NUMBER) { + /* Nothing */ + } else if (state->special_flags == JSONSL_SPECIALf_ZERO || + state->special_flags == (JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED)) { + /* 0 is unsigned! */ + state->special_flags = JSONSL_SPECIALf_UNSIGNED; + } else if (state->special_flags == JSONSL_SPECIALf_DASH) { + /* Still in dash! */ + INVOKE_ERROR(INVALID_NUMBER); + } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { + /* Check that we're not at the end of a token */ + if (STATE_NUM_LAST != '1') { + INVOKE_ERROR(INVALID_NUMBER); + } + } else if (state->special_flags == JSONSL_SPECIALf_TRUE) { + if (STATE_SPECIAL_LENGTH != 4) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + state->nelem = 1; + } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { + if (STATE_SPECIAL_LENGTH != 5) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + } else if (state->special_flags == JSONSL_SPECIALf_NULL) { + if (STATE_SPECIAL_LENGTH != 4) { + INVOKE_ERROR(SPECIAL_INCOMPLETE); + } + } + SPECIAL_POP; + jsn->expecting = ','; + if (is_allowed_whitespace(CUR_CHAR)) { + CONTINUE_NEXT_CHAR(); + } + /** + * This works because we have a non-whitespace token + * which is not a special token. If this is a structural + * character then it will be gracefully handled by the + * switch statement. Otherwise it will default to the 'special' + * state again, + */ + goto GT_STRUCTURAL_TOKEN; + } else if (is_allowed_whitespace(CUR_CHAR)) { + INCR_METRIC(ALLOWED_WHITESPACE); + /* So we're not special. Harmless insignificant whitespace + * passthrough + */ + CONTINUE_NEXT_CHAR(); + } else if (extract_special(CUR_CHAR)) { + /* not a string, whitespace, or structural token. must be special */ + goto GT_SPECIAL_BEGIN; + } + + INCR_GENERIC(CUR_CHAR); + + if (CUR_CHAR == '"') { + GT_QUOTE: + jsn->can_insert = 0; + switch (state_type) { + + /* the end of a string or hash key */ + case JSONSL_T_STRING: + CALLBACK_AND_POP(STRING); + CONTINUE_NEXT_CHAR(); + case JSONSL_T_HKEY: + CALLBACK_AND_POP(HKEY); + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_OBJECT: + state->nelem++; + if ( (state->nelem-1) % 2 ) { + /* Odd, this must be a hash value */ + if (jsn->tok_last != ':') { + INVOKE_ERROR(MISSING_TOKEN); + } + jsn->expecting = ','; /* Can't figure out what to expect next */ + jsn->tok_last = 0; + + STACK_PUSH; + state->type = JSONSL_T_STRING; + DO_CALLBACK(STRING, PUSH); + + } else { + /* hash key */ + if (jsn->expecting != '"') { + INVOKE_ERROR(STRAY_TOKEN); + } + jsn->tok_last = 0; + jsn->expecting = ':'; + + STACK_PUSH; + state->type = JSONSL_T_HKEY; + DO_CALLBACK(HKEY, PUSH); + } + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_LIST: + state->nelem++; + STACK_PUSH; + state->type = JSONSL_T_STRING; + jsn->expecting = ','; + jsn->tok_last = 0; + DO_CALLBACK(STRING, PUSH); + CONTINUE_NEXT_CHAR(); + + case JSONSL_T_SPECIAL: + INVOKE_ERROR(STRAY_TOKEN); + break; + + default: + INVOKE_ERROR(STRING_OUTSIDE_CONTAINER); + break; + } /* switch(state->type) */ + } else if (CUR_CHAR == '\\') { + GT_ESCAPE: + INCR_METRIC(ESCAPES); + /* Escape */ + if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) { + INVOKE_ERROR(ESCAPE_OUTSIDE_STRING); + } + state->nescapes++; + jsn->in_escape = 1; + CONTINUE_NEXT_CHAR(); + } /* " or \ */ + + GT_STRUCTURAL_TOKEN: + switch (CUR_CHAR) { + case ':': + INCR_METRIC(STRUCTURAL_TOKEN); + if (jsn->expecting != CUR_CHAR) { + INVOKE_ERROR(STRAY_TOKEN); + } + jsn->tok_last = ':'; + jsn->can_insert = 1; + jsn->expecting = '"'; + CONTINUE_NEXT_CHAR(); + + case ',': + INCR_METRIC(STRUCTURAL_TOKEN); + /** + * The comma is one of the more generic tokens. + * In the context of an OBJECT, the can_insert flag + * should never be set, and no other action is + * necessary. + */ + if (jsn->expecting != CUR_CHAR) { + /* make this branch execute only when we haven't manually + * just placed the ',' in the expecting register. + */ + INVOKE_ERROR(STRAY_TOKEN); + } + + if (state->type == JSONSL_T_OBJECT) { + /* end of hash value, expect a string as a hash key */ + jsn->expecting = '"'; + } else { + jsn->can_insert = 1; + } + + jsn->tok_last = ','; + jsn->expecting = '"'; + CONTINUE_NEXT_CHAR(); + + /* new list or object */ + /* hashes are more common */ + case '{': + case '[': + INCR_METRIC(STRUCTURAL_TOKEN); + if (!jsn->can_insert) { + INVOKE_ERROR(CANT_INSERT); + } + + ENSURE_HVAL; + state->nelem++; + + STACK_PUSH; + /* because the constants match the opening delimiters, we can do this: */ + state->type = CUR_CHAR; + state->nelem = 0; + jsn->can_insert = 1; + if (CUR_CHAR == '{') { + /* If we're a hash, we expect a key first, which is quouted */ + jsn->expecting = '"'; + } + if (CUR_CHAR == JSONSL_T_OBJECT) { + DO_CALLBACK(OBJECT, PUSH); + } else { + DO_CALLBACK(LIST, PUSH); + } + jsn->tok_last = 0; + CONTINUE_NEXT_CHAR(); + + /* closing of list or object */ + case '}': + case ']': + INCR_METRIC(STRUCTURAL_TOKEN); + if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) { + INVOKE_ERROR(TRAILING_COMMA); + } + + jsn->can_insert = 0; + jsn->level--; + jsn->expecting = ','; + jsn->tok_last = 0; + if (CUR_CHAR == ']') { + if (state->type != '[') { + INVOKE_ERROR(BRACKET_MISMATCH); + } + DO_CALLBACK(LIST, POP); + } else { + if (state->type != '{') { + INVOKE_ERROR(BRACKET_MISMATCH); + } else if (state->nelem && state->nelem % 2 != 0) { + INVOKE_ERROR(VALUE_EXPECTED); + } + DO_CALLBACK(OBJECT, POP); + } + state = jsn->stack + jsn->level; + state->pos_cur = jsn->pos; + CONTINUE_NEXT_CHAR(); + + default: + GT_SPECIAL_BEGIN: + /** + * Not a string, not a structural token, and not benign whitespace. + * Technically we should iterate over the character always, but since + * we are not doing full numerical/value decoding anyway (but only hinting), + * we only check upon entry. + */ + if (state->type != JSONSL_T_SPECIAL) { + int special_flags = extract_special(CUR_CHAR); + if (!special_flags) { + /** + * Try to do some heuristics here anyway to figure out what kind of + * error this is. The 'special' case is a fallback scenario anyway. + */ + if (CUR_CHAR == '\0') { + INVOKE_ERROR(FOUND_NULL_BYTE); + } else if (CUR_CHAR < 0x20) { + INVOKE_ERROR(WEIRD_WHITESPACE); + } else { + INVOKE_ERROR(SPECIAL_EXPECTED); + } + } + ENSURE_HVAL; + state->nelem++; + if (!jsn->can_insert) { + INVOKE_ERROR(CANT_INSERT); + } + STACK_PUSH; + state->type = JSONSL_T_SPECIAL; + state->special_flags = special_flags; + STATE_SPECIAL_LENGTH = 1; + + if (special_flags == JSONSL_SPECIALf_UNSIGNED) { + state->nelem = CUR_CHAR - 0x30; + STATE_NUM_LAST = '1'; + } else { + STATE_NUM_LAST = '-'; + state->nelem = 0; + } + DO_CALLBACK(SPECIAL, PUSH); + } + CONTINUE_NEXT_CHAR(); + } + } +} + +JSONSL_API +const char* jsonsl_strerror(jsonsl_error_t err) +{ + if (err == JSONSL_ERROR_SUCCESS) { + return "SUCCESS"; + } +#define X(t) \ + if (err == JSONSL_ERROR_##t) \ + return #t; + JSONSL_XERR; +#undef X + return ""; +} + +JSONSL_API +const char *jsonsl_strtype(jsonsl_type_t type) +{ +#define X(o,c) \ + if (type == JSONSL_T_##o) \ + return #o; + JSONSL_XTYPE +#undef X + return "UNKNOWN TYPE"; + +} + +/* + * + * JPR/JSONPointer functions + * + * + */ +#ifndef JSONSL_NO_JPR +static +jsonsl_jpr_type_t +populate_component(char *in, + struct jsonsl_jpr_component_st *component, + char **next, + jsonsl_error_t *errp) +{ + unsigned long pctval; + char *c = NULL, *outp = NULL, *end = NULL; + size_t input_len; + jsonsl_jpr_type_t ret = JSONSL_PATH_NONE; + + if (*next == NULL || *(*next) == '\0') { + return JSONSL_PATH_NONE; + } + + /* Replace the next / with a NULL */ + *next = strstr(in, "/"); + if (*next != NULL) { + *(*next) = '\0'; /* drop the forward slash */ + input_len = *next - in; + end = *next; + *next += 1; /* next character after the '/' */ + } else { + input_len = strlen(in); + end = in + input_len + 1; + } + + component->pstr = in; + + /* Check for special components of interest */ + if (*in == JSONSL_PATH_WILDCARD_CHAR && input_len == 1) { + /* Lone wildcard */ + ret = JSONSL_PATH_WILDCARD; + goto GT_RET; + } else if (isdigit(*in)) { + /* ASCII Numeric */ + char *endptr; + component->idx = strtoul(in, &endptr, 10); + if (endptr && *endptr == '\0') { + ret = JSONSL_PATH_NUMERIC; + goto GT_RET; + } + } + + /* Default, it's a string */ + ret = JSONSL_PATH_STRING; + for (c = outp = in; c < end; c++, outp++) { + char origc; + if (*c != '%') { + goto GT_ASSIGN; + } + /* + * c = { [+0] = '%', [+1] = 'b', [+2] = 'e', [+3] = '\0' } + */ + + /* Need %XX */ + if (c+2 >= end) { + *errp = JSONSL_ERROR_PERCENT_BADHEX; + return JSONSL_PATH_INVALID; + } + if (! (isxdigit(*(c+1)) && isxdigit(*(c+2))) ) { + *errp = JSONSL_ERROR_PERCENT_BADHEX; + return JSONSL_PATH_INVALID; + } + + /* Temporarily null-terminate the characters */ + origc = *(c+3); + *(c+3) = '\0'; + pctval = strtoul(c+1, NULL, 16); + *(c+3) = origc; + + *outp = (char) pctval; + c += 2; + continue; + + GT_ASSIGN: + *outp = *c; + } + /* Null-terminate the string */ + for (; outp < c; outp++) { + *outp = '\0'; + } + + GT_RET: + component->ptype = ret; + if (ret != JSONSL_PATH_WILDCARD) { + component->len = strlen(component->pstr); + } + return ret; +} + +JSONSL_API +jsonsl_jpr_t +jsonsl_jpr_new(const char *path, jsonsl_error_t *errp) +{ + char *my_copy = NULL; + int count, curidx; + struct jsonsl_jpr_st *ret = NULL; + struct jsonsl_jpr_component_st *components = NULL; + size_t origlen; + jsonsl_error_t errstacked; + +#define JPR_BAIL(err) *errp = err; goto GT_ERROR; + + if (errp == NULL) { + errp = &errstacked; + } + + if (path == NULL || *path != '/') { + JPR_BAIL(JSONSL_ERROR_JPR_NOROOT); + return NULL; + } + + count = 1; + path++; + { + const char *c = path; + for (; *c; c++) { + if (*c == '/') { + count++; + if (*(c+1) == '/') { + JPR_BAIL(JSONSL_ERROR_JPR_DUPSLASH); + } + } + } + } + if(*path) { + count++; + } + + components = (struct jsonsl_jpr_component_st *) + malloc(sizeof(*components) * count); + if (!components) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + + my_copy = (char *)malloc(strlen(path) + 1); + if (!my_copy) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + + strcpy(my_copy, path); + + components[0].ptype = JSONSL_PATH_ROOT; + + if (*my_copy) { + char *cur = my_copy; + int pathret = JSONSL_PATH_STRING; + curidx = 1; + while (pathret > 0 && curidx < count) { + pathret = populate_component(cur, components + curidx, &cur, errp); + if (pathret > 0) { + curidx++; + } else { + break; + } + } + + if (pathret == JSONSL_PATH_INVALID) { + JPR_BAIL(JSONSL_ERROR_JPR_BADPATH); + } + } else { + curidx = 1; + } + + path--; /*revert path to leading '/' */ + origlen = strlen(path) + 1; + ret = (struct jsonsl_jpr_st *)malloc(sizeof(*ret)); + if (!ret) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + ret->orig = (char *)malloc(origlen); + if (!ret->orig) { + JPR_BAIL(JSONSL_ERROR_ENOMEM); + } + ret->components = components; + ret->ncomponents = curidx; + ret->basestr = my_copy; + ret->norig = origlen-1; + strcpy(ret->orig, path); + + return ret; + + GT_ERROR: + free(my_copy); + free(components); + if (ret) { + free(ret->orig); + } + free(ret); + return NULL; +#undef JPR_BAIL +} + +void jsonsl_jpr_destroy(jsonsl_jpr_t jpr) +{ + free(jpr->components); + free(jpr->basestr); + free(jpr->orig); + free(jpr); +} + +/** + * Call when there is a possibility of a match, either as a final match or + * as a path within a match + * @param jpr The JPR path + * @param component Component corresponding to the current element + * @param prlevel The level of the *parent* + * @param chtype The type of the child + * @return Match status + */ +static jsonsl_jpr_match_t +jsonsl__match_continue(jsonsl_jpr_t jpr, + const struct jsonsl_jpr_component_st *component, + unsigned prlevel, unsigned chtype) +{ + const struct jsonsl_jpr_component_st *next_comp = component + 1; + if (prlevel == jpr->ncomponents - 1) { + /* This is the match. Check the expected type of the match against + * the child */ + if (jpr->match_type == 0 || jpr->match_type == chtype) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_TYPE_MISMATCH; + } + } + if (chtype == JSONSL_T_LIST) { + if (next_comp->ptype == JSONSL_PATH_NUMERIC) { + return JSONSL_MATCH_POSSIBLE; + } else { + return JSONSL_MATCH_TYPE_MISMATCH; + } + } else if (chtype == JSONSL_T_OBJECT) { + if (next_comp->ptype == JSONSL_PATH_NUMERIC) { + return JSONSL_MATCH_TYPE_MISMATCH; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } else { + return JSONSL_MATCH_TYPE_MISMATCH; + } +} + +JSONSL_API +jsonsl_jpr_match_t +jsonsl_path_match(jsonsl_jpr_t jpr, + const struct jsonsl_state_st *parent, + const struct jsonsl_state_st *child, + const char *key, size_t nkey) +{ + const struct jsonsl_jpr_component_st *comp; + if (!parent) { + /* No parent. Return immediately since it's always a match */ + return jsonsl__match_continue(jpr, jpr->components, 0, child->type); + } + + comp = jpr->components + parent->level; + + /* note that we don't need to verify the type of the match, this is + * always done through the previous call to jsonsl__match_continue. + * If we are in a POSSIBLE tree then we can be certain the types (at + * least at this level) are correct */ + if (parent->type == JSONSL_T_OBJECT) { + if (comp->len != nkey || strncmp(key, comp->pstr, nkey) != 0) { + return JSONSL_MATCH_NOMATCH; + } + } else { + if (comp->idx != parent->nelem - 1) { + return JSONSL_MATCH_NOMATCH; + } + } + return jsonsl__match_continue(jpr, comp, parent->level, child->type); +} + +JSONSL_API +jsonsl_jpr_match_t +jsonsl_jpr_match(jsonsl_jpr_t jpr, + unsigned int parent_type, + unsigned int parent_level, + const char *key, + size_t nkey) +{ + /* find our current component. This is the child level */ + int cmpret; + struct jsonsl_jpr_component_st *p_component; + p_component = jpr->components + parent_level; + + if (parent_level >= jpr->ncomponents) { + return JSONSL_MATCH_NOMATCH; + } + + /* Lone query for 'root' element. Always matches */ + if (parent_level == 0) { + if (jpr->ncomponents == 1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + /* Wildcard, always matches */ + if (p_component->ptype == JSONSL_PATH_WILDCARD) { + if (parent_level == jpr->ncomponents-1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + /* Check numeric array index. This gets its special block so we can avoid + * string comparisons */ + if (p_component->ptype == JSONSL_PATH_NUMERIC) { + if (parent_type == JSONSL_T_LIST) { + if (p_component->idx != nkey) { + /* Wrong index */ + return JSONSL_MATCH_NOMATCH; + } else { + if (parent_level == jpr->ncomponents-1) { + /* This is the last element of the path */ + return JSONSL_MATCH_COMPLETE; + } else { + /* Intermediate element */ + return JSONSL_MATCH_POSSIBLE; + } + } + } else if (p_component->is_arridx) { + /* Numeric and an array index (set explicitly by user). But not + * a list for a parent */ + return JSONSL_MATCH_TYPE_MISMATCH; + } + } else if (parent_type == JSONSL_T_LIST) { + return JSONSL_MATCH_TYPE_MISMATCH; + } + + /* Check lengths */ + if (p_component->len != nkey) { + return JSONSL_MATCH_NOMATCH; + } + + /* Check string comparison */ + cmpret = strncmp(p_component->pstr, key, nkey); + if (cmpret == 0) { + if (parent_level == jpr->ncomponents-1) { + return JSONSL_MATCH_COMPLETE; + } else { + return JSONSL_MATCH_POSSIBLE; + } + } + + return JSONSL_MATCH_NOMATCH; +} + +JSONSL_API +void jsonsl_jpr_match_state_init(jsonsl_t jsn, + jsonsl_jpr_t *jprs, + size_t njprs) +{ + size_t ii, *firstjmp; + if (njprs == 0) { + return; + } + jsn->jprs = (jsonsl_jpr_t *)malloc(sizeof(jsonsl_jpr_t) * njprs); + jsn->jpr_count = njprs; + jsn->jpr_root = (size_t*)calloc(1, sizeof(size_t) * njprs * jsn->levels_max); + memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs); + /* Set the initial jump table values */ + + firstjmp = jsn->jpr_root; + for (ii = 0; ii < njprs; ii++) { + firstjmp[ii] = ii+1; + } +} + +JSONSL_API +void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn) +{ + if (jsn->jpr_count == 0) { + return; + } + + free(jsn->jpr_root); + free(jsn->jprs); + jsn->jprs = NULL; + jsn->jpr_root = NULL; + jsn->jpr_count = 0; +} + +/** + * This function should be called exactly once on each element... + * This should also be called in recursive order, since we rely + * on the parent having been initalized for a match. + * + * Since the parent is checked for a match as well, we maintain a 'serial' counter. + * Whenever we traverse an element, we expect the serial to be the same as a global + * integer. If they do not match, we re-initialize the context, and set the serial. + * + * This ensures a type of consistency without having a proactive reset by the + * main lexer itself. + * + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, + struct jsonsl_state_st *state, + const char *key, + size_t nkey, + jsonsl_jpr_match_t *out) +{ + struct jsonsl_state_st *parent_state; + jsonsl_jpr_t ret = NULL; + + /* Jump and JPR tables for our own state and the parent state */ + size_t *jmptable, *pjmptable; + size_t jmp_cur, ii, ourjmpidx; + + if (!jsn->jpr_root) { + *out = JSONSL_MATCH_NOMATCH; + return NULL; + } + + pjmptable = jsn->jpr_root + (jsn->jpr_count * (state->level-1)); + jmptable = pjmptable + jsn->jpr_count; + + /* If the parent cannot match, then invalidate it */ + if (*pjmptable == 0) { + *jmptable = 0; + *out = JSONSL_MATCH_NOMATCH; + return NULL; + } + + parent_state = jsn->stack + state->level - 1; + + if (parent_state->type == JSONSL_T_LIST) { + nkey = (size_t) parent_state->nelem; + } + + *jmptable = 0; + ourjmpidx = 0; + memset(jmptable, 0, sizeof(int) * jsn->jpr_count); + + for (ii = 0; ii < jsn->jpr_count; ii++) { + jmp_cur = pjmptable[ii]; + if (jmp_cur) { + jsonsl_jpr_t jpr = jsn->jprs[jmp_cur-1]; + *out = jsonsl_jpr_match(jpr, + parent_state->type, + parent_state->level, + key, nkey); + if (*out == JSONSL_MATCH_COMPLETE) { + ret = jpr; + *jmptable = 0; + return ret; + } else if (*out == JSONSL_MATCH_POSSIBLE) { + jmptable[ourjmpidx] = ii+1; + ourjmpidx++; + } + } else { + break; + } + } + if (!*jmptable) { + *out = JSONSL_MATCH_NOMATCH; + } + return NULL; +} + +JSONSL_API +const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match) +{ +#define X(T,v) \ + if ( match == JSONSL_MATCH_##T ) \ + return #T; + JSONSL_XMATCH +#undef X + return ""; +} + +#endif /* JSONSL_WITH_JPR */ + +static char * +jsonsl__writeutf8(uint32_t pt, char *out) +{ + #define ADD_OUTPUT(c) *out = (char)(c); out++; + + if (pt < 0x80) { + ADD_OUTPUT(pt); + } else if (pt < 0x800) { + ADD_OUTPUT((pt >> 6) | 0xC0); + ADD_OUTPUT((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + ADD_OUTPUT((pt >> 12) | 0xE0); + ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80); + ADD_OUTPUT((pt & 0x3F) | 0x80); + } else { + ADD_OUTPUT((pt >> 18) | 0xF0); + ADD_OUTPUT(((pt >> 12) & 0x3F) | 0x80); + ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80); + ADD_OUTPUT((pt & 0x3F) | 0x80); + } + return out; + #undef ADD_OUTPUT +} + +/* Thanks snej (https://github.com/mnunberg/jsonsl/issues/9) */ +static int +jsonsl__digit2int(char ch) { + int d = ch - '0'; + if ((unsigned) d < 10) { + return d; + } + d = ch - 'a'; + if ((unsigned) d < 6) { + return d + 10; + } + d = ch - 'A'; + if ((unsigned) d < 6) { + return d + 10; + } + return -1; +} + +/* Assume 's' is at least 4 bytes long */ +static int +jsonsl__get_uescape_16(const char *s) +{ + int ret = 0; + int cur; + + #define GET_DIGIT(off) \ + cur = jsonsl__digit2int(s[off]); \ + if (cur == -1) { return -1; } \ + ret |= (cur << (12 - (off * 4))); + + GET_DIGIT(0); + GET_DIGIT(1); + GET_DIGIT(2); + GET_DIGIT(3); + #undef GET_DIGIT + return ret; +} + +/** + * Utility function to convert escape sequences + */ +JSONSL_API +size_t jsonsl_util_unescape_ex(const char *in, + char *out, + size_t len, + const int toEscape[128], + unsigned *oflags, + jsonsl_error_t *err, + const char **errat) +{ + const unsigned char *c = (const unsigned char*)in; + char *begin_p = out; + unsigned oflags_s; + uint16_t last_codepoint = 0; + + if (!oflags) { + oflags = &oflags_s; + } + *oflags = 0; + + #define UNESCAPE_BAIL(e,offset) \ + *err = JSONSL_ERROR_##e; \ + if (errat) { \ + *errat = (const char*)(c+ (ptrdiff_t)(offset)); \ + } \ + return 0; + + for (; len; len--, c++, out++) { + int uescval; + if (*c != '\\') { + /* Not an escape, so we don't care about this */ + goto GT_ASSIGN; + } + + if (len < 2) { + UNESCAPE_BAIL(ESCAPE_INVALID, 0); + } + if (!is_allowed_escape(c[1])) { + UNESCAPE_BAIL(ESCAPE_INVALID, 1) + } + if ((toEscape && toEscape[(unsigned char)c[1] & 0x7f] == 0 && + c[1] != '\\' && c[1] != '"')) { + /* if we don't want to unescape this string, write the escape sequence to the output */ + *out++ = *c++; + if (--len == 0) + break; + goto GT_ASSIGN; + } + + if (c[1] != 'u') { + /* simple skip-and-replace using pre-defined maps. + * TODO: should the maps actually reflect the desired + * replacement character in toEscape? + */ + char esctmp = get_escape_equiv(c[1]); + if (esctmp) { + /* Check if there is a corresponding replacement */ + *out = esctmp; + } else { + /* Just gobble up the 'reverse-solidus' */ + *out = c[1]; + } + len--; + c++; + /* do not assign, just continue */ + continue; + } + + /* next == 'u' */ + if (len < 6) { + /* Need at least six characters.. */ + UNESCAPE_BAIL(UESCAPE_TOOSHORT, 2); + } + + uescval = jsonsl__get_uescape_16((const char *)c + 2); + if (uescval == -1) { + UNESCAPE_BAIL(PERCENT_BADHEX, -1); + } else if (uescval == 0) { + UNESCAPE_BAIL(INVALID_CODEPOINT, 2); + } + + if (last_codepoint) { + uint16_t w1 = last_codepoint, w2 = (uint16_t)uescval; + uint32_t cp; + + if (uescval < 0xDC00 || uescval > 0xDFFF) { + UNESCAPE_BAIL(INVALID_CODEPOINT, -1); + } + + cp = (w1 & 0x3FF) << 10; + cp |= (w2 & 0x3FF); + cp += 0x10000; + + out = jsonsl__writeutf8(cp, out) - 1; + last_codepoint = 0; + + } else if (uescval < 0xD800 || uescval > 0xDFFF) { + *oflags |= JSONSL_SPECIALf_NONASCII; + out = jsonsl__writeutf8(uescval, out) - 1; + + } else if (uescval > 0xD7FF && uescval < 0xDC00) { + *oflags |= JSONSL_SPECIALf_NONASCII; + last_codepoint = (uint16_t)uescval; + out--; + } else { + UNESCAPE_BAIL(INVALID_CODEPOINT, 2); + } + + /* Post uescape cleanup */ + len -= 5; /* Gobble up 5 chars after 'u' */ + c += 5; + continue; + + /* Only reached by previous branches */ + GT_ASSIGN: + *out = *c; + } + + if (last_codepoint) { + *err = JSONSL_ERROR_INVALID_CODEPOINT; + return 0; + } + + *err = JSONSL_ERROR_SUCCESS; + return out - begin_p; +} + +/** + * Character Table definitions. + * These were all generated via srcutil/genchartables.pl + */ + +/** + * This table contains the beginnings of non-string + * allowable (bareword) values. + */ +static const unsigned short Special_Table[0x80] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2c */ + /* 0x2d */ JSONSL_SPECIALf_DASH /* <-> */, /* 0x2d */ + /* 0x2e */ 0,0, /* 0x2f */ + /* 0x30 */ JSONSL_SPECIALf_ZERO /* <0> */, /* 0x30 */ + /* 0x31 */ JSONSL_SPECIALf_UNSIGNED /* <1> */, /* 0x31 */ + /* 0x32 */ JSONSL_SPECIALf_UNSIGNED /* <2> */, /* 0x32 */ + /* 0x33 */ JSONSL_SPECIALf_UNSIGNED /* <3> */, /* 0x33 */ + /* 0x34 */ JSONSL_SPECIALf_UNSIGNED /* <4> */, /* 0x34 */ + /* 0x35 */ JSONSL_SPECIALf_UNSIGNED /* <5> */, /* 0x35 */ + /* 0x36 */ JSONSL_SPECIALf_UNSIGNED /* <6> */, /* 0x36 */ + /* 0x37 */ JSONSL_SPECIALf_UNSIGNED /* <7> */, /* 0x37 */ + /* 0x38 */ JSONSL_SPECIALf_UNSIGNED /* <8> */, /* 0x38 */ + /* 0x39 */ JSONSL_SPECIALf_UNSIGNED /* <9> */, /* 0x39 */ + /* 0x3a */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x59 */ + /* 0x5a */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x65 */ + /* 0x66 */ JSONSL_SPECIALf_FALSE /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ JSONSL_SPECIALf_NULL /* */, /* 0x6e */ + /* 0x6f */ 0,0,0,0,0, /* 0x73 */ + /* 0x74 */ JSONSL_SPECIALf_TRUE /* */ /* 0x74 */ +}; + +// Bit tables are order such that the MSB is bit 0. +// +/** + * Contains characters which signal the termination of any of the 'special' bareword + * values. + */ +static const char Special_Endings[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ + /* 0x09 */ 1 /* */, /* 0x09 */ + /* 0x0a */ 1 /* */, /* 0x0a */ + /* 0x0b */ 0,0, /* 0x0c */ + /* 0x0d */ 1 /* */, /* 0x0d */ + /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 1 /* */, /* 0x20 */ + /* 0x21 */ 0, /* 0x21 */ + /* 0x22 */ 1 /* " */, /* 0x22 */ + /* 0x23 */ 0,0,0,0,0,0,0,0,0, /* 0x2b */ + /* 0x2c */ 1 /* , */, /* 0x2c */ + /* 0x2d */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x39 */ + /* 0x3a */ 1 /* : */, /* 0x3a */ + /* 0x3b */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5a */ + /* 0x5b */ 1 /* [ */, /* 0x5b */ + /* 0x5c */ 1 /* \ */, /* 0x5c */ + /* 0x5d */ 1 /* ] */, /* 0x5d */ + /* 0x5e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7a */ + /* 0x7b */ 1 /* { */, /* 0x7b */ + /* 0x7c */ 0, /* 0x7c */ + /* 0x7d */ 1 /* } */, /* 0x7d */ + /* 0x7e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9d */ + /* 0x9e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbd */ + /* 0xbe */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdd */ + /* 0xde */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfd */ + /* 0xfe */ 0 /* 0xfe */ +}; +static const uint32_t Special_Endings_bits[0x80 / 32] = { + 0b00000000110010000000000000000000, + 0b10100000000010000000000000100000, + 0b00000000000000000000000000011100, + 0b00000000000000000000000000010100 +}; + +/** + * This table contains entries for the allowed whitespace as per RFC 4627 + */ +static const char Allowed_Whitespace[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ + /* 0x09 */ 1 /* */, /* 0x09 */ + /* 0x0a */ 1 /* */, /* 0x0a */ + /* 0x0b */ 0,0, /* 0x0c */ + /* 0x0d */ 1 /* */, /* 0x0d */ + /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 1 /* */, /* 0x20 */ + /* 0x21 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 */ + /* 0x41 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 */ + /* 0x61 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 */ + /* 0x81 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0 */ + /* 0xa1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xc0 */ + /* 0xc1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xe0 */ + /* 0xe1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xfe */ +}; +static const uint32_t Allowed_Whitespace_bits = 0b00000000011001000000000000000000; + +static const char String_No_Passthrough[0x100] = { + /* 0x00 */ 1 /* */, /* 0x00 */ + /* 0x01 */ 1 /* */, /* 0x01 */ + /* 0x02 */ 1 /* */, /* 0x02 */ + /* 0x03 */ 1 /* */, /* 0x03 */ + /* 0x04 */ 1 /* */, /* 0x04 */ + /* 0x05 */ 1 /* */, /* 0x05 */ + /* 0x06 */ 1 /* */, /* 0x06 */ + /* 0x07 */ 1 /* */, /* 0x07 */ + /* 0x08 */ 1 /* */, /* 0x08 */ + /* 0x09 */ 1 /* */, /* 0x09 */ + /* 0x0a */ 1 /* */, /* 0x0a */ + /* 0x0b */ 1 /* */, /* 0x0b */ + /* 0x0c */ 1 /* */, /* 0x0c */ + /* 0x0d */ 1 /* */, /* 0x0d */ + /* 0x0e */ 1 /* */, /* 0x0e */ + /* 0x0f */ 1 /* */, /* 0x0f */ + /* 0x10 */ 1 /* */, /* 0x10 */ + /* 0x11 */ 1 /* */, /* 0x11 */ + /* 0x12 */ 1 /* */, /* 0x12 */ + /* 0x13 */ 1 /* */, /* 0x13 */ + /* 0x14 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x21 */ + /* 0x22 */ 1 /* <"> */, /* 0x22 */ + /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x42 */ + /* 0x43 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ + /* 0x5c */ 1 /* <\> */, /* 0x5c */ + /* 0x5d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7c */ + /* 0x7d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9c */ + /* 0x9d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbc */ + /* 0xbd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdc */ + /* 0xdd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfc */ + /* 0xfd */ 0,0, /* 0xfe */ +}; + +/** + * Allowable two-character 'common' escapes: + */ +static const char Allowed_Escapes[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0, /* 0x21 */ + /* 0x22 */ 1 /* <"> */, /* 0x22 */ + /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2e */ + /* 0x2f */ 1 /* */, /* 0x2f */ + /* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x4f */ + /* 0x50 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ + /* 0x5c */ 1 /* <\> */, /* 0x5c */ + /* 0x5d */ 0,0,0,0,0, /* 0x61 */ + /* 0x62 */ 1 /* */, /* 0x62 */ + /* 0x63 */ 0,0,0, /* 0x65 */ + /* 0x66 */ 1 /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ 1 /* */, /* 0x6e */ + /* 0x6f */ 0,0,0, /* 0x71 */ + /* 0x72 */ 1 /* */, /* 0x72 */ + /* 0x73 */ 0, /* 0x73 */ + /* 0x74 */ 1 /* */, /* 0x74 */ + /* 0x75 */ 1 /* */, /* 0x75 */ + /* 0x76 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x95 */ + /* 0x96 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb5 */ + /* 0xb6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd5 */ + /* 0xd6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf5 */ + /* 0xf6 */ 0,0,0,0,0,0,0,0,0, /* 0xfe */ +}; +static const uint32_t Allowed_Escapes_bits[0x80 / 32] = { + 0b00000000000000000000000000000000, + 0b00100000000000010000000000000000, + 0b00000000000000000000000000001000, + 0b00100010000000100010110000000000 +}; + +/** + * This table contains the _values_ for a given (single) escaped character. + */ +static unsigned char Escape_Equivs[0x100] = { + /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ + /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3f */ + /* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5f */ + /* 0x60 */ 0,0, /* 0x61 */ + /* 0x62 */ 8 /* */, /* 0x62 */ + /* 0x63 */ 0,0,0, /* 0x65 */ + /* 0x66 */ 12 /* */, /* 0x66 */ + /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ + /* 0x6e */ 10 /* */, /* 0x6e */ + /* 0x6f */ 0,0,0, /* 0x71 */ + /* 0x72 */ 13 /* */, /* 0x72 */ + /* 0x73 */ 0, /* 0x73 */ + /* 0x74 */ 9 /* */, /* 0x74 */ + /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */ + /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */ + /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */ + /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */ + /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */ +}; + +/* Definitions of above-declared static functions */ +static char get_escape_equiv(unsigned c) { + switch(c) { + case 'b': + return '\b'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'f': + return '\f'; + } + return 0; +} + +static unsigned extract_special(unsigned c) { + return (c < 0x80) ? Special_Table[c & 0xff] : 0; +} +static int is_special_end(unsigned c) { + return (c < 0x80) && (Special_Endings_bits[c >> 5] & (1 << (31 - (c & 31)))); +} +static int is_allowed_whitespace(unsigned c) { + return c == ' ' || (c < 0x20 && (Allowed_Whitespace_bits & (1 << (31 - c)))); +} +static int is_allowed_escape(unsigned c) { + return (c < 0x80) && (Allowed_Escapes_bits[c >> 5] & (1 << (31 - (c & 31)))); +} +static int is_simple_char(unsigned c) { + return !(c < 0x14 || c == '"' || c == '\\'); +} + +/* Clean up all our macros! */ +#undef INCR_METRIC +#undef INCR_GENERIC +#undef INCR_STRINGY_CATCH +#undef CASE_DIGITS +#undef INVOKE_ERROR +#undef STACK_PUSH +#undef STACK_POP_NOPOS +#undef STACK_POP +#undef CALLBACK_AND_POP_NOPOS +#undef CALLBACK_AND_POP +#undef SPECIAL_POP +#undef CUR_CHAR +#undef DO_CALLBACK +#undef ENSURE_HVAL +#undef VERIFY_SPECIAL +#undef STATE_SPECIAL_LENGTH +#undef IS_NORMAL_NUMBER +#undef STATE_NUM_LAST +#undef FASTPARSE_EXHAUSTED +#undef FASTPARSE_BREAK diff --git a/app/sjson/jsonsl.h b/app/sjson/jsonsl.h new file mode 100644 index 0000000000..85c7d1e9c3 --- /dev/null +++ b/app/sjson/jsonsl.h @@ -0,0 +1,995 @@ +/** + * JSON Simple/Stacked/Stateful Lexer. + * - Does not buffer data + * - Maintains state + * - Callback oriented + * - Lightweight and fast. One source file and one header file + * + * Copyright (C) 2012-2015 Mark Nunberg + * See included LICENSE file for license details. + */ + +#ifndef JSONSL_H_ +#define JSONSL_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef JSONSL_USE_WCHAR +typedef jsonsl_char_t wchar_t; +typedef jsonsl_uchar_t unsigned wchar_t; +#else +typedef char jsonsl_char_t; +typedef unsigned char jsonsl_uchar_t; +#endif /* JSONSL_USE_WCHAR */ + +/* Stolen from http-parser.h, and possibly others */ +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#if !defined(_MSC_VER) || _MSC_VER<1400 +typedef unsigned int size_t; +typedef int ssize_t; +#endif +#else +#include +#endif + + +#if (!defined(JSONSL_STATE_GENERIC)) && (!defined(JSONSL_STATE_USER_FIELDS)) +#define JSONSL_STATE_GENERIC +#endif /* !defined JSONSL_STATE_GENERIC */ + +#ifdef JSONSL_STATE_GENERIC +#define JSONSL_STATE_USER_FIELDS +#endif /* JSONSL_STATE_GENERIC */ + +/* Additional fields for component object */ +#ifndef JSONSL_JPR_COMPONENT_USER_FIELDS +#define JSONSL_JPR_COMPONENT_USER_FIELDS +#endif + +#ifndef JSONSL_API +/** + * We require a /DJSONSL_DLL so that users already using this as a static + * or embedded library don't get confused + */ +#if defined(_WIN32) && defined(JSONSL_DLL) +#define JSONSL_API __declspec(dllexport) +#else +#define JSONSL_API +#endif /* _WIN32 */ + +#endif /* !JSONSL_API */ + +#ifndef JSONSL_INLINE +#if defined(_MSC_VER) + #define JSONSL_INLINE __inline + #elif defined(__GNUC__) + #define JSONSL_INLINE __inline__ + #else + #define JSONSL_INLINE inline + #endif /* _MSC_VER or __GNUC__ */ +#endif /* JSONSL_INLINE */ + +#define JSONSL_MAX_LEVELS 512 + +struct jsonsl_st; +typedef struct jsonsl_st *jsonsl_t; + +typedef struct jsonsl_jpr_st* jsonsl_jpr_t; + +/** + * This flag is true when AND'd against a type whose value + * must be in "quoutes" i.e. T_HKEY and T_STRING + */ +#define JSONSL_Tf_STRINGY 0xffff00 + +/** + * Constant representing the special JSON types. + * The values are special and aid in speed (the OBJECT and LIST + * values are the char literals of their openings). + * + * Their actual value is a character which attempts to resemble + * some mnemonic reference to the actual type. + * + * If new types are added, they must fit into the ASCII printable + * range (so they should be AND'd with 0x7f and yield something + * meaningful) + */ +#define JSONSL_XTYPE \ + X(STRING, '"'|JSONSL_Tf_STRINGY) \ + X(HKEY, '#'|JSONSL_Tf_STRINGY) \ + X(OBJECT, '{') \ + X(LIST, '[') \ + X(SPECIAL, '^') \ + X(UESCAPE, 'u') +typedef enum { +#define X(o, c) \ + JSONSL_T_##o = c, + JSONSL_XTYPE + JSONSL_T_UNKNOWN = '?', + /* Abstract 'root' object */ + JSONSL_T_ROOT = 0 +#undef X +} jsonsl_type_t; + +/** + * Subtypes for T_SPECIAL. We define them as flags + * because more than one type can be applied to a + * given object. + */ + +#define JSONSL_XSPECIAL \ + X(NONE, 0) \ + X(SIGNED, 1<<0) \ + X(UNSIGNED, 1<<1) \ + X(TRUE, 1<<2) \ + X(FALSE, 1<<3) \ + X(NULL, 1<<4) \ + X(FLOAT, 1<<5) \ + X(EXPONENT, 1<<6) \ + X(NONASCII, 1<<7) +typedef enum { +#define X(o,b) \ + JSONSL_SPECIALf_##o = b, + JSONSL_XSPECIAL +#undef X + /* Handy flags for checking */ + + JSONSL_SPECIALf_UNKNOWN = 1 << 8, + + /** @private Private */ + JSONSL_SPECIALf_ZERO = 1 << 9 | JSONSL_SPECIALf_UNSIGNED, + /** @private */ + JSONSL_SPECIALf_DASH = 1 << 10, + + /** Type is numeric */ + JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED| JSONSL_SPECIALf_UNSIGNED), + + /** Type is a boolean */ + JSONSL_SPECIALf_BOOLEAN = (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE), + + /** Type is an "extended", not integral type (but numeric) */ + JSONSL_SPECIALf_NUMNOINT = (JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT) +} jsonsl_special_t; + + +/** + * These are the various types of stack (or other) events + * which will trigger a callback. + * Like the type constants, this are also mnemonic + */ +#define JSONSL_XACTION \ + X(PUSH, '+') \ + X(POP, '-') \ + X(UESCAPE, 'U') \ + X(ERROR, '!') +typedef enum { +#define X(a,c) \ + JSONSL_ACTION_##a = c, + JSONSL_XACTION + JSONSL_ACTION_UNKNOWN = '?' +#undef X +} jsonsl_action_t; + + +/** + * Various errors which may be thrown while parsing JSON + */ +#define JSONSL_XERR \ +/* Trailing garbage characters */ \ + X(GARBAGE_TRAILING) \ +/* We were expecting a 'special' (numeric, true, false, null) */ \ + X(SPECIAL_EXPECTED) \ +/* The 'special' value was incomplete */ \ + X(SPECIAL_INCOMPLETE) \ +/* Found a stray token */ \ + X(STRAY_TOKEN) \ +/* We were expecting a token before this one */ \ + X(MISSING_TOKEN) \ +/* Cannot insert because the container is not ready */ \ + X(CANT_INSERT) \ +/* Found a '\' outside a string */ \ + X(ESCAPE_OUTSIDE_STRING) \ +/* Found a ':' outside of a hash */ \ + X(KEY_OUTSIDE_OBJECT) \ +/* found a string outside of a container */ \ + X(STRING_OUTSIDE_CONTAINER) \ +/* Found a null byte in middle of string */ \ + X(FOUND_NULL_BYTE) \ +/* Current level exceeds limit specified in constructor */ \ + X(LEVELS_EXCEEDED) \ +/* Got a } as a result of an opening [ or vice versa */ \ + X(BRACKET_MISMATCH) \ +/* We expected a key, but got something else instead */ \ + X(HKEY_EXPECTED) \ +/* We got an illegal control character (bad whitespace or something) */ \ + X(WEIRD_WHITESPACE) \ +/* Found a \u-escape, but there were less than 4 following hex digits */ \ + X(UESCAPE_TOOSHORT) \ +/* Invalid two-character escape */ \ + X(ESCAPE_INVALID) \ +/* Trailing comma */ \ + X(TRAILING_COMMA) \ +/* An invalid number was passed in a numeric field */ \ + X(INVALID_NUMBER) \ +/* Value is missing for object */ \ + X(VALUE_EXPECTED) \ +/* The following are for JPR Stuff */ \ + \ +/* Found a literal '%' but it was only followed by a single valid hex digit */ \ + X(PERCENT_BADHEX) \ +/* jsonpointer URI is malformed '/' */ \ + X(JPR_BADPATH) \ +/* Duplicate slash */ \ + X(JPR_DUPSLASH) \ +/* No leading root */ \ + X(JPR_NOROOT) \ +/* Allocation failure */ \ + X(ENOMEM) \ +/* Invalid unicode codepoint detected (in case of escapes) */ \ + X(INVALID_CODEPOINT) + +typedef enum { + JSONSL_ERROR_SUCCESS = 0, +#define X(e) \ + JSONSL_ERROR_##e, + JSONSL_XERR +#undef X + JSONSL_ERROR_GENERIC +} jsonsl_error_t; + + +/** + * A state is a single level of the stack. + * Non-private data (i.e. the 'data' field, see the STATE_GENERIC section) + * will remain in tact until the item is popped. + * + * As a result, it means a parent state object may be accessed from a child + * object, (the parents fields will all be valid). This allows a user to create + * an ad-hoc hierarchy on top of the JSON one. + * + */ +struct jsonsl_state_st { + /** + * The JSON object type + */ + unsigned int type; + + /** + * The position (in terms of number of bytes since the first call to + * jsonsl_feed()) at which the state was first pushed. This includes + * opening tokens, if applicable. + * + * @note For strings (i.e. type & JSONSL_Tf_STRINGY is nonzero) this will + * be the position of the first quote. + * + * @see jsonsl_st::pos which contains the _current_ position and can be + * used during a POP callback to get the length of the element. + */ + size_t pos_begin; + + /**FIXME: This is redundant as the same information can be derived from + * jsonsl_st::pos at pop-time */ + size_t pos_cur; + + /** If this element is special, then its extended type is here */ + unsigned short special_flags; + + /** + * Level of recursion into nesting. This is mainly a convenience + * variable, as this can technically be deduced from the lexer's + * level parameter (though the logic is not that simple) + */ + unsigned short level; + + + /** + * how many elements in the object/list. + * For objects (hashes), an element is either + * a key or a value. Thus for one complete pair, + * nelem will be 2. + * + * For special types, this will hold the sum of the digits. + * This only holds true for values which are simple signed/unsigned + * numbers. Otherwise a special flag is set, and extra handling is not + * performed. + */ + uint32_t nelem; + + + + /*TODO: merge this and special_flags into a union */ + + + /** + * Useful for an opening nest, this will prevent a callback from being + * invoked on this item or any of its children + */ + int ignore_callback : 1; + + /** + * Counter which is incremented each time an escape ('\') is encountered. + * This is used internally for non-string types and should only be + * inspected by the user if the state actually represents a string + * type. + */ + unsigned int nescapes : 31; + + /** + * Put anything you want here. if JSONSL_STATE_USER_FIELDS is here, then + * the macro expansion happens here. + * + * You can use these fields to store hierarchical or 'tagging' information + * for specific objects. + * + * See the documentation above for the lifetime of the state object (i.e. + * if the private data points to allocated memory, it should be freed + * when the object is popped, as the state object will be re-used) + */ +#ifndef JSONSL_STATE_GENERIC + JSONSL_STATE_USER_FIELDS +#else + + /** + * Otherwise, this is a simple void * pointer for anything you want + */ + void *data; +#endif /* JSONSL_STATE_USER_FIELDS */ +}; + +/**Gets the number of elements in the list. + * @param st The state. Must be of type JSONSL_T_LIST + * @return number of elements in the list + */ +#define JSONSL_LIST_SIZE(st) ((st)->nelem) + +/**Gets the number of key-value pairs in an object + * @param st The state. Must be of type JSONSL_T_OBJECT + * @return the number of key-value pairs in the object + */ +#define JSONSL_OBJECT_SIZE(st) ((st)->nelem / 2) + +/**Gets the numeric value. + * @param st The state. Must be of type JSONSL_T_SPECIAL and + * special_flags must have the JSONSL_SPECIALf_NUMERIC flag + * set. + * @return the numeric value of the state. + */ +#define JSONSL_NUMERIC_VALUE(st) ((st)->nelem) + +/* + * So now we need some special structure for keeping the + * JPR info in sync. Preferrably all in a single block + * of memory (there's no need for separate allocations. + * So we will define a 'table' with the following layout + * + * Level nPosbl JPR1_last JPR2_last JPR3_last + * + * 0 1 NOMATCH POSSIBLE POSSIBLE + * 1 0 NOMATCH NOMATCH COMPLETE + * [ table ends here because no further path is possible] + * + * Where the JPR..n corresponds to the number of JPRs + * requested, and nPosble is a quick flag to determine + * + * the number of possibilities. In the future this might + * be made into a proper 'jump' table, + * + * Since we always mark JPRs from the higher levels descending + * into the lower ones, a prospective child match would first + * look at the parent table to check the possibilities, and then + * see which ones were possible.. + * + * Thus, the size of this blob would be (and these are all ints here) + * nLevels * nJPR * 2. + * + * the 'Width' of the table would be nJPR*2, and the 'height' would be + * nlevels + */ + +/** + * This is called when a stack change ocurs. + * + * @param jsn The lexer + * @param action The type of action, this can be PUSH or POP + * @param state A pointer to the stack currently affected by the action + * @param at A pointer to the position of the input buffer which triggered + * this action. + */ +typedef void (*jsonsl_stack_callback)( + jsonsl_t jsn, + jsonsl_action_t action, + struct jsonsl_state_st* state, + const jsonsl_char_t *at); + + +/** + * This is called when an error is encountered. + * Sometimes it's possible to 'erase' characters (by replacing them + * with whitespace). If you think you have corrected the error, you + * can return a true value, in which case the parser will backtrack + * and try again. + * + * @param jsn The lexer + * @param error The error which was thrown + * @param state the current state + * @param a pointer to the position of the input buffer which triggered + * the error. Note that this is not const, this is because you have the + * possibility of modifying the character in an attempt to correct the + * error + * + * @return zero to bail, nonzero to try again (this only makes sense if + * the input buffer has been modified by this callback) + */ +typedef int (*jsonsl_error_callback)( + jsonsl_t jsn, + jsonsl_error_t error, + struct jsonsl_state_st* state, + jsonsl_char_t *at); + +struct jsonsl_st { + /** Public, read-only */ + + /** This is the current level of the stack */ + unsigned int level; + + /** Flag set to indicate we should stop processing */ + unsigned int stopfl; + + /** + * This is the current position, relative to the beginning + * of the stream. + */ + size_t pos; + + /** This is the 'bytes' variable passed to feed() */ + const jsonsl_char_t *base; + + /** Callback invoked for PUSH actions */ + jsonsl_stack_callback action_callback_PUSH; + + /** Callback invoked for POP actions */ + jsonsl_stack_callback action_callback_POP; + + /** Default callback for any action, if neither PUSH or POP callbacks are defined */ + jsonsl_stack_callback action_callback; + + /** + * Do not invoke callbacks for objects deeper than this level. + * NOTE: This field establishes the lower bound for ignored callbacks, + * and is thus misnamed. `min_ignore_level` would actually make more + * sense, but we don't want to break API. + */ + unsigned int max_callback_level; + + /** The error callback. Invoked when an error happens. Should not be NULL */ + jsonsl_error_callback error_callback; + + /* these are boolean flags you can modify. You will be called + * about notification for each of these types if the corresponding + * variable is true. + */ + + /** + * @name Callback Booleans. + * These determine whether a callback is to be invoked for certain types of objects + * @{*/ + + /** Boolean flag to enable or disable the invokcation for events on this type*/ + int call_SPECIAL; + int call_OBJECT; + int call_LIST; + int call_STRING; + int call_HKEY; + /*@}*/ + + /** + * @name u-Escape handling + * Special handling for the \\u-f00d type sequences. These are meant + * to be translated back into the corresponding octet(s). + * A special callback (if set) is invoked with *at=='u'. An application + * may wish to temporarily suspend parsing and handle the 'u-' sequence + * internally (or not). + */ + + /*@{*/ + + /** Callback to be invoked for a u-escape */ + jsonsl_stack_callback action_callback_UESCAPE; + + /** Boolean flag, whether to invoke the callback */ + int call_UESCAPE; + + /** Boolean flag, whether we should return after encountering a u-escape: + * the callback is invoked and then we return if this is true + */ + int return_UESCAPE; + /*@}*/ + + struct { + int allow_trailing_comma; + } options; + + /** Put anything here */ + void *data; + + /*@{*/ + /** Private */ + int in_escape; + char expecting; + char tok_last; + int can_insert; + unsigned int levels_max; + +#ifndef JSONSL_NO_JPR + size_t jpr_count; + jsonsl_jpr_t *jprs; + + /* Root pointer for JPR matching information */ + size_t *jpr_root; +#endif /* JSONSL_NO_JPR */ + /*@}*/ + + /** + * This is the stack. Its upper bound is levels_max, or the + * nlevels argument passed to jsonsl_new. If you modify this structure, + * make sure that this member is last. + */ + struct jsonsl_state_st stack[1]; +}; + + +/** + * Creates a new lexer object, with capacity for recursion up to nlevels + * + * @param nlevels maximum recursion depth + */ +JSONSL_API +jsonsl_t jsonsl_new(int nlevels); + +JSONSL_API +jsonsl_t jsonsl_init(jsonsl_t jsn, int nlevels); + +JSONSL_API +size_t jsonsl_get_size(int nlevels); + +/** + * Feeds data into the lexer. + * + * @param jsn the lexer object + * @param bytes new data to be fed + * @param nbytes size of new data + */ +JSONSL_API +void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes); + +/** + * Resets the internal parser state. This does not free the parser + * but does clean it internally, so that the next time feed() is called, + * it will be treated as a new stream + * + * @param jsn the lexer + */ +JSONSL_API +void jsonsl_reset(jsonsl_t jsn); + +/** + * Frees the lexer, cleaning any allocated memory taken + * + * @param jsn the lexer + */ +JSONSL_API +void jsonsl_destroy(jsonsl_t jsn); + +/** + * Gets the 'parent' element, given the current one + * + * @param jsn the lexer + * @param cur the current nest, which should be a struct jsonsl_nest_st + */ +static JSONSL_INLINE +struct jsonsl_state_st *jsonsl_last_state(const jsonsl_t jsn, + const struct jsonsl_state_st *state) +{ + /* Don't complain about overriding array bounds */ + if (state->level > 1) { + return jsn->stack + state->level - 1; + } else { + return NULL; + } +} + +/** + * Gets the state of the last fully consumed child of this parent. This is + * only valid in the parent's POP callback. + * + * @param the lexer + * @return A pointer to the child. + */ +static JSONSL_INLINE +struct jsonsl_state_st *jsonsl_last_child(const jsonsl_t jsn, + const struct jsonsl_state_st *parent) +{ + return jsn->stack + (parent->level + 1); +} + +/**Call to instruct the parser to stop parsing and return. This is valid + * only from within a callback */ +static JSONSL_INLINE +void jsonsl_stop(jsonsl_t jsn) +{ + jsn->stopfl = 1; +} + +/** + * This enables receiving callbacks on all events. Doesn't do + * anything special but helps avoid some boilerplate. + * This does not touch the UESCAPE callbacks or flags. + */ +static JSONSL_INLINE +void jsonsl_enable_all_callbacks(jsonsl_t jsn) +{ + jsn->call_HKEY = 1; + jsn->call_STRING = 1; + jsn->call_OBJECT = 1; + jsn->call_SPECIAL = 1; + jsn->call_LIST = 1; +} + +/** + * A macro which returns true if the current state object can + * have children. This means a list type or an object type. + */ +#define JSONSL_STATE_IS_CONTAINER(state) \ + (state->type == JSONSL_T_OBJECT || state->type == JSONSL_T_LIST) + +/** + * These two functions, dump a string representation + * of the error or type, respectively. They will never + * return NULL + */ +JSONSL_API +const char* jsonsl_strerror(jsonsl_error_t err); +JSONSL_API +const char* jsonsl_strtype(jsonsl_type_t jt); + +/** + * Dumps global metrics to the screen. This is a noop unless + * jsonsl was compiled with JSONSL_USE_METRICS + */ +JSONSL_API +void jsonsl_dump_global_metrics(void); + +/* This macro just here for editors to do code folding */ +#ifndef JSONSL_NO_JPR + +/** + * @name JSON Pointer API + * + * JSONPointer API. This isn't really related to the lexer (at least not yet) + * JSONPointer provides an extremely simple specification for providing + * locations within JSON objects. We will extend it a bit and allow for + * providing 'wildcard' characters by which to be able to 'query' the stream. + * + * See http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00 + * + * Currently I'm implementing the 'single query' API which can only use a single + * query component. In the future I will integrate my yet-to-be-published + * Boyer-Moore-esque prefix searching implementation, in order to allow + * multiple paths to be merged into one for quick and efficient searching. + * + * + * JPR (as we'll refer to it within the source) can be used by splitting + * the components into mutliple sections, and incrementally 'track' each + * component. When JSONSL delivers a 'pop' callback for a string, or a 'push' + * callback for an object, we will check to see whether the index matching + * the component corresponding to the current level contains a match + * for our path. + * + * In order to do this properly, a structure must be maintained within the + * parent indicating whether its children are possible matches. This flag + * will be 'inherited' by call children which may conform to the match + * specification, and discarded by all which do not (thereby eliminating + * their children from inheriting it). + * + * A successful match is a complete one. One can provide multiple paths with + * multiple levels of matches e.g. + * /foo/bar/baz/^/blah + * + * @{ + */ + +/** The wildcard character */ +#ifndef JSONSL_PATH_WILDCARD_CHAR +#define JSONSL_PATH_WILDCARD_CHAR '^' +#endif /* WILDCARD_CHAR */ + +#define JSONSL_XMATCH \ + X(COMPLETE,1) \ + X(POSSIBLE,0) \ + X(NOMATCH,-1) \ + X(TYPE_MISMATCH, -2) + +typedef enum { + +#define X(T,v) \ + JSONSL_MATCH_##T = v, + JSONSL_XMATCH + +#undef X + JSONSL_MATCH_UNKNOWN +} jsonsl_jpr_match_t; + +typedef enum { + JSONSL_PATH_STRING = 1, + JSONSL_PATH_WILDCARD, + JSONSL_PATH_NUMERIC, + JSONSL_PATH_ROOT, + + /* Special */ + JSONSL_PATH_INVALID = -1, + JSONSL_PATH_NONE = 0 +} jsonsl_jpr_type_t; + +struct jsonsl_jpr_component_st { + /** The string the component points to */ + char *pstr; + /** if this is a numeric type, the number is 'cached' here */ + unsigned long idx; + /** The length of the string */ + size_t len; + /** The type of component (NUMERIC or STRING) */ + jsonsl_jpr_type_t ptype; + + /** Set this to true to enforce type checking between dict keys and array + * indices. jsonsl_jpr_match() will return TYPE_MISMATCH if it detects + * that an array index is actually a child of a dictionary. */ + short is_arridx; + + /* Extra fields (for more advanced searches. Default is empty) */ + JSONSL_JPR_COMPONENT_USER_FIELDS +}; + +struct jsonsl_jpr_st { + /** Path components */ + struct jsonsl_jpr_component_st *components; + size_t ncomponents; + + /**Type of the match to be expected. If nonzero, will be compared against + * the actual type */ + unsigned match_type; + + /** Base of allocated string for components */ + char *basestr; + + /** The original match string. Useful for returning to the user */ + char *orig; + size_t norig; +}; + +/** + * Create a new JPR object. + * + * @param path the JSONPointer path specification. + * @param errp a pointer to a jsonsl_error_t. If this function returns NULL, + * then more details will be in this variable. + * + * @return a new jsonsl_jpr_t object, or NULL on error. + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp); + +/** + * Destroy a JPR object + */ +JSONSL_API +void jsonsl_jpr_destroy(jsonsl_jpr_t jpr); + +/** + * Match a JSON object against a type and specific level + * + * @param jpr the JPR object + * @param parent_type the type of the parent (should be T_LIST or T_OBJECT) + * @param parent_level the level of the parent + * @param key the 'key' of the child. If the parent is an array, this should be + * empty. + * @param nkey - the length of the key. If the parent is an array (T_LIST), then + * this should be the current index. + * + * NOTE: The key of the child means any kind of associative data related to the + * element. Thus: <<< { "foo" : [ >>, + * the opening array's key is "foo". + * + * @return a status constant. This indicates whether a match was excluded, possible, + * or successful. + */ +JSONSL_API +jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, + unsigned int parent_type, + unsigned int parent_level, + const char *key, size_t nkey); + +/** + * Alternate matching algorithm. This matching algorithm does not use + * JSONPointer but relies on a more structured searching mechanism. It + * assumes that there is a clear distinction between array indices and + * object keys. In this case, the jsonsl_path_component_st::ptype should + * be set to @ref JSONSL_PATH_NUMERIC for an array index (the + * jsonsl_path_comonent_st::is_arridx field will be removed in a future + * version). + * + * @param jpr The path + * @param parent The parent structure. Can be NULL if this is the root object + * @param child The child structure. Should not be NULL + * @param key Object key, if an object + * @param nkey Length of object key + * @return Status constant if successful + * + * @note + * For successful matching, both the key and the path itself should be normalized + * to contain 'proper' utf8 sequences rather than utf16 '\uXXXX' escapes. This + * should currently be done in the application. Another version of this function + * may use a temporary buffer in such circumstances (allocated by the application). + * + * Since this function also checks the state of the child, it should only + * be called on PUSH callbacks, and not POP callbacks + */ +JSONSL_API +jsonsl_jpr_match_t +jsonsl_path_match(jsonsl_jpr_t jpr, + const struct jsonsl_state_st *parent, + const struct jsonsl_state_st *child, + const char *key, size_t nkey); + + +/** + * Associate a set of JPR objects with a lexer instance. + * This should be called before the lexer has been fed any data (and + * behavior is undefined if you don't adhere to this). + * + * After using this function, you may subsequently call match_state() on + * given states (presumably from within the callbacks). + * + * Note that currently the first JPR is the quickest and comes + * pre-allocated with the state structure. Further JPR objects + * are chained. + * + * @param jsn The lexer + * @param jprs An array of jsonsl_jpr_t objects + * @param njprs How many elements in the jprs array. + */ +JSONSL_API +void jsonsl_jpr_match_state_init(jsonsl_t jsn, + jsonsl_jpr_t *jprs, + size_t njprs); + +/** + * This follows the same semantics as the normal match, + * except we infer parent and type information from the relevant state objects. + * The match status (for all possible JPR objects) is set in the *out parameter. + * + * If a match has succeeded, then its JPR object will be returned. In all other + * instances, NULL is returned; + * + * @param jpr The jsonsl_jpr_t handle + * @param state The jsonsl_state_st which is a candidate + * @param key The hash key (if applicable, can be NULL if parent is list) + * @param nkey Length of hash key (if applicable, can be zero if parent is list) + * @param out A pointer to a jsonsl_jpr_match_t. This will be populated with + * the match result + * + * @return If a match was completed in full, then the JPR object containing + * the matching path will be returned. Otherwise, the return is NULL (note, this + * does not mean matching has failed, it can still be part of the match: check + * the out parameter). + */ +JSONSL_API +jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, + struct jsonsl_state_st *state, + const char *key, + size_t nkey, + jsonsl_jpr_match_t *out); + + +/** + * Cleanup any memory allocated and any states set by + * match_state_init() and match_state() + * @param jsn The lexer + */ +JSONSL_API +void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn); + +/** + * Return a string representation of the match result returned by match() + */ +JSONSL_API +const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match); + +/* @}*/ + +/** + * Utility function to convert escape sequences into their original form. + * + * The decoders I've sampled do not seem to specify a standard behavior of what + * to escape/unescape. + * + * RFC 4627 Mandates only that the quoute, backslash, and ASCII control + * characters (0x00-0x1f) be escaped. It is often common for applications + * to escape a '/' - however this may also be desired behavior. the JSON + * spec is not clear on this, and therefore jsonsl leaves it up to you. + * + * Additionally, sometimes you may wish to _normalize_ JSON. This is specifically + * true when dealing with 'u-escapes' which can be expressed perfectly fine + * as utf8. One use case for normalization is JPR string comparison, in which + * case two effectively equivalent strings may not match because one is using + * u-escapes and the other proper utf8. To normalize u-escapes only, pass in + * an empty `toEscape` table, enabling only the `u` index. + * + * @param in The input string. + * @param out An allocated output (should be the same size as in) + * @param len the size of the buffer + * @param toEscape - A sparse array of characters to unescape. Characters + * which are not present in this array, e.g. toEscape['c'] == 0 will be + * ignored and passed to the output in their original form. + * @param oflags If not null, and a \uXXXX escape expands to a non-ascii byte, + * then this variable will have the SPECIALf_NONASCII flag on. + * + * @param err A pointer to an error variable. If an error ocurrs, it will be + * set in this variable + * @param errat If not null and an error occurs, this will be set to point + * to the position within the string at which the offending character was + * encountered. + * + * @return The effective size of the output buffer. + * + * @note + * This function now encodes the UTF8 equivalents of utf16 escapes (i.e. + * 'u-escapes'). Previously this would encode the escapes as utf16 literals, + * which while still correct in some sense was confusing for many (especially + * considering that the inputs were variations of char). + * + * @note + * The output buffer will never be larger than the input buffer, since + * standard escape sequences (i.e. '\t') occupy two bytes in the source + * but only one byte (when unescaped) in the output. Likewise u-escapes + * (i.e. \uXXXX) will occupy six bytes in the source, but at the most + * two bytes when escaped. + */ +JSONSL_API +size_t jsonsl_util_unescape_ex(const char *in, + char *out, + size_t len, + const int toEscape[128], + unsigned *oflags, + jsonsl_error_t *err, + const char **errat); + +/** + * Convenience macro to avoid passing too many parameters + */ +#define jsonsl_util_unescape(in, out, len, toEscape, err) \ + jsonsl_util_unescape_ex(in, out, len, toEscape, NULL, err, NULL) + +#endif /* JSONSL_NO_JPR */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* JSONSL_H_ */ diff --git a/docs/en/modules/cjson.md b/docs/en/modules/cjson.md index 32f7b9b848..bc3e611ac5 100644 --- a/docs/en/modules/cjson.md +++ b/docs/en/modules/cjson.md @@ -1,52 +1,5 @@ # CJSON Module -| Since | Origin / Contributor | Maintainer | Source | -| :----- | :-------------------- | :---------- | :------ | -| 2015-03-16 | [Mark Pulford](http://kyne.com.au/~mark/software/lua-cjson.php), [Zeroday](https://github.com/funshine) | [Zeroday](https://github.com/funshine) | [cjson](../../../app/modules/cjson.c) | -The JSON support module. Allows encoding and decoding to/from JSON. +This module has been replaced by [sjson](sjson.md). It provides a superset of functionality. All references to `cjson` can be replaced by `sjson`. -Please note that nested tables can require a lot of memory to encode. To catch out-of-memory errors, use `pcall()`. -## cjson.encode() - -Encode a Lua table to a JSON string. For details see the [documentation of the original Lua library](http://kyne.com.au/~mark/software/lua-cjson-manual.html#encode). - -####Syntax -`cjson.encode(table)` - -####Parameters -`table` data to encode - -While it also is possible to encode plain strings and numbers rather than a table, it is not particularly useful to do so. - -####Returns -JSON string - -####Example -```lua -ok, json = pcall(cjson.encode, {key="value"}) -if ok then - print(json) -else - print("failed to encode!") -end -``` - -## cjson.decode() - -Decode a JSON string to a Lua table. For details see the [documentation of the original Lua library](http://kyne.com.au/~mark/software/lua-cjson-manual.html#_decode). - -####Syntax -`cjson.decode(str)` - -####Parameters -`str` JSON string to decode - -####Returns -Lua table representation of the JSON data - -####Example -```lua -t = cjson.decode('{"key":"value"}') -for k,v in pairs(t) do print(k,v) end -``` diff --git a/docs/en/modules/sjson.md b/docs/en/modules/sjson.md new file mode 100644 index 0000000000..ab67f773ee --- /dev/null +++ b/docs/en/modules/sjson.md @@ -0,0 +1,234 @@ +# SJSON Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-02-01 | [Philip Gladstone](https://github.com/pjsg) | [Philip Gladstone](https://github.com/pjsg) | [sjson](../../../app/modules/sjson.c) | + +The JSON support module. Allows encoding and decoding to/from JSON. + +Please note that nested tables can require a lot of memory to encode. To catch out-of-memory errors, use `pcall()`. + +This code using the streaming json library [jsonsl](https://github.com/mnunberg/jsonsl) to do the parsing of the string. + +This module can be used in two ways. The simpler way is to use it as a direct drop-in for cjson (you can just do `_G.cjson = sjson`). +The more advanced approach is to use the streaming interface. This allows encoding and decoding of significantly larger objects. + +The handling of json null is as follows: + +- By default, the decoder represents null as sjson.NULL (which is a userdata object). This is the behavior of cjson. +- The encoder always converts any userdata object into null. +- Optionally, a single string can be specified in both the encoder and decoder. This string will be used in encoding/decoding to represent json null values. This string should not be used +anywhere else in your data structures. A suitable value might be `"\0"`. + +When encoding a lua object, if a function is found, then it is invoked (with no arguments) and the (single) returned value is encoded in the place of the function. + +## sjson.encoder() + +This creates an encoder object that can convert a LUA object into a JSON encoded string. + +####Syntax +`sjson.encoder(table [, opts])` + +####Parameters +- `table` data to encode +- `opts` an optional table of options. The possible entries are: + - `depth` the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations. + - `null` the string value to treat as null. + +####Returns +A `sjson.encoder` object. + +## sjson.encoder:read + +This gets a chunk of JSON encoded data. + +####Syntax +`encoder:read([size])` + +####Parameters +- `size` an optional value for the number of bytes to return. The default is 1024. + +####Returns +A string of up to `size` bytes, or `nil` if the encoding is complete and all data has been returned. + +#### Example +The following example prints out (in 64 byte chunks) a JSON encoded string containing the first 4k of every file in the file system. The total string +can be bigger than the total amount of memory on the NodeMCU. +``` +function files() + result = {} + for k,v in pairs(file.list()) do + result[k] = function() return file.open(k):read(4096) end + end + return result +end + +local encoder = sjson.encoder(files()) + +while true do + data = encoder:read(64) + if not data then + break + end + print(data) +end +``` + +## sjson.encode() + +Encode a Lua table to a JSON string. This is a convenience method provided for backwards compatibility with `cjson`. + +####Syntax +`sjson.encode(table [, opts])` + +####Parameters +- `table` data to encode +- `opts` an optional table of options. The possible entries are: + - `depth` the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations. + - `null` the string value to treat as null. + +####Returns +JSON string + +####Example +```lua +ok, json = pcall(sjson.encode, {key="value"}) +if ok then + print(json) +else + print("failed to encode!") +end +``` + +## sjson.decoder() + +This makes a decoder object that can parse a JSON encoded string into a lua object. A metatable can be specified for all the newly created lua tables. This allows +you to handle each value as it is inserted into each table (by implementing the `__newindex` method). + +####Syntax +`sjson.decoder([opts])` + +#### Parameters +- `opts` an optional table of options. The possible entries are: + - `depth` the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations. + - `null` the string value to treat as null. + - `metatable` a table to use as the metatable for all the new tables in the returned object. + +#### Returns +A `sjson.decoder` object + +####Metatable + +There are two principal methods that are invoked in the metatable (if it is present). + +- `__newindex` this is the standard method invoked whenever a new table element is created. +- `checkpath` this is invoked (if defined) whenever a new table is created. It is invoked with two arguments: + - `table` this is the newly created table + - `path` this is a list of the keys from the root. + It must return `true` if this object is wanted in the result, or `false` otherwise. + +For example, when decoding `{ "foo": [1, 2, []] }` the checkpath will be invoked as follows: + +- `checkpath({}, {})` the `table` argument is the object that will correspond with the value of the JSON object. +- `checkpath({}, {"foo"})` the `table` argument is the object that will correspond with the value of the outer JSON array. +- `checkpath({}, {"foo", 3})` the `table` argument is the object that will correspond to the empty inner JSON array. + +When the `checkpath` method is called, the metatable has already be associated with the new table. Thus the `checkpath` method can replace it +if desired. For example, if you are decoding `{ "foo": { "bar": [1,2,3,4], "cat": [5] } }` and, for some reason, you did not want to capture the +value of the `"bar"` key, then there are various ways to do this: + +* In the `__newindex` metamethod, just check for the value of the key and skip the `rawset` if the key is `"bar"`. This only works if you want to skip all the +`"bar"` keys. + +* In the `checkpath` method, if the path is `["foo"]`, then return `false`. + +* Use the following `checkpath`: `checkpath=function(tab, path) tab['__json_path'] = path return true end` This will save the path in each constructed object. Now the `__newindex` method can perform more sophisticated filtering. + +The reason for being able to filter is that it enables processing of very large JSON responses on a memory constrained platform. Many APIs return lots of information +which would exceed the memory budget of the platform. For example, `https://api.github.com/repos/nodemcu/nodemcu-firmware/contents` is over 13kB, and yet, if +you only need the `download_url` keys, then the total size is around 600B. This can be handled with a simple `__newindex` method. + +## sjson.decoder:write + +This provides more data to be parsed into the lua object. + +####Syntax +`decoder:write(string)` + +####Parameters + +- `string` the next piece of JSON encoded data + +####Returns +The constructed lua object or `nil` if the decode is not yet complete. + +####Errors +If a parse error occurrs during this decode, then an error is thrown and the parse is aborted. The object cannot be used again. + + +## sjson.decoder:result + +This gets the decoded lua object, or raises an error if the decode is not yet complete. This can be called multiple times and will return the +same object each time. + +####Syntax +`decoder:result()` + +####Errors +If the decode is not complete, then an error is thrown. + +####Example +``` +local decoder = sjson.decoder() + +decoder:write("[10, 1") +decoder:write("1") +decoder:write(", \"foo\"]") + +for k,v in pairs(decoder:result()) do + print (k, v) +end +``` + +The next example demonstrates the use of the metatable argument. In this case it just prints out the operations, but it could suppress the assignment +altogether if desired. + +``` +local decoder = sjson.decoder({metatable= + {__newindex=function(t,k,v) print("Setting '" .. k .. "' = '" .. tostring(v) .."'") + rawset(t,k,v) end}}) + +decoder:write('[1, 2, {"foo":"bar"}]') + +``` + + +## sjson.decode() + +Decode a JSON string to a Lua table. This is a convenience method provided for backwards compatibility with `cjson`. + +####Syntax +`sjson.decode(str[, opts])` + +####Parameters +- `str` JSON string to decode +- `opts` an optional table of options. The possible entries are: + - `depth` the maximum encoding depth needed to encode the table. The default is 20 which should be enough for nearly all situations. + - `null` the string value to treat as null. + - `metatable` a table to use as the metatable for all the new tables in the returned object. See the metatable section in the description of `sjson.decoder()` above. + +####Returns +Lua table representation of the JSON data + +####Errors +If the string is not valid JSON, then an error is thrown. + +####Example +```lua +t = sjson.decode('{"key":"value"}') +for k,v in pairs(t) do print(k,v) end +``` + +##Constants + +There is one constant -- `sjson.NULL` -- which is used in lua structures to represent the presence of a JSON null. + diff --git a/ld/nodemcu.ld b/ld/nodemcu.ld index a27affa9a0..d2136862b3 100644 --- a/ld/nodemcu.ld +++ b/ld/nodemcu.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 = 0xD0000 + irom0_0_seg : org = 0x40210000, len = 0xE0000 } PHDRS diff --git a/lua_examples/mqtt/mqtt_file.lua b/lua_examples/mqtt/mqtt_file.lua index b491177c8f..6d0d88e40b 100644 --- a/lua_examples/mqtt/mqtt_file.lua +++ b/lua_examples/mqtt/mqtt_file.lua @@ -18,7 +18,7 @@ end -- payload(json): {"cmd":xxx,"content":xxx} function topic1func(m,pl) print("get1: "..pl) - local pack = cjson.decode(pl) + local pack = sjson.decode(pl) if pack.content then if pack.cmd == "open" then file.open(pack.content,"w+") elseif pack.cmd == "write" then file.write(pack.content) diff --git a/lua_examples/somfy.lua b/lua_examples/somfy.lua index f3baa3edf5..59a184678d 100644 --- a/lua_examples/somfy.lua +++ b/lua_examples/somfy.lua @@ -1,4 +1,4 @@ --- Somfy module example (beside somfy module requires also CJSON module) +-- Somfy module example (beside somfy module requires also SJSON module) -- The rolling code number is stored in the file somfy.cfg. A cached write of the somfy.cfg file is implemented in order to reduce the number of write to the EEPROM memory. Together with the logic of the file module it should allow long lasting operation. config_file = "somfy." @@ -43,7 +43,7 @@ function readconfig() end if not ln then ln = "{}" end print("Configuration: "..ln) - config = cjson.decode(ln) + config = sjson.decode(ln) config_saved = deepcopy(config) end @@ -52,7 +52,7 @@ function writeconfighard() file.remove(config_file.."bak") file.rename(config_file.."cfg", config_file.."bak") file.open(config_file.."cfg", "w+") - local ok, cfg = pcall(cjson.encode, config) + local ok, cfg = pcall(sjson.encode, config) if ok then file.writeline(cfg) else @@ -68,8 +68,8 @@ function writeconfig() local savenow = false local savelater = false ---print("Config: "..cjson.encode(config)) ---print("Config saved: "..cjson.encode(config)) +--print("Config: "..sjson.encode(config)) +--print("Config saved: "..sjson.encode(config)) local count = 0 for _ in pairs(config_saved) do count = count + 1 end @@ -134,7 +134,7 @@ end --======================================================================================================-- if not config then readconfig() end if #config == 0 then -- somfy.cfg does not exist - config = cjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]]) + config = sjson.decode([[{"window1":{"rc":1,"address":123},"window2":{"rc":1,"address":124}}]]) config_saved = deepcopy(config) end down('window1', diff --git a/mkdocs.yml b/mkdocs.yml index d2753d2161..3c8f585b2c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,7 @@ pages: - 'rtcmem': 'en/modules/rtcmem.md' - 'rtctime': 'en/modules/rtctime.md' - 'sigma delta': 'en/modules/sigma-delta.md' + - 'sjson': 'en/modules/sjson.md' - 'sntp': 'en/modules/sntp.md' - 'somfy': 'en/modules/somfy.md' - 'spi': 'en/modules/spi.md' From 92cfbb45c330eae5b1c2bf6a3c24a80a18f1747e Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Mon, 27 Mar 2017 09:20:08 +0300 Subject: [PATCH 37/73] Cron fixes (#1884) * Fix cron values >32 * Fix crontab description items order --- app/modules/cron.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/modules/cron.c b/app/modules/cron.c index 0c8af2e8d6..8d93b6af8b 100644 --- a/app/modules/cron.c +++ b/app/modules/cron.c @@ -53,7 +53,7 @@ static uint64_t lcron_parsepart(lua_State *L, char *str, char **end, uint8_t min if (val < min || val > max) { return luaL_error(L, "invalid spec (val %d out of range %d..%d)", val, min, max); } - res |= (1 << (val - min)); + res |= (uint64_t)1 << (val - min); if (**end != ',') break; str = *end + 1; } @@ -67,11 +67,11 @@ static int lcron_parsedesc(lua_State *L, char *str, struct cronent_desc *desc) { if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str); desc->hour = lcron_parsepart(L, s + 1, &s, 0, 23); if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str); - desc->dow = lcron_parsepart(L, s + 1, &s, 0, 6); - if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str); desc->dom = lcron_parsepart(L, s + 1, &s, 1, 31); if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str); desc->mon = lcron_parsepart(L, s + 1, &s, 1, 12); + if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str); + desc->dow = lcron_parsepart(L, s + 1, &s, 0, 6); if (*s != 0) return luaL_error(L, "invalid spec (trailing @%d)", s - str); return 0; } From fc887e9f9f01a791c1b14dcf2b2835acd679dadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Wed, 29 Mar 2017 13:58:40 +0200 Subject: [PATCH 38/73] Fix memory leak in file_stat(). (#1871) * Change vfs_stat() api to pre-allocated buffer for stat info. * Change vfs_readdir() api to stat buffer as well. vfs_item api removed. --- app/fatfs/myfatfs.c | 159 +++++++++-------------------------------- app/modules/file.c | 50 ++++++------- app/platform/vfs.c | 8 +-- app/platform/vfs.h | 59 ++------------- app/platform/vfs_int.h | 38 ++++------ app/spiffs/spiffs.c | 101 +++++++------------------- 6 files changed, 106 insertions(+), 309 deletions(-) diff --git a/app/fatfs/myfatfs.c b/app/fatfs/myfatfs.c index afa73a4d1f..c929ededb0 100644 --- a/app/fatfs/myfatfs.c +++ b/app/fatfs/myfatfs.c @@ -29,22 +29,12 @@ static uint32_t myfatfs_fsize( const struct vfs_file *fd ); static sint32_t myfatfs_ferrno( const struct vfs_file *fd ); static sint32_t myfatfs_closedir( const struct vfs_dir *dd ); -static vfs_item *myfatfs_readdir( const struct vfs_dir *dd ); - -static void myfatfs_iclose( const struct vfs_item *di ); -static uint32_t myfatfs_isize( const struct vfs_item *di ); -static sint32_t myfatfs_time( const struct vfs_item *di, struct vfs_time *tm ); -static const char *myfatfs_name( const struct vfs_item *di ); -static sint32_t myfatfs_is_dir( const struct vfs_item *di ); -static sint32_t myfatfs_is_rdonly( const struct vfs_item *di ); -static sint32_t myfatfs_is_hidden( const struct vfs_item *di ); -static sint32_t myfatfs_is_sys( const struct vfs_item *di ); -static sint32_t myfatfs_is_arch( const struct vfs_item *di ); +static sint32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ); static vfs_vol *myfatfs_mount( const char *name, int num ); static vfs_file *myfatfs_open( const char *name, const char *mode ); static vfs_dir *myfatfs_opendir( const char *name ); -static vfs_item *myfatfs_stat( const char *name ); +static sint32_t myfatfs_stat( const char *name, struct vfs_stat *buf ); static sint32_t myfatfs_remove( const char *name ); static sint32_t myfatfs_rename( const char *oldname, const char *newname ); static sint32_t myfatfs_mkdir( const char *name ); @@ -89,18 +79,6 @@ static vfs_file_fns myfatfs_file_fns = { .ferrno = myfatfs_ferrno }; -static vfs_item_fns myfatfs_item_fns = { - .close = myfatfs_iclose, - .size = myfatfs_isize, - .time = myfatfs_time, - .name = myfatfs_name, - .is_dir = myfatfs_is_dir, - .is_rdonly = myfatfs_is_rdonly, - .is_hidden = myfatfs_is_hidden, - .is_sys = myfatfs_is_sys, - .is_arch = myfatfs_is_arch -}; - static vfs_dir_fns myfatfs_dir_fns = { .close = myfatfs_closedir, .readdir = myfatfs_readdir @@ -130,11 +108,6 @@ struct myvfs_dir { DIR dp; }; -struct myvfs_item { - struct vfs_item vfs_item; - FILINFO fno; -}; - // --------------------------------------------------------------------------- // exported helper functions for FatFS @@ -321,105 +294,45 @@ static sint32_t myfatfs_closedir( const struct vfs_dir *dd ) return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR; } -static vfs_item *myfatfs_readdir( const struct vfs_dir *dd ) +static void myfatfs_fill_stat( const FILINFO *fno, struct vfs_stat *buf ) { - GET_DIR_DP(dd); - struct myvfs_item *di; + c_memset( buf, 0, sizeof( struct vfs_stat ) ); - if (di = c_malloc( sizeof( struct myvfs_item ) )) { - FILINFO *fno = &(di->fno); - - if (FR_OK == (last_result = f_readdir( dp, fno ))) { - // condition "no further item" is signalled with empty name - if (fno->fname[0] != '\0') { - di->vfs_item.fs_type = VFS_FS_FATFS; - di->vfs_item.fns = &myfatfs_item_fns; - return (vfs_item *)di; - } - } - c_free( di ); - } - - return NULL; -} - - -// --------------------------------------------------------------------------- -// dir info functions -// -#define GET_FILINFO_FNO(descr) \ - const struct myvfs_item *mydi = (const struct myvfs_item *)descr; \ - FILINFO *fno = (FILINFO *)&(mydi->fno); - -static void myfatfs_iclose( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); - - // free descriptor memory - c_free( (void *)di ); -} - -static uint32_t myfatfs_isize( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); - - return fno->fsize; -} - -static sint32_t myfatfs_time( const struct vfs_item *di, struct vfs_time *tm ) -{ - GET_FILINFO_FNO(di); + // fill in supported stat entries + c_strncpy( buf->name, fno->fname, FS_OBJ_NAME_LEN+1 ); + buf->name[FS_OBJ_NAME_LEN] = '\0'; + buf->size = fno->fsize; + buf->is_dir = fno->fattrib & AM_DIR ? 1 : 0; + buf->is_rdonly = fno->fattrib & AM_RDO ? 1 : 0; + buf->is_hidden = fno->fattrib & AM_HID ? 1 : 0; + buf->is_sys = fno->fattrib & AM_SYS ? 1 : 0; + buf->is_arch = fno->fattrib & AM_ARC ? 1 : 0; + struct vfs_time *tm = &(buf->tm); tm->year = (fno->fdate >> 9) + 1980; tm->mon = (fno->fdate >> 5) & 0x0f; tm->day = fno->fdate & 0x1f; tm->hour = (fno->ftime >> 11); tm->min = (fno->ftime >> 5) & 0x3f; tm->sec = fno->ftime & 0x3f; - - return VFS_RES_OK; + buf->tm_valid = 1; } -static const char *myfatfs_name( const struct vfs_item *di ) +static sint32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ) { - GET_FILINFO_FNO(di); - - return fno->fname; -} - -static sint32_t myfatfs_is_dir( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); - - return fno->fattrib & AM_DIR ? 1 : 0; -} - -static sint32_t myfatfs_is_rdonly( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); - - return fno->fattrib & AM_RDO ? 1 : 0; -} - -static sint32_t myfatfs_is_hidden( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); - - return fno->fattrib & AM_HID ? 1 : 0; -} - -static sint32_t myfatfs_is_sys( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); + GET_DIR_DP(dd); + FILINFO fno; - return fno->fattrib & AM_SYS ? 1 : 0; -} + if (FR_OK == (last_result = f_readdir( dp, &fno ))) { + // condition "no further item" is signalled with empty name + if (fno.fname[0] != '\0') { + myfatfs_fill_stat( &fno, buf ); -static sint32_t myfatfs_is_arch( const struct vfs_item *di ) -{ - GET_FILINFO_FNO(di); + return VFS_RES_OK; + } + } - return fno->fattrib & AM_ARC ? 1 : 0; + return VFS_RES_ERR; } @@ -521,21 +434,17 @@ static vfs_dir *myfatfs_opendir( const char *name ) return NULL; } -static vfs_item *myfatfs_stat( const char *name ) +static sint32_t myfatfs_stat( const char *name, struct vfs_stat *buf ) { - struct myvfs_item *di; + FILINFO fno; - if (di = c_malloc( sizeof( struct myvfs_item ) )) { - if (FR_OK == (last_result = f_stat( name, &(di->fno) ))) { - di->vfs_item.fs_type = VFS_FS_FATFS; - di->vfs_item.fns = &myfatfs_item_fns; - return (vfs_item *)di; - } else { - c_free( di ); - } - } + if (FR_OK == (last_result = f_stat( name, &fno ))) { + myfatfs_fill_stat( &fno, buf ); - return NULL; + return VFS_RES_OK; + } else { + return VFS_RES_ERR; + } } static sint32_t myfatfs_remove( const char *name ) diff --git a/app/modules/file.c b/app/modules/file.c index daa70cf29b..80a4ea00c9 100644 --- a/app/modules/file.c +++ b/app/modules/file.c @@ -214,14 +214,13 @@ static int file_open( lua_State* L ) static int file_list( lua_State* L ) { vfs_dir *dir; - vfs_item *item; if (dir = vfs_opendir("")) { lua_newtable( L ); - while (item = vfs_readdir(dir)) { - lua_pushinteger(L, vfs_item_size(item)); - lua_setfield(L, -2, vfs_item_name(item)); - vfs_closeitem(item); + struct vfs_stat stat; + while (vfs_readdir(dir, &stat) == VFS_RES_OK) { + lua_pushinteger(L, stat.size); + lua_setfield(L, -2, stat.name); } vfs_closedir(dir); return 1; @@ -270,11 +269,8 @@ static int file_exists( lua_State* L ) const char *basename = vfs_basename( fname ); luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid"); - vfs_item *stat = vfs_stat((char *)fname); - - lua_pushboolean(L, stat ? 1 : 0); - - if (stat) vfs_closeitem(stat); + struct vfs_stat stat; + lua_pushboolean(L, vfs_stat((char *)fname, &stat) == VFS_RES_OK ? 1 : 0); return 1; } @@ -332,58 +328,54 @@ static int file_stat( lua_State* L ) const char *fname = luaL_checklstring( L, 1, &len ); luaL_argcheck( L, c_strlen(fname) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid" ); - vfs_item *stat = vfs_stat( (char *)fname ); - - if (!stat) { + struct vfs_stat stat; + if (vfs_stat( (char *)fname, &stat ) != VFS_RES_OK) { lua_pushnil( L ); return 1; } lua_createtable( L, 0, 7 ); - lua_pushinteger( L, vfs_item_size( stat ) ); + lua_pushinteger( L, stat.size ); lua_setfield( L, -2, "size" ); - lua_pushstring( L, vfs_item_name( stat ) ); + lua_pushstring( L, stat.name ); lua_setfield( L, -2, "name" ); - lua_pushboolean( L, vfs_item_is_dir( stat ) ); + lua_pushboolean( L, stat.is_dir ); lua_setfield( L, -2, "is_dir" ); - lua_pushboolean( L, vfs_item_is_rdonly( stat ) ); + lua_pushboolean( L, stat.is_rdonly ); lua_setfield( L, -2, "is_rdonly" ); - lua_pushboolean( L, vfs_item_is_hidden( stat ) ); + lua_pushboolean( L, stat.is_hidden ); lua_setfield( L, -2, "is_hidden" ); - lua_pushboolean( L, vfs_item_is_sys( stat ) ); + lua_pushboolean( L, stat.is_sys ); lua_setfield( L, -2, "is_sys" ); - lua_pushboolean( L, vfs_item_is_arch( stat ) ); + lua_pushboolean( L, stat.is_arch ); lua_setfield( L, -2, "is_arch" ); // time stamp as sub-table - vfs_time tm; - int got_time = VFS_RES_OK == vfs_item_time( stat, &tm ) ? TRUE : FALSE; - lua_createtable( L, 0, 6 ); - lua_pushinteger( L, got_time ? tm.year : FILE_TIMEDEF_YEAR ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.year : FILE_TIMEDEF_YEAR ); lua_setfield( L, -2, "year" ); - lua_pushinteger( L, got_time ? tm.mon : FILE_TIMEDEF_MON ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.mon : FILE_TIMEDEF_MON ); lua_setfield( L, -2, "mon" ); - lua_pushinteger( L, got_time ? tm.day : FILE_TIMEDEF_DAY ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.day : FILE_TIMEDEF_DAY ); lua_setfield( L, -2, "day" ); - lua_pushinteger( L, got_time ? tm.hour : FILE_TIMEDEF_HOUR ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.hour : FILE_TIMEDEF_HOUR ); lua_setfield( L, -2, "hour" ); - lua_pushinteger( L, got_time ? tm.min : FILE_TIMEDEF_MIN ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.min : FILE_TIMEDEF_MIN ); lua_setfield( L, -2, "min" ); - lua_pushinteger( L, got_time ? tm.sec : FILE_TIMEDEF_SEC ); + lua_pushinteger( L, stat.tm_valid ? stat.tm.sec : FILE_TIMEDEF_SEC ); lua_setfield( L, -2, "sec" ); lua_setfield( L, -2, "time" ); diff --git a/app/platform/vfs.c b/app/platform/vfs.c index 0446a11baf..444d4c4392 100644 --- a/app/platform/vfs.c +++ b/app/platform/vfs.c @@ -139,7 +139,7 @@ vfs_dir *vfs_opendir( const char *name ) return NULL; } -vfs_item *vfs_stat( const char *name ) +sint32_t vfs_stat( const char *name, struct vfs_stat *buf ) { vfs_fs_fns *fs_fns; const char *normname = normalize_path( name ); @@ -147,19 +147,19 @@ vfs_item *vfs_stat( const char *name ) #ifdef BUILD_SPIFFS if (fs_fns = myspiffs_realm( normname, &outname, FALSE )) { - return fs_fns->stat( outname ); + return fs_fns->stat( outname, buf ); } #endif #ifdef BUILD_FATFS if (fs_fns = myfatfs_realm( normname, &outname, FALSE )) { - vfs_item *r = fs_fns->stat( outname ); + sint32_t r = fs_fns->stat( outname, buf ); c_free( outname ); return r; } #endif - return NULL; + return VFS_RES_ERR; } sint32_t vfs_remove( const char *name ) diff --git a/app/platform/vfs.h b/app/platform/vfs.h index 572d037284..53a8e5b9b4 100644 --- a/app/platform/vfs.h +++ b/app/platform/vfs.h @@ -104,57 +104,9 @@ inline sint32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); } // vfs_readdir - read next directory item // dd: dir descriptor -// Returns: item object, or NULL in case of error -inline vfs_item *vfs_readdir( vfs_dir *dd ) { return dd->fns->readdir( dd ); } - -// --------------------------------------------------------------------------- -// dir item functions -// - -// vfs_closeitem - close directory item and free memory -// di: item descriptor -// Returns: nothing -inline void vfs_closeitem( vfs_item *di ) { return di->fns->close( di ); } - -// vfs_item_size - get item's size -// di: item descriptor -// Returns: Item size -inline uint32_t vfs_item_size( vfs_item *di ) { return di->fns->size( di ); } - -// vfs_item_time - get item's modification time -// di: item descriptor -// Returns: Item modification time -inline sint32_t vfs_item_time( vfs_item *di, struct vfs_time *tm ) { return di->fns->time ? di->fns->time( di, tm ) : VFS_RES_ERR; } - -// vfs_item_name - get item's name -// di: item descriptor -// Returns: Item name -inline const char *vfs_item_name( vfs_item *di ) { return di->fns->name( di ); } - -// vfs_item_is_dir - check for directory -// di: item descriptor -// Returns: >0 if item is a directory, 0 if not -inline sint32_t vfs_item_is_dir( vfs_item *di ) { return di->fns->is_dir ? di->fns->is_dir( di ) : 0; } - -// vfs_item_is_rdonly - check for read-only -// di: item descriptor -// Returns: >0 if item is read only, 0 if not -inline sint32_t vfs_item_is_rdonly( vfs_item *di ) { return di->fns->is_rdonly ? di->fns->is_rdonly( di ) : 0; } - -// vfs_item_is_hidden - check for hidden attribute -// di: item descriptor -// Returns: >0 if item is hidden, 0 if not -inline sint32_t vfs_item_is_hidden( vfs_item *di ) { return di->fns->is_hidden ? di->fns->is_hidden( di ) : 0; } - -// vfs_item_is_sys - check for sys attribute -// di: item descriptor -// Returns: >0 if item is sys, 0 if not -inline sint32_t vfs_item_is_sys( vfs_item *di ) { return di->fns->is_sys ? di->fns->is_sys( di ) : 0; } - -// vfs_item_is_arch - check for archive attribute -// di: item descriptor -// Returns: >0 if item is archive, 0 if not -inline sint32_t vfs_item_is_arch( vfs_item *di ) { return di->fns->is_arch ? di->fns->is_arch( di ) : 0; } +// buf: pre-allocated stat structure to be filled in +// Returns: VFS_RES_OK if next item found, otherwise VFS_RES_ERR +inline sint32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); } // --------------------------------------------------------------------------- // volume functions @@ -188,8 +140,9 @@ vfs_dir *vfs_opendir( const char *name ); // vfs_stat - stat file or directory // name: file or directory name -// Returns: Item object, or NULL in case of error -vfs_item *vfs_stat( const char *name ); +// buf: pre-allocated structure to be filled in +// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error +sint32_t vfs_stat( const char *name, struct vfs_stat *buf ); // vfs_remove - remove file or directory // name: file or directory name diff --git a/app/platform/vfs_int.h b/app/platform/vfs_int.h index c1caef0fd7..f1a0539f2d 100644 --- a/app/platform/vfs_int.h +++ b/app/platform/vfs_int.h @@ -47,6 +47,19 @@ struct vfs_file { }; typedef const struct vfs_file vfs_file; +// stat data +struct vfs_stat { + uint32_t size; + char name[FS_OBJ_NAME_LEN+1]; + struct vfs_time tm; + uint8_t tm_valid; + uint8_t is_dir; + uint8_t is_rdonly; + uint8_t is_hidden; + uint8_t is_sys; + uint8_t is_arch; +}; + // file descriptor functions struct vfs_file_fns { sint32_t (*close)( const struct vfs_file *fd ); @@ -61,27 +74,6 @@ struct vfs_file_fns { }; typedef const struct vfs_file_fns vfs_file_fns; -// generic dir item descriptor -struct vfs_item { - int fs_type; - const struct vfs_item_fns *fns; -}; -typedef const struct vfs_item vfs_item; - -// directory item functions -struct vfs_item_fns { - void (*close)( const struct vfs_item *di ); - uint32_t (*size)( const struct vfs_item *di ); - sint32_t (*time)( const struct vfs_item *di, struct vfs_time *tm ); - const char *(*name)( const struct vfs_item *di ); - sint32_t (*is_dir)( const struct vfs_item *di ); - sint32_t (*is_rdonly)( const struct vfs_item *di ); - sint32_t (*is_hidden)( const struct vfs_item *di ); - sint32_t (*is_sys)( const struct vfs_item *di ); - sint32_t (*is_arch)( const struct vfs_item *di ); -}; -typedef const struct vfs_item_fns vfs_item_fns; - // generic dir descriptor struct vfs_dir { int fs_type; @@ -92,7 +84,7 @@ typedef const struct vfs_dir vfs_dir; // dir descriptor functions struct vfs_dir_fns { sint32_t (*close)( const struct vfs_dir *dd ); - vfs_item *(*readdir)( const struct vfs_dir *dd ); + sint32_t (*readdir)( const struct vfs_dir *dd, struct vfs_stat *buf ); }; typedef const struct vfs_dir_fns vfs_dir_fns; @@ -113,7 +105,7 @@ struct vfs_fs_fns { vfs_vol *(*mount)( const char *name, int num ); vfs_file *(*open)( const char *name, const char *mode ); vfs_dir *(*opendir)( const char *name ); - vfs_item *(*stat)( const char *name ); + sint32_t (*stat)( const char *name, struct vfs_stat *buf ); sint32_t (*remove)( const char *name ); sint32_t (*rename)( const char *oldname, const char *newname ); sint32_t (*mkdir)( const char *name ); diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index 4670abd3cb..6090489b73 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -255,17 +255,12 @@ static uint32_t myspiffs_vfs_size( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_ferrno( const struct vfs_file *fd ); static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ); -static vfs_item *myspiffs_vfs_readdir( const struct vfs_dir *dd ); - -static void myspiffs_vfs_iclose( const struct vfs_item *di ); -static uint32_t myspiffs_vfs_isize( const struct vfs_item *di ); -//static const struct tm *myspiffs_vfs_time( const struct vfs_item *di ); -static const char *myspiffs_vfs_name( const struct vfs_item *di ); +static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ); static vfs_vol *myspiffs_vfs_mount( const char *name, int num ); static vfs_file *myspiffs_vfs_open( const char *name, const char *mode ); static vfs_dir *myspiffs_vfs_opendir( const char *name ); -static vfs_item *myspiffs_vfs_stat( const char *name ); +static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf ); static sint32_t myspiffs_vfs_remove( const char *name ); static sint32_t myspiffs_vfs_rename( const char *oldname, const char *newname ); static sint32_t myspiffs_vfs_fsinfo( uint32_t *total, uint32_t *used ); @@ -308,18 +303,6 @@ static vfs_file_fns myspiffs_file_fns = { .ferrno = myspiffs_vfs_ferrno }; -static vfs_item_fns myspiffs_item_fns = { - .close = myspiffs_vfs_iclose, - .size = myspiffs_vfs_isize, - .time = NULL, - .name = myspiffs_vfs_name, - .is_dir = NULL, - .is_rdonly = NULL, - .is_hidden = NULL, - .is_sys = NULL, - .is_arch = NULL -}; - static vfs_dir_fns myspiffs_dd_fns = { .close = myspiffs_vfs_closedir, .readdir = myspiffs_vfs_readdir @@ -339,36 +322,6 @@ struct myvfs_dir { spiffs_DIR d; }; -struct myvfs_stat { - struct vfs_item vfs_item; - spiffs_stat s; -}; - - -// --------------------------------------------------------------------------- -// stat functions -// -#define GET_STAT_S(descr) \ - const struct myvfs_stat *mystat = (const struct myvfs_stat *)descr; \ - spiffs_stat *s = (spiffs_stat *)&(mystat->s); - -static void myspiffs_vfs_iclose( const struct vfs_item *di ) { - // free descriptor memory - c_free( (void *)di ); -} - -static uint32_t myspiffs_vfs_isize( const struct vfs_item *di ) { - GET_STAT_S(di); - - return s->size; -} - -static const char *myspiffs_vfs_name( const struct vfs_item *di ) { - GET_STAT_S(di); - - return s->name; -} - // --------------------------------------------------------------------------- // volume functions @@ -396,25 +349,22 @@ static sint32_t myspiffs_vfs_closedir( const struct vfs_dir *dd ) { c_free( (void *)dd ); } -static vfs_item *myspiffs_vfs_readdir( const struct vfs_dir *dd ) { +static sint32_t myspiffs_vfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf ) { GET_DIR_D(dd); - struct myvfs_stat *stat; struct spiffs_dirent dirent; - if (stat = c_malloc( sizeof( struct myvfs_stat ) )) { - if (SPIFFS_readdir( d, &dirent )) { - stat->vfs_item.fs_type = VFS_FS_FATFS; - stat->vfs_item.fns = &myspiffs_item_fns; - // copy entries to vfs' directory item - stat->s.size = dirent.size; - c_strncpy( stat->s.name, dirent.name, SPIFFS_OBJ_NAME_LEN ); - return (vfs_item *)stat; - } else { - c_free( stat ); - } + if (SPIFFS_readdir( d, &dirent )) { + c_memset( buf, 0, sizeof( struct vfs_stat ) ); + + // copy entries to item + // fill in supported stat entries + c_strncpy( buf->name, dirent.name, FS_OBJ_NAME_LEN+1 ); + buf->name[FS_OBJ_NAME_LEN] = '\0'; + buf->size = dirent.size; + return VFS_RES_OK; } - return NULL; + return VFS_RES_ERR; } @@ -566,20 +516,21 @@ static vfs_dir *myspiffs_vfs_opendir( const char *name ){ return NULL; } -static vfs_item *myspiffs_vfs_stat( const char *name ) { - struct myvfs_stat *s; +static sint32_t myspiffs_vfs_stat( const char *name, struct vfs_stat *buf ) { + spiffs_stat stat; - if (s = (struct myvfs_stat *)c_malloc( sizeof( struct myvfs_stat ) )) { - if (0 <= SPIFFS_stat( &fs, name, &(s->s) )) { - s->vfs_item.fs_type = VFS_FS_SPIFFS; - s->vfs_item.fns = &myspiffs_item_fns; - return (vfs_item *)s; - } else { - c_free( s ); - } - } + if (0 <= SPIFFS_stat( &fs, name, &stat )) { + c_memset( buf, 0, sizeof( struct vfs_stat ) ); - return NULL; + // fill in supported stat entries + c_strncpy( buf->name, stat.name, FS_OBJ_NAME_LEN+1 ); + buf->name[FS_OBJ_NAME_LEN] = '\0'; + buf->size = stat.size; + + return VFS_RES_OK; + } else { + return VFS_RES_ERR; + } } static sint32_t myspiffs_vfs_remove( const char *name ) { From 50a5c02119996d14735c81f7e898975eff859568 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Fri, 31 Mar 2017 00:48:04 -0700 Subject: [PATCH 39/73] Deprecate wifi.sta.eventMonReg (#1889) * Update wifi event monitor documentation Added a note to `wifi.eventmon.register()` and `wifi.sta.eventmonreg()` * Add deprecation note to `wifi.sta.eventMonReg()` --- app/modules/wifi_eventmon.c | 2 ++ docs/en/modules/wifi.md | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/app/modules/wifi_eventmon.c b/app/modules/wifi_eventmon.c index 4b562212b6..abaaa4ae11 100644 --- a/app/modules/wifi_eventmon.c +++ b/app/modules/wifi_eventmon.c @@ -63,6 +63,8 @@ static void wifi_status_cb(int arg) // wifi.sta.eventMonReg() int wifi_station_event_mon_reg(lua_State* L) { + platform_print_deprecation_note("wifi.sta.eventmonreg() is replaced by wifi.eventmon.register()", "in the next version"); + uint8 id=(uint8)luaL_checknumber(L, 1); if ((id > 5)) // verify user specified a valid wifi status { diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 3cca640ebf..706c965ac9 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -398,6 +398,9 @@ none Registers callbacks for WiFi station status events. +!!! note + Please update your program to use the [`wifi.eventmon`](#wifieventmon-module) API, as the `wifi.sta.eventmon___()` API is deprecated. + #### Syntax - `wifi.sta.eventMonReg(wifi_status[, function([previous_state])])` @@ -1371,6 +1374,10 @@ Note: The functions `wifi.sta.eventMon___()` and `wifi.eventmon.___()` are compl ## wifi.eventmon.register() Register/unregister callbacks for WiFi event monitor. + - After a callback is registered, this function may be called to update a callback's function at any time + +!!! note + To ensure all WiFi events are caught, the Wifi event monitor callbacks should be registered as early as possible in `init.lua`. Any events that occur before callbacks are registered will be discarded! #### Syntax wifi.eventmon.register(Event[, function(T)]) From 9c71c64dcd2b2705774a0f3d00c9a5cbc8677afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Fri, 31 Mar 2017 20:31:35 +0200 Subject: [PATCH 40/73] ds3231: require bit module, doc formatting (#1891) --- lua_modules/ds3231/ds3231-example.lua | 4 +- lua_modules/ds3231/ds3231-web.lua | 12 +- lua_modules/ds3231/ds3231.EN.md | 306 +++++++++++--------------- lua_modules/ds3231/ds3231.lua | 18 +- 4 files changed, 145 insertions(+), 195 deletions(-) diff --git a/lua_modules/ds3231/ds3231-example.lua b/lua_modules/ds3231/ds3231-example.lua index 5f3fb90cd1..340bb1be74 100644 --- a/lua_modules/ds3231/ds3231-example.lua +++ b/lua_modules/ds3231/ds3231-example.lua @@ -1,9 +1,9 @@ -require("ds3231") -- ESP-01 GPIO Mapping gpio0, gpio2 = 3, 4 +i2c.setup(gpio0, gpio2, scl, i2c.SLOW) -- call i2c.setup() only once -ds3231.init(gpio0, gpio2) +require("ds3231") second, minute, hour, day, date, month, year = ds3231.getTime(); diff --git a/lua_modules/ds3231/ds3231-web.lua b/lua_modules/ds3231/ds3231-web.lua index 5a28e51f2f..db55854ca9 100644 --- a/lua_modules/ds3231/ds3231-web.lua +++ b/lua_modules/ds3231/ds3231-web.lua @@ -1,9 +1,11 @@ -require('ds3231') - -port = 80 -- ESP-01 GPIO Mapping gpio0, gpio2 = 3, 4 +i2c.setup(gpio0, gpio2, scl, i2c.SLOW) -- call i2c.setup() only once + +require('ds3231') + +port = 80 days = { [1] = "Sunday", @@ -30,8 +32,6 @@ months = { [12] = "December" } -ds3231.init(gpio0, gpio2) - srv=net.createServer(net.TCP) srv:listen(port, function(conn) @@ -51,4 +51,4 @@ srv:listen(port, "") 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 index 0787ef700a..57b4c6d00d 100644 --- a/lua_modules/ds3231/ds3231.EN.md +++ b/lua_modules/ds3231/ds3231.EN.md @@ -1,67 +1,43 @@ -#DS3231 Module -##Require +# 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. +## setTime() -####Returns -nil +#### Description +Sets the current date and time. +If _disableOscillator_ is set to 1 the oscillator will **stop** on battery. -####Example -```lua -ds3231 = require("ds3231") -ds3231.init(3, 4) --- Don't forget to release it after use -ds3231 = nil -package.loaded["ds3231"]=nil -``` +#### Syntax +`ds3231.setTime(second, minute, hour, day, date, month, year[, disableOscillator])` -####See also -**-** []() +#### 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 +- `disableOscillator`: (optional) 0-1, defaults to 0 if omitted +#### Returns +`nil` - -## setTime() -####Description -Sets the current date and time. -if _disableOscillator_ is set to 1 the oscillator will **stop** on battery. - -####Syntax -setTime(second, minute, hour, day, date, month, year, disableOscillator) - -####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 -disableOscillator: (optional) 0-1 - -####Returns -nil - -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + 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); @@ -71,40 +47,37 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - - ## getTime() -####Description + +#### Description Get the current date and time. -####Syntax -getTime() +#### Syntax +`ds3231.getTime()` -####Parameters -nil +#### Parameters +None -####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 +#### 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 +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + 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", +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 @@ -112,45 +85,45 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - ## setAlarm() -####Description + +#### Description Set an alarm to be triggered on SQW pin: -_alarm1_ has a precision of **seconds**; _alarm2_ has a precision of **minutes** (`second` parameter will be ignored). + +_alarm1_ has a precision of **seconds**; _alarm2_ has a precision of **minutes** (`second` parameter will be ignored). Alarms sets gpio.LOW over the SQW pin and let it unchanged until reloaded. When reloaded sets gpio.HIGH. Alarms trigger **only once**, after that, if you want them to trigger again, you need to call `reloadAlarms()` or `setAlarm(...)` again. -Alarm type set the alarm match conditions: -- `ds3231.EVERYSECOND` works only with _alarm1_ and triggers every second; -- `ds3231.EVERYMINUTE` works only with _alarm2_ and triggers every minute (at 00 seconds); -- `ds3231.SECOND` triggers when time match given `seconds` parameter; -- `ds3231.MINUTE` triggers when time match given `seconds` and `minutes` parameters; -- `ds3231.HOUR` triggers when time match given `seconds`, `minutes`, and `hours` parameters; -- `ds3231.DAY` triggers when time match given `seconds`, `minutes`, and `hours` on week day `date/day` parameters; -- `ds3231.DATE` triggers when time match given `seconds`, `minutes`, and `hours` on date (day of the month) `date/day` parameters; - -####Syntax -setAlarm(alarmId, alarmType, seconds, minutes, hours, date/day) - -####Parameters -alarmId: 1-2 -alarmType: 1-7 -seconds: 00-59 -minutes: 00-59 -hours: 00-23 -date/day: 01-31 or 1-7 (Sunday = 1, Saturday = 7) - -####Returns -nil - -####Example +Alarm type set the alarm match conditions: +- `ds3231.EVERYSECOND` works only with _alarm1_ and triggers every second; +- `ds3231.EVERYMINUTE` works only with _alarm2_ and triggers every minute (at 00 seconds); +- `ds3231.SECOND` triggers when time match given `seconds` parameter; +- `ds3231.MINUTE` triggers when time match given `seconds` and `minutes` parameters; +- `ds3231.HOUR` triggers when time match given `seconds`, `minutes`, and `hours` parameters; +- `ds3231.DAY` triggers when time match given `seconds`, `minutes`, and `hours` on week day `date/day` parameters; +- `ds3231.DATE` triggers when time match given `seconds`, `minutes`, and `hours` on date (day of the month) `date/day` parameters; + +#### Syntax +`ds3231.setAlarm(alarmId, alarmType, seconds, minutes, hours, date/day)` + +#### Parameters +- `alarmId`: 1-2 +- `alarmType`: 1-7 +- `seconds`: 00-59 +- `minutes`: 00-59 +- `hours`: 00-23 +- `date/day`: 01-31 or 1-7 (Sunday = 1, Saturday = 7) + +#### Returns +`nil` + +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) -- Setting PIN1 to triggers on interrupt when alarm triggers gpio.mode(1,gpio.INT) @@ -167,28 +140,26 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - ## reloadAlarms() -####Description + +#### Description Reload an already triggered alarm. Otherwise it will never be triggered again. There are two different alarms and they have to be reloaded both to let, even only one, to be triggered again. So there isn't a param to select which alarm to reload. -####Syntax -reloadAlarms() +#### Syntax +`ds3231.reloadAlarms()` -####Parameters -nil +#### Parameters +None -####Returns -nil +#### Returns +`nil` -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) -- Setting PIN1 to triggers on interrupt when alarm triggers gpio.mode(1,gpio.INT) @@ -205,27 +176,25 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - ## enableAlarm() -####Description + +#### Description Enable an already setted alarm with the previous matching conditions. It reloads alarms internally. -####Syntax -enableAlarm(alarmId) +#### Syntax +`ds3231.enableAlarm(alarmId)` -####Parameters -alarmId: 1-2 +#### Parameters +`alarmId`: 1-2 -####Returns -nil +#### Returns +`nil` -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) -- Trigger on x:20:15 ds3231.setAlarm(1,ds3231.MINUTE,15,20) @@ -243,29 +212,29 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - ## disableAlarm() -####Description -Disable an already setted alarm with the previous matching conditions. -if _alarmId_ is not 1 or 2 it disables both alarms. + +#### Description +Disable an already setted alarm with the previous matching conditions. + +if _alarmId_ is not 1 or 2 it disables both alarms. + **Warning**: `disableAlarm()` prevent alarms to trigger interrupt over SQW pin but alarm itself will triggers at the matching conditions as it could be seen on _status byte_. -####Syntax -disableAlarm(alarmId) +#### Syntax +`ds3231.disableAlarm(alarmId)` -####Parameters -alarmId: 0-2 +#### Parameters +`alarmId: 0-2` -####Returns -nil +#### Returns +`nil` -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) -- Trigger on x:20:15 ds3231.setAlarm(1,ds3231.MINUTE,15,20) @@ -283,28 +252,26 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - ## getBytes() -####Description + +#### Description Get bytes of control, for debug purpose, and status of DS3231. To see what they means check the [Datasheet](http://datasheets.maximintegrated.com/en/ds/DS3231.pdf) -####Syntax -getBytes() +#### Syntax +`ds3231.getBytes()` -####Parameters -nil +#### Parameters +None -####Returns -control: integer. Control 0-255 -status: integer. Status 0-143 (bit 6-5-4 unused) +#### Returns +- `control`: integer. Control 0-255 +- `status`: integer. Status 0-143 (bit 6-5-4 unused) -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) control,status = ds3231.getBytes() print('Control byte: '..control) @@ -315,29 +282,27 @@ ds3231 = nil package.loaded["ds3231"]=nil ``` -####See also -**-** []() - - ## resetStopFlag() -####Description -Stop flag on status byte means that the oscillator either is stopped or was stopped for some period and may be used to judge the validity of the timekeeping data. -When setted to 1 this flag keeps that values until changed to 0. + +#### Description +Stop flag on status byte means that the oscillator either is stopped or was stopped for some period and may be used to judge the validity of the timekeeping data. When set to 1 this flag keeps that values until changed to 0. + Call `resetStopFlag()` if you need to check validity of time data after that. -####Syntax -resetStopFlag() +#### Syntax +`ds3231.resetStopFlag()` -####Parameters -nil +#### Parameters +None -####Returns -nil +#### Returns +`nil` -####Example +#### Example ```lua +i2c.setup(3, 4, scl, i2c.SLOW) -- call i2c.setup() only once + ds3231=require("ds3231") -ds3231.init(3, 4) control,status = ds3231.getBytes() if bit.band(bit.rshift(status, 7),1) == 1 then @@ -349,6 +314,3 @@ end 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 index a93064ca68..e7f848b7ce 100644 --- a/lua_modules/ds3231/ds3231.lua +++ b/lua_modules/ds3231/ds3231.lua @@ -5,6 +5,9 @@ -- Tobie Booth -------------------------------------------------------------------------------- +require("bit") +require("i2c") + local moduleName = ... local M = {} _G[moduleName] = M @@ -39,21 +42,6 @@ local function addAlarmBit(val,day) return bit.bor(val,128) 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("[ERROR] i2c config failed!") return nil - end - print("[LOG] DS3231 init done") - i2c.setup(id, sda, scl, i2c.SLOW) -end - --get time from DS3231 function M.getTime() i2c.start(id) From 176443c07fd7aeee0ee65f31e2b06af535f35457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Tue, 4 Apr 2017 20:42:31 +0200 Subject: [PATCH 41/73] Admonition style fix --- docs/en/modules/wifi.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 706c965ac9..dba903bf8a 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -85,7 +85,8 @@ Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: When using the combined Station + AP mode, the same channel will be used for both networks as the radio can only listen on a single channel. -NOTE: WiFi Mode configuration will be retained until changed even if device is turned off. +!!! note + WiFi configuration will be retained until changed even if device is turned off. #### Syntax `wifi.setmode(mode[, save])` From 41a5736d78695df26fc6c920ac3669ea388ffda0 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Tue, 4 Apr 2017 12:31:06 -0700 Subject: [PATCH 42/73] Add wifi.suspend() and node.sleep() (#1231) * Exposed forced sleep API and more Added timer suspend functionality * wifi.suspend * wifi.resume * node.sleep * tmr.suspend * tmr.suspend_all * tmr.resume * tmr.resume_all * Implement timer suspend functionality * Fix for uart TX glitch * Made some modifications to the error reporting --- app/Makefile | 10 +- app/include/misc/dynarr.h | 40 ++ app/include/swTimer/swTimer.h | 53 +++ app/include/user_config.h | 4 + app/misc/Makefile | 52 ++ app/misc/dyn_arr.c | 131 +++++ app/modules/Makefile | 1 + app/modules/node.c | 47 +- app/modules/tmr.c | 146 +++++- app/modules/wifi.c | 125 +++-- app/modules/wifi_common.h | 9 + app/pm/Makefile | 52 ++ app/pm/pmSleep.c | 381 +++++++++++++++ app/pm/pmSleep.h | 69 +++ app/swTimer/Makefile | 52 ++ app/swTimer/swTimer.c | 632 +++++++++++++++++++++++++ docs/en/modules/node.md | 70 +++ docs/en/modules/tmr.md | 118 +++++ docs/en/modules/wifi.md | 88 +++- sdk-overrides/include/ets_sys.h | 2 + sdk-overrides/include/osapi.h | 11 + sdk-overrides/include/user_interface.h | 9 + 22 files changed, 2022 insertions(+), 80 deletions(-) create mode 100644 app/include/misc/dynarr.h create mode 100644 app/include/swTimer/swTimer.h create mode 100644 app/misc/Makefile create mode 100644 app/misc/dyn_arr.c create mode 100644 app/pm/Makefile create mode 100644 app/pm/pmSleep.c create mode 100644 app/pm/pmSleep.h create mode 100644 app/swTimer/Makefile create mode 100644 app/swTimer/swTimer.c diff --git a/app/Makefile b/app/Makefile index 578b0087e1..e18139a9b7 100644 --- a/app/Makefile +++ b/app/Makefile @@ -45,7 +45,11 @@ SUBDIRS= \ http \ fatfs \ esp-gdbstub \ - websocket + websocket \ + swTimer \ + misc \ + pm \ + endif # } PDIR @@ -90,11 +94,15 @@ COMPONENTS_eagle.app.v6 = \ dhtlib/libdhtlib.a \ tsl2561/tsl2561lib.a \ http/libhttp.a \ + pm/libpm.a \ websocket/libwebsocket.a \ esp-gdbstub/libgdbstub.a \ net/libnodemcu_net.a \ mbedtls/libmbedtls.a \ modules/libmodules.a \ + swTimer/libswtimer.a \ + misc/libmisc.a \ + # Inspect the modules library and work out which modules need to be linked. # For each enabled module, a symbol name of the form XYZ_module_selected is diff --git a/app/include/misc/dynarr.h b/app/include/misc/dynarr.h new file mode 100644 index 0000000000..4eb1b1309d --- /dev/null +++ b/app/include/misc/dynarr.h @@ -0,0 +1,40 @@ +#ifndef __DYNARR_H__ +#define __DYNARR_H__ +#include "user_interface.h" +#include "c_stdio.h" +#include "c_stdlib.h" + +//#define DYNARR_DEBUG +//#define DYNARR_ERROR + + +typedef struct _dynarr{ + void* data_ptr; + size_t used; + size_t array_size; + size_t data_size; +} dynarr_t; + +bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size); +bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add); +bool dynarr_remove(dynarr_t* array_ptr, void* element_ptr); +bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size); +bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_ptr); +bool dynarr_free(dynarr_t* array_ptr); + + +#if 0 || defined(DYNARR_DEBUG) || defined(NODE_DEBUG) +#define DYNARR_DBG(fmt, ...) c_printf("\n DYNARR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else +#define DYNARR_DBG(...) + +#endif + +#if 0 || defined(DYNARR_ERROR) || defined(NODE_ERROR) +#define DYNARR_ERR(fmt, ...) c_printf("\n DYNARR: "fmt"\n", ##__VA_ARGS__) +#else +#define DYNARR_ERR(...) + +#endif + +#endif // __DYNARR_H__ diff --git a/app/include/swTimer/swTimer.h b/app/include/swTimer/swTimer.h new file mode 100644 index 0000000000..abbd374566 --- /dev/null +++ b/app/include/swTimer/swTimer.h @@ -0,0 +1,53 @@ +#ifndef __SW_TIMER_H__ +#define __SW_TIMER_H__ +#include "user_interface.h" +//#define SWTMR_DEBUG +#define USE_SWTMR_ERROR_STRINGS + +#if defined(DEVELOP_VERSION) +#define SWTMR_DEBUG +#endif + +#if defined(SWTMR_DEBUG) + #define SWTMR_DBG(fmt, ...) dbg_printf("\tSWTIMER(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else + #define SWTMR_DBG(...) +#endif + +#if defined(NODE_ERROR) + #define SWTMR_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "SWTIMER:", ##__VA_ARGS__) +#else + #define SWTMR_ERR(...) +#endif + +enum SWTMR_STATUS{ + SWTMR_FAIL = 0, + SWTMR_OK = 1, + + SWTMR_MALLOC_FAIL = 10, + SWTMR_TIMER_NOT_ARMED, +// SWTMR_NULL_PTR, + + SWTMR_REGISTRY_NO_REGISTERED_TIMERS, + +// SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED, +// SWTMR_SUSPEND_ARRAY_ADD_FAILED, +// SWTMR_SUSPEND_ARRAY_REMOVE_FAILED, + SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED, + SWTMR_SUSPEND_TIMER_ALREADY_REARMED, + SWTMR_SUSPEND_NO_SUSPENDED_TIMERS, + SWTMR_SUSPEND_TIMER_NOT_SUSPENDED, + +}; + +/* Global Function Declarations */ +void swtmr_register(void* timer_ptr); +void swtmr_unregister(void* timer_ptr); +int swtmr_suspend(os_timer_t* timer_ptr); +int swtmr_resume(os_timer_t* timer_ptr); +void swtmr_print_registry(void); +void swtmr_print_suspended(void); +void swtmr_print_timer_list(void); +const char* swtmr_errorcode2str(int error_value); +bool swtmr_suspended_test(os_timer_t* timer_ptr); +#endif // __SW_TIMER_H__ diff --git a/app/include/user_config.h b/app/include/user_config.h index c786e90955..03745f931d 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -116,6 +116,10 @@ extern void luaL_assertfail(const char *file, int line, const char *message); #define WIFI_SDK_EVENT_MONITOR_ENABLE #define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE +#define ENABLE_TIMER_SUSPEND +#define PMSLEEP_ENABLE + + #define STRBUF_DEFAULT_INCREMENT 32 #endif /* __USER_CONFIG_H__ */ diff --git a/app/misc/Makefile b/app/misc/Makefile new file mode 100644 index 0000000000..3a758ddefe --- /dev/null +++ b/app/misc/Makefile @@ -0,0 +1,52 @@ + +############################################################# +# 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 = libmisc.a +endif + +STD_CFLAGS=-std=gnu11 -Wimplicit + +############################################################# +# 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 ./include +INCLUDES += -I ../include +INCLUDES += -I ../../include +INCLUDES += -I ../lua +INCLUDES += -I ../platform +INCLUDES += -I ../libc + +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/misc/dyn_arr.c b/app/misc/dyn_arr.c new file mode 100644 index 0000000000..b6ebb8c991 --- /dev/null +++ b/app/misc/dyn_arr.c @@ -0,0 +1,131 @@ +#include "misc/dynarr.h" + +#define ARRAY_PTR_CHECK if(array_ptr == NULL || array_ptr->data_ptr == NULL){\ + /**/DYNARR_DBG("array not initialized");\ + return false; \ + } + +bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size){ + if(array_ptr == NULL || data_size == 0 || array_size == 0){ + /**/DYNARR_DBG("Invalid parameter: array_ptr(%p) data_size(%u) array_size(%u)", array_ptr, data_size, array_size); + return false; + } + if(array_ptr->data_ptr != NULL ){ + /**/DYNARR_DBG("Array already initialized: array_ptr->data_ptr=%p", array_ptr->data_ptr); + return false; + } + /**/DYNARR_DBG("Array parameters:\n\t\t\tarray_size(%u)\n\t\t\tdata_size(%u)\n\t\t\ttotal size(bytes):%u", array_size, data_size, (array_size * data_size)); + + void* temp_array = c_zalloc(array_size * data_size); + if(temp_array == NULL){ + /**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (array_size * data_size), system_get_free_heap_size()); + return false; + } + + array_ptr->data_ptr = temp_array; + array_ptr->array_size = array_size; + array_ptr->data_size = data_size; + array_ptr->used = 0; + + return true; +} + +bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add){ + ARRAY_PTR_CHECK; + + if(elements_to_add <= 0){ + /**/DYNARR_DBG("Invalid qty: elements_to_add=%u", elements_to_add); + return false; + } + + size_t new_array_size = array_ptr->array_size + elements_to_add; + + /**/DYNARR_DBG("old size=%u\tnew size=%u\tmem used=%u", + array_ptr->array_size, new_array_size, (new_array_size * array_ptr->data_size)); + + void* temp_array_p = c_realloc(array_ptr->data_ptr, new_array_size * array_ptr->data_size); + if(temp_array_p == NULL){ + /**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (new_array_size * array_ptr->data_size), system_get_free_heap_size()); + return false; + } + + array_ptr->data_ptr = temp_array_p; + + size_t prev_size = array_ptr->array_size; + + array_ptr->array_size = new_array_size; + + //set memory to 0 for newly added array elements + memset((uint8*) array_ptr->data_ptr + (prev_size * array_ptr->data_size), 0, (elements_to_add * array_ptr->data_size)); + + /**/DYNARR_DBG("Array successfully resized"); + return true; +} + +bool dynarr_remove(dynarr_t* array_ptr, void* element_to_remove){ + ARRAY_PTR_CHECK; + + uint8* element_ptr = element_to_remove; + uint8* data_ptr = array_ptr->data_ptr; + + if(dynarr_boundaryCheck(array_ptr, element_to_remove) == FALSE){ + return false; + } + + //overwrite element to be removed by shifting all elements to the left + memmove(element_ptr, element_ptr + array_ptr->data_size, (array_ptr->array_size - 1) * array_ptr->data_size - (element_ptr - data_ptr)); + + //clear newly freed element + memset(data_ptr + ((array_ptr->array_size-1) * array_ptr->data_size), 0, array_ptr->data_size); + + //decrement array used since we removed an element + array_ptr->used--; + /**/DYNARR_DBG("element(%p) removed from array", element_ptr); + return true; +} + +bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size){ + ARRAY_PTR_CHECK; + + if(data_size != array_ptr->data_size){ + /**/DYNARR_DBG("Invalid data size: data_size(%u) != arr->data_size(%u)", data_size, array_ptr->data_size); + return false; + } + + if(array_ptr->array_size == array_ptr->used){ + if(!dynarr_resize(array_ptr, (array_ptr->array_size/2))){ + return false; + } + } + memcpy(((uint8*)array_ptr->data_ptr + (array_ptr->used * array_ptr->data_size)), data_ptr, array_ptr->data_size); + + array_ptr->used++; + return true; +} + +bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_to_check){ + ARRAY_PTR_CHECK; + + uint8* data_ptr = array_ptr->data_ptr; + uint8* element_ptr = element_to_check; + + if(element_ptr < data_ptr || ((element_ptr - data_ptr) / array_ptr->data_size) > array_ptr->array_size - 1){ + /**/DYNARR_DBG("element_ptr(%p) out of bounds: first element ptr:%p last element ptr:%p", + element_ptr, data_ptr, data_ptr + ((array_ptr->array_size - 1) * array_ptr->data_size)); + return false; + } + return true; +} + +bool dynarr_free(dynarr_t* array_ptr){ + ARRAY_PTR_CHECK; + + c_free(array_ptr->data_ptr); + + array_ptr->data_ptr=NULL; + array_ptr->array_size = array_ptr->used = 0; + + /**/DYNARR_DBG("array freed"); + return true; +} + diff --git a/app/modules/Makefile b/app/modules/Makefile index 87d49b7cb6..4c1f348920 100644 --- a/app/modules/Makefile +++ b/app/modules/Makefile @@ -55,6 +55,7 @@ INCLUDES += -I ../fatfs INCLUDES += -I ../http INCLUDES += -I ../sjson INCLUDES += -I ../websocket +INCLUDES += -I ../pm PDIR := ../$(PDIR) sinclude $(PDIR)Makefile diff --git a/app/modules/node.c b/app/modules/node.c index 2a34111f6a..1bfcc12b5e 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -74,19 +74,36 @@ static int node_deepsleep( lua_State* L ) 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() + +#ifdef PMSLEEP_ENABLE +#include "pmSleep.h" + +int node_sleep_resume_cb_ref= LUA_NOREF; +void node_sleep_resume_cb(void) +{ + PMSLEEP_DBG("START"); + pmSleep_execute_lua_cb(&node_sleep_resume_cb_ref); + PMSLEEP_DBG("END"); +} + +// Lua: node.sleep(table) +static int node_sleep( lua_State* L ) +{ + pmSleep_INIT_CFG(cfg); + cfg.sleep_mode=LIGHT_SLEEP_T; + + if(lua_istable(L, 1)){ + pmSleep_parse_table_lua(L, 1, &cfg, NULL, &node_sleep_resume_cb_ref); + } + else{ + return luaL_argerror(L, 1, "must be table"); + } + + cfg.resume_cb_ptr = &node_sleep_resume_cb; + pmSleep_suspend(&cfg); + return 0; +} +#endif //PMSLEEP_ENABLE static int node_info( lua_State* L ) { @@ -570,6 +587,10 @@ static const LUA_REG_TYPE node_map[] = { { LSTRKEY( "restart" ), LFUNCVAL( node_restart ) }, { LSTRKEY( "dsleep" ), LFUNCVAL( node_deepsleep ) }, +#ifdef PMSLEEP_ENABLE + { LSTRKEY( "sleep" ), LFUNCVAL( node_sleep ) }, + PMSLEEP_INT_MAP, +#endif { LSTRKEY( "info" ), LFUNCVAL( node_info ) }, { LSTRKEY( "chipid" ), LFUNCVAL( node_chipid ) }, { LSTRKEY( "flashid" ), LFUNCVAL( node_flashid ) }, diff --git a/app/modules/tmr.c b/app/modules/tmr.c index b85ec80fc3..76ee6d62e7 100755 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -53,12 +53,14 @@ tmr.softwd(int) #include "platform.h" #include "c_types.h" #include "user_interface.h" +#include "swTimer/swTimer.h" #define TIMER_MODE_OFF 3 #define TIMER_MODE_SINGLE 0 #define TIMER_MODE_SEMI 2 #define TIMER_MODE_AUTO 1 -#define TIMER_IDLE_FLAG (1<<7) +#define TIMER_IDLE_FLAG (1<<7) + #define STRINGIFY_VAL(x) #x #define STRINGIFY(x) STRINGIFY_VAL(x) @@ -172,14 +174,14 @@ static int tmr_register(lua_State* L){ lua_pushvalue(L, 4); sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX); if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); //there was a bug in this part, the second part of the following condition was missing if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref) luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); tmr->lua_ref = ref; tmr->mode = mode|TIMER_IDLE_FLAG; tmr->interval = interval; - ets_timer_setfn(&tmr->os, alarm_timer_common, tmr); + os_timer_setfn(&tmr->os, alarm_timer_common, tmr); return 0; } @@ -197,7 +199,7 @@ static int tmr_start(lua_State* L){ lua_pushboolean(L, 0); }else{ tmr->mode &= ~TIMER_IDLE_FLAG; - ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1); + os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); lua_pushboolean(L, 1); } return 1; @@ -221,7 +223,7 @@ static int tmr_stop(lua_State* L){ //we return false if the timer is idle (of not registered) if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){ tmr->mode |= TIMER_IDLE_FLAG; - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); lua_pushboolean(L, 1); }else{ lua_pushboolean(L, 0); @@ -229,6 +231,73 @@ static int tmr_stop(lua_State* L){ return 1; } +#ifdef ENABLE_TIMER_SUSPEND + +static int tmr_suspend(lua_State* L){ + timer_t tmr = tmr_get(L, 1); + + if((tmr->mode & TIMER_IDLE_FLAG) == 1){ + return luaL_error(L, "timer not armed"); + } + + int retval = swtmr_suspend(&tmr->os); + + if(retval != SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + + return 1; +} + +static int tmr_resume(lua_State* L){ + timer_t tmr = tmr_get(L, 1); + + if(swtmr_suspended_test(&tmr->os) == FALSE){ + return luaL_error(L, "timer not suspended"); + } + + int retval = swtmr_resume(&tmr->os); + + if(retval != SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + +static int tmr_suspend_all (lua_State *L) +{ + sint32 retval = swtmr_suspend(NULL); + // lua_pushnumber(L, swtmr_suspend(NULL)); + if(retval!=SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + +static int tmr_resume_all (lua_State *L) +{ + sint32 retval = swtmr_resume(NULL); + if(retval!=SWTMR_OK){ + return luaL_error(L, swtmr_errorcode2str(retval)); + } + else{ + lua_pushboolean(L, true); + } + return 1; +} + + +#endif + // Lua: tmr.unregister( id / ref ) static int tmr_unregister(lua_State* L){ timer_t tmr = tmr_get(L, 1); @@ -239,7 +308,7 @@ static int tmr_unregister(lua_State* L){ } if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF) - ets_timer_disarm(&tmr->os); + os_timer_disarm(&tmr->os); if(tmr->lua_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref); tmr->lua_ref = LUA_NOREF; @@ -256,8 +325,8 @@ static int tmr_interval(lua_State* L){ if(tmr->mode != TIMER_MODE_OFF){ tmr->interval = interval; if(!(tmr->mode&TIMER_IDLE_FLAG)){ - ets_timer_disarm(&tmr->os); - ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1); + os_timer_disarm(&tmr->os); + os_timer_arm(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO); } } return 0; @@ -271,9 +340,15 @@ static int tmr_state(lua_State* L){ lua_pushnil(L); return 1; } - lua_pushboolean(L, (tmr->mode&TIMER_IDLE_FLAG)==0); - lua_pushinteger(L, tmr->mode&(~TIMER_IDLE_FLAG)); - return 2; + + lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0); + lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG)); +#ifdef ENABLE_TIMER_SUSPEND + lua_pushboolean(L, swtmr_suspended_test(&tmr->os)); +#else + lua_pushnil(L); +#endif + return 3; } /*I left the led comments 'couse I don't know @@ -348,10 +423,27 @@ static int tmr_create( lua_State *L ) { ud->lua_ref = LUA_NOREF; ud->self_ref = LUA_NOREF; ud->mode = TIMER_MODE_OFF; - ets_timer_disarm(&ud->os); + os_timer_disarm(&ud->os); return 1; } + +#if defined(SWTMR_DEBUG) +static void tmr_printRegistry(lua_State* L){ + swtmr_print_registry(); +} + +static void tmr_printSuspended(lua_State* L){ + swtmr_print_suspended(); +} + +static void tmr_printTimerlist(lua_State* L){ + swtmr_print_timer_list(); +} + + +#endif + // Module function map static const LUA_REG_TYPE tmr_dyn_map[] = { @@ -362,11 +454,24 @@ static const LUA_REG_TYPE tmr_dyn_map[] = { { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) }, +#ifdef ENABLE_TIMER_SUSPEND + { LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) }, + { LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) }, +#endif { LSTRKEY( "__gc" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "__index" ), LROVAL( tmr_dyn_map ) }, { LNILKEY, LNILVAL } }; +#if defined(SWTMR_DEBUG) +static const LUA_REG_TYPE tmr_dbg_map[] = { + { LSTRKEY( "printRegistry" ), LFUNCVAL( tmr_printRegistry ) }, + { LSTRKEY( "printSuspended" ), LFUNCVAL( tmr_printSuspended ) }, + { LSTRKEY( "printTimerlist" ), LFUNCVAL( tmr_printTimerlist ) }, + { LNILKEY, LNILVAL } +}; +#endif + static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) }, { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) }, @@ -376,11 +481,20 @@ static const LUA_REG_TYPE tmr_map[] = { { LSTRKEY( "register" ), LFUNCVAL( tmr_register ) }, { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) }, { LSTRKEY( "start" ), LFUNCVAL( tmr_start ) }, - { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, + { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) }, +#ifdef ENABLE_TIMER_SUSPEND + { LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) }, + { LSTRKEY( "suspend_all" ), LFUNCVAL( tmr_suspend_all ) }, + { LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) }, + { LSTRKEY( "resume_all" ), LFUNCVAL( tmr_resume_all ) }, +#endif { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) }, { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) }, { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) }, { LSTRKEY( "create" ), LFUNCVAL( tmr_create ) }, +#if defined(SWTMR_DEBUG) + { LSTRKEY( "debug" ), LROVAL( tmr_dbg_map ) }, +#endif { LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) }, { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) }, { LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) }, @@ -396,14 +510,16 @@ int luaopen_tmr( lua_State *L ){ alarm_timers[i].lua_ref = LUA_NOREF; alarm_timers[i].self_ref = LUA_REFNIL; alarm_timers[i].mode = TIMER_MODE_OFF; + //improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call. ets_timer_disarm(&alarm_timers[i].os); } last_rtc_time=system_get_rtc_time(); // Right now is time 0 last_rtc_time_us=0; + //improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call. ets_timer_disarm(&rtc_timer); - ets_timer_setfn(&rtc_timer, rtc_callback, NULL); - ets_timer_arm_new(&rtc_timer, 1000, 1, 1); + os_timer_setfn(&rtc_timer, rtc_callback, NULL); + os_timer_arm(&rtc_timer, 1000, 1); return 0; } diff --git a/app/modules/wifi.c b/app/modules/wifi.c index 4415e84dc5..fb4bc464b6 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -26,10 +26,6 @@ static uint8 getap_output_format=0; #define INVALID_MAC_STR "MAC:FF:FF:FF:FF:FF:FF" -//wifi.sleep variables -#define FPM_SLEEP_MAX_TIME 0xFFFFFFF -static bool FLAG_wifi_force_sleep_enabled=0; - #ifdef WIFI_SMART_ENABLE static void wifi_smart_succeed_cb(sc_status status, void *pdata){ NODE_DBG("wifi_smart_succeed_cb is called.\n"); @@ -302,66 +298,92 @@ static int wifi_getphymode( lua_State* L ) return 1; } -// Lua: wifi.sleep() -static int wifi_sleep(lua_State* L) +#ifdef PMSLEEP_ENABLE +/* Begin WiFi suspend functions*/ +#include "pmSleep.h" + +static int wifi_resume_cb_ref = LUA_NOREF; // Holds resume callback reference +static int wifi_suspend_cb_ref = LUA_NOREF; // Holds suspend callback reference + +void wifi_pmSleep_suspend_CB(void) { - uint8 desired_sleep_state = 2; - sint8 wifi_fpm_do_sleep_return_value = 1; - if(lua_isnumber(L, 1)) + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + if (wifi_suspend_cb_ref != LUA_NOREF) { - if(luaL_checknumber(L, 1) == 0) - { - desired_sleep_state = 0; - } - else if(luaL_checknumber(L, 1) == 1) - { - desired_sleep_state = 1; - } + lua_State* L = lua_getstate(); // Get main Lua thread pointer + lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_suspend_cb_ref); // Push suspend callback onto stack + lua_unref(L, wifi_suspend_cb_ref); // remove suspend callback from LUA_REGISTRY + wifi_suspend_cb_ref = LUA_NOREF; // Update variable since reference is no longer valid + lua_call(L, 0, 0); // Execute suspend callback } - if (!FLAG_wifi_force_sleep_enabled && desired_sleep_state == 1) + else { - uint8 wifi_current_opmode = wifi_get_opmode(); - if (wifi_current_opmode == 1 || wifi_current_opmode == 3) - { - wifi_station_disconnect(); - } - // set WiFi mode to null mode - wifi_set_opmode(NULL_MODE); - // set force sleep type - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep_return_value = wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME); - if (wifi_fpm_do_sleep_return_value == 0) - { - FLAG_wifi_force_sleep_enabled = TRUE; - } - else - { - wifi_fpm_close(); - FLAG_wifi_force_sleep_enabled = FALSE; - } + PMSLEEP_DBG("\n\tDBG: lua cb unavailable\n"); } - else if(FLAG_wifi_force_sleep_enabled && desired_sleep_state == 0) + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return; +} + +void wifi_pmSleep_resume_CB(void) +{ + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + // If resume callback was defined + pmSleep_execute_lua_cb(&wifi_resume_cb_ref); + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return; +} + +// Lua: wifi.suspend({duration, suspend_cb, resume_cb, preserve_mode}) +static int wifi_suspend(lua_State* L) +{ + // If no parameters were provided + if (lua_isnone(L, 1)) { - FLAG_wifi_force_sleep_enabled = FALSE; - // wake up to use WiFi again - wifi_fpm_do_wakeup(); - wifi_fpm_close(); + // Return current WiFi suspension state + lua_pushnumber(L, pmSleep_get_state()); + return 1; // Return WiFi suspension state } - if (desired_sleep_state == 1 && FLAG_wifi_force_sleep_enabled == FALSE) + pmSleep_INIT_CFG(cfg); + cfg.sleep_mode = MODEM_SLEEP_T; + if(lua_istable(L, 1)) { - lua_pushnil(L); - lua_pushnumber(L, wifi_fpm_do_sleep_return_value); + pmSleep_parse_table_lua(L, 1, &cfg, &wifi_suspend_cb_ref, &wifi_resume_cb_ref); } else + return luaL_argerror(L, 1, "must be table"); + cfg.resume_cb_ptr = &wifi_pmSleep_resume_CB; + cfg.suspend_cb_ptr = &wifi_pmSleep_suspend_CB; + pmSleep_suspend(&cfg); + return 0; +} +// Lua: wifi.resume([Resume_CB]) +static int wifi_resume(lua_State* L) +{ + PMSLEEP_DBG("\n\tDBG: %s start\n", __func__); + uint8 fpm_state = pmSleep_get_state(); +// If forced sleep api is not enabled, return error + if (fpm_state == 0) { - lua_pushnumber(L, FLAG_wifi_force_sleep_enabled); - lua_pushnil(L); + return luaL_error(L, "WIFi not suspended"); } - return 2; + + // If a resume callback was provided + if (lua_isfunction(L, 1)) + { + // If there is already a resume callback reference + lua_pushvalue(L, 1); //Push resume callback to the top of the stack + register_lua_cb(L, &wifi_resume_cb_ref); + PMSLEEP_DBG("\n\tDBG: Resume CB registered\n"); + } + pmSleep_resume(NULL); + PMSLEEP_DBG("\n\tDBG: %s end\n", __func__); + return 0; } +/* End WiFi suspend functions*/ +#endif + // Lua: wifi.nullmodesleep() static int wifi_null_mode_auto_sleep(lua_State* L) { @@ -1659,7 +1681,10 @@ static const LUA_REG_TYPE wifi_map[] = { { LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) }, { LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) }, { LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) }, - { LSTRKEY( "sleep" ), LFUNCVAL( wifi_sleep ) }, +#ifdef PMSLEEP_ENABLE + { LSTRKEY( "suspend" ), LFUNCVAL( wifi_suspend ) }, + { LSTRKEY( "resume" ), LFUNCVAL( wifi_resume ) }, +#endif { LSTRKEY( "nullmodesleep" ), LFUNCVAL( wifi_null_mode_auto_sleep ) }, #ifdef WIFI_SMART_ENABLE { LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) }, diff --git a/app/modules/wifi_common.h b/app/modules/wifi_common.h index 65b7a7bd0f..df8a77a74b 100644 --- a/app/modules/wifi_common.h +++ b/app/modules/wifi_common.h @@ -52,6 +52,15 @@ void wifi_change_default_host_name(void); #define EVENT_DBG(...) //c_printf(__VA_ARGS__) #endif +enum wifi_suspension_state +{ + WIFI_AWAKE = 0, + WIFI_SUSPENSION_PENDING = 1, + WIFI_SUSPENDED = 2 +}; + + + #ifdef WIFI_SDK_EVENT_MONITOR_ENABLE extern const LUA_REG_TYPE wifi_event_monitor_map[]; void wifi_eventmon_init(); diff --git a/app/pm/Makefile b/app/pm/Makefile new file mode 100644 index 0000000000..8143bb2afa --- /dev/null +++ b/app/pm/Makefile @@ -0,0 +1,52 @@ + +############################################################# +# 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 = libpm.a +endif + +STD_CFLAGS=-std=gnu11 -Wimplicit + +############################################################# +# 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 ./include +INCLUDES += -I ../include +INCLUDES += -I ../../include +INCLUDES += -I ../lua +INCLUDES += -I ../platform +INCLUDES += -I ../libc + +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/pm/pmSleep.c b/app/pm/pmSleep.c new file mode 100644 index 0000000000..ad8c75b918 --- /dev/null +++ b/app/pm/pmSleep.c @@ -0,0 +1,381 @@ +#include "pmSleep.h" +#ifdef PMSLEEP_ENABLE +#define STRINGIFY_VAL(x) #x +#define STRINGIFY(x) STRINGIFY_VAL(x) + +//holds duration error string +//uint32 PMSLEEP_SLEEP_MAX_TIME=FPM_SLEEP_MAX_TIME-1; +const char *PMSLEEP_DURATION_ERR_STR="duration: 0 or "STRINGIFY(PMSLEEP_SLEEP_MIN_TIME)"-"STRINGIFY(PMSLEEP_SLEEP_MAX_TIME)" us"; + + +/* INTERNAL VARIABLES */ +static void (*user_suspend_cb)(void) = NULL; +static void (*user_resume_cb)(void) = NULL; +static uint8 resume_opmode = 0; +static os_timer_t wifi_suspended_test_timer; +static uint8 autosleep_setting_temp = 0; +static os_timer_t null_mode_check_timer; +static pmSleep_param_t current_config; + + +/* INTERNAL FUNCTION DECLARATIONS */ +static void suspend_all_timers(void); +static void null_mode_check_timer_cb(void* arg); +static void resume_all_timers(void); +static inline void register_lua_cb(lua_State* L,int* cb_ref); +static void resume_cb(void); +static void wifi_suspended_timer_cb(int arg); + +/* INTERNAL FUNCTIONS */ + +#include "swTimer/swTimer.h" +static void suspend_all_timers(void){ +#ifdef ENABLE_TIMER_SUSPEND + swtmr_suspend(NULL); +#endif + return; +} + +static void resume_all_timers(void){ +#ifdef ENABLE_TIMER_SUSPEND + swtmr_resume(NULL); +#endif + return; +} + +static void null_mode_check_timer_cb(void* arg){ + if (wifi_get_opmode() == NULL_MODE){ + //check if uart 0 tx buffer is empty and uart 1 tx buffer is empty + if(current_config.sleep_mode == LIGHT_SLEEP_T){ + if((READ_PERI_REG(UART_STATUS(0)) & (UART_TXFIFO_CNT<= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) || + (Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR); + cfg->sleep_duration = (uint32)Linteger; // Get suspend duration + } + else{ + return luaL_argerror( L, table_idx, "duration: must be number" ); + } + } + else{ + return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR ); + } + lua_pop(L, 1); + + if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend + lua_getfield(L, table_idx, "suspend_cb"); + if( !lua_isnil(L, -1) ){ /* found? */ + if( lua_isfunction(L, -1) ){ + lua_pushvalue(L, -1); // Push resume callback to the top of the stack + register_lua_cb(L, suspend_lua_cb_ref); + } + else{ + return luaL_argerror( L, table_idx, "suspend_cb: must be function" ); + } + } + lua_pop(L, 1); + } + else if (cfg->sleep_mode == LIGHT_SLEEP_T){ //CPU suspend +#ifdef ENABLE_TIMER_SUSPEND + lua_getfield(L, table_idx, "wake_pin"); + if( !lua_isnil(L, -1) ){ /* found? */ + if( lua_isnumber(L, -1) ){ + Linteger_tmp=lua_tointeger(L, -1); + luaL_argcheck(L, (platform_gpio_exists(Linteger_tmp) && Linteger_tmp > 0), table_idx, "wake_pin: Invalid interrupt pin"); + cfg->wake_pin = Linteger_tmp; + } + else{ + return luaL_argerror( L, table_idx, "wake_pin: must be number" ); + } + } + else if(cfg->sleep_duration == 0){ + return luaL_argerror( L, table_idx, "wake_pin: must specify pin if sleep duration is indefinite" ); + } + lua_pop(L, 1); + + lua_getfield(L, table_idx, "int_type"); + if( !lua_isnil(L, -1) ){ /* found? */ + if( lua_isnumber(L, -1) ){ + Linteger_tmp=lua_tointeger(L, -1); + luaL_argcheck(L, (Linteger_tmp == GPIO_PIN_INTR_ANYEDGE || Linteger_tmp == GPIO_PIN_INTR_HILEVEL + || Linteger_tmp == GPIO_PIN_INTR_LOLEVEL || Linteger_tmp == GPIO_PIN_INTR_NEGEDGE + || Linteger_tmp == GPIO_PIN_INTR_POSEDGE ), 1, "int_type: invalid interrupt type"); + cfg->int_type = Linteger_tmp; + } + else{ + return luaL_argerror( L, table_idx, "int_type: must be number" ); + } + } + else{ + cfg->int_type = GPIO_PIN_INTR_LOLEVEL; + } + lua_pop(L, 1); +#endif + } + else{ + return luaL_error(L, "FPM Sleep mode not available"); + } + + lua_getfield(L, table_idx, "resume_cb"); + if( !lua_isnil(L, -1) ){ /* found? */ + if( lua_isfunction(L, -1) ){ + lua_pushvalue(L, -1); // Push resume callback to the top of the stack + register_lua_cb(L, resume_lua_cb_ref); + } + else{ + return luaL_argerror( L, table_idx, "resume_cb: must be function" ); + } + } + lua_pop(L, 1); + + lua_getfield(L, table_idx, "preserve_mode"); + if( !lua_isnil(L, -1) ){ /* found? */ + if( lua_isboolean(L, -1) ){ + cfg->preserve_opmode=lua_toboolean(L, -1); + } + else{ + return luaL_argerror( L, table_idx, "preseve_mode: must be boolean" ); + } + } + else{ + cfg->preserve_opmode = true; + } + lua_pop(L, 1); + + //if sleep duration is zero, set indefinite sleep duration + if( cfg->sleep_duration == 0 ){ + cfg->sleep_duration = FPM_SLEEP_MAX_TIME; + } + + return 0; +} + +//This function resumes ESP from MODEM_SLEEP +void pmSleep_resume(void (*resume_cb_ptr)(void)){ + PMSLEEP_DBG("START"); + uint8 fpm_state = pmSleep_get_state(); + + if(fpm_state>0){ + if(resume_cb_ptr != NULL){ + user_resume_cb = resume_cb_ptr; + } + wifi_fpm_do_wakeup(); // Wake up from sleep + resume_cb(); // Finish WiFi wakeup + } + + PMSLEEP_DBG("END"); + return; +} + +//this function puts the ESP8266 into MODEM_SLEEP or LIGHT_SLEEP +void pmSleep_suspend(pmSleep_param_t *cfg){ + PMSLEEP_DBG("START"); + + lua_State* L = lua_getstate(); +#ifndef ENABLE_TIMER_SUSPEND + if(cfg->sleep_mode == LIGHT_SLEEP_T){ + luaL_error(L, "timer suspend API is disabled, light sleep unavailable"); + return; + } +#endif + uint8 current_wifi_mode = wifi_get_opmode(); // Get Current WiFi mode + + user_resume_cb = cfg->resume_cb_ptr; //pointer to hold address of user_cb + user_suspend_cb = cfg->suspend_cb_ptr; //pointer to hold address of user_cb + + // If Preserve_wifi_mode parameter is TRUE and current WiFi mode is not NULL_MODE + if (cfg->preserve_opmode && current_wifi_mode != 0){ + resume_opmode = current_wifi_mode; + } + + + if (current_wifi_mode == STATION_MODE || current_wifi_mode == STATIONAP_MODE){ + wifi_station_disconnect(); // Disconnect from Access Point + } + + //the null mode sleep functionality interferes with the forced sleep API and must be disabled + if(get_fpm_auto_sleep_flag() == 1){ + autosleep_setting_temp = 1; + wifi_fpm_auto_sleep_set_in_null_mode(0); + } + + // If wifi_set_opmode_current is successful + if (wifi_set_opmode_current(NULL_MODE)){ + PMSLEEP_DBG("sleep_mode is %s", cfg->sleep_mode == MODEM_SLEEP_T ? "MODEM_SLEEP_T" : "LIGHT_SLEEP_T"); + + wifi_fpm_set_sleep_type(cfg->sleep_mode); + + wifi_fpm_open(); // Enable force sleep API + + if (cfg->sleep_mode == LIGHT_SLEEP_T){ +#ifdef ENABLE_TIMER_SUSPEND + if(platform_gpio_exists(cfg->wake_pin) && cfg->wake_pin > 0){ + PMSLEEP_DBG("Wake-up pin is %d\t interrupt type is %d", cfg->wake_pin, cfg->int_type); + + if((cfg->int_type != GPIO_PIN_INTR_ANYEDGE && cfg->int_type != GPIO_PIN_INTR_HILEVEL + && cfg->int_type != GPIO_PIN_INTR_LOLEVEL && cfg->int_type != GPIO_PIN_INTR_NEGEDGE + && cfg->int_type != GPIO_PIN_INTR_POSEDGE )){ + wifi_fpm_close(); + PMSLEEP_DBG("Invalid interrupt type"); + return; + } + + GPIO_DIS_OUTPUT(pin_num[cfg->wake_pin]); + PIN_FUNC_SELECT(pin_mux[cfg->wake_pin], pin_func[cfg->wake_pin]); + wifi_enable_gpio_wakeup(pin_num[cfg->wake_pin], cfg->int_type); + } + else if(cfg->sleep_duration == FPM_SLEEP_MAX_TIME && cfg->wake_pin == 255){ + wifi_fpm_close(); + PMSLEEP_DBG("No wake-up pin defined"); + return; + } +#endif + + } + + wifi_fpm_set_wakeup_cb(resume_cb); // Set resume C callback + + c_memcpy(¤t_config, cfg, sizeof(pmSleep_param_t)); + PMSLEEP_DBG("sleep duration is %d", current_config.sleep_duration); + + //this timer intentionally bypasses the swtimer timer registration process + ets_timer_disarm(&null_mode_check_timer); + ets_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false); + ets_timer_arm_new(&null_mode_check_timer, 1, 1, 1); + } + else{ + PMSLEEP_ERR("opmode change fail"); + } + PMSLEEP_DBG("END"); + return; +} + +#endif diff --git a/app/pm/pmSleep.h b/app/pm/pmSleep.h new file mode 100644 index 0000000000..a3c6ff34ad --- /dev/null +++ b/app/pm/pmSleep.h @@ -0,0 +1,69 @@ +#ifndef __FPM_SLEEP_H__ +#define __FPM_SLEEP_H__ +#include "user_interface.h" +#include "c_types.h" +#include "lauxlib.h" +#include "gpio.h" +#include "platform.h" +#include "task/task.h" +#include "c_string.h" + +#if defined(DEVELOP_VERSION) +#define PMSLEEP_DEBUG +#endif + +#if defined(PMSLEEP_DEBUG) + #define PMSLEEP_DBG(fmt, ...) dbg_printf("\tPMSLEEP(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__) +#else + #define PMSLEEP_DBG(...) //c_printf(__VA_ARGS__) +#endif + +#if defined(NODE_ERROR) + #define PMSLEEP_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "PMSLEEP:", ##__VA_ARGS__) +#else + #define PMSLEEP_ERR(...) +#endif + + + + + +#define PMSLEEP_SLEEP_MIN_TIME 50000 +#define PMSLEEP_SLEEP_MAX_TIME 268435454 //FPM_MAX_SLEEP_TIME-1 +#define pmSleep_INIT_CFG(X) pmSleep_param_t X = {.sleep_duration=0, .wake_pin=255, \ + .preserve_opmode=TRUE, .suspend_cb_ptr=NULL, .resume_cb_ptr=NULL} + +#define PMSLEEP_INT_MAP \ + { LSTRKEY( "INT_BOTH" ), LNUMVAL( GPIO_PIN_INTR_ANYEDGE ) }, \ + { LSTRKEY( "INT_UP" ), LNUMVAL( GPIO_PIN_INTR_POSEDGE ) }, \ + { LSTRKEY( "INT_DOWN" ), LNUMVAL( GPIO_PIN_INTR_NEGEDGE ) }, \ + { LSTRKEY( "INT_HIGH" ), LNUMVAL( GPIO_PIN_INTR_HILEVEL ) }, \ + { LSTRKEY( "INT_LOW" ), LNUMVAL( GPIO_PIN_INTR_LOLEVEL ) } + + + +typedef struct pmSleep_param{ + uint32 sleep_duration; + uint8 sleep_mode; + uint8 wake_pin; + uint8 int_type; + bool preserve_opmode; + void (*suspend_cb_ptr)(void); + void (*resume_cb_ptr)(void); +}pmSleep_param_t; //structure to hold pmSleep configuration + + +enum PMSLEEP_STATE{ + PMSLEEP_AWAKE = 0, + PMSLEEP_SUSPENSION_PENDING = 1, + PMSLEEP_SUSPENDED = 2 +}; + +uint8 pmSleep_get_state(void); +void pmSleep_resume(void (*resume_cb_ptr)(void)); +void pmSleep_suspend(pmSleep_param_t *param); +void pmSleep_execute_lua_cb(int* cb_ref); +int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref); + + +#endif // __FPM_SLEEP_H__ diff --git a/app/swTimer/Makefile b/app/swTimer/Makefile new file mode 100644 index 0000000000..eaa132c911 --- /dev/null +++ b/app/swTimer/Makefile @@ -0,0 +1,52 @@ + +############################################################# +# 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 = libswtimer.a +endif + +STD_CFLAGS=-std=gnu11 -Wimplicit + +############################################################# +# 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 ./include +INCLUDES += -I ../include +INCLUDES += -I ../../include +INCLUDES += -I ../lua +INCLUDES += -I ../platform +INCLUDES += -I ../libc + +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/app/swTimer/swTimer.c b/app/swTimer/swTimer.c new file mode 100644 index 0000000000..9c86c4aa12 --- /dev/null +++ b/app/swTimer/swTimer.c @@ -0,0 +1,632 @@ +/* swTimer.c SDK timer suspend API + * + * SDK software timer API info: + * + * The SDK software timer uses a linked list called `os_timer_t* timer_list` to keep track of + * all currently armed timers. + * + * The SDK software timer API executes in a task. The priority of this task in relation to the + * application level tasks is unknown (at time of writing). + * + * + * To determine when a timer's callback should be executed, the respective timer's `timer_expire` + * variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is + * less than the current FRC2 count, the timer's callback is fired. + * + * The timers in this list are organized in an ascending order starting with the timer + * with the lowest timer_expire. + * + * When a timer expires that has a timer_period greater than 0, timer_expire is changed to + * current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position. + * + * when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond. + * + * + * TIMER SUSPEND API: + * + * Timer registry: + * void sw_timer_register(void* timer_ptr); + * - Adds timers to the timer registry by adding it to a queue that is later + * processed by timer_register_task that performs the registry maintenance + * + * void sw_timer_unregister(void* timer_ptr); + * - Removes timers from the timer registry by adding it to a queue that is later + * processed by timer_unregister_task that performs the registry maintenance + * + * + * int sw_timer_suspend(os_timer_t* timer_ptr); + * - Suspend a single active timer or suspend all active timers. + * - if no timer pointer is provided, timer_ptr == NULL, then all currently active timers will be suspended. + * + * int sw_timer_resume(os_timer_t* timer_ptr); + * - Resume a single suspended timer or resume all suspended timers. + * - if no timer pointer is provided, timer_ptr == NULL, then all currently suspended timers will be resumed. + * + * + * + */ +#include "swTimer/swTimer.h" +#include "c_stdio.h" +#include "misc/dynarr.h" +#include "task/task.h" + +#ifdef ENABLE_TIMER_SUSPEND + +/* Settings */ +#define TIMER_REGISTRY_INITIAL_SIZE 10 +#ifdef USE_SWTMR_ERROR_STRINGS +static const char* SWTMR_ERROR_STRINGS[]={ + [SWTMR_MALLOC_FAIL] = "Out of memory!", + [SWTMR_TIMER_NOT_ARMED] = "Timer is not armed", +// [SWTMR_NULL_PTR] = "A NULL pointer was passed to timer suspend api", + [SWTMR_REGISTRY_NO_REGISTERED_TIMERS] = "No timers in registry", +// [SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED] = "Suspend array init fail", +// [SWTMR_SUSPEND_ARRAY_ADD_FAILED] = "Unable to add suspended timer to array", +// [SWTMR_SUSPEND_ARRAY_REMOVE_FAILED] = "Unable to remove suspended timer from array", + [SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED] = "Already suspended", + [SWTMR_SUSPEND_TIMER_ALREADY_REARMED] = "Already been re-armed", + [SWTMR_SUSPEND_NO_SUSPENDED_TIMERS] = "No suspended timers", + [SWTMR_SUSPEND_TIMER_NOT_SUSPENDED] = "Not suspended", + +}; +#endif + +/* Private Function Declarations */ +static inline bool timer_armed_check(os_timer_t* timer_ptr); +static inline int timer_do_suspend(os_timer_t* timer_ptr); +static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr); +static void timer_register_task(task_param_t param, uint8 priority); +static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr); +static inline void timer_registry_remove_unarmed(void); +static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr); +static void timer_unregister_task(task_param_t param, uint8 priority); + +/* Private Variable Definitions */ + static task_handle_t timer_reg_task_id = false; + static task_handle_t timer_unreg_task_id = false; + + static dynarr_t timer_registry = {0}; + static dynarr_t suspended_timers = {0}; + + typedef struct registry_queue{ + struct registry_queue* next; + os_timer_t* timer_ptr; + }registry_queue_t; + + static registry_queue_t* register_queue = NULL; + static registry_queue_t* unregister_queue = NULL; + +/* Private Function Definitions */ + +//NOTE: Interrupts are temporarily blocked during the execution of this function +static inline bool timer_armed_check(os_timer_t* timer_ptr){ + bool retval = FALSE; + + // we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though. + ETS_INTR_LOCK(); + + os_timer_t* timer_list_ptr = timer_list; //get head node pointer of timer_list + + //if present find timer_ptr in timer_list rand return result + while(timer_list_ptr != NULL){ + if(timer_list_ptr == timer_ptr){ + retval = TRUE; + break; + } + timer_list_ptr = timer_list_ptr->timer_next; + } + + //we are done with timer_list, it is now safe to unlock interrupts + ETS_INTR_UNLOCK(); + + //return value + return retval; +} + +static inline int timer_do_suspend(os_timer_t* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_ERR("timer_ptr is invalid"); + return SWTMR_FAIL; + } + + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + + if(timer_armed_check(timer_ptr) == FALSE){ + return SWTMR_TIMER_NOT_ARMED; + } + + os_timer_t** suspended_timer_ptr = timer_suspended_check(timer_ptr); + + uint32 expire_temp = 0; + uint32 period_temp = timer_ptr->timer_period; + + if(timer_ptr->timer_expire < frc2_count){ + expire_temp = 5; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value) + } + else{ + expire_temp = timer_ptr->timer_expire - frc2_count; + } + + ets_timer_disarm(timer_ptr); + timer_unregister_task((task_param_t)timer_ptr, false); + + timer_ptr->timer_expire = expire_temp; + timer_ptr->timer_period = period_temp; + + if(suspended_timers.data_ptr == NULL){ + if(!dynarr_init(&suspended_timers, 10, sizeof(os_timer_t*))){ + SWTMR_ERR("Suspend array init fail"); + return SWTMR_FAIL; + } + } + + if(suspended_timer_ptr == NULL){ +// return SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED; + if(!dynarr_add(&suspended_timers, &timer_ptr, sizeof(timer_ptr))){ + SWTMR_ERR("Unable to add suspended timer to array"); + return SWTMR_FAIL; + } + } + + return SWTMR_OK; +} + +//NOTE: Interrupts are temporarily blocked during the execution of this function +static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr){ + if(suspended_timer_ptr == NULL){ + SWTMR_ERR("suspended_timer_ptr is invalid"); + return SWTMR_FAIL; + } + + os_timer_t* timer_list_ptr = NULL; + os_timer_t* resume_timer_ptr = *suspended_timer_ptr; + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + + //verify timer has not been rearmed + if(timer_armed_check(resume_timer_ptr) == TRUE){ + SWTMR_DBG("Timer(%p) already rearmed, removing from array", resume_timer_ptr); + if(!dynarr_remove(&suspended_timers, suspended_timer_ptr)){ + SWTMR_ERR("Failed to remove timer from suspend array"); + return SWTMR_FAIL; + } + return SWTMR_OK; + } + + //Prepare timer for resume + resume_timer_ptr->timer_expire += frc2_count; + + timer_register_task((task_param_t)resume_timer_ptr, false); + SWTMR_DBG("Removing timer(%p) from suspend array", resume_timer_ptr); + + //This section performs the actual resume of the suspended timer + + // we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though. + ETS_INTR_LOCK(); + + timer_list_ptr = timer_list; + + while(timer_list_ptr != NULL){ + if(resume_timer_ptr->timer_expire > timer_list_ptr->timer_expire){ + if(timer_list_ptr->timer_next != NULL){ + if(resume_timer_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){ + resume_timer_ptr->timer_next = timer_list_ptr->timer_next; + timer_list_ptr->timer_next = resume_timer_ptr; + break; + } + else{ + //next timer in timer_list + } + } + else{ + timer_list_ptr->timer_next = resume_timer_ptr; + resume_timer_ptr->timer_next = NULL; + break; + } + } + else if(timer_list_ptr == timer_list){ + resume_timer_ptr->timer_next=timer_list_ptr; + timer_list = timer_list_ptr = resume_timer_ptr; + break; + } + + timer_list_ptr = timer_list_ptr->timer_next; + } + + //we no longer need to block interrupts + ETS_INTR_UNLOCK(); + + return SWTMR_OK; +} + +static void timer_register_task(task_param_t param, uint8 priority){ + if(timer_registry.data_ptr==NULL){ + if(!dynarr_init(&timer_registry, TIMER_REGISTRY_INITIAL_SIZE, sizeof(os_timer_t*))){ + SWTMR_ERR("timer registry init Fail!"); + return; + } + } + + os_timer_t* timer_ptr = NULL; + + //if a timer pointer is provided, override normal queue processing behavior + if(param != 0){ + timer_ptr = (os_timer_t*)param; + } + else{ + //process an item in the register queue + if(register_queue == NULL){ + /**/SWTMR_ERR("ERROR: REGISTER QUEUE EMPTY"); + return; + } + + registry_queue_t* queue_temp = register_queue; + register_queue = register_queue->next; + + timer_ptr = queue_temp->timer_ptr; + + c_free(queue_temp); + + if(register_queue != NULL){ + SWTMR_DBG("register_queue not empty, posting task"); + task_post_low(timer_reg_task_id, false); + } + } + + os_timer_t** suspended_tmr_ptr = timer_suspended_check(timer_ptr); + if(suspended_tmr_ptr != NULL){ + if(!dynarr_remove(&suspended_timers, suspended_tmr_ptr)){ + SWTMR_ERR("failed to remove %p from suspend registry", suspended_tmr_ptr); + } + SWTMR_DBG("removed timer from suspended timers"); + } + + if(timer_registry_check(timer_ptr) != NULL){ + /**/SWTMR_DBG("timer(%p) found in registry, returning", timer_ptr); + return; + } + + if(!dynarr_add(&timer_registry, &timer_ptr, sizeof(timer_ptr))){ + /**/SWTMR_ERR("Registry append failed"); + return; + } + + return; +} + +static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr){ + if(timer_registry.data_ptr == NULL){ + return NULL; + } + if(timer_registry.used > 0){ + + os_timer_t** timer_registry_array = timer_registry.data_ptr; + + for(uint32 i=0; i < timer_registry.used; i++){ + if(timer_registry_array[i] == timer_ptr){ + /**/SWTMR_DBG("timer(%p) is registered", timer_registry_array[i]); + return &timer_registry_array[i]; + } + } + } + + return NULL; +} + +static inline void timer_registry_remove_unarmed(void){ + if(timer_registry.data_ptr == NULL){ + return; + } + if(timer_registry.used > 0){ + os_timer_t** timer_registry_array = timer_registry.data_ptr; + for(uint32 i=0; i < timer_registry.used; i++){ + if(timer_armed_check(timer_registry_array[i]) == FALSE){ + timer_unregister_task((task_param_t)timer_registry_array[i], false); + } + } + } +} + + +static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr){ + if(suspended_timers.data_ptr == NULL){ + return NULL; + } + if(suspended_timers.used > 0){ + + os_timer_t** suspended_timer_array = suspended_timers.data_ptr; + + for(uint32 i=0; i < suspended_timers.used; i++){ + if(suspended_timer_array[i] == timer_ptr){ + return &suspended_timer_array[i]; + } + } + } + + return NULL; +} + +static void timer_unregister_task(task_param_t param, uint8 priority){ + if(timer_registry.data_ptr == NULL){ + return; + } + os_timer_t* timer_ptr = NULL; + + if(param != false){ + timer_ptr = (os_timer_t*)param; + } + else{ + + if(unregister_queue == NULL) { + SWTMR_ERR("ERROR register queue empty"); + return; + } + registry_queue_t* queue_temp = unregister_queue; + timer_ptr = queue_temp->timer_ptr; + unregister_queue = unregister_queue->next; + c_free(queue_temp); + if(unregister_queue != NULL){ + SWTMR_DBG("unregister_queue not empty, posting task"); + task_post_low(timer_unreg_task_id, false); + } + } + if(timer_armed_check(timer_ptr) == TRUE){ + SWTMR_DBG("%p still armed, can't remove from registry", timer_ptr); + return; + } + + + os_timer_t** registry_ptr = timer_registry_check(timer_ptr); + if(registry_ptr != NULL){ + if(!dynarr_remove(&timer_registry, registry_ptr)){ + + /**/SWTMR_ERR("Failed to remove timer from registry"); + /**/SWTMR_DBG("registry_ptr = %p", registry_ptr); + return; + } + } + else{ + //timer not in registry + } + return; +} + +/* Global Function Definitions */ + +#if defined(SWTMR_DEBUG) + +void swtmr_print_registry(void){ + volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS); + uint32 time_till_fire = 0; + uint32 time = system_get_time(); + + timer_registry_remove_unarmed(); + time = system_get_time()-time; + + /**/SWTMR_DBG("registry_remove_unarmed_timers() took %u us", time); + + os_timer_t** timer_array = timer_registry.data_ptr; + + c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n FRC2 COUNT %u\n", + timer_registry.used, timer_registry.array_size, timer_registry.array_size * timer_registry.data_size, frc2_count); + c_printf("\n Registered timer array contents:\n"); + c_printf(" %-5s %-10s %-10s %-13s %-10s %-10s %-10s\n", "idx", "ptr", "expire", "period(tick)", "period(ms)", "fire(tick)", "fire(ms)"); + + for(uint32 i=0; i < timer_registry.used; i++){ + time_till_fire = (timer_array[i]->timer_expire - frc2_count); + c_printf(" %-5d %-10p %-10d %-13d %-10d %-10d %-10d\n", i, timer_array[i], timer_array[i]->timer_expire, timer_array[i]->timer_period, (uint32)(timer_array[i]->timer_period/312.5), time_till_fire, (uint32)(time_till_fire/312.5)); + + } + + return; +} + +void swtmr_print_suspended(void){ + os_timer_t** susp_timer_array = suspended_timers.data_ptr; + + c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n", + suspended_timers.used, suspended_timers.array_size, suspended_timers.array_size * suspended_timers.data_size); + c_printf("\n Suspended timer array contents:\n"); + c_printf(" %-5s %-10s %-15s %-15s %-14s %-10s\n", "idx", "ptr", "time left(tick)", "time left(ms)", "period(tick)", "period(ms)"); + + for(uint32 i=0; i < suspended_timers.used; i++){ + c_printf(" %-5d %-10p %-15d %-15d %-14d %-10d\n", i, susp_timer_array[i], susp_timer_array[i]->timer_expire, (uint32)(susp_timer_array[i]->timer_expire/312.5), susp_timer_array[i]->timer_period, (uint32)(susp_timer_array[i]->timer_period/312.5)); + + } + return; +} + +void swtmr_print_timer_list(void){ + volatile uint32 frc2_count=RTC_REG_READ(FRC2_COUNT_ADDRESS); + os_timer_t* timer_list_ptr=NULL; + uint32 time_till_fire=0; + c_printf("\n\tcurrent FRC2 count:%u\n", frc2_count); + c_printf(" timer_list contents:\n"); + c_printf(" %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", "ptr", "expire", "period", "func", "arg", "fire(tick)", "fire(ms)"); + + ETS_INTR_LOCK(); + + timer_list_ptr=timer_list; + + while(timer_list_ptr != NULL){ + time_till_fire=(timer_list_ptr->timer_expire - frc2_count) / 312.5; + + c_printf(" %-10p %-10u %-10u %-10p %-10p %-10u %-10u\n", + timer_list_ptr, (uint32)(timer_list_ptr->timer_expire), + (uint32)(timer_list_ptr->timer_period ), timer_list_ptr->timer_func, + timer_list_ptr->timer_arg, (timer_list_ptr->timer_expire - frc2_count), time_till_fire); + + timer_list_ptr=timer_list_ptr->timer_next; + } + ETS_INTR_UNLOCK(); + c_printf(" NOTE: some timers in the above list belong to the SDK and can not be suspended\n"); + return; +} + +#endif + +int swtmr_suspend(os_timer_t* timer_ptr){ + int return_value = SWTMR_OK; + + if(timer_ptr != NULL){ + // Timer pointer was provided, suspending specified timer + + return_value = timer_do_suspend(timer_ptr); + + if(return_value != SWTMR_OK){ + return return_value; + } + } + else{ + //timer pointer not found, suspending all timers + + if(timer_registry.data_ptr == NULL){ + return SWTMR_REGISTRY_NO_REGISTERED_TIMERS; + } + + timer_registry_remove_unarmed(); + + os_timer_t** tmr_reg_arr = timer_registry.data_ptr; + os_timer_t* temp_ptr = tmr_reg_arr[0]; + + while(temp_ptr != NULL){ + return_value = timer_do_suspend(temp_ptr); + + if(return_value != SWTMR_OK){ + return return_value; + } + + temp_ptr = tmr_reg_arr[0]; + } + } + return return_value; +} + +int swtmr_resume(os_timer_t* timer_ptr){ + + if(suspended_timers.data_ptr == NULL){ + return SWTMR_SUSPEND_NO_SUSPENDED_TIMERS; + } + + os_timer_t** suspended_tmr_array = suspended_timers.data_ptr; + os_timer_t** suspended_timer_ptr = NULL; + int retval=SWTMR_OK; + + if(timer_ptr != NULL){ + suspended_timer_ptr = timer_suspended_check(timer_ptr); + if(suspended_timer_ptr == NULL){ + //timer not suspended + return SWTMR_SUSPEND_TIMER_NOT_SUSPENDED; + } + + retval = timer_do_resume_single(suspended_timer_ptr); + if(retval != SWTMR_OK){ + return retval; + } + } + else{ + suspended_timer_ptr = &suspended_tmr_array[0]; + + while(suspended_timers.used > 0){ + retval = timer_do_resume_single(suspended_timer_ptr); + if(retval != SWTMR_OK){ + SWTMR_ERR("Unable to continue resuming timers, error(%u)", retval); + return retval; + } + suspended_timer_ptr = &suspended_tmr_array[0]; + } + } + return SWTMR_OK; +} + +void swtmr_register(void* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_DBG("error: timer_ptr is NULL"); + return; + } + + registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t)); + + if(queue_temp == NULL){ + SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size()); + return; + } + queue_temp->timer_ptr = timer_ptr; + + if(register_queue == NULL){ + register_queue = queue_temp; + + if(timer_reg_task_id == false) timer_reg_task_id = task_get_id(timer_register_task); + task_post_low(timer_reg_task_id, false); + SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr); + } + else{ + registry_queue_t* register_queue_tail = register_queue; + + while(register_queue_tail->next != NULL){ + register_queue_tail = register_queue_tail->next; + } + + register_queue_tail->next = queue_temp; + SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr); + } + + return; +} + +void swtmr_unregister(void* timer_ptr){ + if(timer_ptr == NULL){ + SWTMR_DBG("error: timer_ptr is NULL"); + return; + } + + registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t)); + + if(queue_temp == NULL){ + SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size()); + return; + } + queue_temp->timer_ptr=timer_ptr; + + if(unregister_queue == NULL){ + unregister_queue = queue_temp; + if(timer_unreg_task_id==false) timer_unreg_task_id=task_get_id(timer_unregister_task); + task_post_low(timer_unreg_task_id, false); + SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr); + } + else{ + registry_queue_t* unregister_queue_tail=unregister_queue; + while(unregister_queue_tail->next != NULL){ + unregister_queue_tail=unregister_queue_tail->next; + } + unregister_queue_tail->next = queue_temp; +// SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr); + } + + return; +} + +const char* swtmr_errorcode2str(int error_value){ +#ifdef USE_SWTMR_ERROR_STRINGS + if(SWTMR_ERROR_STRINGS[error_value] == NULL){ + SWTMR_ERR("error string %d not found", error_value); + return NULL; + } + else{ + return SWTMR_ERROR_STRINGS[error_value]; + } +#else + SWTMR_ERR("error(%u)", error_value); + return "ERROR! for more info, use debug build"; +#endif + +} + +bool swtmr_suspended_test(os_timer_t* timer_ptr){ + os_timer_t** test_var = timer_suspended_check(timer_ptr); + if(test_var == NULL){ + return false; + } + return true; +} + +#endif diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index a957936a0d..683f571748 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -127,6 +127,11 @@ node.dsleep(1000000, 4) node.dsleep(nil,4) ``` +#### See also +[`wifi.suspend()`](wifi.md#wifisuspend) +[`wifi.resume()`](wifi.md#wifiresume) +[`node.sleep()`](#nodesleep) + ## node.flashid() Returns the flash chip ID. @@ -321,6 +326,71 @@ target CPU frequency (number) node.setcpufreq(node.CPU80MHZ) ``` + +## node.sleep() + +Put NodeMCU in light sleep mode to reduce current consumption. + +* NodeMCU can not enter light sleep mode if wifi is suspended. +* All active timers will be suspended and then resumed when NodeMCU wakes from sleep. +* Any previously suspended timers will be resumed when NodeMCU wakes from sleep. + +#### Syntax +`node.sleep({wake_gpio[, duration, int_type, resume_cb, preserve_mode]})` + +#### Parameters +- `duration` Sleep duration in microseconds(μs). If a sleep duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs (0:4:28.000454)) +- `wake_pin` 1-12, pin to attach wake interrupt to. Note that pin 0(GPIO 16) does not support interrupts. + - If sleep duration is indefinite, `wake_pin` must be specified + - Please refer to the [`GPIO module`](gpio.md) for more info on the pin map. +- `int_type` type of interrupt that you would like to wake on. (Optional, Default: `node.INT_LOW`) + - valid interrupt modes: + - `node.INT_UP` Rising edge + - `node.INT_DOWN` Falling edge + - `node.INT_BOTH` Both edges + - `node.INT_LOW` Low level + - `node.INT_HIGH` High level +- `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional) +- `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true) + - If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes. + - If false, discard WiFi mode and leave NodeMCU in `wifi.NULL_MODE`. WiFi mode will be restored to original mode on restart. + +#### Returns +- `nil` + +#### Example + +```lua + +--Put NodeMCU in light sleep mode indefinitely with resume callback and wake interrupt + cfg={} + cfg.wake_pin=3 + cfg.resume_cb=function() print("WiFi resume") end + + node.sleep(cfg) + +--Put NodeMCU in light sleep mode with interrupt, resume callback and discard WiFi mode + cfg={} + cfg.wake_pin=3 --GPIO0 + cfg.resume_cb=function() print("WiFi resume") end + cfg.preserve_mode=false + + node.sleep(cfg) + +--Put NodeMCU in light sleep mode for 10 seconds with resume callback + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + + node.sleep(cfg) + +``` + +#### See also +[`wifi.suspend()`](wifi.md#wifisuspend) +[`wifi.resume()`](wifi.md#wifiresume) +[`node.dsleep()`](#nodedsleep) + ## node.stripdebug() Controls the amount of debug information kept during [`node.compile()`](#nodecompile), and allows removal of debug information from already compiled Lua code. diff --git a/docs/en/modules/tmr.md b/docs/en/modules/tmr.md index 919682d2f9..8c815f9f3a 100644 --- a/docs/en/modules/tmr.md +++ b/docs/en/modules/tmr.md @@ -62,9 +62,11 @@ Functions supported in timer object: - [`t:alarm()`](#tmralarm) - [`t:interval()`](#tmrinterval) - [`t:register()`](#tmrregister) +- [`t:resume()`](#tmrresume) - [`t:start()`](#tmrstart) - [`t:state()`](#tmrstate) - [`t:stop()`](#tmrstop) +- [`t:suspend()`](#tmrsuspend) - [`t:unregister()`](#tmrunregister) #### Parameters @@ -182,6 +184,61 @@ mytimer:start() - [`tmr.create()`](#tmrcreate) - [`tmr.alarm()`](#tmralarm) +## tmr.resume() + +Resume an individual timer. + +Resumes a timer that has previously been suspended with either `tmr.suspend` or `tmr.suspend_all` + +#### Syntax +`tmr.resume(id/ref)` + +#### Parameters +`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate)) + +#### Returns +`true` if timer was resumed successfully + +#### Example +```lua +--resume timer mytimer +mytimer:resume() + +--alternate metod +tmr.resume(mytimer) + +``` +#### See also +[`tmr.suspend()`](#tmrsuspend) +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume_all()`](#tmrresumeall) + +## tmr.resume_all() + +Resume all timers. + +Resumes all timers including those previously been suspended with either `tmr.suspend` or `tmr.suspend_all` + +#### Syntax +`tmr.resume_all()` + +#### Parameters +none + +#### Returns +`true` if timers were resumed successfully + +#### Example +```lua +--resume all previously suspended timers +tmr.resume_all() + +``` +#### See also +[`tmr.suspend()`](#tmrsuspend) +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume()`](#tmrresume) + ## tmr.softwd() Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted. @@ -279,6 +336,67 @@ if not mytimer:stop() then print("timer not stopped, not registered?") end - [`tmr.stop()`](#tmrstop) - [`tmr.unregister()`](#tmrunregister) +## tmr.suspend() + +Suspend an armed timer. + +* Timers can be suspended at any time after they are armed. +* If a timer is rearmed with `tmr.start` or `tmr.alarm` any matching suspended timers will be discarded. + +#### Syntax +`tmr.suspend(id/ref)` + +#### Parameters +`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate)) + +#### Returns +`true` if timer was resumed successfully + +#### Example +```lua +--suspend timer mytimer +mytimer:suspend() + +--alternate metod +tmr.suspend(mytimer) + +``` +#### See also +[`tmr.suspend_all()`](#tmrsuspendall) +[`tmr.resume()`](#tmrresume) +[`tmr.resume_all()`](#tmrresumeall) + + +## tmr.suspend_all() + +Suspend all currently armed timers. + +!!! Warning + This function suspends ALL active timers, including any active timers started by the NodeMCU subsystem or other modules. this may cause parts of your program to stop functioning properly. + USE THIS FUNCTION AT YOUR OWN RISK! + + +#### Syntax +`tmr.suspend_all()` + +#### Parameters +none + +#### Returns +`true` if timers were suspended successfully + +#### Example +```lua +--suspend timer mytimer +tmr.suspend_all() + +``` +#### See also +[`tmr.suspendl()`](#tmrsuspend) +[`tmr.resume()`](#tmrresume) +[`tmr.resume_all()`](#tmrresumeall) + + ## tmr.time() Returns the system uptime, in seconds. Limited to 31 bits, after that it wraps around back to zero. diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index dba903bf8a..ec235658d0 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -74,6 +74,37 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi. #### See also [`wifi.setphymode()`](#wifisetphymode) +## wifi.resume() + +Wake up WiFi from suspended state or cancel pending wifi suspension + +#### Syntax +`wifi.resume([resume_cb])` + +#### Parameters +- `resume_cb` Callback to execute when WiFi wakes from suspension. + !!! note "Note:" + + Any previously provided callbacks will be replaced! + +#### Returns +`nil` + +#### Example + +```lua +--Resume wifi from timed or indefinite sleep +wifi.resume() + +--Resume wifi from timed or indefinite sleep w/ resume callback +wifi.resume(function() print("WiFi resume") end) +``` + +#### See also +[`wifi.suspend()`](#wifisuspend) +[`node.sleep()`](node.md#nodesleep) +[`node.dsleep()`](node.md#nodedsleep) + ## wifi.setmode() Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes: @@ -222,6 +253,61 @@ none #### See also [`wifi.startsmart()`](#wifistartsmart) +## wifi.suspend() + +Suspend Wifi to reduce current consumption. + +This function is also useful for preventing WiFi stack related crashes when executing functions or tasks that take longer than ~500ms + +#### Syntax +`wifi.suspend({duration[, suspend_cb, resume_cb, preserve_mode]})` + +#### Parameters +- `duration` Suspend duration in microseconds(μs). If a suspend duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs (0:4:28.000454)) +- `suspend_cb` Callback to execute when WiFi is suspended. (Optional) +- `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional) +- `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true) + - If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes. + - If false, discard WiFi mode and leave NodeMCU in [`wifi.NULL_MODE`](#wifigetmode). WiFi mode will be restored to original mode on restart. + +#### Returns +- `suspend_state` if no parameters are provided, current WiFi suspension state will be returned + - States: + - `0` WiFi is awake. + - `1` WiFi suspension is pending. (Waiting for idle task) + - `2` WiFi is suspended. + + +#### Example + +```lua +--get current wifi suspension state +print(wifi.suspend()) + +--Suspend WiFi for 10 seconds with suspend/resume callbacks + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + cfg.suspend_cb=function() print("WiFi suspended") end + + wifi.suspend(cfg) + +--Suspend WiFi for 10 seconds with suspend/resume callbacks and discard WiFi mode + cfg={} + cfg.duration=10*1000*1000 + cfg.resume_cb=function() print("WiFi resume") end + cfg.suspend_cb=function() print("WiFfi suspended") end + cfg.preserve_mode=false + + wifi.suspend(cfg) + +``` + +#### See also +[`wifi.resume()`](#wifiresume) +[`node.sleep()`](node.md#nodesleep) +[`node.dsleep()`](node.md#nodedsleep) + # wifi.sta Module ## wifi.sta.autoconnect() @@ -1010,7 +1096,7 @@ The current state which can be one of the following: ## wifi.ap.config() -Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE-9997C3. +Sets SSID and password in AP mode. Be sure to make the password at least 8 characters long! If you don't it will default to *no* password and not set the SSID! It will still work as an access point but use a default SSID like e.g. NODE_9997C3. #### Syntax `wifi.ap.config(cfg)` diff --git a/sdk-overrides/include/ets_sys.h b/sdk-overrides/include/ets_sys.h index b92b036b3e..eb4cf80d29 100644 --- a/sdk-overrides/include/ets_sys.h +++ b/sdk-overrides/include/ets_sys.h @@ -9,4 +9,6 @@ int ets_sprintf(char *str, const char *format, ...) __attribute__ ((format (pri int ets_vsprintf (char *d, const char *s, va_list ap); +extern ETSTimer *timer_list; + #endif /* SDK_OVERRIDES_INCLUDE_ETS_SYS_H_ */ diff --git a/sdk-overrides/include/osapi.h b/sdk-overrides/include/osapi.h index 6e8c56e3b2..46b797f2ba 100644 --- a/sdk-overrides/include/osapi.h +++ b/sdk-overrides/include/osapi.h @@ -16,4 +16,15 @@ void call_user_start(void); #include_next "osapi.h" +#ifdef ENABLE_TIMER_SUSPEND +extern void swtmr_register(void* timer_ptr); +#undef os_timer_arm +#define os_timer_arm(timer_ptr, duration, mode) do{swtmr_register(timer_ptr); \ + ets_timer_arm_new(timer_ptr, duration, mode, 1);}while(0); + +extern void swtmr_unregister(void* timer_ptr); +#undef os_timer_disarm +#define os_timer_disarm(timer_ptr) do{swtmr_unregister(timer_ptr); ets_timer_disarm(timer_ptr);}while(0); +#endif + #endif diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 246b02b91b..54dff6b8e5 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -15,4 +15,13 @@ enum ext_flash_size_map { // Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf void system_deep_sleep_instant(uint32 time_in_us); +//force sleep API +#define FPM_SLEEP_MAX_TIME 268435455 //0xFFFFFFF +void wifi_fpm_set_wakeup_cb(void (*fpm_wakeup_cb_func)(void)); +bool fpm_is_open(void); +bool fpm_rf_is_closed(void); +uint8 get_fpm_auto_sleep_flag(void); + + + #endif /* SDK_OVERRIDES_INCLUDE_USER_INTERFACE_H_ */ From 66ffa6cdc4558345dd4b6dd98a5701c1dbdcb454 Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Tue, 4 Apr 2017 16:22:04 -0400 Subject: [PATCH 43/73] Fix the error callback from not being called sometimes (#1683) * Fix the error callback from not being called sometimes * Moved the setting of the reconnect status to after the connack is recevied * Increase the irom0_seg size * Updated the documentation * Make it clearer that autoreconnect is deprecated --- app/modules/mqtt.c | 46 +++++++++++++++++++++++++++++------------ docs/en/modules/mqtt.md | 34 +++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index c9813af2b3..579a65538d 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -1,5 +1,5 @@ // Module for mqtt - +// #include "module.h" #include "lauxlib.h" #include "platform.h" @@ -42,10 +42,14 @@ typedef struct mqtt_event_data_t uint16_t data_offset; } mqtt_event_data_t; +#define RECONNECT_OFF 0 +#define RECONNECT_POSSIBLE 1 +#define RECONNECT_ON 2 + typedef struct mqtt_state_t { uint16_t port; - int auto_reconnect; + uint8_t auto_reconnect; // 0 is not auto_reconnect. 1 is auto reconnect, but never connected. 2 is auto reconnect, but once connected mqtt_connect_info_t* connect_info; uint16_t message_length; uint16_t message_length_read; @@ -106,7 +110,7 @@ static void mqtt_socket_disconnected(void *arg) // tcp only } } - if(mud->mqtt_state.auto_reconnect){ + if(mud->mqtt_state.auto_reconnect == RECONNECT_ON) { mud->pesp_conn->reverse = mud; mud->pesp_conn->type = ESPCONN_TCP; mud->pesp_conn->state = ESPCONN_NONE; @@ -153,7 +157,7 @@ static void mqtt_socket_reconnected(void *arg, sint8_t err) mud->event_timeout = 0; // no need to count anymore - if(mud->mqtt_state.auto_reconnect){ + if(mud->mqtt_state.auto_reconnect == RECONNECT_ON) { pesp_conn->proto.tcp->remote_port = mud->mqtt_state.port; pesp_conn->proto.tcp->local_port = espconn_port(); socket_connect(pesp_conn); @@ -166,6 +170,9 @@ static void mqtt_socket_reconnected(void *arg, sint8_t err) { espconn_disconnect(pesp_conn); } + + mqtt_connack_fail(mud, MQTT_CONN_FAIL_SERVER_NOT_FOUND); + mqtt_socket_disconnected(arg); } NODE_DBG("leave mqtt_socket_reconnected.\n"); @@ -287,7 +294,7 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) switch(mud->connState){ case MQTT_CONNECT_SENDING: case MQTT_CONNECT_SENT: - mud->event_timeout = 0; + mud->event_timeout = 0; if(mqtt_get_type(in_buffer) != MQTT_MSG_TYPE_CONNACK){ NODE_DBG("MQTT: Invalid packet\r\n"); @@ -330,6 +337,9 @@ static void mqtt_socket_received(void *arg, char *pdata, unsigned short len) } else { mud->connState = MQTT_DATA; NODE_DBG("MQTT: Connected\r\n"); + if (mud->mqtt_state.auto_reconnect == RECONNECT_POSSIBLE) { + mud->mqtt_state.auto_reconnect = RECONNECT_ON; + } if(mud->cb_connect_ref == LUA_NOREF) break; if(mud->self_ref == LUA_NOREF) @@ -603,6 +613,16 @@ void mqtt_socket_timer(void *arg) NODE_DBG("Can not connect to broker.\n"); os_timer_disarm(&mud->mqttTimer); mqtt_connack_fail(mud, MQTT_CONN_FAIL_SERVER_NOT_FOUND); +#ifdef CLIENT_SSL_ENABLE + if(mud->secure) + { + espconn_secure_disconnect(mud->pesp_conn); + } + else +#endif + { + espconn_disconnect(mud->pesp_conn); + } } else if(mud->connState == MQTT_CONNECT_SENDING){ // MQTT_CONNECT send time out. NODE_DBG("sSend MQTT_CONNECT failed.\n"); mud->connState = MQTT_INIT; @@ -779,7 +799,7 @@ static int mqtt_socket_client( lua_State* L ) mud->connect_info.keepalive = keepalive; mud->mqtt_state.pending_msg_q = NULL; - mud->mqtt_state.auto_reconnect = 0; + mud->mqtt_state.auto_reconnect = RECONNECT_OFF; mud->mqtt_state.port = 1883; mud->mqtt_state.connect_info = &mud->connect_info; @@ -924,7 +944,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { dns_reconn_count++; if( dns_reconn_count >= 5 ){ - NODE_ERR( "DNS Fail!\n" ); + NODE_DBG( "DNS Fail!\n" ); // Note: should delete the pesp_conn or unref self_ref here. struct espconn *pesp_conn = arg; @@ -938,7 +958,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) mqtt_socket_disconnected(arg); // although not connected, but fire disconnect callback to release every thing. return -1; } - NODE_ERR( "DNS retry %d!\n", dns_reconn_count ); + NODE_DBG( "DNS retry %d!\n", dns_reconn_count ); host_ip.addr = 0; return espconn_gethostbyname(pesp_conn, name, &host_ip, socket_dns_foundcb); } @@ -968,7 +988,7 @@ static int mqtt_socket_connect( lua_State* L ) ip_addr_t ipaddr; const char *domain; int stack = 1; - unsigned secure = 0, auto_reconnect = 0; + unsigned secure = 0, auto_reconnect = RECONNECT_OFF; int top = lua_gettop(L); sint8 espconn_status; @@ -1054,11 +1074,11 @@ static int mqtt_socket_connect( lua_State* L ) { auto_reconnect = lua_tointeger(L, stack); stack++; - if ( auto_reconnect != 0 && auto_reconnect != 1 ){ - auto_reconnect = 0; // default to 0 + if ( auto_reconnect != RECONNECT_OFF && auto_reconnect != RECONNECT_POSSIBLE ){ + auto_reconnect = RECONNECT_OFF; // default to 0 } } else { - auto_reconnect = 0; // default to 0 + auto_reconnect = RECONNECT_OFF; // default to 0 } mud->mqtt_state.auto_reconnect = auto_reconnect; @@ -1128,7 +1148,7 @@ static int mqtt_socket_close( lua_State* L ) return 1; } - mud->mqtt_state.auto_reconnect = 0; // stop auto reconnect. + mud->mqtt_state.auto_reconnect = RECONNECT_OFF; // stop auto reconnect. sint8 espconn_status = ESPCONN_CONN; if (mud->connected) { diff --git a/docs/en/modules/mqtt.md b/docs/en/modules/mqtt.md index 6609ac758a..a79f9d4e9a 100644 --- a/docs/en/modules/mqtt.md +++ b/docs/en/modules/mqtt.md @@ -18,7 +18,7 @@ Creates a MQTT client. - `keepalive` keepalive seconds - `username` user name - `password` user password -- `cleansession` 0/1 for `false`/`true` +- `cleansession` 0/1 for `false`/`true`. Default is 1 (`true`). #### Returns MQTT client @@ -96,13 +96,41 @@ Connects to the broker specified by the given host, port, and secure options. - `host` host, domain or IP (string) - `port` broker port (number), default 1883 - `secure` 0/1 for `false`/`true`, default 0. Take note of constraints documented in the [net module](net.md). -- `autoreconnect` 0/1 for `false`/`true`, default 0 +- `autoreconnect` 0/1 for `false`/`true`, default 0. This option is *deprecated*. - `function(client)` callback function for when the connection was established -- `function(client, reason)` callback function for when the connection could not be established +- `function(client, reason)` callback function for when the connection could not be established. No further callbacks should be called. #### Returns `true` on success, `false` otherwise +#### Notes + +Don't use `autoreconnect`. Let me repeat that, don't use `autoreconnect`. You should handle the errors explicitly and appropriately for +your application. In particular, the default for `cleansession` above is `true`, so all subscriptions are destroyed when the connection +is lost for any reason. + +In order to acheive a consistent connection, handle errors in the error callback. For example: + +``` +function handle_mqtt_error(client, reason) + tmr.create():alarm(10 * 1000, tmr.ALARM_SINGLE, do_mqtt_connect) +end + +function do_mqtt_connect() + mqtt:connect("server", function(client) print("connected") end, handle_mqtt_error) +end +``` + +In reality, the connected function should do something useful! + +This is the description of how the `autoreconnect` functionality may (or may not) work. + +> When `autoreconnect` is set, then the connection will be re-established when it breaks. No error indication will be given (but all the +> subscriptions may be lost if `cleansession` is true). However, if the +> very first connection fails, then no reconnect attempt is made, and the error is signalled through the callback (if any). The first connection +> is considered a success if the client connects to a server and gets back a good response packet in response to its MQTT connection request. +> This implies (for example) that the username and password are correct. + #### Connection failure callback reason codes: | Constant | Value | Description | From b645100d2880c29f798bb66d9cb8d5d8f41e0ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Tue, 4 Apr 2017 22:52:51 +0200 Subject: [PATCH 44/73] Add autoconnect deprecation warning to MQTT code --- app/modules/mqtt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c index 579a65538d..986b23ed6d 100644 --- a/app/modules/mqtt.c +++ b/app/modules/mqtt.c @@ -1073,6 +1073,9 @@ static int mqtt_socket_connect( lua_State* L ) if ( (stack<=top) && lua_isnumber(L, stack) ) { auto_reconnect = lua_tointeger(L, stack); + if ( auto_reconnect == RECONNECT_POSSIBLE ) { + platform_print_deprecation_note("autoreconnect == 1 is deprecated", "in the next version"); + } stack++; if ( auto_reconnect != RECONNECT_OFF && auto_reconnect != RECONNECT_POSSIBLE ){ auto_reconnect = RECONNECT_OFF; // default to 0 From b2cbf52a4b5e2b6e29b57fcbe4acf3ac8cfca361 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Tue, 4 Apr 2017 21:57:10 -0700 Subject: [PATCH 45/73] Fix error in documentation added with merge of PR#1231 (#1901) --- docs/en/modules/node.md | 12 ++++++------ docs/en/modules/tmr.md | 24 ++++++++++++------------ docs/en/modules/wifi.md | 12 ++++++------ 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index 683f571748..7ce0f1dd0c 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -128,9 +128,9 @@ node.dsleep(nil,4) ``` #### See also -[`wifi.suspend()`](wifi.md#wifisuspend) -[`wifi.resume()`](wifi.md#wifiresume) -[`node.sleep()`](#nodesleep) +- [`wifi.suspend()`](wifi.md#wifisuspend) +- [`wifi.resume()`](wifi.md#wifiresume) +- [`node.sleep()`](#nodesleep) ## node.flashid() @@ -387,9 +387,9 @@ Put NodeMCU in light sleep mode to reduce current consumption. ``` #### See also -[`wifi.suspend()`](wifi.md#wifisuspend) -[`wifi.resume()`](wifi.md#wifiresume) -[`node.dsleep()`](#nodedsleep) +- [`wifi.suspend()`](wifi.md#wifisuspend) +- [`wifi.resume()`](wifi.md#wifiresume) +- [`node.dsleep()`](#nodedsleep) ## node.stripdebug() diff --git a/docs/en/modules/tmr.md b/docs/en/modules/tmr.md index 8c815f9f3a..afc9089a6c 100644 --- a/docs/en/modules/tmr.md +++ b/docs/en/modules/tmr.md @@ -209,9 +209,9 @@ tmr.resume(mytimer) ``` #### See also -[`tmr.suspend()`](#tmrsuspend) -[`tmr.suspend_all()`](#tmrsuspendall) -[`tmr.resume_all()`](#tmrresumeall) +- [`tmr.suspend()`](#tmrsuspend) +- [`tmr.suspend_all()`](#tmrsuspendall) +- [`tmr.resume_all()`](#tmrresumeall) ## tmr.resume_all() @@ -235,9 +235,9 @@ tmr.resume_all() ``` #### See also -[`tmr.suspend()`](#tmrsuspend) -[`tmr.suspend_all()`](#tmrsuspendall) -[`tmr.resume()`](#tmrresume) +- [`tmr.suspend()`](#tmrsuspend) +- [`tmr.suspend_all()`](#tmrsuspendall) +- [`tmr.resume()`](#tmrresume) ## tmr.softwd() @@ -362,9 +362,9 @@ tmr.suspend(mytimer) ``` #### See also -[`tmr.suspend_all()`](#tmrsuspendall) -[`tmr.resume()`](#tmrresume) -[`tmr.resume_all()`](#tmrresumeall) +- [`tmr.suspend_all()`](#tmrsuspendall) +- [`tmr.resume()`](#tmrresume) +- [`tmr.resume_all()`](#tmrresumeall) ## tmr.suspend_all() @@ -392,9 +392,9 @@ tmr.suspend_all() ``` #### See also -[`tmr.suspendl()`](#tmrsuspend) -[`tmr.resume()`](#tmrresume) -[`tmr.resume_all()`](#tmrresumeall) +- [`tmr.suspendl()`](#tmrsuspend) +- [`tmr.resume()`](#tmrresume) +- [`tmr.resume_all()`](#tmrresumeall) ## tmr.time() diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index ec235658d0..42c2569959 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -101,9 +101,9 @@ wifi.resume(function() print("WiFi resume") end) ``` #### See also -[`wifi.suspend()`](#wifisuspend) -[`node.sleep()`](node.md#nodesleep) -[`node.dsleep()`](node.md#nodedsleep) +- [`wifi.suspend()`](#wifisuspend) +- [`node.sleep()`](node.md#nodesleep) +- [`node.dsleep()`](node.md#nodedsleep) ## wifi.setmode() @@ -304,9 +304,9 @@ print(wifi.suspend()) ``` #### See also -[`wifi.resume()`](#wifiresume) -[`node.sleep()`](node.md#nodesleep) -[`node.dsleep()`](node.md#nodedsleep) +- [`wifi.resume()`](#wifiresume) +- [`node.sleep()`](node.md#nodesleep) +- [`node.dsleep()`](node.md#nodedsleep) # wifi.sta Module From b1dcb7059333d30ee154666274d047fa8985d463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Mon, 10 Apr 2017 23:02:20 +0200 Subject: [PATCH 46/73] File doc fix (#1905) * clarify differing command support for spiffs and fatfs * fix toc --- docs/en/modules/file.md | 75 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/docs/en/modules/file.md b/docs/en/modules/file.md index 2b1b5b9399..3d679c40d8 100644 --- a/docs/en/modules/file.md +++ b/docs/en/modules/file.md @@ -7,7 +7,7 @@ The file module provides access to the file system and its individual files. The file system is a flat file system, with no notion of subdirectories/folders. -Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card is [FatFS is enabled](../sdcard.md). +Besides the SPIFFS file system on internal flash, this module can also access FAT partitions on an external SD card if [FatFS is enabled](../sdcard.md). ```lua -- open file in flash: @@ -32,6 +32,10 @@ Change current directory (and drive). This will be used when no drive/directory Current directory defaults to the root of internal SPIFFS (`/FLASH`) after system start. +!!! note + + Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware. + #### Syntax `file.chdir(dir)` @@ -73,7 +77,9 @@ end Format the file system. Completely erases any existing file system and writes a new one. Depending on the size of the flash chip in the ESP, this may take several seconds. -Not supported for SD cards. +!!! note + + Function is not supported for SD cards. #### Syntax `file.format()` @@ -91,7 +97,9 @@ none Returns the flash address and physical size of the file system area, in bytes. -Not supported for SD cards. +!!! note + + Function is not supported for SD cards. #### Syntax `file.fscfg()` @@ -156,7 +164,9 @@ end Mounts a FatFs volume on SD card. -Not supported for internal flash. +!!! note + + Function is only available when [FatFS support](../sdcard.md#enabling-fatfs) is compiled into the firmware and it is not supported for internal flash. #### Syntax `file.mount(ldrv[, pin])` @@ -217,7 +227,7 @@ When done with the file, it must be closed using `file.close()`. `file.open(filename, mode)` #### Parameters -- `filename` file to be opened, directories are not supported +- `filename` file to be opened - `mode`: - "r": read mode (the default) - "w": write mode @@ -248,8 +258,8 @@ end ``` #### See also -- [`file.close()`](#fileclose) -- [`file.readline()`](#filereadline) +- [`file.close()`](#fileclose-fileobjclose) +- [`file.readline()`](#filereadline-fileobjreadline) ## file.remove() @@ -296,17 +306,19 @@ file.rename("temp.lua","init.lua") ## file.stat() -Get attribtues of a file or directory in a table: +Get attribtues of a file or directory in a table. Elements of the table are: - `size` file size in bytes - `name` file name - `time` table with time stamp information. Default is 1970-01-01 00:00:00 in case time stamps are not supported (on SPIFFS). - - `year` - - `mon` - - `day` - - `hour` - - `min` - - `sec` + + - `year` + - `mon` + - `day` + - `hour` + - `min` + - `sec` + - `is_dir` flag `true` if item is a directory, otherwise `false` - `is_rdonly` flag `true` if item is read-only, otherwise `false` - `is_hidden` flag `true` if item is hidden, otherwise `false` @@ -387,8 +399,7 @@ end The maximum number of open files on SPIFFS is determined at compile time by `SPIFFS_MAX_OPEN_FILES` in `user_config.h`. -## file.close() -## file.obj:close() +## file.close(), file.obj:close() Closes the open file, if any. @@ -406,10 +417,9 @@ none #### See also [`file.open()`](#fileopen) -## file.flush() -## file.obj:flush() +## file.flush(), file.obj:flush() -Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose) performs an implicit flush as well. +Flushes any pending writes to the file system, ensuring no data is lost on a restart. Closing the open file using [`file.close()` / `fd:close()`](#fileclose-fileobjclose) performs an implicit flush as well. #### Syntax `file.flush()` @@ -436,10 +446,9 @@ end ``` #### See also -[`file.close()` / `file.obj:close()`](#fileclose) +[`file.close()` / `file.obj:close()`](#fileclose-fileobjclose) -## file.read() -## file.obj:read() +## file.read(), file.obj:read() Read content from the open file. @@ -482,10 +491,9 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.readline()` / `file.obj:readline()`](#filereadline) +- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline) -## file.readline() -## file.obj:readline() +## file.readline(), file.obj:readline() Read the next line from the open file. Lines are defined as zero or more bytes ending with a EOL ('\n') byte. If the next line is longer than 1024, this function only returns the first 1024 bytes. @@ -511,12 +519,11 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.close()` / `file.obj:close()`](#fileclose) -- [`file.read()` / `file.obj:read()`](#fileread) +- [`file.close()` / `file.obj:close()`](#fileclose-fileobjclose) +- [`file.read()` / `file.obj:read()`](#fileread-fileobjread) -## file.seek() -## file.obj:seek() +## file.seek(), file.obj:seek() Sets and gets the file position, measured from the beginning of the file, to the position given by offset plus a base specified by the string whence. @@ -549,8 +556,7 @@ end #### See also [`file.open()`](#fileopen) -## file.write() -## file.obj:write() +## file.write(), file.obj:write() Write a string to the open file. @@ -588,10 +594,9 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.writeline()` / `file.obj:writeline()`](#filewriteline) +- [`file.writeline()` / `file.obj:writeline()`](#filewriteline-fileobjwriteline) -## file.writeline() -## file.obj:writeline() +## file.writeline(), file.obj:writeline() Write a string to the open file and append '\n' at the end. @@ -618,4 +623,4 @@ end #### See also - [`file.open()`](#fileopen) -- [`file.readline()` / `file.obj:readline()`](#filereadline) +- [`file.readline()` / `file.obj:readline()`](#filereadline-fileobjreadline) From dba73cf2149389212126e2be8a9706a429b79918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 13 Apr 2017 21:42:18 +0200 Subject: [PATCH 47/73] Fix HTML anchors in ToC generator --- docs/js/extra.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/js/extra.js b/docs/js/extra.js index fef3df6ca7..596fe815e8 100644 --- a/docs/js/extra.js +++ b/docs/js/extra.js @@ -38,7 +38,8 @@ var nodemcu = nodemcu || {}; } function createTocTableRow(func, intro) { // fragile attempt to auto-create the in-page anchor - var href = func.replace(/\.|:/g, '').replace('()', '').replace(' --', '-').replace(/ /g, '-'); + // good tests: file.md, + var href = func.replace(/[\.:\(\)]/g, '').replace(/ --|, | /g, '-'); var link = '' + func + ''; return '' + link + '' + intro + ''; } @@ -220,4 +221,4 @@ var nodemcu = nodemcu || {}; } return values; } -}()); \ No newline at end of file +}()); From 039026995a83c273db718cadc5542131e202ffe4 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Sun, 16 Apr 2017 10:38:08 -0700 Subject: [PATCH 48/73] Update wifi suspend documentation (#1910) * Added message about potential for wifi crashes and more Added message about potential for wifi crashes caused by long running functions Modified description for wifi.resume() and wifi.suspend() --- docs/en/modules/wifi.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 42c2569959..c16288e3f7 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -3,6 +3,10 @@ | :----- | :-------------------- | :---------- | :------ | | 2015-05-12 | [Zeroday](https://github.com/funshine) | [dnc40085](https://github.com/dnc40085) | [wifi.c](../../../app/modules/wifi.c)| +!!! important + The WiFi subsystem is maintained by background tasks that must run periodically. Any function or task that takes longer than 15ms (milliseconds) may cause the WiFi subsystem to crash. To avoid these potential crashes, it is advised that the WiFi subsystem be suspended with [wifi.suspend()](#wifisuspend) prior to the execution of any tasks or functions that exceed this 15ms guideline. + + The NodeMCU WiFi control is spread across several tables: - `wifi` for overall WiFi configuration @@ -78,6 +82,10 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi. Wake up WiFi from suspended state or cancel pending wifi suspension +!!! note + Wifi resume occurs asynchronously, this means that the resume request will only be processed when control of the processor is passed back to the SDK (after MyResumeFunction() has completed) + The resume callback also occurs asynchronously and will only execute after wifi has resumed normal operation. + #### Syntax `wifi.resume([resume_cb])` @@ -257,7 +265,10 @@ none Suspend Wifi to reduce current consumption. -This function is also useful for preventing WiFi stack related crashes when executing functions or tasks that take longer than ~500ms +!!! note + Wifi suspension occurs asynchronously, this means that the suspend request will only be processed when control of the processor is passed back to the SDK (after MySuspendFunction() has completed) + The suspend callback also occurs asynchronously and will only execute after wifi has been successfully been suspended. + #### Syntax `wifi.suspend({duration[, suspend_cb, resume_cb, preserve_mode]})` From 2f8b35365afebeae42e71fa4f658665877b1339b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 16 Apr 2017 20:31:38 +0200 Subject: [PATCH 49/73] Prevent determineSelectedLanguageCode() from failing for offline site --- docs/js/extra.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/js/extra.js b/docs/js/extra.js index 596fe815e8..665f3437ec 100644 --- a/docs/js/extra.js +++ b/docs/js/extra.js @@ -181,7 +181,7 @@ var nodemcu = nodemcu || {}; // path is like /en///build/ -> extract 'lang' // split[0] is an '' because the path starts with the separator selectedLanguageCode = path.split('/')[3]; - } else { + } else if (!window.location.href.startsWith('file://')) { // path is like //build/ -> extract 'lang' selectedLanguageCode = path.substr(1, 2); } From 971627f3d3681adc44cfbaaf9926edb3f4a8f663 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Sun, 16 Apr 2017 23:27:12 -0700 Subject: [PATCH 50/73] Fix admonition formatting error in wifi module docs added with #1910 (#1912) * Fix admonition formatting error in wifi module docs added with #1910 Modified documentation for: wifi.suspend() wifi.resume() * More wifi documentation changes --- docs/en/modules/wifi.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index c16288e3f7..7e9d83448b 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -80,11 +80,10 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi. ## wifi.resume() -Wake up WiFi from suspended state or cancel pending wifi suspension +Wake up WiFi from suspended state or cancel pending wifi suspension. !!! note - Wifi resume occurs asynchronously, this means that the resume request will only be processed when control of the processor is passed back to the SDK (after MyResumeFunction() has completed) - The resume callback also occurs asynchronously and will only execute after wifi has resumed normal operation. + Wifi resume occurs asynchronously, this means that the resume request will only be processed when control of the processor is passed back to the SDK (after MyResumeFunction() has completed). The resume callback also executes asynchronously and will only execute after wifi has resumed normal operation. #### Syntax `wifi.resume([resume_cb])` @@ -262,12 +261,10 @@ none [`wifi.startsmart()`](#wifistartsmart) ## wifi.suspend() - Suspend Wifi to reduce current consumption. !!! note - Wifi suspension occurs asynchronously, this means that the suspend request will only be processed when control of the processor is passed back to the SDK (after MySuspendFunction() has completed) - The suspend callback also occurs asynchronously and will only execute after wifi has been successfully been suspended. + Wifi suspension occurs asynchronously, this means that the suspend request will only be processed when control of the processor is passed back to the SDK (after MySuspendFunction() has completed). The suspend callback also executes asynchronously and will only execute after wifi has been successfully been suspended. #### Syntax From 84c5275c37cf8c215ebb1e346b2ea096554c03af Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Mon, 17 Apr 2017 04:45:32 -0700 Subject: [PATCH 51/73] Moved the documentation for wifi.nullmodesleep to the correct spot (#1913) --- docs/en/modules/wifi.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 7e9d83448b..8178c1d83d 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -78,6 +78,26 @@ The current physical mode as one of `wifi.PHYMODE_B`, `wifi.PHYMODE_G` or `wifi. #### See also [`wifi.setphymode()`](#wifisetphymode) +## wifi.nullmodesleep() + +Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. + +!!! note + This function **does not** store it's setting in flash, if auto sleep in NULL_MODE is not desired, `wifi.nullmodesleep(false)` must be called after power-up, restart, or wake from deep sleep. + +#### Syntax +`wifi.nullmodesleep([enable])` + +#### Parameters +- `enable` + - `true` Enable WiFi auto sleep in NULL_MODE. (Default setting) + - `false` Disable WiFi auto sleep in NULL_MODE. + +#### Returns +- `sleep_enabled` Current/New NULL_MODE sleep setting + - If `wifi.nullmodesleep()` is called with no arguments, current setting is returned. + - If `wifi.nullmodesleep()` is called with `enable` argument, confirmation of new setting is returned. + ## wifi.resume() Wake up WiFi from suspended state or cancel pending wifi suspension. @@ -188,26 +208,6 @@ physical mode after setup #### See also [`wifi.getphymode()`](#wifigetphymode) -## wifi.nullmodesleep() - -Configures whether or not WiFi automatically goes to sleep in NULL_MODE. Enabled by default. - -!!! note - This function **does not** store it's setting in flash, if auto sleep in NULL_MODE is not desired, `wifi.nullmodesleep(false)` must be called after power-up, restart, or wake from deep sleep. - -#### Syntax -`wifi.nullmodesleep([enable])` - -#### Parameters -- `enable` - - `true` Enable WiFi auto sleep in NULL_MODE. (Default setting) - - `false` Disable WiFi auto sleep in NULL_MODE. - -#### Returns -- `sleep_enabled` Current/New NULL_MODE sleep setting - - If `wifi.nullmodesleep()` is called with no arguments, current setting is returned. - - If `wifi.nullmodesleep()` is called with `enable` argument, confirmation of new setting is returned. - ## wifi.startsmart() Starts to auto configuration, if success set up SSID and password automatically. From 3f787ac42116778d92cce1b380da405135bc0a03 Mon Sep 17 00:00:00 2001 From: flip111 Date: Tue, 18 Apr 2017 21:04:36 +0200 Subject: [PATCH 52/73] Update telnet link (#1918) --- docs/en/modules/ws2812.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/ws2812.md b/docs/en/modules/ws2812.md index 2b2ed56c59..5e395d81a2 100644 --- a/docs/en/modules/ws2812.md +++ b/docs/en/modules/ws2812.md @@ -13,7 +13,7 @@ handle two led strips at the same time. **WARNING**: In dual mode, you will loose access to the Lua's console through the serial port (it will be reconfigured to support WS2812-like protocol). If you want to keep access to Lua's console, you will have to -use an other input channel like a TCP server (see [example](https://github.com/nodemcu/nodemcu-firmware/blob/master/examples/telnet.lua)) +use an other input channel like a TCP server (see [example](https://github.com/nodemcu/nodemcu-firmware/blob/master/lua_examples/telnet.lua)) ## ws2812.init() Initialize UART1 and GPIO2, should be called once and before write(). From d777fdc50a8728ead03ec202797760072fb2e0ab Mon Sep 17 00:00:00 2001 From: tjhowse Date: Thu, 20 Apr 2017 04:16:27 +1000 Subject: [PATCH 53/73] Add module for TCS34725 colour sensor. (#1895) * Add module for TCS34725 colour sensor. * Fix implicit return. * Fix timer disarm after init completion callback. * Rework init() into setup() as per PR#1887. * Add new module to mkdocs and user_modules. * Fix unclear debug messages. * Documentation updates. * Cleanup comments and formatting. * Don't auto-enable the sensor. * Add callback function to enable. * Simplify Read16. * Remove Lua state argument to enable callback. * Remove self_ref. --- app/include/user_modules.h | 1 + app/modules/tcs34725.c | 355 ++++++++++++++++++++++++++++++++++++ docs/en/modules/tcs34725.md | 102 +++++++++++ mkdocs.yml | 1 + 4 files changed, 459 insertions(+) create mode 100644 app/modules/tcs34725.c create mode 100644 docs/en/modules/tcs34725.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 4c1291f90b..f6e7cf1a48 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -59,6 +59,7 @@ #define LUA_USE_MODULES_SPI //#define LUA_USE_MODULES_STRUCT //#define LUA_USE_MODULES_SWITEC +// #define LUA_USE_MODULES_TCS34725 //#define LUA_USE_MODULES_TM1829 #define LUA_USE_MODULES_TLS #define LUA_USE_MODULES_TMR diff --git a/app/modules/tcs34725.c b/app/modules/tcs34725.c new file mode 100644 index 0000000000..311314d8d2 --- /dev/null +++ b/app/modules/tcs34725.c @@ -0,0 +1,355 @@ +// *************************************************************************** +// TCS34725 module for ESP8266 with nodeMCU +// +// Written by K. Townsend (microBuilder.eu), Adapted for nodeMCU by Travis Howse (tjhowse gmail.com) +// +// BSD (see license.txt) +// *************************************************************************** + +// Original header: +/**************************************************************************/ +/*! + @file tcs34725.c + @author K. Townsend (microBuilder.eu) + @ingroup Sensors + @brief Driver for the TAOS TCS34725 I2C digital RGB/color sensor + @license BSD (see license.txt) +*/ +/**************************************************************************/ + +//#define NODE_DEBUG + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "c_math.h" + +// #define TCS34725_ADDRESS (0x29<<1) +#define TCS34725_ADDRESS (0x29) +#define TCS34725_BUS_ID (0x00) /* ?? Not sure what this is for . Nodemcu I2C bus ID? */ +#define TCS34725_READBIT (0x01) + +#define TCS34725_COMMAND_BIT (0x80) + +#define TCS34725_ENABLE (0x00) +#define TCS34725_ENABLE_AIEN (0x10) /* RGBC Interrupt Enable */ +#define TCS34725_ENABLE_WEN (0x08) /* Wait enable - Writing 1 activates the wait timer */ +#define TCS34725_ENABLE_AEN (0x02) /* RGBC Enable - Writing 1 actives the ADC, 0 disables it */ +#define TCS34725_ENABLE_PON (0x01) /* Power on - Writing 1 activates the internal oscillator, 0 disables it */ +#define TCS34725_ATIME (0x01) /* Integration time */ +#define TCS34725_WTIME (0x03) /* Wait time (if TCS34725_ENABLE_WEN is asserted) */ +#define TCS34725_WTIME_2_4MS (0xFF) /* WLONG0 = 2.4ms WLONG1 = 0.029s */ +#define TCS34725_WTIME_204MS (0xAB) /* WLONG0 = 204ms WLONG1 = 2.45s */ +#define TCS34725_WTIME_614MS (0x00) /* WLONG0 = 614ms WLONG1 = 7.4s */ +#define TCS34725_AILTL (0x04) /* Clear channel lower interrupt threshold */ +#define TCS34725_AILTH (0x05) +#define TCS34725_AIHTL (0x06) /* Clear channel upper interrupt threshold */ +#define TCS34725_AIHTH (0x07) +#define TCS34725_PERS (0x0C) /* Persistence register - basic SW filtering mechanism for interrupts */ +#define TCS34725_PERS_NONE (0b0000) /* Every RGBC cycle generates an interrupt */ +#define TCS34725_PERS_1_CYCLE (0b0001) /* 1 clean channel value outside threshold range generates an interrupt */ +#define TCS34725_PERS_2_CYCLE (0b0010) /* 2 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_3_CYCLE (0b0011) /* 3 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_5_CYCLE (0b0100) /* 5 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_10_CYCLE (0b0101) /* 10 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_15_CYCLE (0b0110) /* 15 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_20_CYCLE (0b0111) /* 20 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_25_CYCLE (0b1000) /* 25 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_30_CYCLE (0b1001) /* 30 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_35_CYCLE (0b1010) /* 35 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_40_CYCLE (0b1011) /* 40 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_45_CYCLE (0b1100) /* 45 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_50_CYCLE (0b1101) /* 50 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_55_CYCLE (0b1110) /* 55 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_PERS_60_CYCLE (0b1111) /* 60 clean channel values outside threshold range generates an interrupt */ +#define TCS34725_CONFIG (0x0D) +#define TCS34725_CONFIG_WLONG (0x02) /* Choose between short and long (12x) wait times via TCS34725_WTIME */ +#define TCS34725_CONTROL (0x0F) /* Set the gain level for the sensor */ +#define TCS34725_ID (0x12) /* 0x44 = TCS34721/TCS34725, 0x4D = TCS34723/TCS34727 */ +#define TCS34725_STATUS (0x13) +#define TCS34725_STATUS_AINT (0x10) /* RGBC Clean channel interrupt */ +#define TCS34725_STATUS_AVALID (0x01) /* Indicates that the RGBC channels have completed an integration cycle */ +#define TCS34725_CDATAL (0x14) /* Clear channel data */ +#define TCS34725_CDATAH (0x15) +#define TCS34725_RDATAL (0x16) /* Red channel data */ +#define TCS34725_RDATAH (0x17) +#define TCS34725_GDATAL (0x18) /* Green channel data */ +#define TCS34725_GDATAH (0x19) +#define TCS34725_BDATAL (0x1A) /* Blue channel data */ +#define TCS34725_BDATAH (0x1B) + +#define TCS34725_EN_DELAY 30 + +typedef enum +{ + TCS34725_INTEGRATIONTIME_2_4MS = 0xFF, /**< 2.4ms - 1 cycle - Max Count: 1024 */ + TCS34725_INTEGRATIONTIME_24MS = 0xF6, /**< 24ms - 10 cycles - Max Count: 10240 */ + TCS34725_INTEGRATIONTIME_101MS = 0xD5, /**< 101ms - 42 cycles - Max Count: 43008 */ + TCS34725_INTEGRATIONTIME_154MS = 0xC0, /**< 154ms - 64 cycles - Max Count: 65535 */ + TCS34725_INTEGRATIONTIME_700MS = 0x00 /**< 700ms - 256 cycles - Max Count: 65535 */ +} +tcs34725IntegrationTime_t; + +typedef enum +{ + TCS34725_GAIN_1X = 0x00, /**< No gain */ + TCS34725_GAIN_4X = 0x01, /**< 2x gain */ + TCS34725_GAIN_16X = 0x02, /**< 16x gain */ + TCS34725_GAIN_60X = 0x03 /**< 60x gain */ +} +tcs34725Gain_t; +static void temp_setup_debug(int line, const char *str); +uint8_t tcs34725Setup(lua_State* L); +uint8_t tcs34725Enable(lua_State* L); +uint8_t tcs34725Disable(lua_State* L); +uint8_t tcs34725GetRawData(lua_State* L); +uint8_t tcs34725LuaSetIntegrationTime(lua_State* L); +uint8_t tcs34725SetIntegrationTime(tcs34725IntegrationTime_t it, lua_State* L); +uint8_t tcs34725LuaSetGain(lua_State* L); +uint8_t tcs34725SetGain(tcs34725Gain_t gain, lua_State* L); + +static bool _tcs34725Initialised = false; +static int32_t _tcs34725SensorID = 0; +static tcs34725Gain_t _tcs34725Gain = TCS34725_GAIN_1X; +static tcs34725IntegrationTime_t _tcs34725IntegrationTime = TCS34725_INTEGRATIONTIME_2_4MS; + +os_timer_t tcs34725_timer; // timer for forced mode readout +sint32_t cb_tcs_en; + +/**************************************************************************/ +/*! + @brief Writes an 8 bit values over I2C +*/ +/**************************************************************************/ +uint8_t tcs34725Write8 (uint8_t reg, uint8_t value) +{ + platform_i2c_send_start(TCS34725_BUS_ID); + platform_i2c_send_address(TCS34725_BUS_ID, TCS34725_ADDRESS, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(TCS34725_BUS_ID, TCS34725_COMMAND_BIT | reg ); + platform_i2c_send_byte(TCS34725_BUS_ID, value); + platform_i2c_send_stop(TCS34725_BUS_ID); + return 0; +} + +/**************************************************************************/ +/*! + @brief Reads a 8 bit values over I2C +*/ +/**************************************************************************/ +uint8_t tcs34725Read8(uint8_t reg) +{ + uint8_t value; + + platform_i2c_send_start(TCS34725_BUS_ID); + platform_i2c_send_address(TCS34725_BUS_ID, TCS34725_ADDRESS, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(TCS34725_BUS_ID, TCS34725_COMMAND_BIT | reg); + platform_i2c_send_stop(TCS34725_BUS_ID); + + platform_i2c_send_start(TCS34725_BUS_ID); + platform_i2c_send_address(TCS34725_BUS_ID, TCS34725_ADDRESS, PLATFORM_I2C_DIRECTION_RECEIVER); + value = platform_i2c_recv_byte(TCS34725_BUS_ID, 0); + platform_i2c_send_stop(TCS34725_BUS_ID); + + return value; +} + +/**************************************************************************/ +/*! + @brief Reads a 16 bit values over I2C +*/ +/**************************************************************************/ +uint16_t tcs34725Read16(uint8_t reg) +{ + uint8_t low = tcs34725Read8(reg); + uint8_t high = tcs34725Read8(++reg); + + return (high << 8) | low; +} +/**************************************************************************/ +/*! + @brief Finishes enabling the device +*/ +/**************************************************************************/ +uint8_t tcs34725EnableDone() +{ + dbg_printf("Enable finished\n"); + lua_State *L = lua_getstate(); + os_timer_disarm (&tcs34725_timer); + tcs34725Write8(TCS34725_ENABLE, TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN); + + /* Ready to go ... set the initialised flag */ + _tcs34725Initialised = true; + + /* This needs to take place after the initialisation flag! */ + tcs34725SetIntegrationTime(TCS34725_INTEGRATIONTIME_2_4MS, L); + tcs34725SetGain(TCS34725_GAIN_60X, L); + + lua_rawgeti(L, LUA_REGISTRYINDEX, cb_tcs_en); // Get the callback to call + luaL_unref(L, LUA_REGISTRYINDEX, cb_tcs_en); // Unregister the callback to avoid leak + cb_tcs_en = LUA_NOREF; + lua_call(L, 0, 0); + + return 0; +} + +/**************************************************************************/ +/*! + @brief Enables the device +*/ +/**************************************************************************/ +uint8_t tcs34725Enable(lua_State* L) +{ + dbg_printf("Enable begun\n"); + + if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) { + if (cb_tcs_en != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, cb_tcs_en); + } + lua_pushvalue(L, 1); + cb_tcs_en = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + return luaL_error(L, "Enable argument must be a function."); + } + + tcs34725Write8(TCS34725_ENABLE, TCS34725_ENABLE_PON); + // Start a timer to wait TCS34725_EN_DELAY before calling tcs34725EnableDone + os_timer_disarm (&tcs34725_timer); + os_timer_setfn (&tcs34725_timer, (os_timer_func_t *)tcs34725EnableDone, NULL); + os_timer_arm (&tcs34725_timer, TCS34725_EN_DELAY, 0); // trigger callback when readout is ready + + return 0; +} + +/**************************************************************************/ +/*! + @brief Disables the device (putting it in lower power sleep mode) +*/ +/**************************************************************************/ +uint8_t tcs34725Disable(lua_State* L) +{ + /* Turn the device off to save power */ + uint8_t reg = 0; + reg = tcs34725Read8(TCS34725_ENABLE); + tcs34725Write8(TCS34725_ENABLE, reg & ~(TCS34725_ENABLE_PON | TCS34725_ENABLE_AEN)); + _tcs34725Initialised = false; + return 0; +} + +/**************************************************************************/ +/*! + @brief Initialises the I2C block +*/ +/**************************************************************************/ +uint8_t tcs34725Setup(lua_State* L) +{ + uint8_t id = 0; + + /* Make sure we have the right IC (0x44 = TCS34725 and TCS34721) */ + id = tcs34725Read8(TCS34725_ID); + dbg_printf("id: %x\n",id); + if (id != 0x44) { + return luaL_error(L, "No TCS34725 found."); + } + + lua_pushinteger(L, 1); + return 1; +} + +/**************************************************************************/ +/*! + @brief Sets the integration time to the specified value +*/ +/**************************************************************************/ +uint8_t tcs34725LuaSetIntegrationTime(lua_State* L) +{ + tcs34725IntegrationTime_t it = luaL_checkinteger(L, 1); + return tcs34725SetIntegrationTime(it,L); +} + +/**************************************************************************/ +/*! + @brief Sets the integration time to the specified value +*/ +/**************************************************************************/ +uint8_t tcs34725SetIntegrationTime(tcs34725IntegrationTime_t it, lua_State* L) +{ + if (!_tcs34725Initialised) + { + tcs34725Setup(L); + } + + tcs34725Write8(TCS34725_ATIME, it); + _tcs34725IntegrationTime = it; + + return 0; +} + +/**************************************************************************/ +/*! + @brief Sets gain to the specified value from Lua +*/ +/**************************************************************************/ +uint8_t tcs34725LuaSetGain(lua_State* L) +{ + tcs34725Gain_t gain = luaL_checkinteger(L, 1); + return tcs34725SetGain(gain,L); +} + +/**************************************************************************/ +/*! + @brief Sets gain to the specified value +*/ +/**************************************************************************/ +uint8_t tcs34725SetGain(tcs34725Gain_t gain, lua_State* L) +{ + if (!_tcs34725Initialised) + { + return luaL_error(L, "TCS34725 not initialised."); + } + + tcs34725Write8(TCS34725_CONTROL, gain); + _tcs34725Gain = gain; + + return 0; +} + +/**************************************************************************/ +/*! + @brief Reads the raw red, green, blue and clear channel values +*/ +/**************************************************************************/ +uint8_t tcs34725GetRawData(lua_State* L) +{ + uint16_t r; + uint16_t g; + uint16_t b; + uint16_t c; + + if (!_tcs34725Initialised) + { + return luaL_error(L, "TCS34725 not initialised."); + } + + c = tcs34725Read16(TCS34725_CDATAL); + r = tcs34725Read16(TCS34725_RDATAL); + g = tcs34725Read16(TCS34725_GDATAL); + b = tcs34725Read16(TCS34725_BDATAL); + lua_pushinteger(L, c); + lua_pushinteger(L, r); + lua_pushinteger(L, g); + lua_pushinteger(L, b); + return 4; +} + + +static const LUA_REG_TYPE tcs34725_map[] = { + { LSTRKEY( "setup" ), LFUNCVAL(tcs34725Setup)}, + { LSTRKEY( "enable" ), LFUNCVAL(tcs34725Enable)}, + { LSTRKEY( "disable" ), LFUNCVAL(tcs34725Disable)}, + { LSTRKEY( "raw" ), LFUNCVAL(tcs34725GetRawData)}, + { LSTRKEY( "setGain" ), LFUNCVAL(tcs34725LuaSetGain)}, + { LSTRKEY( "setIntegrationTime" ), LFUNCVAL(tcs34725LuaSetIntegrationTime)}, + { LNILKEY, LNILVAL} +}; + +NODEMCU_MODULE(TCS34725, "tcs34725", tcs34725_map, NULL); \ No newline at end of file diff --git a/docs/en/modules/tcs34725.md b/docs/en/modules/tcs34725.md new file mode 100644 index 0000000000..ae5a553cc0 --- /dev/null +++ b/docs/en/modules/tcs34725.md @@ -0,0 +1,102 @@ +# TCS34725 module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-02 | [tjhowse](https://github.com/tjhowse) | [tjhowse](https://github.com/tjhowse) | [tcs34725.c](../../../app/modules/tcs34725.c)| + +This module provides a simple interface to [TCS34725 colour/light sensors](https://www.adafruit.com/product/1334) (Adafruit). + +Note that you must call [`setup()`](#tcs34725setup) before you can start reading values! + +## tcs34725.setup() + +setupializes module. setupialization is mandatory before values can be read. + +#### Syntax + +`tcs34725.setup()` + +#### Returns +`0` if setup has failed (no sensor connected?), `1` if sensor is TCS34725 + +#### Example +```lua +tcs34725.setup() +tcs34725.enable(function() + print("TCS34275 Enabled") + clear,red,green,blue=tcs34725.raw() +end) +``` + +## tcs34725.enable(function()) + +Enables the sensor. Can be used to wake up after a disable. + +#### Syntax +```lua +tcs34725.enable(function() + print("TCS34275 Enabled") + clear,red,green,blue=tcs34725.raw() +end) +``` +#### Parameters +A function called when the sensor has finished initialising. + +#### Returns +0 + +## tcs34725.disable() + +Disables the sensor. Enables a low-power sleep mode. + +#### Syntax +`tcs34725.disable()` + +#### Returns +0 + +## tcs34725.raw() + +Reads the clear, red, green and blue values from the sensor. + +#### Syntax +`clear,red,green,blue=tcs34725.raw()` + +#### Returns +clear, red, green, blue in uint16_t. + +## tcs34725.setGain() + +Sets the gain of the sensor. Must be called after the sensor is enabled. + +#### Syntax +`tcs34725.setGain(gain)` + +#### Parameters +|gain|Gain| +|-----|-----------------| +|0x00|TCS34725_GAIN_1X| +|0x01|TCS34725_GAIN_4X| +|0x02|TCS34725_GAIN_16X| +|0x03|TCS34725_GAIN_60X| + +#### Returns +0 + +## tcs34725.setIntegrationTime() + +Sets the integration time of the sensor. Must be called after the sensor is enabled. + +#### Syntax +`tcs34725.setIntegrationTime(time)` + +#### Parameters +|time|Gain| +|-----|-----------------| +|0xFF|TCS34725_INTEGRATIONTIME_2_4MS| +|0xF6|TCS34725_INTEGRATIONTIME_24MS| +|0xD5|TCS34725_INTEGRATIONTIME_101MS| +|0xC0|TCS34725_INTEGRATIONTIME_154MS| +|0x00|TCS34725_INTEGRATIONTIME_700MS| + +#### Returns +0 diff --git a/mkdocs.yml b/mkdocs.yml index 3c8f585b2c..08f05b5485 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,6 +78,7 @@ pages: - 'spi': 'en/modules/spi.md' - 'struct': 'en/modules/struct.md' - 'switec': 'en/modules/switec.md' + - 'tcs34725': 'en/modules/tcs34725.md' - 'tls': 'en/modules/tls.md' - 'tm1829': 'en/modules/tm1829.md' - 'tmr': 'en/modules/tmr.md' From 332bcb39a338b62303f921b3df298a6a4e50de19 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Wed, 19 Apr 2017 14:16:44 -0400 Subject: [PATCH 54/73] mqtt: fix several buffer length checks (#1906) Partially addresses nodemcu/nodemcu-firmware#1773. --- app/mqtt/mqtt_msg.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/mqtt/mqtt_msg.c b/app/mqtt/mqtt_msg.c index 8306fc6538..0f206fcc91 100644 --- a/app/mqtt/mqtt_msg.c +++ b/app/mqtt/mqtt_msg.c @@ -162,7 +162,7 @@ const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) } totlen += i; - if(i + 2 >= *length) + if(i + 2 > *length) return NULL; topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; @@ -191,12 +191,12 @@ const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) } totlen += i; - if(i + 2 >= *length) + if(i + 2 > *length) return NULL; topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; - if(i + topiclen >= *length){ + if(i + topiclen > *length){ *length = 0; return NULL; } @@ -204,7 +204,7 @@ const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) if(mqtt_get_qos(buffer) > 0) { - if(i + 2 >= *length) + if(i + 2 > *length) return NULL; i += 2; } @@ -231,6 +231,9 @@ uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length) int i; int topiclen; + if(mqtt_get_qos(buffer) <= 0) + return 0; + for(i = 1; i < length; ++i) { if((buffer[i] & 0x80) == 0) @@ -240,23 +243,17 @@ uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length) } } - if(i + 2 >= length) + if(i + 2 > length) return 0; topiclen = buffer[i++] << 8; topiclen |= buffer[i++]; - if(i + topiclen >= length) + 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; - } + if(i + 2 > length) + return 0; return (buffer[i] << 8) | buffer[i + 1]; } From 1fcb3259ce52f0ae76e146d7f9e364b30a3880c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Thu, 20 Apr 2017 09:29:43 +0200 Subject: [PATCH 55/73] Update module count --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac8cd0590e..b7c06b02c7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The NodeMCU *firmware* is a companion project to the popular [NodeMCU dev kits]( - Easy to program wireless node and/or access point - Based on Lua 5.1.4 (without *debug, os* modules) - Asynchronous event-driven programming model -- 40+ built-in modules +- 55+ built-in modules - Firmware available with or without floating point support (integer-only uses less memory) - Up-to-date documentation at [https://nodemcu.readthedocs.io](https://nodemcu.readthedocs.io) @@ -64,4 +64,4 @@ See [https://nodemcu.readthedocs.io/en/dev/en/support/](https://nodemcu.readthed # License -[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html) \ No newline at end of file +[MIT](https://github.com/nodemcu/nodemcu-firmware/blob/master/LICENSE) © [zeroday](https://github.com/NodeMCU)/[nodemcu.com](http://nodemcu.com/index_en.html) From 9d11543fa9d31f1607309849efc34a363e16522b Mon Sep 17 00:00:00 2001 From: Metin KOC Date: Thu, 20 Apr 2017 23:23:51 +0300 Subject: [PATCH 56/73] Hdc1080 Module Add (#1880) * Hdc1080 Module Add * for Float/Integer Build fixes * disable module for default * document fix * Deprecate init() in favor of setup() * Fix grammar and code sample * Deprecate init() in favor of setup() * Update hdc1080.md * Update hdc1080.md * Mini fix * Update user_modules.h --- app/include/user_modules.h | 2 + app/modules/hdc1080.c | 136 +++++++++++++++++++++++++++++++++++++ docs/en/modules/hdc1080.md | 56 +++++++++++++++ mkdocs.yml | 2 + 4 files changed, 196 insertions(+) create mode 100644 app/modules/hdc1080.c create mode 100644 docs/en/modules/hdc1080.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index f6e7cf1a48..d6d92f191f 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -33,6 +33,7 @@ #define LUA_USE_MODULES_FILE //#define LUA_USE_MODULES_GDBSTUB #define LUA_USE_MODULES_GPIO +//#define LUA_USE_MODULES_HDC1080 //#define LUA_USE_MODULES_HMC5883L //#define LUA_USE_MODULES_HTTP //#define LUA_USE_MODULES_HX711 @@ -76,3 +77,4 @@ #endif /* LUA_CROSS_COMPILER */ #endif /* __USER_MODULES_H__ */ + diff --git a/app/modules/hdc1080.c b/app/modules/hdc1080.c new file mode 100644 index 0000000000..508cc849b3 --- /dev/null +++ b/app/modules/hdc1080.c @@ -0,0 +1,136 @@ +/* + * Driver for TI Texas Instruments HDC1080 Temperature/Humidity Sensor. + * Code By Metin KOC + * Sixfab Inc. metin@sixfab.com + * Code based on ADXL345 driver. + */ +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "c_stdlib.h" +#include "c_string.h" +#include "c_math.h" + +static const uint32_t hdc1080_i2c_id = 0; +static const uint8_t hdc1080_i2c_addr = 0x40; + + +#define HDC1080_TEMPERATURE_REGISTER 0X00 +#define HDC1080_HUMIDITY_REGISTER 0X01 +#define HDC1080_CONFIG_REGISTER 0X02 + + +static int hdc1080_setup(lua_State* L) { + + // Configure Sensor + platform_i2c_send_start(hdc1080_i2c_id); + platform_i2c_send_address(hdc1080_i2c_id, hdc1080_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(hdc1080_i2c_id, HDC1080_CONFIG_REGISTER); + platform_i2c_send_byte(hdc1080_i2c_id, 0x05); //Bit[10] to 1 for 11 bit resolution , Set Bit[9:8] to 01 for 11 bit resolution. + platform_i2c_send_byte(hdc1080_i2c_id, 0x00); + platform_i2c_send_stop(hdc1080_i2c_id); + + return 0; +} + +static int hdc1080_init(lua_State* L) { + + uint32_t sda; + uint32_t scl; + + platform_print_deprecation_note("hdc1080.init() is replaced by hdc1080.setup()", "in the next version"); + + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + return luaL_error(L, "wrong arg range"); + } + + sda = luaL_checkinteger(L, 1); + scl = luaL_checkinteger(L, 2); + + if (scl == 0 || sda == 0) { + return luaL_error(L, "no i2c for D0"); + } + + platform_i2c_setup(hdc1080_i2c_id, sda, scl, PLATFORM_I2C_SPEED_SLOW); + + // remove sda and scl parameters from stack + lua_remove(L, 1); + lua_remove(L, 1); + + return hdc1080_setup(L); +} + +static int hdc1080_read(lua_State* L) { + + uint8_t data[2]; + + #ifdef LUA_NUMBER_INTEGRAL + int temp; + int humidity; + #else + float temp; + float humidity; + #endif + + int i; + + platform_i2c_send_start(hdc1080_i2c_id); + platform_i2c_send_address(hdc1080_i2c_id, hdc1080_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(hdc1080_i2c_id, HDC1080_TEMPERATURE_REGISTER); + platform_i2c_send_stop(hdc1080_i2c_id); + + os_delay_us(7000); + + platform_i2c_send_start(hdc1080_i2c_id); + platform_i2c_send_address(hdc1080_i2c_id, hdc1080_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + + for (i=0; i<2; i++) { + data[i] = platform_i2c_recv_byte(hdc1080_i2c_id, 1); + } + + platform_i2c_send_stop(hdc1080_i2c_id); + + #ifdef LUA_NUMBER_INTEGRAL + temp = ((((data[0]<<8)|data[1])*165)>>16)-40; + lua_pushinteger(L, (int)temp); + #else + temp = ((float)((data[0]<<8)|data[1])/(float)pow(2,16))*165.0f-40.0f; + lua_pushnumber(L, temp); + #endif + + + platform_i2c_send_start(hdc1080_i2c_id); + platform_i2c_send_address(hdc1080_i2c_id, hdc1080_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(hdc1080_i2c_id, HDC1080_HUMIDITY_REGISTER); + platform_i2c_send_stop(hdc1080_i2c_id); + + os_delay_us(7000); + + platform_i2c_send_start(hdc1080_i2c_id); + platform_i2c_send_address(hdc1080_i2c_id, hdc1080_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + + for (i=0; i<2; i++) { + data[i] = platform_i2c_recv_byte(hdc1080_i2c_id, 1); + } + + platform_i2c_send_stop(hdc1080_i2c_id); + + #ifdef LUA_NUMBER_INTEGRAL + humidity = ((((data[0]<<8)|data[1]))*100)>>16; + lua_pushinteger(L, (int)humidity); + #else + humidity = ((float)((data[0]<<8)|data[1])/(float)pow(2,16))*100.0f; + lua_pushnumber(L, humidity); + #endif + + return 2; +} + +static const LUA_REG_TYPE hdc1080_map[] = { + { LSTRKEY( "read" ), LFUNCVAL( hdc1080_read )}, + { LSTRKEY( "setup" ), LFUNCVAL( hdc1080_setup )}, + { LSTRKEY( "init" ), LFUNCVAL( hdc1080_init )}, + { LNILKEY, LNILVAL} +}; + +NODEMCU_MODULE(HDC1080, "hdc1080", hdc1080_map, NULL); diff --git a/docs/en/modules/hdc1080.md b/docs/en/modules/hdc1080.md new file mode 100644 index 0000000000..d5e3cddd11 --- /dev/null +++ b/docs/en/modules/hdc1080.md @@ -0,0 +1,56 @@ +# HDC1080 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-01 | [Metin KOC](https://github.com/saucompeng) | [Metin KOC](https://github.com/saucompeng) | [hdc1080.c](../../../app/modules/hdc1080.c)| + + +This module provides access to the [HDC1080](http://www.ti.com/product/HDC1080) low power, high accuracy digital humidity sensor with temperature sensor. + +## hdc1080.read() +Samples the sensor then returns temperature and humidity value. + +#### Syntax +`hdc1080.read()` + +#### Returns +Temperature data in centigrade and humidity data in percentage (0-100) (integer/float) + +#### Example +```lua +local sda, scl = 1, 2 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +hdc1080.setup() +local temperature,humidity = hdc1080.read() +print(temperature) +print(humidity) +``` + +## hdc1080.setup() +Initializes the module. + +#### Syntax +`hdc1080.setup()` + +#### Parameters +- None + +#### Returns +`nil` + + +## hdc1080.init(sda,scl) +Initializes the module and sets the pin configuration. + +!!! attention + + This function is deprecated and will be removed in upcoming releases. Use `hdc1080.setup()` instead. + +#### Syntax +`hdc1080.init(sda, scl)` + +#### Parameters +- `sda` data pin +- `scl` clock pin + +#### Returns +`nil` diff --git a/mkdocs.yml b/mkdocs.yml index 08f05b5485..c4d69c0c0d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -52,6 +52,7 @@ pages: - 'file': 'en/modules/file.md' - 'gdbstub': 'en/modules/gdbstub.md' - 'gpio': 'en/modules/gpio.md' + - 'hdc1080': 'en/modules/hdc1080.md' - 'hmc5883l': 'en/modules/hmc5883l.md' - 'http': 'en/modules/http.md' - 'hx711' : 'en/modules/hx711.md' @@ -94,3 +95,4 @@ pages: - 'xpt2046': 'en/modules/xpt2046.md' #- Deutsch: # - Home: 'de/index.md' + From ef1654fa1ea0c0c5fda5cea1661232a30922bde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Fri, 21 Apr 2017 07:49:27 +0200 Subject: [PATCH 57/73] Add 'connection: close' note --- docs/en/modules/net.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 69a9ac0959..6184b210b0 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -318,9 +318,12 @@ Otherwise, all connection errors (with normal close) passed to disconnection eve ```lua srv = net.createConnection(net.TCP, 0) srv:on("receive", function(sck, c) print(c) end) +-- Wait for connection before sending. srv:on("connection", function(sck, c) - -- Wait for connection before sending. - sck:send("GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") + -- 'Connection: close' rather than 'Connection: keep-alive' to have server + -- initiate a close of the connection after final response (frees memory + -- earlier here), http://bit.ly/2pkOrsi + sck:send("GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\nAccept: */*\r\n\r\n") end) srv:connect(80,"httpbin.org") ``` From 572e4235fb40e86dcecc42ad223467a263227698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sat, 22 Apr 2017 22:57:37 +0200 Subject: [PATCH 58/73] Unwind bit.ly link --- docs/en/modules/net.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/net.md b/docs/en/modules/net.md index 6184b210b0..e258689933 100644 --- a/docs/en/modules/net.md +++ b/docs/en/modules/net.md @@ -322,7 +322,7 @@ srv:on("receive", function(sck, c) print(c) end) srv:on("connection", function(sck, c) -- 'Connection: close' rather than 'Connection: keep-alive' to have server -- initiate a close of the connection after final response (frees memory - -- earlier here), http://bit.ly/2pkOrsi + -- earlier here), https://tools.ietf.org/html/rfc7230#section-6.6 sck:send("GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\nAccept: */*\r\n\r\n") end) srv:connect(80,"httpbin.org") From f5fac7a19e5285ccfa4a48e5b5b48fbefa2230b4 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Mon, 24 Apr 2017 00:59:30 -0700 Subject: [PATCH 59/73] Updated documentation and example for wps.start() (#1933) --- docs/en/modules/wps.md | 109 +++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 16 deletions(-) diff --git a/docs/en/modules/wps.md b/docs/en/modules/wps.md index 72089ace1b..8aca670a20 100644 --- a/docs/en/modules/wps.md +++ b/docs/en/modules/wps.md @@ -34,6 +34,9 @@ none ## wps.start() Start WiFi WPS function. WPS must be enabled prior calling this function. +!!! note + This function only configures the station with the AP's info, it does not connect to AP automatically. + #### Syntax `wps.start([function(status)])` @@ -45,21 +48,95 @@ Start WiFi WPS function. WPS must be enabled prior calling this function. #### Example ```lua -wps.enable() -wps.start(function(status) - if status == wps.SUCCESS then - print("SUCCESS!") - elseif status == wps.FAILED then - print("Failed") - elseif status == wps.TIMEOUT then - print("Timeout") - elseif status == wps.WEP then - print("WEP not supported") - elseif status == wps.SCAN_ERR then - print("WPS AP not found") - else - print(status) + --Basic example + wifi.setmode(wifi.STATION) + wps.enable() + wps.start(function(status) + if status == wps.SUCCESS then + wps.disable() + print("WPS: Success, connecting to AP...") + wifi.sta.connect() + return + elseif status == wps.FAILED then + print("WPS: Failed") + elseif status == wps.TIMEOUT then + print("WPS: Timeout") + elseif status == wps.WEP then + print("WPS: WEP not supported") + elseif status == wps.SCAN_ERR then + print("WPS: AP not found") + else + print(status) + end + wps.disable() + end) + + --Full example + do + -- Register wifi station event callbacks + wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, function(T) + print("\n\tSTA - CONNECTED".."\n\tSSID: "..T.SSID.."\n\tBSSID: ".. + T.BSSID.."\n\tChannel: "..T.channel) + end) + wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) + print("\n\tSTA - GOT IP".."\n\tStation IP: "..T.IP.."\n\tSubnet mask: ".. + T.netmask.."\n\tGateway IP: "..T.gateway) + end) + + wifi.setmode(wifi.STATION) + + wps_retry_func = function() + if wps_retry_count == nil then wps_retry_count = 0 end + if wps_retry_count < 3 then + wps.disable() + wps.enable() + wps_retry_count = wps_retry_count + 1 + wps_retry_timer = tmr.create() + wps_retry_timer:alarm(3000, tmr.ALARM_SINGLE, function() wps.start(wps_cb) end) + print("retry #"..wps_retry_count) + else + wps_retry_count = nil + wps_retry_timer = nil + wps_retry_func = nil + wps_cb = nil + end + end + + wps_cb = function(status) + if status == wps.SUCCESS then + wps.disable() + print("WPS: success, connecting to AP...") + wifi.sta.connect() + wps_retry_count = nil + wps_retry_timer = nil + wps_retry_func = nil + wps_cb = nil + return + elseif status == wps.FAILED then + print("WPS: Failed") + wps_retry_func() + return + elseif status == wps.TIMEOUT then + print("WPS: Timeout") + wps_retry_func() + return + elseif status == wps.WEP then + print("WPS: WEP not supported") + elseif status == wps.SCAN_ERR then + print("WPS: AP not found") + wps_retry_func() + return + else + print(status) + end + wps.disable() + wps_retry_count = nil + wps_retry_timer = nil + wps_retry_func = nil + wps_cb = nil + end + wps.enable() + wps.start(wps_cb) end - wps.disable() -end) + ``` From 9dbae1c963eb6f1d6b446a32ca3f12c0bb39e0d4 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Mon, 24 Apr 2017 13:06:54 -0700 Subject: [PATCH 60/73] Expose UART 1 (#1934) * Expose UART 1 and update uart module documentation --- app/platform/cpu_esp8266.h | 2 +- docs/en/modules/uart.md | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h index 63ab4238d1..b763d9cd2f 100644 --- a/app/platform/cpu_esp8266.h +++ b/app/platform/cpu_esp8266.h @@ -11,7 +11,7 @@ // Number of resources (0 if not available/not implemented) #define NUM_GPIO GPIO_PIN_NUM #define NUM_SPI 2 -#define NUM_UART 1 +#define NUM_UART 2 #define NUM_PWM GPIO_PIN_NUM #define NUM_ADC 1 #define NUM_CAN 0 diff --git a/docs/en/modules/uart.md b/docs/en/modules/uart.md index 1b08cddc7a..141b9aa4da 100644 --- a/docs/en/modules/uart.md +++ b/docs/en/modules/uart.md @@ -7,6 +7,10 @@ The [UART](https://en.wikipedia.org/wiki/Universal_asynchronous_receiver/transmi The default setup for the uart is controlled by build-time settings. The default rate is 115,200 bps. In addition, auto-baudrate detection is enabled for the first two minutes after platform boot. This will cause a switch to the correct baud rate once a few characters are received. Auto-baudrate detection is disabled when `uart.setup` is called. + +!!! important + Although there are two UARTs(0 and 1) available to NodeMCU, **UART 1 is not capable of receiving data and is therefore transmit only**. + ## uart.alt() Change UART pin assignment. @@ -28,6 +32,9 @@ Sets the callback function to handle UART events. Currently only the "data" event is supported. +!!! note + Due to limitations of the ESP8266, only UART 0 is capable of receiving data. + #### Syntax `uart.on(method, [number/end_char], [function], [run_input])` @@ -77,7 +84,7 @@ end, 0) `uart.setup(id, baud, databits, parity, stopbits[, echo])` #### Parameters -- `id` always zero, only one uart supported +- `id` UART id (0 or 1). - `baud` one of 300, 600, 1200, 2400, 4800, 9600, 19200, 31250, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400 - `databits` one of 5, 6, 7, 8 - `parity` `uart.PARITY_NONE`, `uart.PARITY_ODD`, or `uart.PARITY_EVEN` @@ -101,7 +108,7 @@ Returns the current configuration parameters of the UART. `uart.getconfig(id)` #### Parameters -- `id` always zero, only one uart supported +- `id` UART id (0 or 1). #### Returns Four values as follows: @@ -127,7 +134,7 @@ Write string or byte to the UART. `uart.write(id, data1 [, data2, ...])` #### Parameters -- `id` always 0, only one UART supported +- `id` UART id (0 or 1). - `data1`... string or byte to send via UART #### Returns From 3e24b1c75b4b4fd469be784fd5d596f0eb3871ff Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Wed, 26 Apr 2017 04:00:34 -0700 Subject: [PATCH 61/73] Added deprecation message to wifi.sta.config() for argument style cfg (#1939) Documentation for the old(argument) style of station configuration was removed in a previous PR, so I added a deprecation message to inform developer of the upcoming removal of the feature. --- app/modules/wifi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/modules/wifi.c b/app/modules/wifi.c index fb4bc464b6..e1c8f94c4b 100644 --- a/app/modules/wifi.c +++ b/app/modules/wifi.c @@ -836,6 +836,8 @@ static int wifi_station_config( lua_State* L ) } else //to be deprecated { + platform_print_deprecation_note("Argument style station configuration is replaced by table style station configuration", "in the next version"); + const char *ssid = luaL_checklstring( L, 1, &sl ); luaL_argcheck(L, (sl>=0 && sl Date: Thu, 27 Apr 2017 23:16:33 +0200 Subject: [PATCH 62/73] add si7021 module (#1921) * add si7021 module * add settings function for resolution and heater --- app/include/user_modules.h | 1 + app/modules/si7021.c | 265 +++++++++++++++++++++++++++++++++++++ docs/en/modules/si7021.md | 152 +++++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 419 insertions(+) create mode 100644 app/modules/si7021.c create mode 100644 docs/en/modules/si7021.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index d6d92f191f..de91061478 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -53,6 +53,7 @@ //#define LUA_USE_MODULES_RTCFIFO //#define LUA_USE_MODULES_RTCMEM //#define LUA_USE_MODULES_RTCTIME +//#define LUA_USE_MODULES_SI7021 //#define LUA_USE_MODULES_SIGMA_DELTA //#define LUA_USE_MODULES_SJSON //#define LUA_USE_MODULES_SNTP diff --git a/app/modules/si7021.c b/app/modules/si7021.c new file mode 100644 index 0000000000..fbcbebc43b --- /dev/null +++ b/app/modules/si7021.c @@ -0,0 +1,265 @@ +//*************************************************************************** +// Si7021 module for ESP8266 with nodeMCU +// fetchbot @github +// MIT license, http://opensource.org/licenses/MIT +//*************************************************************************** + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "osapi.h" + +//*************************************************************************** +// I2C ADDRESS DEFINITON +//*************************************************************************** + +#define SI7021_I2C_ADDRESS (0x40) + +//*************************************************************************** +// COMMAND DEFINITON +//*************************************************************************** + +#define SI7021_CMD_MEASURE_RH_HOLD (0xE5) +#define SI7021_CMD_MEASURE_RH_NOHOLD (0xF5) +#define SI7021_CMD_MEASURE_TEMP_HOLD (0xE3) +#define SI7021_CMD_MEASURE_TEMP_NOHOLD (0xF3) +#define SI7021_CMD_READ_PREV_TEMP (0xE0) +#define SI7021_CMD_RESET (0xFE) +#define SI7021_CMD_WRITE_RHT_REG (0xE6) +#define SI7021_CMD_READ_RHT_REG (0xE7) +#define SI7021_CMD_WRITE_HEATER_REG (0x51) +#define SI7021_CMD_READ_HEATER_REG (0x11) +#define SI7021_CMD_ID1 (0xFA0F) +#define SI7021_CMD_ID2 (0xFCC9) +#define SI7021_CMD_FIRM_REV (0x84B8) + +//*************************************************************************** +// REGISTER DEFINITON +//*************************************************************************** + +#define SI7021_RH12_TEMP14 (0x00) +#define SI7021_RH08_TEMP12 (0x01) +#define SI7021_RH10_TEMP13 (0x80) +#define SI7021_RH11_TEMP11 (0x81) +#define SI7021_HEATER_ENABLE (0x04) +#define SI7021_HEATER_DISABLE (0x00) + +//*************************************************************************** + +static const uint32_t si7021_i2c_id = 0; +static uint8_t si7021_i2c_addr = SI7021_I2C_ADDRESS; +static uint8_t si7021_res = 0x00; +static uint8_t si7021_config = 0x3A; +static uint8_t si7021_heater = 0x00; +static uint8_t si7021_heater_setting = 0x00; + +static uint8_t write_byte(uint8_t reg) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_stop(si7021_i2c_id); +} + +static uint8_t write_reg(uint8_t reg, uint8_t data) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_byte(si7021_i2c_id, data); + platform_i2c_send_stop(si7021_i2c_id); +} + +static uint8_t read_reg(uint8_t reg, uint8_t *buf, uint8_t size) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, reg); + platform_i2c_send_stop(si7021_i2c_id); + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + os_delay_us(25000); + while (size-- > 0) + *buf++ = platform_i2c_recv_byte(si7021_i2c_id, size > 0); + platform_i2c_send_stop(si7021_i2c_id); + return 1; +} + +static uint8_t read_serial(uint16_t reg, uint8_t *buf, uint8_t size) { + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(si7021_i2c_id, (uint8_t)(reg >> 8)); + platform_i2c_send_byte(si7021_i2c_id, (uint8_t)(reg & 0xFF)); + // platform_i2c_send_stop(si7021_i2c_id); + platform_i2c_send_start(si7021_i2c_id); + platform_i2c_send_address(si7021_i2c_id, si7021_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + while (size-- > 0) + *buf++ = platform_i2c_recv_byte(si7021_i2c_id, size > 0); + platform_i2c_send_stop(si7021_i2c_id); + return 1; +} + +// CRC8 +uint8_t si7021_crc8(uint8_t crc, uint8_t *buf, uint8_t size) { + while (size--) { + crc ^= *buf++; + for (uint8_t i = 0; i < 8; i++) { + if (crc & 0x80) { + crc = (crc << 1) ^ 0x31; + } else crc <<= 1; + } + } + return crc; +} + +static int si7021_lua_setup(lua_State* L) { + + write_byte(SI7021_CMD_RESET); + os_delay_us(50000); + + // check for device on i2c bus + uint8_t buf_r[1]; + read_reg(SI7021_CMD_READ_RHT_REG, buf_r, 1); + if (buf_r[0] != 0x3A) + return luaL_error(L, "found no device"); + + return 0; +} + +// Change sensor settings and returns them +// Lua: res, vdds, heater[, heater_set] = si7021.settings(RESOLUTION[,HEATER,HEATER_SETTING]) +static int si7021_lua_setting(lua_State* L) { + + // check variable + if (!lua_isnumber(L, 1)) { + return luaL_error(L, "wrong arg range"); + } + + si7021_res = luaL_checkinteger(L, 1); + if (!((si7021_res == SI7021_RH12_TEMP14) || (si7021_res == SI7021_RH08_TEMP12) || (si7021_res == SI7021_RH10_TEMP13) || (si7021_res == SI7021_RH11_TEMP11))) { + return luaL_error(L, "Invalid argument: resolution"); + } + + si7021_config = (si7021_res | 0x3A); + write_reg(SI7021_CMD_WRITE_RHT_REG,si7021_config); + + // Parse optional parameters + if (lua_isnumber(L, 2)) { + + if (!lua_isnumber(L, 2) || !lua_isnumber(L, 3)) { + return luaL_error(L, "wrong arg range"); + } + + si7021_heater = luaL_checkinteger(L, 2); + if (!((si7021_heater == SI7021_HEATER_ENABLE) || (si7021_heater == SI7021_HEATER_DISABLE))) { + return luaL_error(L, "Invalid argument: heater"); + } + + si7021_heater_setting = luaL_checkinteger(L, 3); + if ((si7021_heater_setting < 0x00) || (si7021_heater_setting > 0x0F)) { + return luaL_error(L, "Invalid argument: heater_setting"); + } + + si7021_config = (si7021_res | si7021_heater | 0x3A); + write_reg(SI7021_CMD_WRITE_RHT_REG,si7021_config); + write_reg(SI7021_CMD_WRITE_HEATER_REG,(si7021_heater_setting & 0x0F)); + } + + uint8_t buf_c[1]; + uint8_t buf_h[1]; + read_reg(SI7021_CMD_READ_RHT_REG, buf_c, 1); + read_reg(SI7021_CMD_READ_HEATER_REG, buf_h, 1); + + lua_pushinteger(L, ((buf_c[0] >> 6) + (buf_c[0] & 0x01))); + lua_pushinteger(L, ((buf_c[0] >> 6) & 0x01)); + lua_pushinteger(L, ((buf_c[0] >> 2) & 0x01)); + lua_pushinteger(L, (buf_h[0] & 0x0F)); + + return 4; +} + +// Reads sensor values from device and returns them +// Lua: hum, temp, humdec, tempdec = si7021.read() +static int si7021_lua_read(lua_State* L) { + + uint8_t buf_h[3]; // first two byte data, third byte crc + read_reg(SI7021_CMD_MEASURE_RH_HOLD, buf_h, 3); + if (buf_h[2] != si7021_crc8(0, buf_h, 2)) //crc check + return luaL_error(L, "crc error"); + double hum = (uint16_t)((buf_h[0] << 8) | buf_h[1]); + hum = ((hum * 125) / 65536 - 6); + int humdec = (int)((hum - (int)hum) * 1000); + + uint8_t buf_t[2]; // two byte data, no crc on combined temp measurement + read_reg(SI7021_CMD_READ_PREV_TEMP, buf_t, 2); + double temp = (uint16_t)((buf_t[0] << 8) | buf_t[1]); + temp = ((temp * 175.72) / 65536 - 46.85); + int tempdec = (int)((temp - (int)temp) * 1000); + + lua_pushnumber(L, hum); + lua_pushnumber(L, temp); + lua_pushinteger(L, humdec); + lua_pushinteger(L, tempdec); + + return 4; +} + +// Reads electronic serial number from device and returns them +// Lua: serial = si7021.serial() +static int si7021_lua_serial(lua_State* L) { + + uint32_t serial_a; + uint8_t crc = 0; + uint8_t buf_s_1[8]; // every second byte contains crc + read_serial(SI7021_CMD_ID1, buf_s_1, 8); + for(uint8_t i = 0; i <= 6; i+=2) { + crc = si7021_crc8(crc, buf_s_1+i, 1); + if (buf_s_1[i+1] != crc) + return luaL_error(L, "crc error"); + serial_a = (serial_a << 8) + buf_s_1[i]; + } + + uint32_t serial_b; + crc = 0; + uint8_t buf_s_2[6]; // every third byte contains crc + read_serial(SI7021_CMD_ID2, buf_s_2, 6); + for(uint8_t i = 0; i <=3; i+=3) { + crc = si7021_crc8(crc, buf_s_2+i, 2); + if (buf_s_2[i+2] != crc) + return luaL_error(L, "crc error"); + serial_b = (serial_b << 16) + (buf_s_2[i] << 8) + buf_s_2[i+1]; + } + + lua_pushinteger(L, serial_a); + lua_pushinteger(L, serial_b); + + return 2; +} + +// Reads electronic firmware revision from device and returns them +// Lua: firmware = si7021.firmware() +static int si7021_lua_firmware(lua_State* L) { + + uint8_t firmware; + uint8_t buf_f[1]; + read_serial(SI7021_CMD_FIRM_REV, buf_f, 1); + firmware = buf_f[0]; + + lua_pushinteger(L, firmware); + + return 1; +} + +static const LUA_REG_TYPE si7021_map[] = { + { LSTRKEY( "setup" ), LFUNCVAL(si7021_lua_setup) }, + { LSTRKEY( "setting" ), LFUNCVAL(si7021_lua_setting) }, + { LSTRKEY( "read" ), LFUNCVAL(si7021_lua_read) }, + { LSTRKEY( "serial" ), LFUNCVAL(si7021_lua_serial) }, + { LSTRKEY( "firmware" ), LFUNCVAL(si7021_lua_firmware) }, + { LSTRKEY( "RH12_TEMP14" ), LNUMVAL(SI7021_RH12_TEMP14) }, + { LSTRKEY( "RH08_TEMP12" ), LNUMVAL(SI7021_RH08_TEMP12) }, + { LSTRKEY( "RH10_TEMP13" ), LNUMVAL(SI7021_RH10_TEMP13) }, + { LSTRKEY( "RH11_TEMP11" ), LNUMVAL(SI7021_RH11_TEMP11) }, + { LSTRKEY( "HEATER_ENABLE" ), LNUMVAL(SI7021_HEATER_ENABLE) }, + { LSTRKEY( "HEATER_DISABLE" ), LNUMVAL(SI7021_HEATER_DISABLE) }, + { LNILKEY, LNILVAL } +}; + +NODEMCU_MODULE(SI7021, "si7021", si7021_map, NULL); diff --git a/docs/en/modules/si7021.md b/docs/en/modules/si7021.md new file mode 100644 index 0000000000..fc406bf61c --- /dev/null +++ b/docs/en/modules/si7021.md @@ -0,0 +1,152 @@ +# Si7021 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-19 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [si7021.c](../../../app/modules/si7021.c)| + +This module provides access to the Si7021 humidity and temperature sensor. + +## si7021.firmware() +Read the internal firmware revision of the Si7021 sensor. + +#### Syntax +`si7021.firmware()` + +#### Parameters +none + +#### Returns +`fwrev` Firmware version +* `0xFF` Firmware version 1.0 +* `0x20` Firmware version 2.0 + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +fwrev = si7021.firmware() +print(string.format("FW: %X\r\n", fwrev)) +``` + +## si7021.setting() +Settings for the sensors configuration register to adjust measurement resolution, on-chip heater and read the supply voltage status. + +#### Syntax +`si7021.setting(RESOLUTION[, HEATER, HEATER_SETTING])` + +#### Parameters +- `RESOLUTION` + * `si7021.RH12_TEMP14` Relative Humidity 12 bit - Temperature 14 bit (default) + * `si7021.RH08_TEMP12` Relative Humidity 8 bit - Temperature 12 bit + * `si7021.RH10_TEMP13` Relative Humidity 10 bit - Temperature 13 bit + * `si7021.RH11_TEMP11` Relative Humidity 11 bit - Temperature 11 bit +- `HEATER` optional + * `si7021.HEATER_ENABLE` On-chip Heater Enable + * `si7021.HEATER_DISABLE` On-chip Heater Disable (default) +- `HEATER_SETTING` optional + * `0x00` - `0x0F` 3.09 mA - 94.20 mA + +#### Returns +- `resolution` + * `0` Relative Humidity 12 bit - Temperature 14 bit + * `1` Relative Humidity 8 bit - Temperature 12 bit + * `2` Relative Humidity 10 bit - Temperature 13 bit + * `3` Relative Humidity 11 bit - Temperature 11 bit +- `vdds` + * `0` VDD OK (1.9V - 3.6V) + * `1` VDD LOW (1.8V - 1.9V) +- `heater` + * `0` Disabled + * `1` Enabled +- `heater_setting` + * `0` - `15` + +#### Example +```lua +local id, sda, scl = 0, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +res, vdds, heater, heater_set = si7021.setting(si7021.RH12_TEMP14) +res, vdds, heater, heater_set = si7021.setting(si7021.RH12_TEMP14, si7021.HEATER_ENABLE, 0x01) +``` + +## si7021.setup() +Initializes the device on fixed I²C device address (0x40). + +#### Syntax +`si7021.setup()` + +#### Parameters +none + +#### Returns +`nil` + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() +``` + +## si7021.read() + +#### Syntax +`si7021.read()` + +#### Parameters +none + +#### Returns +- `hum` humidity (see note below) +- `temp` temperature (see note below) +- `hum_dec` humidity decimal +- `temp_dec` temperature decimal + +!!! note + + If using float firmware then `hum` and `temp` are floating point numbers. On an integer firmware, the final values have to be concatenated from `hum` and `hum_dec` / `temp` and `temp_dec`. + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +hum, temp, hum_dec, temp_dec = si7021.read() + +-- Integer firmware using this example +print(string.format("Humidity:\t\t%d.%03d\nTemperature:\t%d.%03d\n", hum, hum_dec, temp, temp_dec)) + +-- Float firmware using this example +print("Humidity: "..hum.."\n".."Temperature: "..temp) +``` + +## si7021.serial() +Read the individualized 64-bit electronic serial number of the Si7021 sensor. + +#### Syntax +`si7021.serial()` + +#### Parameters +none + +#### Returns +- `sna` 32-bit serial number part a +- `snb` 32-bit serial number part b, upper byte contains the device identification + * `0x00` or `0xFF` engineering samples + * `0x0D` `13` Si7013 + * `0x14` `20` Si7020 + * `0x15` `21` Si7021 + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +sna, snb = si7021.serial() +print(string.format("SN:\t\t%X%X\nDevice:\tSi70%d", sna, snb, bit.rshift(snb,24))) +``` diff --git a/mkdocs.yml b/mkdocs.yml index c4d69c0c0d..c9909ae924 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,6 +72,7 @@ pages: - 'rtcfifo': 'en/modules/rtcfifo.md' - 'rtcmem': 'en/modules/rtcmem.md' - 'rtctime': 'en/modules/rtctime.md' + - 'si7021' : 'en/modules/si7021' - 'sigma delta': 'en/modules/sigma-delta.md' - 'sjson': 'en/modules/sjson.md' - 'sntp': 'en/modules/sntp.md' From f1bc858021774f8ce189d2fdc5b103b2712ea021 Mon Sep 17 00:00:00 2001 From: devsaurus Date: Thu, 27 Apr 2017 23:22:08 +0200 Subject: [PATCH 63/73] remove si7021 from lua_modules tree --- lua_modules/si7021/README.md | 6 ++ lua_modules/si7021/si7021-example.lua | 24 ----- lua_modules/si7021/si7021-lewei.lua | 41 -------- lua_modules/si7021/si7021.EN.md | 131 -------------------------- lua_modules/si7021/si7021.lua | 104 -------------------- 5 files changed, 6 insertions(+), 300 deletions(-) create mode 100644 lua_modules/si7021/README.md delete mode 100644 lua_modules/si7021/si7021-example.lua delete mode 100644 lua_modules/si7021/si7021-lewei.lua delete mode 100644 lua_modules/si7021/si7021.EN.md delete mode 100644 lua_modules/si7021/si7021.lua diff --git a/lua_modules/si7021/README.md b/lua_modules/si7021/README.md new file mode 100644 index 0000000000..9e409651a4 --- /dev/null +++ b/lua_modules/si7021/README.md @@ -0,0 +1,6 @@ +Support for this Lua module has been discontinued. + +Equivalent functionality is available from the si7021 module in the NodeMCU +firmware code base. Refer to `docs/en/modules/si7021.md` for API documentation. + +The original Lua code can be found in the [git repository](https://github.com/nodemcu/nodemcu-firmware/tree/3e24b1c75b4b4fd469be784fd5d596f0eb3871ff/lua_modules/si7021). diff --git a/lua_modules/si7021/si7021-example.lua b/lua_modules/si7021/si7021-example.lua deleted file mode 100644 index 5c6ed6ccb4..0000000000 --- a/lua_modules/si7021/si7021-example.lua +++ /dev/null @@ -1,24 +0,0 @@ - -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 deleted file mode 100644 index 28ac9189f8..0000000000 --- a/lua_modules/si7021/si7021-lewei.lua +++ /dev/null @@ -1,41 +0,0 @@ - - --创建一个定时器 - 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 deleted file mode 100644 index 983bb97840..0000000000 --- a/lua_modules/si7021/si7021.EN.md +++ /dev/null @@ -1,131 +0,0 @@ -# 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 deleted file mode 100644 index 161e57d253..0000000000 --- a/lua_modules/si7021/si7021.lua +++ /dev/null @@ -1,104 +0,0 @@ --- *************************************************************************** --- 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 From 3fbb84c48af12f2a4284f38ef487e14eae2ecf6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 30 Apr 2017 19:25:13 +0200 Subject: [PATCH 64/73] Add missing .md --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index c9909ae924..a7aa04c9fe 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,7 +72,7 @@ pages: - 'rtcfifo': 'en/modules/rtcfifo.md' - 'rtcmem': 'en/modules/rtcmem.md' - 'rtctime': 'en/modules/rtctime.md' - - 'si7021' : 'en/modules/si7021' + - 'si7021' : 'en/modules/si7021.md' - 'sigma delta': 'en/modules/sigma-delta.md' - 'sjson': 'en/modules/sjson.md' - 'sntp': 'en/modules/sntp.md' From 201ba9c9599a4197b901ee6c1a1f7c07a4ab3217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Sun, 30 Apr 2017 20:08:37 +0200 Subject: [PATCH 65/73] Put functions in alphabetical order --- docs/en/modules/si7021.md | 120 +++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/docs/en/modules/si7021.md b/docs/en/modules/si7021.md index fc406bf61c..9317d61355 100644 --- a/docs/en/modules/si7021.md +++ b/docs/en/modules/si7021.md @@ -29,6 +29,66 @@ fwrev = si7021.firmware() print(string.format("FW: %X\r\n", fwrev)) ``` +## si7021.read() + +#### Syntax +`si7021.read()` + +#### Parameters +none + +#### Returns +- `hum` humidity (see note below) +- `temp` temperature (see note below) +- `hum_dec` humidity decimal +- `temp_dec` temperature decimal + +!!! note + + If using float firmware then `hum` and `temp` are floating point numbers. On an integer firmware, the final values have to be concatenated from `hum` and `hum_dec` / `temp` and `temp_dec`. + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +hum, temp, hum_dec, temp_dec = si7021.read() + +-- Integer firmware using this example +print(string.format("Humidity:\t\t%d.%03d\nTemperature:\t%d.%03d\n", hum, hum_dec, temp, temp_dec)) + +-- Float firmware using this example +print("Humidity: "..hum.."\n".."Temperature: "..temp) +``` + +## si7021.serial() +Read the individualized 64-bit electronic serial number of the Si7021 sensor. + +#### Syntax +`si7021.serial()` + +#### Parameters +none + +#### Returns +- `sna` 32-bit serial number part a +- `snb` 32-bit serial number part b, upper byte contains the device identification + * `0x00` or `0xFF` engineering samples + * `0x0D` `13` Si7013 + * `0x14` `20` Si7020 + * `0x15` `21` Si7021 + +#### Example +```lua +local sda, scl = 6, 5 +i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once +si7021.setup() + +sna, snb = si7021.serial() +print(string.format("SN:\t\t%X%X\nDevice:\tSi70%d", sna, snb, bit.rshift(snb,24))) +``` + ## si7021.setting() Settings for the sensors configuration register to adjust measurement resolution, on-chip heater and read the supply voltage status. @@ -90,63 +150,3 @@ local sda, scl = 6, 5 i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once si7021.setup() ``` - -## si7021.read() - -#### Syntax -`si7021.read()` - -#### Parameters -none - -#### Returns -- `hum` humidity (see note below) -- `temp` temperature (see note below) -- `hum_dec` humidity decimal -- `temp_dec` temperature decimal - -!!! note - - If using float firmware then `hum` and `temp` are floating point numbers. On an integer firmware, the final values have to be concatenated from `hum` and `hum_dec` / `temp` and `temp_dec`. - -#### Example -```lua -local sda, scl = 6, 5 -i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once -si7021.setup() - -hum, temp, hum_dec, temp_dec = si7021.read() - --- Integer firmware using this example -print(string.format("Humidity:\t\t%d.%03d\nTemperature:\t%d.%03d\n", hum, hum_dec, temp, temp_dec)) - --- Float firmware using this example -print("Humidity: "..hum.."\n".."Temperature: "..temp) -``` - -## si7021.serial() -Read the individualized 64-bit electronic serial number of the Si7021 sensor. - -#### Syntax -`si7021.serial()` - -#### Parameters -none - -#### Returns -- `sna` 32-bit serial number part a -- `snb` 32-bit serial number part b, upper byte contains the device identification - * `0x00` or `0xFF` engineering samples - * `0x0D` `13` Si7013 - * `0x14` `20` Si7020 - * `0x15` `21` Si7021 - -#### Example -```lua -local sda, scl = 6, 5 -i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once -si7021.setup() - -sna, snb = si7021.serial() -print(string.format("SN:\t\t%X%X\nDevice:\tSi70%d", sna, snb, bit.rshift(snb,24))) -``` From e491f4b74365feb16675e7a212033b75cbfa0eea Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Fri, 5 May 2017 04:28:45 -0700 Subject: [PATCH 66/73] Update init.lua example to use new wifi.sta.config() implementation (#1953) * Update init.lua example in upload.md with new station config format * Fixed typo in description of wifi.eventmon.register() * Fixed typo and improved example init.lua in docs/en/upload.md --- docs/en/modules/wifi.md | 2 +- docs/en/upload.md | 69 ++++++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 8178c1d83d..1941cf3cd0 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -1504,7 +1504,7 @@ T: Table returned by event. - `wifi.eventmon.STA_DISCONNECTED`: Station was disconnected from access point. - `SSID`: SSID of access point. - `BSSID`: BSSID of access point. - - `REASON`: See [wifi.eventmon.reason](#wifieventmonreason) below. + - `reason`: See [wifi.eventmon.reason](#wifieventmonreason) below. - `wifi.eventmon.STA_AUTHMODE_CHANGE`: Access point has changed authorization mode. - `old_auth_mode`: Old wifi authorization mode. - `new_auth_mode`: New wifi authorization mode. diff --git a/docs/en/upload.md b/docs/en/upload.md index 9ff9a3b075..1eb5867e0a 100644 --- a/docs/en/upload.md +++ b/docs/en/upload.md @@ -56,24 +56,65 @@ function startup() end end +-- Define WiFi station event callbacks +wifi_connect_event = function(T) + print("Connection to AP("..T.SSID..") established!") + print("Waiting for IP address...") + if disconnect_ct ~= nil then disconnect_ct = nil end +end + +wifi_got_ip_event = function(T) + -- Note: Having an ip address does not mean there is internet access! + -- Internet connectivity can be determined with net.dns.resolve(). + print("Wifi connection is ready! IP address is: "..T.IP) + print("Startup will resume momentarily, you have 3 seconds to abort.") + print("Waiting...") + tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) +end + +wifi_disconnect_event = function(T) + if T.reason == wifi.eventmon.reason.ASSOC_LEAVE then + --the station has disassociated from a previously connected AP + return + end + -- total_tries: how many times the station will attempt to connect to the AP. + local total_tries = 75 + print("\nWiFi connection to AP("..T.SSID..") has failed!") + + --There are many possible disconnect reasons, the following iterates through + --the list and returns the string corresponding to the disconnect reason. + for key,val in pairs(wifi.eventmon.reason) do + if val == T.reason then + print("Disconnect reason:"..val.."("..key..")") + break + end + end + + if disconnect_ct == nil then + disconnect_ct = 1 + else + disconnect_ct = disconnect_ct + 1 + end + if disconnect_ct < total_tries then + print("Retrying connection...(attempt "..(disconnect_ct+1).." of "..total_tries..")") + else + wifi.sta.disconnect() + print("Aborting connection to AP!") + disconnect_ct = nil + end +end + +-- Register WiFi Station event callbacks +wifi.eventmon.register(wifi.eventmon.STA_CONNECTED, wifi_connect_event) +wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_got_ip_event) +wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_disconnect_event) + print("Connecting to WiFi access point...") wifi.setmode(wifi.STATION) -wifi.sta.config(SSID, PASSWORD) +wifi.sta.config({ssid=SSID, pwd=PASSWORD, save=true}) -- wifi.sta.connect() not necessary because config() uses auto-connect=true by default -tmr.create():alarm(1000, tmr.ALARM_AUTO, function(cb_timer) - if wifi.sta.getip() == nil then - print("Waiting for IP address...") - else - cb_timer:unregister() - print("WiFi connection established, IP address: " .. wifi.sta.getip()) - print("You have 3 seconds to abort") - print("Waiting...") - tmr.create():alarm(3000, tmr.ALARM_SINGLE, startup) - end -end) -``` -Inspired by [https://github.com/ckuehnel/NodeMCU-applications](https://github.com/ckuehnel/NodeMCU-applications) +``` # Compiling Lua on your PC for Uploading From cceb770d6790c9dfb747faf27dd69175a30db5f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20St=C3=B6r?= Date: Fri, 5 May 2017 13:39:13 +0200 Subject: [PATCH 67/73] Add a few tiny corrections --- docs/en/upload.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/en/upload.md b/docs/en/upload.md index 1eb5867e0a..441bf3d818 100644 --- a/docs/en/upload.md +++ b/docs/en/upload.md @@ -64,7 +64,7 @@ wifi_connect_event = function(T) end wifi_got_ip_event = function(T) - -- Note: Having an ip address does not mean there is internet access! + -- Note: Having an IP address does not mean there is internet access! -- Internet connectivity can be determined with net.dns.resolve(). print("Wifi connection is ready! IP address is: "..T.IP) print("Startup will resume momentarily, you have 3 seconds to abort.") @@ -77,7 +77,7 @@ wifi_disconnect_event = function(T) --the station has disassociated from a previously connected AP return end - -- total_tries: how many times the station will attempt to connect to the AP. + -- total_tries: how many times the station will attempt to connect to the AP. Should consider AP reboot duration. local total_tries = 75 print("\nWiFi connection to AP("..T.SSID..") has failed!") @@ -85,7 +85,7 @@ wifi_disconnect_event = function(T) --the list and returns the string corresponding to the disconnect reason. for key,val in pairs(wifi.eventmon.reason) do if val == T.reason then - print("Disconnect reason:"..val.."("..key..")") + print("Disconnect reason: "..val.."("..key..")") break end end From cba40213c663b7216c5f8544ea5fac045011dcf3 Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Sat, 6 May 2017 03:32:56 -0700 Subject: [PATCH 68/73] Update AP_PROBEREQRECVED example message (#1956) The example for the eventmon registration for AP_PROBEREQRECVED was displaying "STATION DISCONNECTED" when it should say "PROBE REQUEST RECEIVED". --- docs/en/modules/wifi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 1941cf3cd0..47d4ef9fb7 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -1560,7 +1560,7 @@ T: Table returned by event. end) wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) - print("\n\tAP - STATION DISCONNECTED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI) + print("\n\tAP - PROBE REQUEST RECEIVED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI) end) ``` #### See also From faefc095959832c5eaf7e6a9dcaae957fe42ac6a Mon Sep 17 00:00:00 2001 From: fetchbot Date: Sun, 7 May 2017 09:49:57 +0200 Subject: [PATCH 69/73] add ads1115 module (#1942) * add ads1115 module * replace os_delay with os_timer * typo --- app/include/user_modules.h | 2 +- app/modules/ads1115.c | 448 +++++++++++++++++++++++++++++++++++++ docs/en/modules/ads1115.md | 176 +++++++++++++++ mkdocs.yml | 2 +- 4 files changed, 626 insertions(+), 2 deletions(-) create mode 100644 app/modules/ads1115.c create mode 100644 docs/en/modules/ads1115.md diff --git a/app/include/user_modules.h b/app/include/user_modules.h index de91061478..06714516c2 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -18,6 +18,7 @@ // See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions. // New modules should be disabled by default and added in alphabetical order. #define LUA_USE_MODULES_ADC +//#define LUA_USE_MODULES_ADS1115 //#define LUA_USE_MODULES_ADXL345 //#define LUA_USE_MODULES_AM2320 //#define LUA_USE_MODULES_APA102 @@ -78,4 +79,3 @@ #endif /* LUA_CROSS_COMPILER */ #endif /* __USER_MODULES_H__ */ - diff --git a/app/modules/ads1115.c b/app/modules/ads1115.c new file mode 100644 index 0000000000..e956fbb323 --- /dev/null +++ b/app/modules/ads1115.c @@ -0,0 +1,448 @@ +//*************************************************************************** +// Si7021 module for ESP8266 with nodeMCU +// fetchbot @github +// MIT license, http://opensource.org/licenses/MIT +//*************************************************************************** + +#include "module.h" +#include "lauxlib.h" +#include "platform.h" +#include "osapi.h" + +//*************************************************************************** +// I2C ADDRESS DEFINITON +//*************************************************************************** + +#define ADS1115_I2C_ADDR_GND (0x48) +#define ADS1115_I2C_ADDR_VDD (0x49) +#define ADS1115_I2C_ADDR_SDA (0x4A) +#define ADS1115_I2C_ADDR_SCL (0x4B) + +//*************************************************************************** +// POINTER REGISTER +//*************************************************************************** + +#define ADS1115_POINTER_MASK (0x03) +#define ADS1115_POINTER_CONVERSION (0x00) +#define ADS1115_POINTER_CONFIG (0x01) +#define ADS1115_POINTER_THRESH_LOW (0x02) +#define ADS1115_POINTER_THRESH_HI (0x03) + +//*************************************************************************** +// CONFIG REGISTER +//*************************************************************************** + +#define ADS1115_OS_MASK (0x8000) +#define ADS1115_OS_NON (0x0000) +#define ADS1115_OS_SINGLE (0x8000) // Write: Set to start a single-conversion +#define ADS1115_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress +#define ADS1115_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion + +#define ADS1115_MUX_MASK (0x7000) +#define ADS1115_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default) +#define ADS1115_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3 +#define ADS1115_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3 +#define ADS1115_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3 +#define ADS1115_MUX_SINGLE_0 (0x4000) // Single-ended AIN0 +#define ADS1115_MUX_SINGLE_1 (0x5000) // Single-ended AIN1 +#define ADS1115_MUX_SINGLE_2 (0x6000) // Single-ended AIN2 +#define ADS1115_MUX_SINGLE_3 (0x7000) // Single-ended AIN3 + +#define ADS1115_PGA_MASK (0x0E00) +#define ADS1115_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3 +#define ADS1115_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1 +#define ADS1115_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default) +#define ADS1115_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4 +#define ADS1115_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8 +#define ADS1115_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16 + +#define ADS1115_MODE_MASK (0x0100) +#define ADS1115_MODE_CONTIN (0x0000) // Continuous conversion mode +#define ADS1115_MODE_SINGLE (0x0100) // Power-down single-shot mode (default) + +#define ADS1115_DR_MASK (0x00E0) +#define ADS1115_DR_8SPS (0x0000) // 8 samples per second +#define ADS1115_DR_16SPS (0x0020) // 16 samples per second +#define ADS1115_DR_32SPS (0x0040) // 32 samples per second +#define ADS1115_DR_64SPS (0x0060) // 64 samples per second +#define ADS1115_DR_128SPS (0x0080) // 128 samples per second (default) +#define ADS1115_DR_250SPS (0x00A0) // 250 samples per second +#define ADS1115_DR_475SPS (0x00C0) // 475 samples per second +#define ADS1115_DR_860SPS (0x00E0) // 860 samples per second + +#define ADS1115_CMODE_MASK (0x0010) +#define ADS1115_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default) +#define ADS1115_CMODE_WINDOW (0x0010) // Window comparator + +#define ADS1115_CPOL_MASK (0x0008) +#define ADS1115_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default) +#define ADS1115_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active + +#define ADS1115_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted +#define ADS1115_CLAT_NONLAT (0x0000) // Non-latching comparator (default) +#define ADS1115_CLAT_LATCH (0x0004) // Latching comparator + +#define ADS1115_CQUE_MASK (0x0003) +#define ADS1115_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions +#define ADS1115_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions +#define ADS1115_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions +#define ADS1115_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default) + +//*************************************************************************** + +static const uint8_t ads1115_i2c_id = 0; +static const uint8_t general_i2c_addr = 0x00; +static const uint8_t ads1115_i2c_reset = 0x06; +static uint8_t ads1115_i2c_addr = ADS1115_I2C_ADDR_GND; +static uint16_t ads1115_os = ADS1115_OS_SINGLE; +static uint16_t ads1115_gain = ADS1115_PGA_6_144V; +static uint16_t ads1115_samples = ADS1115_DR_128SPS; +static uint16_t ads1115_channel = ADS1115_MUX_SINGLE_0; +static uint16_t ads1115_comp = ADS1115_CQUE_NONE; +static uint16_t ads1115_mode = ADS1115_MODE_SINGLE; +static uint16_t ads1115_threshold_low = 0x8000; +static uint16_t ads1115_threshold_hi = 0x7FFF; +static uint16_t ads1115_config = 0x8583; +static uint16_t ads1115_conversion = 0; +static double ads1115_volt = 0; +os_timer_t ads1115_timer; // timer for conversion delay +int ads1115_timer_ref; // callback when readout is ready + +static int ads1115_lua_readoutdone(void); + +static uint8_t write_reg(uint8_t reg, uint16_t config) { + platform_i2c_send_start(ads1115_i2c_id); + platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(ads1115_i2c_id, reg); + platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config >> 8)); + platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config & 0xFF)); + platform_i2c_send_stop(ads1115_i2c_id); +} + +static uint16_t read_reg(uint8_t reg) { + platform_i2c_send_start(ads1115_i2c_id); + platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(ads1115_i2c_id, reg); + platform_i2c_send_stop(ads1115_i2c_id); + platform_i2c_send_start(ads1115_i2c_id); + platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER); + uint16_t buf = (platform_i2c_recv_byte(ads1115_i2c_id, 1) << 8); + buf += platform_i2c_recv_byte(ads1115_i2c_id, 0); + platform_i2c_send_stop(ads1115_i2c_id); + return buf; +} + +// convert ADC value to voltage corresponding to PGA settings +static double get_volt(uint16_t value) { + + double volt = 0; + + switch (ads1115_gain) { + case (ADS1115_PGA_6_144V): + volt = (int16_t)value * 0.1875; + break; + case (ADS1115_PGA_4_096V): + volt = (int16_t)value * 0.125; + break; + case (ADS1115_PGA_2_048V): + volt = (int16_t)value * 0.0625; + break; + case (ADS1115_PGA_1_024V): + volt = (int16_t)value * 0.03125; + break; + case (ADS1115_PGA_0_512V): + volt = (int16_t)value * 0.015625; + break; + case (ADS1115_PGA_0_256V): + volt = (int16_t)value * 0.0078125; + break; + } + + return volt; +} + +// convert threshold in volt to ADC value corresponding to PGA settings +static uint8_t get_value(int16_t *volt) { + + switch (ads1115_gain) { + case (ADS1115_PGA_6_144V): + if ((*volt >= 6144) || (*volt < -6144) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.1875; + break; + case (ADS1115_PGA_4_096V): + if ((*volt >= 4096) || (*volt < -4096) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.125; + break; + case (ADS1115_PGA_2_048V): + if ((*volt >= 2048) || (*volt < -2048) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.0625; + break; + case (ADS1115_PGA_1_024V): + if ((*volt >= 1024) || (*volt < -1024) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.03125; + break; + case (ADS1115_PGA_0_512V): + if ((*volt >= 512) || (*volt < -512) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.015625; + break; + case (ADS1115_PGA_0_256V): + if ((*volt >= 256) || (*volt < -256) || ((*volt < 0) && (ads1115_channel >> 14))) return 1; + *volt = *volt / 0.0078125; + break; + } + + return 0; +} + +// Initializes ADC +// Lua: ads11115.setup(ADDRESS) +static int ads1115_lua_setup(lua_State *L) { + + // check variables + if (!lua_isnumber(L, 1)) { + return luaL_error(L, "wrong arg range"); + } + + ads1115_i2c_addr = luaL_checkinteger(L, 1); + if (!((ads1115_i2c_addr == ADS1115_I2C_ADDR_GND) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_VDD) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SDA) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SCL))) { + return luaL_error(L, "Invalid argument: adddress"); + } + + platform_i2c_send_start(ads1115_i2c_id); + platform_i2c_send_address(ads1115_i2c_id, general_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER); + platform_i2c_send_byte(ads1115_i2c_id, ads1115_i2c_reset); + platform_i2c_send_stop(ads1115_i2c_id); + + // check for device on i2c bus + if (read_reg(ADS1115_POINTER_CONFIG) != 0x8583) { + return luaL_error(L, "found no device"); + } + + return 0; +} + +// Change ADC settings +// Lua: ads1115.setting(GAIN,SAMPLES,CHANNEL,MODE[,CONVERSION_RDY][,COMPARATOR,THRESHOLD_LOW,THRESHOLD_HI]) +static int ads1115_lua_setting(lua_State *L) { + + // check variables + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4)) { + return luaL_error(L, "wrong arg range"); + } + + ads1115_gain = luaL_checkinteger(L, 1); + if (!((ads1115_gain == ADS1115_PGA_6_144V) || (ads1115_gain == ADS1115_PGA_4_096V) || (ads1115_gain == ADS1115_PGA_2_048V) || (ads1115_gain == ADS1115_PGA_1_024V) || (ads1115_gain == ADS1115_PGA_0_512V) || (ads1115_gain == ADS1115_PGA_0_256V))) { + return luaL_error(L, "Invalid argument: gain"); + } + + ads1115_samples = luaL_checkinteger(L, 2); + if (!((ads1115_samples == ADS1115_DR_8SPS) || (ads1115_samples == ADS1115_DR_16SPS) || (ads1115_samples == ADS1115_DR_32SPS) || (ads1115_samples == ADS1115_DR_64SPS) || (ads1115_samples == ADS1115_DR_128SPS) || (ads1115_samples == ADS1115_DR_250SPS) || (ads1115_samples == ADS1115_DR_475SPS) || (ads1115_samples == ADS1115_DR_860SPS))) { + return luaL_error(L, "Invalid argument: samples"); + } + + ads1115_channel = luaL_checkinteger(L, 3); + if (!((ads1115_channel == ADS1115_MUX_SINGLE_0) || (ads1115_channel == ADS1115_MUX_SINGLE_1) || (ads1115_channel == ADS1115_MUX_SINGLE_2) || (ads1115_channel == ADS1115_MUX_SINGLE_3) || (ads1115_channel == ADS1115_MUX_DIFF_0_1) || (ads1115_channel == ADS1115_MUX_DIFF_0_3) || (ads1115_channel == ADS1115_MUX_DIFF_1_3) || (ads1115_channel == ADS1115_MUX_DIFF_2_3))) { + return luaL_error(L, "Invalid argument: channel"); + } + + ads1115_mode = luaL_checkinteger(L, 4); + if (!((ads1115_mode == ADS1115_MODE_SINGLE) || (ads1115_mode == ADS1115_MODE_CONTIN))) { + return luaL_error(L, "Invalid argument: mode"); + } + + if (ads1115_mode == ADS1115_MODE_SINGLE) { + ads1115_os = ADS1115_OS_SINGLE; + } else { + ads1115_os = ADS1115_OS_NON; + } + + ads1115_comp = ADS1115_CQUE_NONE; + + // Parse optional parameters + if (lua_isnumber(L, 5) && !(lua_isnumber(L, 6) || lua_isnumber(L, 7))) { + + // conversion ready mode + ads1115_comp = luaL_checkinteger(L, 5); + if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) { + return luaL_error(L, "Invalid argument: conversion ready mode"); + } + + ads1115_threshold_low = 0x7FFF; + ads1115_threshold_hi = 0x8000; + + write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low); + write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi); + + } else if (lua_isnumber(L, 5) && lua_isnumber(L, 6) && lua_isnumber(L, 7)) { + + // comparator mode + ads1115_comp = luaL_checkinteger(L, 5); + if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) { + return luaL_error(L, "Invalid argument: comparator mode"); + } + + ads1115_threshold_low = luaL_checkinteger(L, 5); + ads1115_threshold_hi = luaL_checkinteger(L, 6); + + if ((int16_t)ads1115_threshold_low > (int16_t)ads1115_threshold_hi) { + return luaL_error(L, "Invalid argument: threshold_low > threshold_hi"); + } + + if (get_value(&ads1115_threshold_low)) { + return luaL_error(L, "Invalid argument: threshold_low"); + } + + if (get_value(&ads1115_threshold_hi)) { + return luaL_error(L, "Invalid argument: threshold_hi"); + } + + write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low); + write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi); + + } + + ads1115_config = (ads1115_os | ads1115_channel | ads1115_gain | ads1115_mode | ads1115_samples | ADS1115_CMODE_TRAD | ADS1115_CPOL_ACTVLOW | ADS1115_CLAT_NONLAT | ads1115_comp); + + write_reg(ADS1115_POINTER_CONFIG, ads1115_config); + + return 0; +} + +// Read the conversion register from the ADC +// Lua: ads1115.startread(function(volt, voltdec, adc) print(volt,voltdec,adc) end) +static int ads1115_lua_startread(lua_State *L) { + + if (((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV)) && (ads1115_threshold_low == 0x7FFF) && (ads1115_threshold_hi == 0x8000)) { + + if (ads1115_mode == ADS1115_MODE_SINGLE) { + write_reg(ADS1115_POINTER_CONFIG, ads1115_config); + } + + return 0; + + } else { + + luaL_argcheck(L, (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION), 1, "Must be function"); + lua_pushvalue(L, 1); + ads1115_timer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + + if (ads1115_mode == ADS1115_MODE_SINGLE) { + write_reg(ADS1115_POINTER_CONFIG, ads1115_config); + } + + // Start a timer to wait until ADC conversion is done + os_timer_disarm (&ads1115_timer); + os_timer_setfn (&ads1115_timer, (os_timer_func_t *)ads1115_lua_readoutdone, NULL); + + switch (ads1115_samples) { + case (ADS1115_DR_8SPS): + os_timer_arm (&ads1115_timer, 150, 0); + break; + case (ADS1115_DR_16SPS): + os_timer_arm (&ads1115_timer, 75, 0); + break; + case (ADS1115_DR_32SPS): + os_timer_arm (&ads1115_timer, 35, 0); + break; + case (ADS1115_DR_64SPS): + os_timer_arm (&ads1115_timer, 20, 0); + break; + case (ADS1115_DR_128SPS): + os_timer_arm (&ads1115_timer, 10, 0); + break; + case (ADS1115_DR_250SPS): + os_timer_arm (&ads1115_timer, 5, 0); + break; + case (ADS1115_DR_475SPS): + os_timer_arm (&ads1115_timer, 3, 0); + break; + case (ADS1115_DR_860SPS): + os_timer_arm (&ads1115_timer, 2, 0); + break; + } + + return 0; + } +} + +// adc conversion timer callback +static int ads1115_lua_readoutdone(void) { + + ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION); + ads1115_volt = get_volt(ads1115_conversion); + int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000); + ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec; + + lua_State *L = lua_getstate(); + os_timer_disarm (&ads1115_timer); + + lua_rawgeti (L, LUA_REGISTRYINDEX, ads1115_timer_ref); + luaL_unref (L, LUA_REGISTRYINDEX, ads1115_timer_ref); + ads1115_timer_ref = LUA_NOREF; + + lua_pushnumber(L, ads1115_volt); + lua_pushinteger(L, ads1115_voltdec); + lua_pushinteger(L, ads1115_conversion); + + lua_call (L, 3, 0); +} + +// Read the conversion register from the ADC +// Lua: volt,voltdec,adc = ads1115.read() +static int ads1115_lua_read(lua_State *L) { + + ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION); + ads1115_volt = get_volt(ads1115_conversion); + int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000); + ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec; + + lua_pushnumber(L, ads1115_volt); + lua_pushinteger(L, ads1115_voltdec); + lua_pushinteger(L, ads1115_conversion); + + return 3; +} + +static const LUA_REG_TYPE ads1115_map[] = { + { LSTRKEY( "setup" ), LFUNCVAL(ads1115_lua_setup) }, + { LSTRKEY( "setting" ), LFUNCVAL(ads1115_lua_setting) }, + { LSTRKEY( "startread" ), LFUNCVAL(ads1115_lua_startread) }, + { LSTRKEY( "read" ), LFUNCVAL(ads1115_lua_read) }, + { LSTRKEY( "ADDR_GND" ), LNUMVAL(ADS1115_I2C_ADDR_GND) }, + { LSTRKEY( "ADDR_VDD" ), LNUMVAL(ADS1115_I2C_ADDR_VDD) }, + { LSTRKEY( "ADDR_SDA" ), LNUMVAL(ADS1115_I2C_ADDR_SDA) }, + { LSTRKEY( "ADDR_SCL" ), LNUMVAL(ADS1115_I2C_ADDR_SCL) }, + { LSTRKEY( "SINGLE_SHOT" ), LNUMVAL(ADS1115_MODE_SINGLE) }, + { LSTRKEY( "CONTINUOUS" ), LNUMVAL(ADS1115_MODE_CONTIN) }, + { LSTRKEY( "DIFF_0_1" ), LNUMVAL(ADS1115_MUX_DIFF_0_1) }, + { LSTRKEY( "DIFF_0_3" ), LNUMVAL(ADS1115_MUX_DIFF_0_3) }, + { LSTRKEY( "DIFF_1_3" ), LNUMVAL(ADS1115_MUX_DIFF_1_3) }, + { LSTRKEY( "DIFF_2_3" ), LNUMVAL(ADS1115_MUX_DIFF_2_3) }, + { LSTRKEY( "SINGLE_0" ), LNUMVAL(ADS1115_MUX_SINGLE_0) }, + { LSTRKEY( "SINGLE_1" ), LNUMVAL(ADS1115_MUX_SINGLE_1) }, + { LSTRKEY( "SINGLE_2" ), LNUMVAL(ADS1115_MUX_SINGLE_2) }, + { LSTRKEY( "SINGLE_3" ), LNUMVAL(ADS1115_MUX_SINGLE_3) }, + { LSTRKEY( "GAIN_6_144V" ), LNUMVAL(ADS1115_PGA_6_144V) }, + { LSTRKEY( "GAIN_4_096V" ), LNUMVAL(ADS1115_PGA_4_096V) }, + { LSTRKEY( "GAIN_2_048V" ), LNUMVAL(ADS1115_PGA_2_048V) }, + { LSTRKEY( "GAIN_1_024V" ), LNUMVAL(ADS1115_PGA_1_024V) }, + { LSTRKEY( "GAIN_0_512V" ), LNUMVAL(ADS1115_PGA_0_512V) }, + { LSTRKEY( "GAIN_0_256V" ), LNUMVAL(ADS1115_PGA_0_256V) }, + { LSTRKEY( "DR_8SPS" ), LNUMVAL(ADS1115_DR_8SPS) }, + { LSTRKEY( "DR_16SPS" ), LNUMVAL(ADS1115_DR_16SPS) }, + { LSTRKEY( "DR_32SPS" ), LNUMVAL(ADS1115_DR_32SPS) }, + { LSTRKEY( "DR_64SPS" ), LNUMVAL(ADS1115_DR_64SPS) }, + { LSTRKEY( "DR_128SPS" ), LNUMVAL(ADS1115_DR_128SPS) }, + { LSTRKEY( "DR_250SPS" ), LNUMVAL(ADS1115_DR_250SPS) }, + { LSTRKEY( "DR_475SPS" ), LNUMVAL(ADS1115_DR_475SPS) }, + { LSTRKEY( "DR_860SPS" ), LNUMVAL(ADS1115_DR_860SPS) }, + { LSTRKEY( "CONV_RDY_1" ), LNUMVAL(ADS1115_CQUE_1CONV) }, + { LSTRKEY( "CONV_RDY_2" ), LNUMVAL(ADS1115_CQUE_2CONV) }, + { LSTRKEY( "CONV_RDY_4" ), LNUMVAL(ADS1115_CQUE_4CONV) }, + { LSTRKEY( "COMP_1CONV" ), LNUMVAL(ADS1115_CQUE_1CONV) }, + { LSTRKEY( "COMP_2CONV" ), LNUMVAL(ADS1115_CQUE_2CONV) }, + { LSTRKEY( "COMP_4CONV" ), LNUMVAL(ADS1115_CQUE_4CONV) }, + { LNILKEY, LNILVAL } +}; + +NODEMCU_MODULE(ADS1115, "ads1115", ads1115_map, NULL); \ No newline at end of file diff --git a/docs/en/modules/ads1115.md b/docs/en/modules/ads1115.md new file mode 100644 index 0000000000..f73310b49b --- /dev/null +++ b/docs/en/modules/ads1115.md @@ -0,0 +1,176 @@ +# ADS1115 Module +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../../app/modules/ads1115.c)| + +This module provides access to the ADS1115 16-Bit analog-to-digital converter. + +!!! caution + + The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND. + +## ads1115.read() +Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt. + +#### Syntax +`volt, volt_dec, adc = ads1115.read()` + +#### Parameters +none + +#### Returns +- `volt` voltage in mV (see note below) +- `volt_dec` voltage decimal (see note below) +- `adc` raw adc value + +!!! note + + If using float firmware then `volt` is a floating point number. On an integer firmware, the final value has to be concatenated from `volt` and `volt_dec`. + +#### Example +```lua +local id, alert_pin, sda, scl = 0, 7, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) +ads1115.setup(ads1115.ADDR_GND) + +-- continuous mode +ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS) +-- read adc result with read() +volt, volt_dec, adc = ads1115.read() +print(volt, volt_dec, adc) + +-- comparator +ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000) +local function comparator(level, when) + -- read adc result with read() when threshold reached + volt, volt_dec, adc = ads1115.read() + print(volt, volt_dec, adc) +end +gpio.mode(alert_pin, gpio.INT) +gpio.trig(alert_pin, "both", comparator) +-- read adc result with read() +volt, volt_dec, adc = ads1115.read() +print(volt, volt_dec, adc) +``` + +## ads1115.setting() +Configuration settings for the ADC. + +#### Syntax +`ads1115.setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI])` + +#### Parameters +- `GAIN` Programmable gain amplifier + * `ads1115.GAIN_6_144V` 2/3x Gain + * `ads1115.GAIN_4_096V` 1x Gain + * `ads1115.GAIN_2_048V` 2x Gain + * `ads1115.GAIN_1_024V` 4x Gain + * `ads1115.GAIN_0_512V` 8x Gain + * `ads1115.GAIN_0_256V` 16x Gain +- `SAMPLES` Data rate in samples per second + * `ads1115.DR_8SPS` + * `ads1115.DR_16SPS` + * `ads1115.DR_32SPS` + * `ads1115.DR_64SPS` + * `ads1115.DR_128SPS` + * `ads1115.DR_250SPS` + * `ads1115.DR_475SPS` + * `ads1115.DR_860SPS` +- `CHANNEL` Input multiplexer for single-ended or differential measurement + * `ads1115.SINGLE_0` channel 0 to GND + * `ads1115.SINGLE_1` channel 1 to GND + * `ads1115.SINGLE_2` channel 2 to GND + * `ads1115.SINGLE_3` channel 3 to GND + * `ads1115.DIFF_0_1` channel 0 to 1 + * `ads1115.DIFF_0_3` channel 0 to 3 + * `ads1115.DIFF_1_3` channel 1 to 3 + * `ads1115.DIFF_2_3` channel 2 to 3 +- `MODE` Device operating mode + * `ads1115.SINGLE_SHOT` single-shot mode + * `ads1115.CONTINUOUS` continuous mode +- `CONVERSION_RDY` Number of conversions after conversion ready asserts (optional) + * `ads1115.CONV_RDY_1` + * `ads1115.CONV_RDY_2` + * `ads1115.CONV_RDY_4` +- `COMPARATOR` Number of conversions after comparator asserts (optional) + * `ads1115.COMP_1CONV` + * `ads1115.COMP_2CONV` + * `ads1115.COMP_4CONV` +- `THRESHOLD_LOW` + * `0` - `+ GAIN_MAX` in mV for single-ended inputs + * `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs +- `THRESHOLD_HI` + * `0` - `+ GAIN_MAX` in mV for single-ended inputs + * `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs + +#### Returns +`nil` + +#### Example +```lua +local id, sda, scl = 0, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) +ads1115.setup(ads1115.ADDR_GND) + +ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT) +``` + +## ads1115.setup() +Initializes the device on the defined I²C device address. + +#### Syntax +`ads1115.setup(ADDRESS)` + +#### Parameters +- `ADDRESS` + * `ads1115.ADDR_GND` + * `ads1115.ADDR_VDD` + * `ads1115.ADDR_SDA` + * `ads1115.ADDR_SCL` + +#### Returns +`nil` + +#### Example +```lua +local id, sda, scl = 0, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) + +ads1115.setup(ads1115.ADDR_GND) +``` + +## ads1115.startread() +Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained. + +#### Syntax +`ads1115.startread([CALLBACK])` + +#### Parameters +- `CALLBACK` callback function which will be invoked after the adc conversion is done + * `function(volt, volt_dec, adc) end` + +#### Returns +- `nil` + +#### Example +```lua +local id, alert_pin, sda, scl = 0, 7, 6, 5 +i2c.setup(id, sda, scl, i2c.SLOW) +ads1115.setup(ads1115.ADDR_GND) + +-- single shot +ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT) +-- start adc conversion and get result in callback after conversion is ready +ads1115.startread(function(volt, volt_dec, adc) print(volt, volt_dec, adc) end) + +-- conversion ready +ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1) +local function conversion_ready(level, when) + volt, volt_dec, adc = ads1115.read() + print(volt, volt_dec, adc) +end +gpio.mode(alert_pin, gpio.INT) +gpio.trig(alert_pin, "down", conversion_ready) +-- start conversion and get result with read() after conversion ready pin asserts +ads1115.startread() +``` diff --git a/mkdocs.yml b/mkdocs.yml index a7aa04c9fe..613cdd81a0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -36,6 +36,7 @@ pages: - Support: 'en/support.md' - Modules: - 'adc': 'en/modules/adc.md' + - 'ads1115' : 'en/modules/ads1115.md' - 'adxl345': 'en/modules/adxl345.md' - 'am2320': 'en/modules/am2320.md' - 'apa102': 'en/modules/apa102.md' @@ -96,4 +97,3 @@ pages: - 'xpt2046': 'en/modules/xpt2046.md' #- Deutsch: # - Home: 'de/index.md' - From 3aef438aa018b7e17aab377ff851c3a5fccb3fca Mon Sep 17 00:00:00 2001 From: Frank Exoo Date: Mon, 8 May 2017 13:39:27 +0200 Subject: [PATCH 70/73] Documenting creating integer build (#1961) --- docs/en/build.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/en/build.md b/docs/en/build.md index 0ce62751fd..edf4a6b015 100644 --- a/docs/en/build.md +++ b/docs/en/build.md @@ -55,6 +55,22 @@ editing `BIT_RATE_DEFAULT` in `app/include/user_config.h`: Note that, by default, the firmware runs an auto-baudrate detection algorithm so that typing a few characters at boot time will cause the firmware to lock onto that baud rate (between 1200 and 230400). +### Integer build +By default a build will be generated supporting floating-point variables. +To reduce memory size an integer build can be created. You can change this +either by uncommenting `LUA_NUMBER_INTEGRAL` in `app/include/user_config.h`: + +```c +#define LUA_NUMBER_INTEGRAL +``` + +OR by overriding this with the `make` command as it's [done during the CI +build](https://github.com/nodemcu/nodemcu-firmware/blob/master/.travis.yml#L30): + +``` +make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL .... +``` + ### Tag Your Build Identify your firmware builds by editing `app/include/user_version.h` From c4516f1a17a6aed75eec9aa778ad15a60560cdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Thu, 11 May 2017 07:13:23 +0200 Subject: [PATCH 71/73] Upgrade to SDK 2.1.0. (#1962) --- Makefile | 16 ++++++++-------- README.md | 4 ++-- app/include/rom.h | 8 -------- app/include/user_version.h | 4 ++-- app/modules/tmr.c | 6 +++--- app/tsl2561/tsl2561.c | 12 +++++++++--- docs/en/build.md | 2 +- ld/nodemcu.ld | 2 +- sdk-overrides/include/osapi.h | 1 - sdk-overrides/include/user_interface.h | 4 ---- 10 files changed, 26 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 55874008eb..6ec1632b4e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ .NOTPARALLEL: # SDK version NodeMCU is locked to -SDK_VER:=2.0.0 +SDK_VER:=2.1.0 # no patch: SDK_BASE_VER equals SDK_VER and sdk dir depends on sdk_extracted SDK_BASE_VER:=$(SDK_VER) @@ -12,8 +12,8 @@ SDK_DIR_DEPENDS:=sdk_extracted #SDK_BASE_VER:=1.5.4 #SDK_DIR_DEPENDS:=sdk_patched -SDK_FILE_VER:=$(SDK_BASE_VER)_16_08_10 -SDK_FILE_SHA1:=b0127a99b45b3778be4a752387ab8dc0f6dd7810 +SDK_FILE_VER:=$(SDK_BASE_VER) +SDK_FILE_SHA1:=66a4272894dc1bcec19f5f8bf79fee80f60a021b #SDK_PATCH_VER:=$(SDK_VER)_patch_20160704 #SDK_PATCH_SHA1:=388d9e91df74e3b49fca126da482cf822cf1ebf1 # Ensure we search "our" SDK before the tool-chain's SDK (if any) @@ -203,10 +203,10 @@ all: $(SDK_DIR_DEPENDS) pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER) sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER) -$(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip +$(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip mkdir -p "$(dir $@)" - (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK/lib/* ESP8266_NONOS_SDK/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK/include/* ESP8266_NONOS_SDK/bin/esp_init_data_default.bin) - mv $(dir $@)/ESP8266_NONOS_SDK $(dir $@)/esp_iot_sdk_v$(SDK_VER) + (cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_VER) && unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK-$(SDK_VER)/lib/* ESP8266_NONOS_SDK-$(SDK_VER)/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK-$(SDK_VER)/include/* ESP8266_NONOS_SDK-$(SDK_VER)/bin/esp_init_data_default.bin) + mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_VER) $(dir $@)/esp_iot_sdk_v$(SDK_VER) rm -f $(SDK_DIR)/lib/liblwip.a touch $@ @@ -217,9 +217,9 @@ $(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VE rm -f $(SDK_DIR)/lib/liblwip.a touch $@ -$(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_FILE_VER).zip: +$(TOP_DIR)/cache/v$(SDK_FILE_VER).zip: mkdir -p "$(dir $@)" - wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused http://espressif.com/sites/default/files/sdks/esp8266_nonos_sdk_v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; } + wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/espressif/ESP8266_NONOS_SDK/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; } (echo "$(SDK_FILE_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; } $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip: diff --git a/README.md b/README.md index b7c06b02c7..9fcdd3b0de 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# **NodeMCU 2.0.0** # +# **NodeMCU 2.1.0** # [![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware) @@ -7,7 +7,7 @@ ### A Lua based firmware for ESP8266 WiFi SOC -NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 2.0.0](https://espressif.com/sites/default/files/sdks/esp8266_nonos_sdk_v2.0.0_16_08_10.zip) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK. +NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 2.1.0](https://github.com/espressif/ESP8266_NONOS_SDK/releases/tag/v2.1.0) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK. The NodeMCU *firmware* is a companion project to the popular [NodeMCU dev kits](https://github.com/nodemcu/nodemcu-devkit-v1.0), ready-made open source development boards with ESP8266-12E chips. diff --git a/app/include/rom.h b/app/include/rom.h index c2ad7d3dec..f64d50a01e 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -46,7 +46,6 @@ extern void mem_init(void * start_addr); // Interrupt Service Routine functions typedef void (*ets_isr_fn) (void *arg); -extern int ets_isr_attach (unsigned int interrupt, ets_isr_fn, void *arg); extern void ets_isr_mask (unsigned intr); extern void ets_isr_unmask (unsigned intr); @@ -119,14 +118,11 @@ void *ets_memset (void *dst, int c, size_t n); int ets_memcmp (const void *s1, const void *s2, size_t n); char *ets_strcpy (char *dst, const char *src); -size_t ets_strlen (const char *s); int ets_strcmp (const char *s1, const char *s2); int ets_strncmp (const char *s1, const char *s2, size_t n); char *ets_strncpy(char *dest, const char *src, size_t n); char *ets_strstr(const char *haystack, const char *needle); -void ets_delay_us (uint32_t us); - int ets_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); void ets_str2macaddr (uint8_t *dst, const char *str); @@ -140,13 +136,9 @@ void Cache_Read_Disable(void); void ets_intr_lock(void); void ets_intr_unlock(void); -void ets_install_putc1(void *routine); - int rand(void); void srand(unsigned int); -void uart_div_modify(int no, unsigned int freq); - /* Returns 0 on success, 1 on failure */ uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size); uint8_t SPIWrite(uint32_t dst_addr, const uint32_t *src, uint32_t size); diff --git a/app/include/user_version.h b/app/include/user_version.h index 09f97e8e35..9e75bf6e96 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -2,11 +2,11 @@ #define __USER_VERSION_H__ #define NODE_VERSION_MAJOR 2U -#define NODE_VERSION_MINOR 0U +#define NODE_VERSION_MINOR 1U #define NODE_VERSION_REVISION 0U #define NODE_VERSION_INTERNAL 0U -#define NODE_VERSION "NodeMCU 2.0.0" +#define NODE_VERSION "NodeMCU 2.1.0" #ifndef BUILD_DATE #define BUILD_DATE "unspecified" #endif diff --git a/app/modules/tmr.c b/app/modules/tmr.c index 76ee6d62e7..19f416e4ff 100755 --- a/app/modules/tmr.c +++ b/app/modules/tmr.c @@ -128,9 +128,9 @@ static int tmr_delay( lua_State* L ){ sint32_t us = luaL_checkinteger(L, 1); if(us <= 0) return luaL_error(L, "wrong arg range"); - while(us >= 1000000){ - us -= 1000000; - os_delay_us(1000000); + while(us >= 10000){ + us -= 10000; + os_delay_us(10000); system_soft_wdt_feed (); } if(us>0){ diff --git a/app/tsl2561/tsl2561.c b/app/tsl2561/tsl2561.c index cd825de09c..b67defef5c 100644 --- a/app/tsl2561/tsl2561.c +++ b/app/tsl2561/tsl2561.c @@ -76,6 +76,12 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X; static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT; static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL; +static void delay_ms(uint16_t ms) +{ + while (ms--) + os_delay_us(1000); +} + /**************************************************************************/ /*! @brief Writes an 8 bit values over I2C @@ -230,13 +236,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) { // Wait x ms for ADC to complete switch (_tsl2561IntegrationTime) { case TSL2561_INTEGRATIONTIME_13MS: - os_delay_us(14000); //systickDelay(14); + delay_ms(14); //systickDelay(14); break; case TSL2561_INTEGRATIONTIME_101MS: - os_delay_us(102000); //systickDelay(102); + delay_ms(102); //systickDelay(102); break; default: - os_delay_us(404000); //systickDelay(404); + delay_ms(404); //systickDelay(404); break; } diff --git a/docs/en/build.md b/docs/en/build.md index edf4a6b015..52ea4898b3 100644 --- a/docs/en/build.md +++ b/docs/en/build.md @@ -75,7 +75,7 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL .... Identify your firmware builds by editing `app/include/user_version.h` ```c -#define NODE_VERSION "NodeMCU 2.0.0+myname" +#define NODE_VERSION "NodeMCU 2.1.0+myname" #ifndef BUILD_DATE #define BUILD_DATE "YYYYMMDD" #endif diff --git a/ld/nodemcu.ld b/ld/nodemcu.ld index d2136862b3..a5e32380be 100644 --- a/ld/nodemcu.ld +++ b/ld/nodemcu.ld @@ -105,7 +105,7 @@ SECTIONS /* SDK libraries that used in bootup process, interruption handling * and other ways where flash cache (iROM) is unavailable: */ - *libmain.a:*(.literal .text) + *libmain.a:*(.literal .literal.* .text .text.*) *libnet80211.a:*(.literal .text) *libphy.a:*(.literal .text) *libpp.a:*(.literal .text) diff --git a/sdk-overrides/include/osapi.h b/sdk-overrides/include/osapi.h index 46b797f2ba..3f89877c20 100644 --- a/sdk-overrides/include/osapi.h +++ b/sdk-overrides/include/osapi.h @@ -2,7 +2,6 @@ #define _SDK_OVERRIDE_OSAPI_H_ #include "rom.h" -void ets_timer_arm_new (ETSTimer *a, int b, int c, int isMstimer); int atoi(const char *nptr); int os_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index 54dff6b8e5..b029751293 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -7,14 +7,10 @@ bool wifi_softap_deauth(uint8 mac[6]); uint8 get_fpm_auto_sleep_flag(void); enum ext_flash_size_map { - FLASH_SIZE_32M_MAP_2048_2048 = 7, FLASH_SIZE_64M_MAP = 8, FLASH_SIZE_128M_MAP = 9 }; -// Documented in section 4.5 of 9b-esp8266_low_power_solutions_en.pdf -void system_deep_sleep_instant(uint32 time_in_us); - //force sleep API #define FPM_SLEEP_MAX_TIME 268435455 //0xFFFFFFF void wifi_fpm_set_wakeup_cb(void (*fpm_wakeup_cb_func)(void)); From 447fcd397d68c3c3a6272023fa8de8ebb717e82e Mon Sep 17 00:00:00 2001 From: dnc40085 Date: Mon, 15 May 2017 00:05:22 -0700 Subject: [PATCH 72/73] Exposed wifi event OPMODE_CHANGED. (#1967) --- app/modules/wifi_eventmon.c | 19 +++++++++++++++---- docs/en/modules/wifi.md | 11 ++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/app/modules/wifi_eventmon.c b/app/modules/wifi_eventmon.c index abaaa4ae11..05af3c38cb 100644 --- a/app/modules/wifi_eventmon.c +++ b/app/modules/wifi_eventmon.c @@ -154,9 +154,10 @@ static void wifi_event_monitor_handle_event_cb(System_Event_t *evt) if((wifi_event_cb_ref[evt->event] != LUA_NOREF) || ((wifi_event_cb_ref[EVENT_MAX] != LUA_NOREF) && !(evt->event == EVENT_STAMODE_CONNECTED || evt->event == EVENT_STAMODE_DISCONNECTED || - evt->event == EVENT_STAMODE_AUTHMODE_CHANGE||evt->event==EVENT_STAMODE_GOT_IP || - evt->event == EVENT_STAMODE_DHCP_TIMEOUT||evt->event==EVENT_SOFTAPMODE_STACONNECTED || - evt->event == EVENT_SOFTAPMODE_STADISCONNECTED||evt->event==EVENT_SOFTAPMODE_PROBEREQRECVED))) + evt->event == EVENT_STAMODE_AUTHMODE_CHANGE || evt->event == EVENT_STAMODE_GOT_IP || + evt->event == EVENT_STAMODE_DHCP_TIMEOUT || evt->event == EVENT_SOFTAPMODE_STACONNECTED || + evt->event == EVENT_SOFTAPMODE_STADISCONNECTED || evt->event == EVENT_SOFTAPMODE_PROBEREQRECVED || + evt->event == EVENT_OPMODE_CHANGED))) { evt_queue_t *temp = (evt_queue_t*)c_malloc(sizeof(evt_queue_t)); //allocate memory for new queue item temp->evt = (System_Event_t*)c_malloc(sizeof(System_Event_t)); //allocate memory to hold event structure @@ -279,7 +280,16 @@ static void wifi_event_monitor_process_event_queue(task_param_t param, uint8 pri evt->event_info.ap_probereqrecved.rssi); break; - default://if event is not implemented, push table with userdata containing event data + case EVENT_OPMODE_CHANGED: + EVENT_DBG("\n\tOPMODE_CHANGED\n"); + wifi_add_int_field(L, "old_mode", evt->event_info.opmode_changed.old_opmode); + wifi_add_int_field(L, "new_mode", evt->event_info.opmode_changed.new_opmode); + EVENT_DBG("\topmode: %u -> %u\n", + evt->event_info.opmode_changed.old_opmode, + evt->event_info.opmode_changed.new_opmode); + break; + + default://if event is not implemented, return event id EVENT_DBG("\n\tswitch/case default\n"); wifi_add_sprintf_field(L, "info", "event %u not implemented", evt->event); break; @@ -350,6 +360,7 @@ const LUA_REG_TYPE wifi_event_monitor_map[] = { LSTRKEY( "AP_STACONNECTED" ), LNUMVAL( EVENT_SOFTAPMODE_STACONNECTED ) }, { LSTRKEY( "AP_STADISCONNECTED" ), LNUMVAL( EVENT_SOFTAPMODE_STADISCONNECTED ) }, { LSTRKEY( "AP_PROBEREQRECVED" ), LNUMVAL( EVENT_SOFTAPMODE_PROBEREQRECVED ) }, + { LSTRKEY( "WIFI_MODE_CHANGED" ), LNUMVAL( EVENT_OPMODE_CHANGED ) }, { LSTRKEY( "EVENT_MAX" ), LNUMVAL( EVENT_MAX ) }, #ifdef WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE { LSTRKEY( "reason" ), LROVAL( wifi_event_monitor_reason_map ) }, diff --git a/docs/en/modules/wifi.md b/docs/en/modules/wifi.md index 47d4ef9fb7..0f81d36d6c 100644 --- a/docs/en/modules/wifi.md +++ b/docs/en/modules/wifi.md @@ -1523,6 +1523,9 @@ T: Table returned by event. - `wifi.eventmon.AP_PROBEREQRECVED`: A probe request was received. - `MAC`: MAC address of the client that is probing the access point. - `RSSI`: Received Signal Strength Indicator of client. +- `wifi.eventmon.WIFI_MODE_CHANGE`: WiFi mode has changed. + - `old_auth_mode`: Old WiFi mode. + - `new_auth_mode`: New WiFi mode. #### Example @@ -1537,7 +1540,7 @@ T: Table returned by event. T.BSSID.."\n\treason: "..T.reason) end) - wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, Function(T) + wifi.eventmon.register(wifi.eventmon.STA_AUTHMODE_CHANGE, function(T) print("\n\tSTA - AUTHMODE CHANGE".."\n\told_auth_mode: ".. T.old_auth_mode.."\n\tnew_auth_mode: "..T.new_auth_mode) end) @@ -1562,6 +1565,11 @@ T: Table returned by event. wifi.eventmon.register(wifi.eventmon.AP_PROBEREQRECVED, function(T) print("\n\tAP - PROBE REQUEST RECEIVED".."\n\tMAC: ".. T.MAC.."\n\tRSSI: "..T.RSSI) end) + + wifi.eventmon.register(wifi.eventmon.WIFI_MODE_CHANGED, function(T) + print("\n\tSTA - WIFI MODE CHANGED".."\n\told_mode: ".. + T.old_mode.."\n\tnew_mode: "..T.new_mode) + end) ``` #### See also - [`wifi.eventmon.unregister()`](#wifieventmonunregister) @@ -1588,6 +1596,7 @@ Event: WiFi event you would like to set a callback for. - wifi.eventmon.AP_STACONNECTED - wifi.eventmon.AP_STADISCONNECTED - wifi.eventmon.AP_PROBEREQRECVED + - wifi.eventmon.WIFI_MODE_CHANGED #### Returns `nil` From 4e4dfc1d888997904bbc881bb62e2f74a70d68e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnim=20L=C3=A4uger?= Date: Tue, 16 May 2017 16:50:36 +0200 Subject: [PATCH 73/73] Align 8 MB & 16 MB flash sizes with SDK 2.1.0. (#1968) * align 8MB and 16MB auto flash support with sdk 2.1.0 * remove SAFE_API * fix flash size mismatch detection logic --- app/include/user_config.h | 1 - app/modules/adc.c | 6 +-- app/modules/node.c | 8 ---- app/platform/cpu_esp8266.h | 10 ----- app/platform/flash_api.c | 62 +++++--------------------- app/platform/flash_api.h | 17 +------ app/spiffs/spiffs.c | 10 +---- app/user/user_main.c | 47 ++++++++++--------- docs/en/flash.md | 6 +-- sdk-overrides/include/user_interface.h | 5 --- 10 files changed, 48 insertions(+), 124 deletions(-) diff --git a/app/include/user_config.h b/app/include/user_config.h index 03745f931d..393da734ca 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -8,7 +8,6 @@ // #define FLASH_8M // #define FLASH_16M #define FLASH_AUTOSIZE -#define FLASH_SAFE_API // This adds the asserts in LUA. It also adds some useful extras to the // node module. This is all silent in normal operation and so can be enabled diff --git a/app/modules/adc.c b/app/modules/adc.c index 3ccd90aa44..83904bbeea 100644 --- a/app/modules/adc.c +++ b/app/modules/adc.c @@ -34,7 +34,7 @@ static int adc_init107( lua_State *L ) // Note 32bit alignment so we can safely cast to uint32 for the flash api char init_data[SPI_FLASH_SEC_SIZE] __attribute__((aligned(4))); - if (SPI_FLASH_RESULT_OK != flash_safe_read ( + if (SPI_FLASH_RESULT_OK != flash_read ( init_sector * SPI_FLASH_SEC_SIZE, (uint32 *)init_data, sizeof(init_data))) return luaL_error(L, "flash read error"); @@ -48,10 +48,10 @@ static int adc_init107( lua_State *L ) // Nope, it differs, we need to rewrite it init_data[107] = byte107; - if (SPI_FLASH_RESULT_OK != flash_safe_erase_sector (init_sector)) + if (SPI_FLASH_RESULT_OK != flash_erase (init_sector)) return luaL_error(L, "flash erase error"); - if (SPI_FLASH_RESULT_OK != flash_safe_write ( + if (SPI_FLASH_RESULT_OK != flash_write ( init_sector * SPI_FLASH_SEC_SIZE, (uint32 *)init_data, sizeof(init_data))) return luaL_error(L, "flash write error"); diff --git a/app/modules/node.c b/app/modules/node.c index 1bfcc12b5e..8302ec8cfe 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -112,11 +112,7 @@ static int node_info( lua_State* L ) lua_pushinteger(L, NODE_VERSION_REVISION); lua_pushinteger(L, system_get_chip_id()); // chip id lua_pushinteger(L, spi_flash_get_id()); // flash id -#if defined(FLASH_SAFE_API) - lua_pushinteger(L, flash_safe_get_size_byte() / 1024); // flash size in KB -#else lua_pushinteger(L, flash_rom_get_size_byte() / 1024); // flash size in KB -#endif // defined(FLASH_SAFE_API) lua_pushinteger(L, flash_rom_get_mode()); lua_pushinteger(L, flash_rom_get_speed()); return 8; @@ -154,11 +150,7 @@ static int node_flashsize( lua_State* L ) { flash_rom_set_size_byte(luaL_checkinteger(L, 1)); } -#if defined(FLASH_SAFE_API) - uint32_t sz = flash_safe_get_size_byte(); -#else uint32_t sz = flash_rom_get_size_byte(); -#endif // defined(FLASH_SAFE_API) lua_pushinteger( L, sz ); return 1; } diff --git a/app/platform/cpu_esp8266.h b/app/platform/cpu_esp8266.h index b763d9cd2f..eb6d34068a 100644 --- a/app/platform/cpu_esp8266.h +++ b/app/platform/cpu_esp8266.h @@ -32,11 +32,7 @@ #elif defined(FLASH_16M) #define FLASH_SEC_NUM 0x1000 #elif defined(FLASH_AUTOSIZE) -#if defined(FLASH_SAFE_API) -#define FLASH_SEC_NUM (flash_safe_get_sec_num()) -#else #define FLASH_SEC_NUM (flash_rom_get_sec_num()) -#endif // defined(FLASH_SAFE_API) #else #define FLASH_SEC_NUM 0x80 #endif @@ -55,15 +51,9 @@ // 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); -#if defined(FLASH_SAFE_API) -#define flash_write flash_safe_write -#define flash_erase flash_safe_erase_sector -#define flash_read flash_safe_read -#else #define flash_write spi_flash_write #define flash_erase spi_flash_erase_sector #define flash_read spi_flash_read -#endif // defined(FLASH_SAFE_API) #define CACHE_FLASH_CTRL_REG 0x3ff0000c #define CACHE_FLASH_ACTIVE 0x00000100 diff --git a/app/platform/flash_api.c b/app/platform/flash_api.c index bee2149c43..05f1239043 100644 --- a/app/platform/flash_api.c +++ b/app/platform/flash_api.c @@ -10,71 +10,33 @@ uint32_t flash_detect_size_byte(void) { + // enable operations on whole physical flash, SDK might have restricted + // the flash size already + extern SpiFlashChip * flashchip; + uint32 orig_chip_size = flashchip->chip_size; + flashchip->chip_size = FLASH_SIZE_16MBYTE; + #define FLASH_BUFFER_SIZE_DETECT 32 uint32_t dummy_size = FLASH_SIZE_256KBYTE; uint8_t data_orig[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0}; uint8_t data_new[FLASH_BUFFER_SIZE_DETECT] ICACHE_STORE_ATTR = {0}; - if (SPI_FLASH_RESULT_OK == flash_safe_read(0, (uint32 *)data_orig, FLASH_BUFFER_SIZE_DETECT)) + if (SPI_FLASH_RESULT_OK == flash_read(0, (uint32 *)data_orig, FLASH_BUFFER_SIZE_DETECT)) { dummy_size = FLASH_SIZE_256KBYTE; while ((dummy_size < FLASH_SIZE_16MBYTE) && - (SPI_FLASH_RESULT_OK == flash_safe_read(dummy_size, (uint32 *)data_new, FLASH_BUFFER_SIZE_DETECT)) && + (SPI_FLASH_RESULT_OK == flash_read(dummy_size, (uint32 *)data_new, FLASH_BUFFER_SIZE_DETECT)) && (0 != os_memcmp(data_orig, data_new, FLASH_BUFFER_SIZE_DETECT)) ) { dummy_size *= 2; } }; - return dummy_size; -#undef FLASH_BUFFER_SIZE_DETECT -} - -uint32_t flash_safe_get_size_byte(void) -{ - static uint32_t flash_size = 0; - if (flash_size == 0) - { - flash_size = flash_detect_size_byte(); -#if !defined(FLASH_SAFE_API) - // clip maximum flash size to 4MByte if "SAFE API" is not used - if (flash_size > FLASH_SIZE_4MBYTE) { - flash_size = FLASH_SIZE_4MBYTE; - } -#endif - } - return flash_size; -} - -uint16_t flash_safe_get_sec_num(void) -{ - return (flash_safe_get_size_byte() / (SPI_FLASH_SEC_SIZE)); -} -SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size) -{ - SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; - FLASH_SAFEMODE_ENTER(); - result = spi_flash_read(src_addr, (uint32 *) des_addr, size); - FLASH_SAFEMODE_LEAVE(); - return result; -} + // revert temporary setting + flashchip->chip_size = orig_chip_size; -SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size) -{ - SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; - FLASH_SAFEMODE_ENTER(); - result = spi_flash_write(des_addr, src_addr, size); - FLASH_SAFEMODE_LEAVE(); - return result; -} - -SpiFlashOpResult flash_safe_erase_sector(uint16 sec) -{ - SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; - FLASH_SAFEMODE_ENTER(); - result = spi_flash_erase_sector(sec); - FLASH_SAFEMODE_LEAVE(); - return result; + return dummy_size; +#undef FLASH_BUFFER_SIZE_DETECT } SPIFlashInfo flash_rom_getinfo(void) diff --git a/app/platform/flash_api.h b/app/platform/flash_api.h index 7ea697fc7e..b0fb16be20 100644 --- a/app/platform/flash_api.h +++ b/app/platform/flash_api.h @@ -20,16 +20,6 @@ #define FLASH_SIZE_8MBYTE (FLASH_SIZE_64MBIT / 8) #define FLASH_SIZE_16MBYTE (FLASH_SIZE_128MBIT/ 8) -#define FLASH_SAFEMODE_ENTER() \ -do { \ - extern SpiFlashChip * flashchip; \ - flashchip->chip_size = FLASH_SIZE_16MBYTE - - -#define FLASH_SAFEMODE_LEAVE() \ - flashchip->chip_size = flash_rom_get_size_byte(); \ -} while(0) - /****************************************************************************** * ROM Function definition * Note: It is unsafe to use ROM function, but it may efficient. @@ -89,15 +79,10 @@ typedef struct uint32_t segment_size; } ICACHE_STORE_TYPEDEF_ATTR SPIFlashInfo; -uint32_t flash_detect_size_byte(void); -uint32_t flash_safe_get_size_byte(void); -uint16_t flash_safe_get_sec_num(void); -SpiFlashOpResult flash_safe_read(uint32 src_addr, uint32 *des_addr, uint32 size); -SpiFlashOpResult flash_safe_write(uint32 des_addr, uint32 *src_addr, uint32 size); -SpiFlashOpResult flash_safe_erase_sector(uint16 sec); SPIFlashInfo flash_rom_getinfo(void); uint8_t flash_rom_get_size_type(void); uint32_t flash_rom_get_size_byte(void); +uint32_t flash_detect_size_byte(void); bool flash_rom_set_size_type(uint8_t); bool flash_rom_set_size_byte(uint32_t); uint16_t flash_rom_get_sec_num(void); diff --git a/app/spiffs/spiffs.c b/app/spiffs/spiffs.c index 6090489b73..08d0dfb533 100644 --- a/app/spiffs/spiffs.c +++ b/app/spiffs/spiffs.c @@ -53,14 +53,8 @@ static bool myspiffs_set_location(spiffs_config *cfg, int align, int offset, int #ifdef SPIFFS_FIXED_LOCATION cfg->phys_addr = (SPIFFS_FIXED_LOCATION + block_size - 1) & ~(block_size-1); #else - if (flash_safe_get_size_byte() <= FLASH_SIZE_4MBYTE) { - // 256kByte - 4MByte modules: SPIFFS partition starts right after firmware image - cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset; - cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1); - } else { - // > 4MByte modules: SPIFFS partition starts after SDK data - cfg->phys_addr = flash_rom_get_size_byte(); - } + cfg->phys_addr = ( u32_t )platform_flash_get_first_free_block_address( NULL ) + offset; + cfg->phys_addr = (cfg->phys_addr + align - 1) & ~(align - 1); #endif #ifdef SPIFFS_SIZE_1M_BOUNDARY cfg->phys_size = ((0x100000 - (SYS_PARAM_SEC_NUM * INTERNAL_FLASH_SECTOR_SIZE) - ( ( u32_t )cfg->phys_addr )) & ~(block_size - 1)) & 0xfffff; diff --git a/app/user/user_main.c b/app/user/user_main.c index 7af00ecc90..95a1ab9616 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -72,9 +72,24 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) * is deliberately quite terse and not as readable as one might like. */ SPIFlashInfo sfi; + + // enable operations on >4MB flash chip + extern SpiFlashChip * flashchip; + uint32 orig_chip_size = flashchip->chip_size; + flashchip->chip_size = FLASH_SIZE_16MBYTE; + SPIRead (0, (uint32_t *)(&sfi), sizeof (sfi)); // Cache read not enabled yet, safe to use - if (sfi.size < 2) // Compensate for out-of-order 4mbit vs 2mbit values - sfi.size ^= 1; + // handle all size entries + switch (sfi.size) { + case 0: sfi.size = 1; break; // SIZE_4MBIT + case 1: sfi.size = 0; break; // SIZE_2MBIT + case 5: sfi.size = 3; break; // SIZE_16MBIT_8M_8M + case 6: // fall-through + case 7: sfi.size = 4; break; // SIZE_32MBIT_8M_8M, SIZE_32MBIT_16M_16M + case 8: sfi.size = 5; break; // SIZE_64MBIT + case 9: sfi.size = 6; break; // SIZE_128MBIT + default: break; + } uint32_t flash_end_addr = (256 * 1024) << sfi.size; uint32_t init_data_hdr = 0xffffffff; uint32_t init_data_addr = flash_end_addr - 4 * SPI_FLASH_SEC_SIZE; @@ -85,6 +100,9 @@ void TEXT_SECTION_ATTR user_start_trampoline (void) SPIWrite (init_data_addr, init_data, 4 * (init_data_end - init_data)); } + // revert temporary setting + flashchip->chip_size = orig_chip_size; + call_user_start (); } @@ -122,21 +140,10 @@ void nodemcu_init(void) return; } - if( flash_safe_get_size_byte() <= FLASH_SIZE_4MBYTE ) { - if( flash_safe_get_size_byte() != flash_rom_get_size_byte() ) { - NODE_ERR("Self adjust flash size.\n"); - // Fit hardware real flash size. - flash_rom_set_size_byte(flash_safe_get_size_byte()); - - system_restart (); - // Don't post the start_lua task, we're about to reboot... - return; - } - } else if( (flash_rom_get_size_byte() < FLASH_SIZE_1MBYTE) || - (flash_rom_get_size_byte() > FLASH_SIZE_4MBYTE) ) { - NODE_ERR("Locking flash size for SDK to 1MByte.\n"); - // SDK/ROM can't handle flash size > 4MByte, ensure a minimum of 1MByte for firmware image - flash_rom_set_size_byte(FLASH_SIZE_1MBYTE); + if( flash_detect_size_byte() != flash_rom_get_size_byte() ) { + NODE_ERR("Self adjust flash size.\n"); + // Fit hardware real flash size. + flash_rom_set_size_byte(flash_detect_size_byte()); system_restart (); // Don't post the start_lua task, we're about to reboot... @@ -190,7 +197,7 @@ void user_rf_pre_init(void) uint32 user_rf_cal_sector_set(void) { - enum ext_flash_size_map size_map = system_get_flash_size_map(); + enum flash_size_map size_map = system_get_flash_size_map(); uint32 rf_cal_sec = 0; switch (size_map) { @@ -213,11 +220,11 @@ user_rf_cal_sector_set(void) rf_cal_sec = 1024 - 5; break; - case FLASH_SIZE_64M_MAP: + case FLASH_SIZE_64M_MAP_1024_1024: rf_cal_sec = 2048 - 5; break; - case FLASH_SIZE_128M_MAP: + case FLASH_SIZE_128M_MAP_1024_1024: rf_cal_sec = 4096 - 5; break; diff --git a/docs/en/flash.md b/docs/en/flash.md index b33bbcd7c2..255ce7ca30 100644 --- a/docs/en/flash.md +++ b/docs/en/flash.md @@ -32,8 +32,6 @@ Run the following command to flash an *aggregated* binary as is produced for exa - esptool.py is under heavy development. It's advised you run the latest version (check with `esptool.py version`). Since this documentation may not have been able to keep up refer to the [esptool flash modes documentation](https://github.com/themadinventor/esptool#flash-modes) for current options and parameters. - In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing: `esptool.py --port erase_flash` -- Modules with flash chips larger than 4 MByte (e.g. WeMos D1 mini pro) need to be manually configured to at least 1 MByte: Firmware image and SDK init data occupy the first MByte, while the remaining 7/15 MByte of the flash are used for SPIFFS: -`esptool.py --port write_flash -fm -fs 8m 0x00000 .bin` ### NodeMCU Flasher > A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source. @@ -102,12 +100,14 @@ Espressif refers to this area as "System Param" and it resides in the last four The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty. -If you need to customize init data then first download the [Espressif SDK 2.0.0](https://espressif.com/sites/default/files/sdks/esp8266_nonos_sdk_v2.0.0_16_08_10.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. +If you need to customize init data then first download the [Espressif SDK 2.1.0](https://github.com/espressif/ESP8266_NONOS_SDK/archive/v2.1.0.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. - `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc. - `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc. - `0x1fc000` for 2 MB - `0x3fc000` for 4 MB, modules like ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini +- `0x7fc000` for 8 MB +- `0xffc000` for 16 MB, modules like WeMos D1 mini pro See "4.1 Non-FOTA Flash Map" and "6.3 RF Initialization Configuration" of the [ESP8266 Getting Started Guide](https://espressif.com/en/support/explore/get-started/esp8266/getting-started-guide) for details on init data addresses and customization. diff --git a/sdk-overrides/include/user_interface.h b/sdk-overrides/include/user_interface.h index b029751293..200ee6df53 100644 --- a/sdk-overrides/include/user_interface.h +++ b/sdk-overrides/include/user_interface.h @@ -6,11 +6,6 @@ bool wifi_softap_deauth(uint8 mac[6]); uint8 get_fpm_auto_sleep_flag(void); -enum ext_flash_size_map { - FLASH_SIZE_64M_MAP = 8, - FLASH_SIZE_128M_MAP = 9 -}; - //force sleep API #define FPM_SLEEP_MAX_TIME 268435455 //0xFFFFFFF void wifi_fpm_set_wakeup_cb(void (*fpm_wakeup_cb_func)(void));