diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..45aedeab
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: cpp
+before_install:
+- sudo apt-get install -y python-serial srecord
+install:
+- wget https://github.com/GeorgeHahn/nodemcu-firmware/raw/travis/tools/esp-open-sdk.tar.gz -O tools/esp-open-sdk.tar.gz
+- tar -zxvf tools/esp-open-sdk.tar.gz
+- export PATH=$PATH:$PWD/esp-open-sdk/sdk:$PWD/esp-open-sdk/xtensa-lx106-elf/bin
+script:
+- make all
+- cd bin/
+- file_name="nodemcu-firmware_v${TRAVIS_TAG}.${TRAVIS_BUILD_NUMBER}.bin"
+- srec_cat -output ${file_name} -binary 0x00000.bin -binary -fill 0xff 0x00000 0x10000 0x10000.bin -binary -offset 0x10000
+deploy:
+ provider: releases
+ api_key:
+ secure: Swecz5lWvsuSbchSbVQ1rmCPN9nQIN5p/HlZNIEdEgAgnoLcJxRV4P8poVTB37jiA8Pck+8x2nWXpg74Rqik0i3KlPNvDfg5o4rIazWLNs4bc1Tbcpt44XAzFKKLYnDnWQUGcqjk7BcAXuNAF2X/fPBCVhFbHVg3Z7cDb32RsNw=
+ file: "$TRAVIS_BUILD_DIR/bin/${file_name}"
+ skip_cleanup: true
+ on:
+ tags: true
+ repo: nodemcu/nodemcu-firmware
diff --git a/README.md b/README.md
index d5a142d4..fb55a684 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
-# **NodeMcu** #
+# **NodeMCU** #
version 0.9.5
+
+[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware)
+
###A lua based firmware for wifi-soc esp8266
Build on [ESP8266 sdk 0.9.5](http://bbs.espressif.com/viewtopic.php?f=5&t=154)
Lua core based on [eLua project](http://www.eluaproject.net/)
@@ -7,24 +10,26 @@ File system based on [spiffs](https://github.com/pellepl/spiffs)
Open source development kit for NodeMCU [nodemcu-devkit](https://github.com/nodemcu/nodemcu-devkit)
Flash tool for NodeMCU [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
-wiki: [nodemcu wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)
-api: [nodemcu api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en)
+wiki: [NodeMCU wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)
+api: [NodeMCU api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en)
home: [nodemcu.com](http://www.nodemcu.com)
bbs: [Chinese bbs](http://bbs.nodemcu.com)
+docs: [NodeMCU docs](http://www.nodemcu.com/docs/)
Tencent QQ group: 309957875
# Summary
- Easy to access wireless router
-- Based on Lua 5.1.4 (without *io, math, debug, os* module.)
+- Based on Lua 5.1.4 (without *debug, os* module.)
- Event-Drive programming preferred.
-- Build-in file, timer, pwm, i2c, spi, 1-wire, net, mqtt, gpio, wifi, adc, uart and system api.
+- Build-in file, timer, pwm, i2c, spi, 1-wire, net, mqtt, coap, gpio, wifi, adc, uart and system api.
- GPIO pin re-mapped, use the index to access gpio, i2c, pwm.
# To Do List (pull requests are very welcomed)
+- loadable c module
- fix wifi smart connect
- add spi module (done)
- add mqtt module (done)
-- add coap module (in coap branch)
+- add coap module (done)
- cross compiler (done)
# Change log
@@ -98,15 +103,19 @@ build pre_build bin.
#### [*] D0(GPIO16) can only be used as gpio read/write. no interrupt supported. no pwm/i2c/ow supported.
#Build option
-####file ./app/include/user_config.h
+####file ./app/include/user_modules.h
```c
-// #define FLASH_512K
-// #define FLASH_1M
-// #define FLASH_2M
-// #define FLASH_4M
-#define FLASH_AUTOSIZE
-...
+#define LUA_USE_BUILTIN_STRING // for string.xxx()
+#define LUA_USE_BUILTIN_TABLE // for table.xxx()
+#define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx()
+#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
+// #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work
+
+// #define LUA_USE_BUILTIN_OS // for os.xxx(), not work
+// #define LUA_USE_BUILTIN_DEBUG // for debug.xxx(), not work
+
#define LUA_USE_MODULES
+
#ifdef LUA_USE_MODULES
#define LUA_USE_MODULES_NODE
#define LUA_USE_MODULES_FILE
@@ -115,15 +124,17 @@ build pre_build bin.
#define LUA_USE_MODULES_NET
#define LUA_USE_MODULES_PWM
#define LUA_USE_MODULES_I2C
+#define LUA_USE_MODULES_SPI
#define LUA_USE_MODULES_TMR
#define LUA_USE_MODULES_ADC
#define LUA_USE_MODULES_UART
#define LUA_USE_MODULES_OW
#define LUA_USE_MODULES_BIT
+#define LUA_USE_MODULES_MQTT
+// #define LUA_USE_MODULES_COAP // need about 4k more ram for now
+#define LUA_USE_MODULES_U8G
#define LUA_USE_MODULES_WS2812
#endif /* LUA_USE_MODULES */
-...
-// LUA_NUMBER_INTEGRAL
```
#Flash the firmware
diff --git a/app/.gitignore b/app/.gitignore
index 2e2287fd..c6d8ed75 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -1,2 +1,3 @@
*.output*
+mapfile
!.gitignore
diff --git a/app/Makefile b/app/Makefile
index 0f318cea..78f95518 100644
--- a/app/Makefile
+++ b/app/Makefile
@@ -30,6 +30,7 @@ SUBDIRS= \
platform \
libc \
lua \
+ coap \
mqtt \
u8glib \
smart \
@@ -77,6 +78,7 @@ COMPONENTS_eagle.app.v6 = \
platform/libplatform.a \
libc/liblibc.a \
lua/liblua.a \
+ coap/coap.a \
mqtt/mqtt.a \
u8glib/u8glib.a \
smart/smart.a \
@@ -106,7 +108,8 @@ LINKFLAGS_eagle.app.v6 = \
-lsmartconfig \
-lssl \
$(DEP_LIBS_eagle.app.v6) \
- -Wl,--end-group
+ -Wl,--end-group \
+ -lm
DEPENDS_eagle.app.v6 = \
$(LD_FILE) \
diff --git a/app/coap/LICENSE.txt b/app/coap/LICENSE.txt
new file mode 100644
index 00000000..4dd7b4c1
--- /dev/null
+++ b/app/coap/LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2013 Toby Jaffey
+ 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/coap/Makefile b/app/coap/Makefile
new file mode 100644
index 00000000..3947069c
--- /dev/null
+++ b/app/coap/Makefile
@@ -0,0 +1,45 @@
+
+#############################################################
+# 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 = coap.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
+INCLUDES += -I ../lua
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+
diff --git a/app/coap/coap.c b/app/coap/coap.c
new file mode 100644
index 00000000..6dded680
--- /dev/null
+++ b/app/coap/coap.c
@@ -0,0 +1,555 @@
+#include "user_config.h"
+#include "c_stdio.h"
+#include "c_string.h"
+#include "coap.h"
+#include "uri.h"
+
+extern void endpoint_setup(void);
+extern const coap_endpoint_t endpoints[];
+
+#ifdef COAP_DEBUG
+void coap_dumpHeader(coap_header_t *hdr)
+{
+ c_printf("Header:\n");
+ c_printf(" ver 0x%02X\n", hdr->ver);
+ c_printf(" t 0x%02X\n", hdr->ver);
+ c_printf(" tkl 0x%02X\n", hdr->tkl);
+ c_printf(" code 0x%02X\n", hdr->code);
+ c_printf(" id 0x%02X%02X\n", hdr->id[0], hdr->id[1]);
+}
+
+void coap_dump(const uint8_t *buf, size_t buflen, bool bare)
+{
+ if (bare)
+ {
+ while(buflen--)
+ c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
+ }
+ else
+ {
+ c_printf("Dump: ");
+ while(buflen--)
+ c_printf("%02X%s", *buf++, (buflen > 0) ? " " : "");
+ c_printf("\n");
+ }
+}
+#endif
+
+int coap_parseHeader(coap_header_t *hdr, const uint8_t *buf, size_t buflen)
+{
+ if (buflen < 4)
+ return COAP_ERR_HEADER_TOO_SHORT;
+ hdr->ver = (buf[0] & 0xC0) >> 6;
+ if (hdr->ver != 1)
+ return COAP_ERR_VERSION_NOT_1;
+ hdr->t = (buf[0] & 0x30) >> 4;
+ hdr->tkl = buf[0] & 0x0F;
+ hdr->code = buf[1];
+ hdr->id[0] = buf[2];
+ hdr->id[1] = buf[3];
+ return 0;
+}
+
+int coap_buildHeader(const coap_header_t *hdr, uint8_t *buf, size_t buflen)
+{
+ // build header
+ if (buflen < 4)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+
+ buf[0] = (hdr->ver & 0x03) << 6;
+ buf[0] |= (hdr->t & 0x03) << 4;
+ buf[0] |= (hdr->tkl & 0x0F);
+ buf[1] = hdr->code;
+ buf[2] = hdr->id[0];
+ buf[3] = hdr->id[1];
+ return 4;
+}
+
+int coap_parseToken(coap_buffer_t *tokbuf, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
+{
+ if (hdr->tkl == 0)
+ {
+ tokbuf->p = NULL;
+ tokbuf->len = 0;
+ return 0;
+ }
+ else
+ if (hdr->tkl <= 8)
+ {
+ if (4 + hdr->tkl > buflen)
+ return COAP_ERR_TOKEN_TOO_SHORT; // tok bigger than packet
+ tokbuf->p = buf+4; // past header
+ tokbuf->len = hdr->tkl;
+ return 0;
+ }
+ else
+ {
+ // invalid size
+ return COAP_ERR_TOKEN_TOO_SHORT;
+ }
+}
+
+int coap_buildToken(const coap_buffer_t *tokbuf, const coap_header_t *hdr, uint8_t *buf, size_t buflen)
+{
+ // inject token
+ uint8_t *p;
+ if (buflen < 4 + hdr->tkl)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+ p = buf + 4;
+ if ((hdr->tkl > 0) && (hdr->tkl != tokbuf->len))
+ return COAP_ERR_UNSUPPORTED;
+
+ if (hdr->tkl > 0)
+ c_memcpy(p, tokbuf->p, hdr->tkl);
+
+ // http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1
+ // inject options
+ return hdr->tkl;
+}
+
+// advances p
+int coap_parseOption(coap_option_t *option, uint16_t *running_delta, const uint8_t **buf, size_t buflen)
+{
+ const uint8_t *p = *buf;
+ uint16_t len, delta;
+ uint8_t headlen = 1;
+
+ if (buflen < headlen) // too small
+ return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
+
+ delta = (p[0] & 0xF0) >> 4;
+ len = p[0] & 0x0F;
+
+ // These are untested and may be buggy
+ if (delta == 13)
+ {
+ headlen++;
+ if (buflen < headlen)
+ return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
+ delta = p[1] + 13;
+ p++;
+ }
+ else
+ if (delta == 14)
+ {
+ headlen += 2;
+ if (buflen < headlen)
+ return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
+ delta = ((p[1] << 8) | p[2]) + 269;
+ p+=2;
+ }
+ else
+ if (delta == 15)
+ return COAP_ERR_OPTION_DELTA_INVALID;
+
+ if (len == 13)
+ {
+ headlen++;
+ if (buflen < headlen)
+ return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
+ len = p[1] + 13;
+ p++;
+ }
+ else
+ if (len == 14)
+ {
+ headlen += 2;
+ if (buflen < headlen)
+ return COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER;
+ len = ((p[1] << 8) | p[2]) + 269;
+ p+=2;
+ }
+ else
+ if (len == 15)
+ return COAP_ERR_OPTION_LEN_INVALID;
+
+ if ((p + 1 + len) > (*buf + buflen))
+ return COAP_ERR_OPTION_TOO_BIG;
+
+ //printf("option num=%d\n", delta + *running_delta);
+ option->num = delta + *running_delta;
+ option->buf.p = p+1;
+ option->buf.len = len;
+ //coap_dump(p+1, len, false);
+
+ // advance buf
+ *buf = p + 1 + len;
+ *running_delta += delta;
+
+ return 0;
+}
+
+// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3.1
+int coap_parseOptionsAndPayload(coap_option_t *options, uint8_t *numOptions, coap_buffer_t *payload, const coap_header_t *hdr, const uint8_t *buf, size_t buflen)
+{
+ size_t optionIndex = 0;
+ const uint8_t *p = buf + 4 + hdr->tkl;
+ const uint8_t *end = buf + buflen;
+ int rc;
+ uint16_t delta = 0;
+ if (p > end)
+ return COAP_ERR_OPTION_OVERRUNS_PACKET; // out of bounds
+
+ //coap_dump(p, end - p);
+
+ // 0xFF is payload marker
+ while((optionIndex < *numOptions) && (p < end) && (*p != 0xFF))
+ {
+ if (0 != (rc = coap_parseOption(&options[optionIndex], &delta, &p, end-p)))
+ return rc;
+ optionIndex++;
+ }
+ *numOptions = optionIndex;
+
+ if (p+1 < end && *p == 0xFF) // payload marker
+ {
+ payload->p = p+1;
+ payload->len = end-(p+1);
+ }
+ else
+ {
+ payload->p = NULL;
+ payload->len = 0;
+ }
+
+ return 0;
+}
+
+int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen)
+{
+ int n = 0;
+ uint8_t *p = buf;
+ uint8_t len, delta = 0;
+
+ if (buflen < 5)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+
+ coap_option_nibble(optDelta, &delta);
+ coap_option_nibble(length, &len);
+
+ *p++ = (0xFF & (delta << 4 | len));
+ n++;
+ if (delta == 13)
+ {
+ *p++ = (optDelta - 13);
+ n++;
+ }
+ else
+ if (delta == 14)
+ {
+ *p++ = ((optDelta-269) >> 8);
+ *p++ = (0xFF & (optDelta-269));
+ n+=2;
+ }
+ if (len == 13)
+ {
+ *p++ = (length - 13);
+ n++;
+ }
+ else
+ if (len == 14)
+ {
+ *p++ = (length >> 8);
+ *p++ = (0xFF & (length-269));
+ n+=2;
+ }
+ return n;
+}
+
+#ifdef COAP_DEBUG
+void coap_dumpOptions(coap_option_t *opts, size_t numopt)
+{
+ size_t i;
+ c_printf(" Options:\n");
+ for (i=0;ihdr);
+ coap_dumpOptions(pkt->opts, pkt->numopts);
+ c_printf("Payload: ");
+ coap_dump(pkt->payload.p, pkt->payload.len, true);
+ c_printf("\n");
+}
+#endif
+
+int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen)
+{
+ int rc;
+
+ // coap_dump(buf, buflen, false);
+
+ if (0 != (rc = coap_parseHeader(&pkt->hdr, buf, buflen)))
+ return rc;
+// coap_dumpHeader(&hdr);
+ if (0 != (rc = coap_parseToken(&pkt->tok, &pkt->hdr, buf, buflen)))
+ return rc;
+ pkt->numopts = MAXOPT;
+ if (0 != (rc = coap_parseOptionsAndPayload(pkt->opts, &(pkt->numopts), &(pkt->payload), &pkt->hdr, buf, buflen)))
+ return rc;
+// coap_dumpOptions(opts, numopt);
+ return 0;
+}
+
+// options are always stored consecutively, so can return a block with same option num
+const coap_option_t * coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count)
+{
+ // FIXME, options is always sorted, can find faster than this
+ size_t i;
+ const coap_option_t *first = NULL;
+ *count = 0;
+ for (i=0;inumopts;i++)
+ {
+ if (pkt->opts[i].num == num)
+ {
+ if (NULL == first)
+ first = &pkt->opts[i];
+ (*count)++;
+ }
+ else
+ {
+ if (NULL != first)
+ break;
+ }
+ }
+ return first;
+}
+
+int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf)
+{
+ if (buf->len+1 > strbuflen)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+ c_memcpy(strbuf, buf->p, buf->len);
+ strbuf[buf->len] = 0;
+ return 0;
+}
+
+int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt)
+{
+ size_t opts_len = 0, hdr_len = 0, tok_len = 0;
+ size_t i;
+ uint8_t *p = buf;
+ size_t left = *buflen;
+ uint16_t running_delta = 0;
+
+ hdr_len = coap_buildHeader(&(pkt->hdr), buf, *buflen);
+ p += hdr_len;
+ left -= hdr_len;
+
+ tok_len = coap_buildToken(&(pkt->tok), &(pkt->hdr), buf, *buflen);
+ p += tok_len;
+ left -= tok_len;
+
+ for (i=0;inumopts;i++)
+ {
+ uint8_t len, delta = 0;
+ uint16_t optDelta = 0;
+ int rc = 0;
+
+ if (p-buf > *buflen)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+ optDelta = pkt->opts[i].num - running_delta;
+
+ rc = coap_buildOptionHeader(optDelta, pkt->opts[i].buf.len, p, left);
+ p += rc;
+ left -= rc;
+
+ c_memcpy(p, pkt->opts[i].buf.p, pkt->opts[i].buf.len);
+ p += pkt->opts[i].buf.len;
+ left -= pkt->opts[i].buf.len;
+ running_delta = pkt->opts[i].num;
+ }
+
+ opts_len = (p - buf) - 4; // number of bytes used by options
+
+ if (pkt->payload.len > 0)
+ {
+ if (*buflen < 4 + 1 + pkt->payload.len + opts_len)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+ buf[4 + opts_len] = 0xFF; // payload marker
+ c_memcpy(buf+5 + opts_len, pkt->payload.p, pkt->payload.len);
+ *buflen = opts_len + 5 + pkt->payload.len;
+ }
+ else
+ *buflen = opts_len + 4;
+ return 0;
+}
+
+void coap_option_nibble(uint16_t value, uint8_t *nibble)
+{
+ if (value<13)
+ {
+ *nibble = (0xFF & value);
+ }
+ else
+ if (((uint32_t)value)<=0xFF+13)
+ {
+ *nibble = 13;
+ } else if (((uint32_t)value) -269 <=0xFFFF)
+ {
+ *nibble = 14;
+ }
+}
+
+int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type)
+{
+ pkt->hdr.ver = 0x01;
+ pkt->hdr.t = COAP_TYPE_ACK;
+ pkt->hdr.tkl = 0;
+ pkt->hdr.code = rspcode;
+ pkt->hdr.id[0] = msgid_hi;
+ pkt->hdr.id[1] = msgid_lo;
+ pkt->numopts = 0;
+
+ // need token in response
+ if (tok) {
+ pkt->hdr.tkl = tok->len;
+ pkt->tok = *tok;
+ }
+
+ // safe because 1 < MAXOPT
+ pkt->opts[0].num = COAP_OPTION_CONTENT_FORMAT;
+ pkt->opts[0].buf.p = scratch->p;
+ if (scratch->len < 2)
+ return COAP_ERR_BUFFER_TOO_SMALL;
+ scratch->p[0] = ((uint16_t)content_type & 0xFF00) >> 8;
+ scratch->p[1] = ((uint16_t)content_type & 0x00FF);
+ pkt->opts[0].buf.len = 2;
+ pkt->payload.p = content;
+ pkt->payload.len = content_len;
+ return 0;
+}
+
+
+unsigned int coap_encode_var_bytes(unsigned char *buf, unsigned int val) {
+ unsigned int n, i;
+
+ for (n = 0, i = val; i && n < sizeof(val); ++n)
+ i >>= 8;
+
+ i = n;
+ while (i--) {
+ buf[i] = val & 0xff;
+ val >>= 8;
+ }
+
+ return n;
+}
+
+static uint8_t _token_data[4]={'n','o','d','e'};
+coap_buffer_t the_token = { _token_data, 4 };
+static unsigned short message_id;
+
+int coap_make_request(coap_rw_buffer_t *scratch, coap_packet_t *pkt, coap_msgtype_t t, coap_method_t m, coap_uri_t *uri, const uint8_t *payload, size_t payload_len)
+{
+ int res;
+ pkt->hdr.ver = 0x01;
+ pkt->hdr.t = t;
+ pkt->hdr.tkl = 0;
+ pkt->hdr.code = m;
+ pkt->hdr.id[0] = (message_id >> 8) & 0xFF; //msgid_hi;
+ pkt->hdr.id[1] = message_id & 0xFF; //msgid_lo;
+ message_id++;
+ NODE_DBG("message_id: %d.\n", message_id);
+ pkt->numopts = 0;
+
+ if (the_token.len) {
+ pkt->hdr.tkl = the_token.len;
+ pkt->tok = the_token;
+ }
+
+ if (scratch->len < 2) // TBD...
+ return COAP_ERR_BUFFER_TOO_SMALL;
+
+ uint8_t *saved = scratch->p;
+
+ /* split arg into Uri-* options */
+ // const char *addr = uri->host.s;
+ // if(uri->host.length && (c_strlen(addr) != uri->host.length || c_memcmp(addr, uri->host.s, uri->host.length) != 0)){
+ if(uri->host.length){
+ /* add Uri-Host */
+ // addr is destination address
+ pkt->opts[pkt->numopts].num = COAP_OPTION_URI_HOST;
+ pkt->opts[pkt->numopts].buf.p = uri->host.s;
+ pkt->opts[pkt->numopts].buf.len = uri->host.length;
+ pkt->numopts++;
+ }
+
+ if (uri->port != COAP_DEFAULT_PORT) {
+ pkt->opts[pkt->numopts].num = COAP_OPTION_URI_PORT;
+ res = coap_encode_var_bytes(scratch->p, uri->port);
+ pkt->opts[pkt->numopts].buf.len = res;
+ pkt->opts[pkt->numopts].buf.p = scratch->p;
+ scratch->p += res;
+ scratch->len -= res;
+ pkt->numopts++;
+ }
+
+ if (uri->path.length) {
+ res = coap_split_path(scratch, pkt, uri->path.s, uri->path.length);
+ }
+
+ if (uri->query.length) {
+ res = coap_split_query(scratch, pkt, uri->query.s, uri->query.length);
+ }
+
+ pkt->payload.p = payload;
+ pkt->payload.len = payload_len;
+ scratch->p = saved; // save back the pointer.
+ return 0;
+}
+
+// FIXME, if this looked in the table at the path before the method then
+// it could more easily return 405 errors
+int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt)
+{
+ const coap_option_t *opt;
+ int i;
+ uint8_t count;
+ const coap_endpoint_t *ep = endpoints;
+
+ while(NULL != ep->handler)
+ {
+ if (ep->method != inpkt->hdr.code)
+ goto next;
+ if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
+ {
+ // if (count != ep->path->count)
+ if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
+ goto next;
+ for (i=0;ipath->count;i++)
+ {
+ if (opt[i].buf.len != c_strlen(ep->path->elems[i]))
+ goto next;
+ if (0 != c_memcmp(ep->path->elems[i], opt[i].buf.p, opt[i].buf.len))
+ goto next;
+ }
+ // pre-path match!
+ if (count==ep->path->count+1 && ep->user_entry == NULL)
+ goto next;
+ return ep->handler(ep, scratch, inpkt, outpkt, inpkt->hdr.id[0], inpkt->hdr.id[1]);
+ }
+next:
+ ep++;
+ }
+
+ coap_make_response(scratch, outpkt, NULL, 0, inpkt->hdr.id[0], inpkt->hdr.id[1], &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+
+ return 0;
+}
+
+void coap_setup(void)
+{
+ message_id = (unsigned short)rand(); // calculate only once
+}
+
+inline int
+check_token(coap_packet_t *pkt) {
+ return pkt->tok.len == the_token.len && c_memcmp(pkt->tok.p, the_token.p, the_token.len) == 0;
+}
diff --git a/app/coap/coap.h b/app/coap/coap.h
new file mode 100644
index 00000000..1c8827a6
--- /dev/null
+++ b/app/coap/coap.h
@@ -0,0 +1,186 @@
+#ifndef COAP_H
+#define COAP_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "c_stdint.h"
+#include "c_stddef.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+#define MAXOPT 16
+#define MAX_MESSAGE_SIZE 1152
+#define MAX_PAYLOAD_SIZE 1024
+#define MAX_REQUEST_SIZE 576
+#define MAX_REQ_SCRATCH_SIZE 60
+
+#define COAP_RESPONSE_CLASS(C) (((C) >> 5) & 0xFF)
+
+// http://tools.ietf.org/html/draft-ietf-core-coap-18#section-3
+typedef struct
+{
+ uint8_t ver;
+ uint8_t t;
+ uint8_t tkl;
+ uint8_t code;
+ uint8_t id[2];
+} coap_header_t;
+
+typedef struct
+{
+ const uint8_t *p;
+ size_t len;
+} coap_buffer_t;
+
+typedef struct
+{
+ uint8_t *p;
+ size_t len;
+} coap_rw_buffer_t;
+
+typedef struct
+{
+ uint8_t num;
+ coap_buffer_t buf;
+} coap_option_t;
+
+typedef struct
+{
+ coap_header_t hdr;
+ coap_buffer_t tok;
+ uint8_t numopts;
+ coap_option_t opts[MAXOPT];
+ coap_buffer_t payload;
+ coap_rw_buffer_t scratch; // scratch->p = malloc(...) , and free it when done.
+} coap_packet_t;
+
+/////////////////////////////////////////
+
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.2
+typedef enum
+{
+ COAP_OPTION_IF_MATCH = 1,
+ COAP_OPTION_URI_HOST = 3,
+ COAP_OPTION_ETAG = 4,
+ COAP_OPTION_IF_NONE_MATCH = 5,
+ COAP_OPTION_OBSERVE = 6,
+ COAP_OPTION_URI_PORT = 7,
+ COAP_OPTION_LOCATION_PATH = 8,
+ COAP_OPTION_URI_PATH = 11,
+ COAP_OPTION_CONTENT_FORMAT = 12,
+ COAP_OPTION_MAX_AGE = 14,
+ COAP_OPTION_URI_QUERY = 15,
+ COAP_OPTION_ACCEPT = 17,
+ COAP_OPTION_LOCATION_QUERY = 20,
+ COAP_OPTION_PROXY_URI = 35,
+ COAP_OPTION_PROXY_SCHEME = 39
+} coap_option_num_t;
+
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1
+typedef enum
+{
+ COAP_METHOD_GET = 1,
+ COAP_METHOD_POST = 2,
+ COAP_METHOD_PUT = 3,
+ COAP_METHOD_DELETE = 4
+} coap_method_t;
+
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.1
+typedef enum
+{
+ COAP_TYPE_CON = 0,
+ COAP_TYPE_NONCON = 1,
+ COAP_TYPE_ACK = 2,
+ COAP_TYPE_RESET = 3
+} coap_msgtype_t;
+
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.2
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.1.2
+#define MAKE_RSPCODE(clas, det) ((clas << 5) | (det))
+typedef enum
+{
+ COAP_RSPCODE_CONTENT = MAKE_RSPCODE(2, 5),
+ COAP_RSPCODE_NOT_FOUND = MAKE_RSPCODE(4, 4),
+ COAP_RSPCODE_BAD_REQUEST = MAKE_RSPCODE(4, 0),
+ COAP_RSPCODE_CHANGED = MAKE_RSPCODE(2, 4)
+} coap_responsecode_t;
+
+//http://tools.ietf.org/html/draft-ietf-core-coap-18#section-12.3
+typedef enum
+{
+ COAP_CONTENTTYPE_NONE = -1, // bodge to allow us not to send option block
+ COAP_CONTENTTYPE_TEXT_PLAIN = 0,
+ COAP_CONTENTTYPE_APPLICATION_LINKFORMAT = 40,
+} coap_content_type_t;
+
+///////////////////////
+
+typedef enum
+{
+ COAP_ERR_NONE = 0,
+ COAP_ERR_HEADER_TOO_SHORT = 1,
+ COAP_ERR_VERSION_NOT_1 = 2,
+ COAP_ERR_TOKEN_TOO_SHORT = 3,
+ COAP_ERR_OPTION_TOO_SHORT_FOR_HEADER = 4,
+ COAP_ERR_OPTION_TOO_SHORT = 5,
+ COAP_ERR_OPTION_OVERRUNS_PACKET = 6,
+ COAP_ERR_OPTION_TOO_BIG = 7,
+ COAP_ERR_OPTION_LEN_INVALID = 8,
+ COAP_ERR_BUFFER_TOO_SMALL = 9,
+ COAP_ERR_UNSUPPORTED = 10,
+ COAP_ERR_OPTION_DELTA_INVALID = 11,
+} coap_error_t;
+
+///////////////////////
+typedef struct coap_endpoint_t coap_endpoint_t;
+
+typedef int (*coap_endpoint_func)(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo);
+#define MAX_SEGMENTS 3 // 2 = /foo/bar, 3 = /foo/bar/baz
+#define MAX_SEGMENTS_SIZE 16
+typedef struct
+{
+ int count;
+ const char *elems[MAX_SEGMENTS];
+} coap_endpoint_path_t;
+
+typedef struct coap_luser_entry coap_luser_entry;
+
+struct coap_luser_entry{
+ lua_State *L;
+ // int ref;
+ // char name[MAX_SEGMENTS_SIZE+1]; // +1 for string '\0'
+ const char *name;
+ coap_luser_entry *next;
+};
+
+struct coap_endpoint_t
+{
+ coap_method_t method;
+ coap_endpoint_func handler;
+ const coap_endpoint_path_t *path;
+ const char *core_attr;
+ coap_luser_entry *user_entry;
+};
+
+///////////////////////
+void coap_dumpPacket(coap_packet_t *pkt);
+int coap_parse(coap_packet_t *pkt, const uint8_t *buf, size_t buflen);
+int coap_buffer_to_string(char *strbuf, size_t strbuflen, const coap_buffer_t *buf);
+const coap_option_t *coap_findOptions(const coap_packet_t *pkt, uint8_t num, uint8_t *count);
+int coap_build(uint8_t *buf, size_t *buflen, const coap_packet_t *pkt);
+void coap_dump(const uint8_t *buf, size_t buflen, bool bare);
+int coap_make_response(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const uint8_t *content, size_t content_len, uint8_t msgid_hi, uint8_t msgid_lo, const coap_buffer_t* tok, coap_responsecode_t rspcode, coap_content_type_t content_type);
+int coap_handle_req(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt);
+void coap_option_nibble(uint16_t value, uint8_t *nibble);
+void coap_setup(void);
+void endpoint_setup(void);
+
+int coap_buildOptionHeader(unsigned short optDelta, size_t length, uint8_t *buf, size_t buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/coap_client.c b/app/coap/coap_client.c
new file mode 100644
index 00000000..37a1a5f0
--- /dev/null
+++ b/app/coap/coap_client.c
@@ -0,0 +1,67 @@
+#include "user_config.h"
+#include "c_types.h"
+
+#include "coap.h"
+#include "hash.h"
+#include "node.h"
+
+extern coap_queue_t *gQueue;
+
+void coap_client_response_handler(char *data, unsigned short len, unsigned short size, const uint32_t ip, const uint32_t port)
+{
+ NODE_DBG("coap_client_response_handler is called.\n");
+ coap_packet_t pkt;
+ int rc;
+
+ if (0 != (rc = coap_parse(&pkt, data, len))){
+ NODE_DBG("Bad packet rc=%d\n", rc);
+ }
+ else
+ {
+#ifdef COAP_DEBUG
+ coap_dumpPacket(&pkt);
+#endif
+ /* check if this is a response to our original request */
+ if (!check_token(&pkt)) {
+ /* drop if this was just some message, or send RST in case of notification */
+ if (pkt.hdr.t == COAP_TYPE_CON || pkt.hdr.t == COAP_TYPE_NONCON){
+ // coap_send_rst(pkt); // send RST response
+ // or, just ignore it.
+ }
+ goto end;
+ }
+
+ if (pkt.hdr.t == COAP_TYPE_RESET) {
+ NODE_DBG("got RST\n");
+ goto end;
+ }
+
+ coap_tid_t id = COAP_INVALID_TID;
+ coap_transaction_id(ip, port, &pkt, &id);
+ /* transaction done, remove the node from queue */
+ // stop timer
+ coap_timer_stop();
+ // remove the node
+ coap_remove_node(&gQueue, id);
+ // calculate time elapsed
+ coap_timer_update(&gQueue);
+ coap_timer_start(&gQueue);
+
+ if (COAP_RESPONSE_CLASS(pkt.hdr.code) == 2)
+ {
+ /* There is no block option set, just read the data and we are done. */
+ NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
+ NODE_DBG((char *)(pkt.payload.p));
+ }
+ else if (COAP_RESPONSE_CLASS(pkt.hdr.code) >= 4)
+ {
+ NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
+ NODE_DBG((char *)(pkt.payload.p));
+ }
+ }
+
+end:
+ if(!gQueue){ // if there is no node pending in the queue, disconnect from host.
+
+ }
+}
diff --git a/app/coap/coap_client.h b/app/coap/coap_client.h
new file mode 100644
index 00000000..5559adfe
--- /dev/null
+++ b/app/coap/coap_client.h
@@ -0,0 +1,14 @@
+#ifndef _COAP_SERVER_H
+#define _COAP_SERVER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+size_t coap_server_respond(char *data, unsigned short len, unsigned short size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/coap_io.c b/app/coap/coap_io.c
new file mode 100644
index 00000000..4b93356c
--- /dev/null
+++ b/app/coap/coap_io.c
@@ -0,0 +1,71 @@
+#include "c_string.h"
+#include "coap_io.h"
+#include "node.h"
+#include "espconn.h"
+#include "coap_timer.h"
+
+extern coap_queue_t *gQueue;
+
+/* releases space allocated by PDU if free_pdu is set */
+coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu) {
+ coap_tid_t id = COAP_INVALID_TID;
+ uint32_t ip = 0, port = 0;
+ if ( !pesp_conn || !pdu )
+ return id;
+
+ espconn_sent(pesp_conn, (unsigned char *)(pdu->msg.p), pdu->msg.len);
+
+ if(pesp_conn->type == ESPCONN_TCP){
+ c_memcpy(&ip, pesp_conn->proto.tcp->remote_ip, sizeof(ip));
+ port = pesp_conn->proto.tcp->remote_port;
+ }else{
+ c_memcpy(&ip, pesp_conn->proto.udp->remote_ip, sizeof(ip));
+ port = pesp_conn->proto.udp->remote_port;
+ }
+ coap_transaction_id(ip, port, pdu->pkt, &id);
+ return id;
+}
+
+coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu) {
+ coap_queue_t *node;
+ coap_tick_t diff;
+ uint32_t r;
+
+ node = coap_new_node();
+ if (!node) {
+ NODE_DBG("coap_send_confirmed: insufficient memory\n");
+ return COAP_INVALID_TID;
+ }
+
+ node->retransmit_cnt = 0;
+ node->id = coap_send(pesp_conn, pdu);
+ if (COAP_INVALID_TID == node->id) {
+ NODE_DBG("coap_send_confirmed: error sending pdu\n");
+ coap_free_node(node);
+ return COAP_INVALID_TID;
+ }
+ r = rand();
+
+ /* add randomized RESPONSE_TIMEOUT to determine retransmission timeout */
+ node->timeout = COAP_DEFAULT_RESPONSE_TIMEOUT * COAP_TICKS_PER_SECOND +
+ (COAP_DEFAULT_RESPONSE_TIMEOUT >> 1) *
+ ((COAP_TICKS_PER_SECOND * (r & 0xFF)) >> 8);
+
+ node->pconn = pesp_conn;
+ node->pdu = pdu;
+
+ /* Set timer for pdu retransmission. If this is the first element in
+ * the retransmission queue, the base time is set to the current
+ * time and the retransmission time is node->timeout. If there is
+ * already an entry in the sendqueue, we must check if this node is
+ * to be retransmitted earlier. Therefore, node->timeout is first
+ * normalized to the timeout and then inserted into the queue with
+ * an adjusted relative time.
+ */
+ coap_timer_stop();
+ coap_timer_update(&gQueue);
+ node->t = node->timeout;
+ coap_insert_node(&gQueue, node);
+ coap_timer_start(&gQueue);
+ return node->id;
+}
diff --git a/app/coap/coap_io.h b/app/coap/coap_io.h
new file mode 100644
index 00000000..801b7328
--- /dev/null
+++ b/app/coap/coap_io.h
@@ -0,0 +1,20 @@
+#ifndef _COAP_IO_H
+#define _COAP_IO_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "espconn.h"
+#include "pdu.h"
+#include "hash.h"
+
+coap_tid_t coap_send(struct espconn *pesp_conn, coap_pdu_t *pdu);
+
+coap_tid_t coap_send_confirmed(struct espconn *pesp_conn, coap_pdu_t *pdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/coap_server.c b/app/coap/coap_server.c
new file mode 100644
index 00000000..d87bd8f7
--- /dev/null
+++ b/app/coap/coap_server.c
@@ -0,0 +1,53 @@
+#include "user_config.h"
+#include "c_types.h"
+
+#include "coap.h"
+
+size_t coap_server_respond(char *data, unsigned short len, unsigned short size)
+{
+ NODE_DBG("coap_server_respond is called.\n");
+ if(len>size){
+ NODE_DBG("len:%d, size:%d\n",len,size);
+ return 0;
+ }
+ size_t rsplen = size;
+ coap_packet_t pkt;
+ uint8_t scratch_raw[4];
+ coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)};
+ int rc;
+
+#ifdef COAP_DEBUG
+ NODE_DBG("Received: ");
+ coap_dump(data, len, true);
+ NODE_DBG("\n");
+#endif
+
+ if (0 != (rc = coap_parse(&pkt, data, len))){
+ NODE_DBG("Bad packet rc=%d\n", rc);
+ return 0;
+ }
+ else
+ {
+ coap_packet_t rsppkt;
+#ifdef COAP_DEBUG
+ coap_dumpPacket(&pkt);
+#endif
+ coap_handle_req(&scratch_buf, &pkt, &rsppkt);
+ if (0 != (rc = coap_build(data, &rsplen, &rsppkt))){
+ NODE_DBG("coap_build failed rc=%d\n", rc);
+ return 0;
+ }
+ else
+ {
+#ifdef COAP_DEBUG
+ NODE_DBG("Responding: ");
+ coap_dump(data, rsplen, true);
+ NODE_DBG("\n");
+#endif
+#ifdef COAP_DEBUG
+ coap_dumpPacket(&rsppkt);
+#endif
+ }
+ return rsplen;
+ }
+}
diff --git a/app/coap/coap_server.h b/app/coap/coap_server.h
new file mode 100644
index 00000000..5559adfe
--- /dev/null
+++ b/app/coap/coap_server.h
@@ -0,0 +1,14 @@
+#ifndef _COAP_SERVER_H
+#define _COAP_SERVER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+size_t coap_server_respond(char *data, unsigned short len, unsigned short size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/coap_timer.c b/app/coap/coap_timer.c
new file mode 100644
index 00000000..9856ef83
--- /dev/null
+++ b/app/coap/coap_timer.c
@@ -0,0 +1,78 @@
+#include "node.h"
+#include "coap_timer.h"
+#include "os_type.h"
+
+static os_timer_t coap_timer;
+static coap_tick_t basetime = 0;
+
+void coap_timer_elapsed(coap_tick_t *diff){
+ coap_tick_t now = system_get_time() / 1000; // coap_tick_t is in ms. also sys_timer
+ if(now>=basetime){
+ *diff = now-basetime;
+ } else {
+ *diff = now + SYS_TIME_MAX -basetime;
+ }
+ basetime = now;
+}
+
+void coap_timer_tick(void *arg){
+ if( !arg )
+ return;
+ coap_queue_t **queue = (coap_queue_t **)arg;
+ if( !(*queue) )
+ return;
+
+ coap_queue_t *node = coap_pop_next( queue );
+ /* re-initialize timeout when maximum number of retransmissions are not reached yet */
+ if (node->retransmit_cnt < COAP_DEFAULT_MAX_RETRANSMIT) {
+ node->retransmit_cnt++;
+ node->t = node->timeout << node->retransmit_cnt;
+
+ NODE_DBG("** retransmission #%d of transaction %d\n",
+ node->retransmit_cnt, (((uint16_t)(node->pdu->pkt->hdr.id[0]))<<8)+node->pdu->pkt->hdr.id[1]);
+ node->id = coap_send(node->pconn, node->pdu);
+ if (COAP_INVALID_TID == node->id) {
+ NODE_DBG("retransmission: error sending pdu\n");
+ coap_delete_node(node);
+ } else {
+ coap_insert_node(queue, node);
+ }
+ } else {
+ /* And finally delete the node */
+ coap_delete_node( node );
+ }
+
+ coap_timer_start(queue);
+}
+
+void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t){
+ os_timer_disarm(&coap_timer);
+ os_timer_setfn(&coap_timer, (os_timer_func_t *)coap_timer_tick, queue);
+ os_timer_arm(&coap_timer, t, 0); // no repeat
+}
+
+void coap_timer_stop(void){
+ os_timer_disarm(&coap_timer);
+}
+
+void coap_timer_update(coap_queue_t ** queue){
+ if (!queue)
+ return;
+ coap_tick_t diff = 0;
+ coap_queue_t *first = *queue;
+ coap_timer_elapsed(&diff); // update: basetime = now, diff = now - oldbase, means time elapsed
+ if (first) {
+ // diff ms time is elapsed, re-calculate the first node->t
+ if (first->t >= diff){
+ first->t -= diff;
+ } else {
+ first->t = 0; // when timer enabled, time out almost immediately
+ }
+ }
+}
+
+void coap_timer_start(coap_queue_t ** queue){
+ if(*queue){ // if there is node in the queue, set timeout to its ->t.
+ coap_timer_setup(queue, (*queue)->t);
+ }
+}
diff --git a/app/coap/coap_timer.h b/app/coap/coap_timer.h
new file mode 100644
index 00000000..7f14a527
--- /dev/null
+++ b/app/coap/coap_timer.h
@@ -0,0 +1,31 @@
+#ifndef _COAP_TIMER_H
+#define _COAP_TIMER_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "node.h"
+
+#define SYS_TIME_MAX (0xFFFFFFFF / 1000)
+
+#define COAP_DEFAULT_RESPONSE_TIMEOUT 2 /* response timeout in seconds */
+#define COAP_DEFAULT_MAX_RETRANSMIT 4 /* max number of retransmissions */
+#define COAP_TICKS_PER_SECOND 1000 // ms
+#define DEFAULT_MAX_TRANSMIT_WAIT 90
+
+void coap_timer_elapsed(coap_tick_t *diff);
+
+void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t);
+
+void coap_timer_stop(void);
+
+void coap_timer_update(coap_queue_t ** queue);
+
+void coap_timer_start(coap_queue_t ** queue);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/endpoints.c b/app/coap/endpoints.c
new file mode 100644
index 00000000..a3d2d9d6
--- /dev/null
+++ b/app/coap/endpoints.c
@@ -0,0 +1,282 @@
+#include "c_stdio.h"
+#include "c_string.h"
+#include "coap.h"
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+
+#include "os_type.h"
+
+static char rsp[MAX_PAYLOAD_SIZE] = "";
+const uint16_t rsplen = MAX_PAYLOAD_SIZE;
+void build_well_known_rsp(void);
+
+void endpoint_setup(void)
+{
+ coap_setup();
+ build_well_known_rsp();
+}
+
+static const coap_endpoint_path_t path_well_known_core = {2, {".well-known", "core"}};
+static int handle_get_well_known_core(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
+{
+ return coap_make_response(scratch, outpkt, (const uint8_t *)rsp, c_strlen(rsp), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_APPLICATION_LINKFORMAT);
+}
+
+static const coap_endpoint_path_t path_variable = {2, {"v1", "v"}};
+static int handle_get_variable(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
+{
+ const coap_option_t *opt;
+ uint8_t count;
+ if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
+ {
+ if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
+ {
+ NODE_DBG("should never happen.\n");
+ goto end;
+ }
+ if (count == ep->path->count + 1)
+ {
+ coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
+ while(NULL != h){
+ if (opt[count-1].buf.len != c_strlen(h->name))
+ {
+ h = h->next;
+ continue;
+ }
+ if (0 == c_memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
+ {
+ NODE_DBG("/v1/v/");
+ NODE_DBG((char *)h->name);
+ NODE_DBG(" match.\n");
+ if(h->L == NULL)
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+ if(c_strlen(h->name))
+ {
+ lua_getglobal(h->L, h->name);
+ if (!lua_isnumber(h->L, -1)) {
+ NODE_DBG ("should be a number.\n");
+ lua_pop(h->L, 1);
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+ } else {
+ const char *res = lua_tostring(h->L,-1);
+ lua_pop(h->L, 1);
+ return coap_make_response(scratch, outpkt, (const uint8_t *)res, c_strlen(res), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+ }
+ }
+ } else {
+ h = h->next;
+ }
+ }
+ }else{
+ NODE_DBG("/v1/v match.\n");
+ goto end;
+ }
+ }
+ NODE_DBG("none match.\n");
+end:
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+}
+
+static const coap_endpoint_path_t path_function = {2, {"v1", "f"}};
+static int handle_post_function(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
+{
+ const coap_option_t *opt;
+ uint8_t count;
+ if (NULL != (opt = coap_findOptions(inpkt, COAP_OPTION_URI_PATH, &count)))
+ {
+ if ((count != ep->path->count ) && (count != ep->path->count + 1)) // +1 for /f/[function], /v/[variable]
+ {
+ NODE_DBG("should never happen.\n");
+ goto end;
+ }
+ if (count == ep->path->count + 1)
+ {
+ coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
+ while(NULL != h){
+ if (opt[count-1].buf.len != c_strlen(h->name))
+ {
+ h = h->next;
+ continue;
+ }
+ if (0 == c_memcmp(h->name, opt[count-1].buf.p, opt[count-1].buf.len))
+ {
+ NODE_DBG("/v1/f/");
+ NODE_DBG((char *)h->name);
+ NODE_DBG(" match.\n");
+
+ if(h->L == NULL)
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+
+ if(c_strlen(h->name))
+ {
+ lua_getglobal(h->L, h->name);
+ if (lua_type(h->L, -1) != LUA_TFUNCTION) {
+ NODE_DBG ("should be a function\n");
+ lua_pop(h->L, 1);
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+ } else {
+ lua_pushlstring(h->L, inpkt->payload.p, inpkt->payload.len); // make sure payload.p is filled with '\0' after payload.len, or use lua_pushlstring
+ lua_call(h->L, 1, 1);
+ if (!lua_isnil(h->L, -1)){ /* get return? */
+ if( lua_isstring(h->L, -1) ) // deal with the return string
+ {
+ size_t len = 0;
+ const char *ret = luaL_checklstring( h->L, -1, &len );
+ if(len > MAX_PAYLOAD_SIZE){
+ luaL_error( h->L, "return string:tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+ }
+ NODE_DBG((char *)ret);
+ NODE_DBG("\n");
+ return coap_make_response(scratch, outpkt, ret, len, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+ }
+ } else {
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+ }
+ }
+ }
+ } else {
+ h = h->next;
+ }
+ }
+ }else{
+ NODE_DBG("/v1/f match.\n");
+ goto end;
+ }
+ }
+ NODE_DBG("none match.\n");
+end:
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_NOT_FOUND, COAP_CONTENTTYPE_NONE);
+}
+
+extern lua_Load gLoad;
+extern os_timer_t lua_timer;
+extern void dojob(lua_Load *load);
+static const coap_endpoint_path_t path_command = {2, {"v1", "c"}};
+static int handle_post_command(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
+{
+ if (inpkt->payload.len == 0)
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_BAD_REQUEST, COAP_CONTENTTYPE_TEXT_PLAIN);
+ if (inpkt->payload.len > 0)
+ {
+ lua_Load *load = &gLoad;
+ if(load->line_position == 0){
+ coap_buffer_to_string(load->line, load->len,&inpkt->payload);
+ load->line_position = c_strlen(load->line)+1;
+ // load->line[load->line_position-1] = '\n';
+ // load->line[load->line_position] = 0;
+ // load->line_position++;
+ load->done = 1;
+ NODE_DBG("Get command:\n");
+ NODE_DBG(load->line); // buggy here
+ NODE_DBG("\nResult(if any):\n");
+ os_timer_disarm(&lua_timer);
+ os_timer_setfn(&lua_timer, (os_timer_func_t *)dojob, load);
+ os_timer_arm(&lua_timer, READLINE_INTERVAL, 0); // no repeat
+ }
+ return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+ }
+}
+
+static uint32_t id = 0;
+static const coap_endpoint_path_t path_id = {2, {"v1", "id"}};
+static int handle_get_id(const coap_endpoint_t *ep, coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo)
+{
+ id = system_get_chip_id();
+ return coap_make_response(scratch, outpkt, (const uint8_t *)(&id), sizeof(uint32_t), id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
+}
+
+coap_luser_entry var_head = {NULL,NULL,NULL};
+coap_luser_entry *variable_entry = &var_head;
+
+coap_luser_entry func_head = {NULL,NULL,NULL};
+coap_luser_entry *function_entry = &func_head;
+
+const coap_endpoint_t endpoints[] =
+{
+ {COAP_METHOD_GET, handle_get_well_known_core, &path_well_known_core, "ct=40", NULL},
+ {COAP_METHOD_GET, handle_get_variable, &path_variable, "ct=0", &var_head},
+ {COAP_METHOD_POST, handle_post_function, &path_function, NULL, &func_head},
+ {COAP_METHOD_POST, handle_post_command, &path_command, NULL, NULL},
+ {COAP_METHOD_GET, handle_get_id, &path_id, "ct=0", NULL},
+ {(coap_method_t)0, NULL, NULL, NULL, NULL}
+};
+
+void build_well_known_rsp(void)
+{
+ const coap_endpoint_t *ep = endpoints;
+ int i;
+ uint16_t len = rsplen;
+
+ c_memset(rsp, 0, sizeof(rsp));
+
+ len--; // Null-terminated string
+
+ while(NULL != ep->handler)
+ {
+ if (NULL == ep->core_attr) {
+ ep++;
+ continue;
+ }
+ if (NULL == ep->user_entry){
+ if (0 < c_strlen(rsp)) {
+ c_strncat(rsp, ",", len);
+ len--;
+ }
+
+ c_strncat(rsp, "<", len);
+ len--;
+
+ for (i = 0; i < ep->path->count; i++) {
+ c_strncat(rsp, "/", len);
+ len--;
+
+ c_strncat(rsp, ep->path->elems[i], len);
+ len -= c_strlen(ep->path->elems[i]);
+ }
+
+ c_strncat(rsp, ">;", len);
+ len -= 2;
+
+ c_strncat(rsp, ep->core_attr, len);
+ len -= c_strlen(ep->core_attr);
+ } else {
+ coap_luser_entry *h = ep->user_entry->next; // ->next: skip the first entry(head)
+ while(NULL != h){
+ if (0 < c_strlen(rsp)) {
+ c_strncat(rsp, ",", len);
+ len--;
+ }
+
+ c_strncat(rsp, "<", len);
+ len--;
+
+ for (i = 0; i < ep->path->count; i++) {
+ c_strncat(rsp, "/", len);
+ len--;
+
+ c_strncat(rsp, ep->path->elems[i], len);
+ len -= c_strlen(ep->path->elems[i]);
+ }
+
+ c_strncat(rsp, "/", len);
+ len--;
+
+ c_strncat(rsp, h->name, len);
+ len -= c_strlen(h->name);
+
+ c_strncat(rsp, ">;", len);
+ len -= 2;
+
+ c_strncat(rsp, ep->core_attr, len);
+ len -= c_strlen(ep->core_attr);
+
+ h = h->next;
+ }
+ }
+ ep++;
+ }
+}
+
diff --git a/app/coap/hash.c b/app/coap/hash.c
new file mode 100644
index 00000000..6e6c3a24
--- /dev/null
+++ b/app/coap/hash.c
@@ -0,0 +1,30 @@
+#include "hash.h"
+#include "c_string.h"
+/* Caution: When changing this, update COAP_DEFAULT_WKC_HASHKEY
+ * accordingly (see int coap_hash_path());
+ */
+void coap_hash(const unsigned char *s, unsigned int len, coap_key_t h) {
+ size_t j;
+
+ while (len--) {
+ j = sizeof(coap_key_t)-1;
+
+ while (j) {
+ h[j] = ((h[j] << 7) | (h[j-1] >> 1)) + h[j];
+ --j;
+ }
+
+ h[0] = (h[0] << 7) + h[0] + *s++;
+ }
+}
+
+void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id) {
+ coap_key_t h;
+ c_memset(h, 0, sizeof(coap_key_t));
+
+ /* Compare the transport address. */
+ coap_hash((const unsigned char *)&(port), sizeof(port), h);
+ coap_hash((const unsigned char *)&(ip), sizeof(ip), h);
+ coap_hash((const unsigned char *)(pkt->hdr.id), sizeof(pkt->hdr.id), h);
+ *id = ((h[0] << 8) | h[1]) ^ ((h[2] << 8) | h[3]);
+}
diff --git a/app/coap/hash.h b/app/coap/hash.h
new file mode 100644
index 00000000..3980089d
--- /dev/null
+++ b/app/coap/hash.h
@@ -0,0 +1,23 @@
+#ifndef _HASH_H
+#define _HASH_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "coap.h"
+
+typedef unsigned char coap_key_t[4];
+
+/* CoAP transaction id */
+/*typedef unsigned short coap_tid_t; */
+typedef int coap_tid_t;
+#define COAP_INVALID_TID -1
+
+void coap_transaction_id(const uint32_t ip, const uint32_t port, const coap_packet_t *pkt, coap_tid_t *id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/node.c b/app/coap/node.c
new file mode 100644
index 00000000..5eeb7269
--- /dev/null
+++ b/app/coap/node.c
@@ -0,0 +1,141 @@
+#include "c_string.h"
+#include "c_stdlib.h"
+#include "node.h"
+
+static inline coap_queue_t *
+coap_malloc_node(void) {
+ return (coap_queue_t *)c_zalloc(sizeof(coap_queue_t));
+}
+
+void coap_free_node(coap_queue_t *node) {
+ c_free(node);
+}
+
+int coap_insert_node(coap_queue_t **queue, coap_queue_t *node) {
+ coap_queue_t *p, *q;
+ if ( !queue || !node )
+ return 0;
+
+ /* set queue head if empty */
+ if ( !*queue ) {
+ *queue = node;
+ return 1;
+ }
+
+ /* replace queue head if PDU's time is less than head's time */
+ q = *queue;
+ if (node->t < q->t) {
+ node->next = q;
+ *queue = node;
+ q->t -= node->t; /* make q->t relative to node->t */
+ return 1;
+ }
+
+ /* search for right place to insert */
+ do {
+ node->t -= q->t; /* make node-> relative to q->t */
+ p = q;
+ q = q->next;
+ } while (q && q->t <= node->t);
+
+ /* insert new item */
+ if (q) {
+ q->t -= node->t; /* make q->t relative to node->t */
+ }
+ node->next = q;
+ p->next = node;
+ return 1;
+}
+
+int coap_delete_node(coap_queue_t *node) {
+ if ( !node )
+ return 0;
+
+ coap_delete_pdu(node->pdu);
+ coap_free_node(node);
+
+ return 1;
+}
+
+void coap_delete_all(coap_queue_t *queue) {
+ if ( !queue )
+ return;
+
+ coap_delete_all( queue->next );
+ coap_delete_node( queue );
+}
+
+coap_queue_t * coap_new_node(void) {
+ coap_queue_t *node;
+ node = coap_malloc_node();
+
+ if ( ! node ) {
+ return NULL;
+ }
+
+ c_memset(node, 0, sizeof(*node));
+ return node;
+}
+
+coap_queue_t * coap_peek_next( coap_queue_t *queue ) {
+ if ( !queue )
+ return NULL;
+
+ return queue;
+}
+
+coap_queue_t * coap_pop_next( coap_queue_t **queue ) { // this function is called inside timeout callback only.
+ coap_queue_t *next;
+
+ if ( !(*queue) )
+ return NULL;
+
+ next = *queue;
+ *queue = (*queue)->next;
+ // if (queue) {
+ // queue->t += next->t;
+ // }
+ next->next = NULL;
+ return next;
+}
+
+int coap_remove_node( coap_queue_t **queue, const coap_tid_t id){
+ coap_queue_t *p, *q, *node;
+ if ( !queue )
+ return 0;
+ if ( !*queue ) // if empty
+ return 0;
+
+ q = *queue;
+ if (q->id == id) {
+ node = q;
+ *queue = q->next;
+ node->next = NULL;
+ if(*queue){
+ (*queue)->t += node->t;
+ }
+ coap_delete_node(node);
+ return 1;
+ }
+
+ /* search for right node to remove */
+ while (q && q->id != id) {
+ p = q;
+ q = q->next;
+ }
+
+ /* find the node */
+ if (q) {
+ node = q; /* save the node */
+ p->next = q->next; /* remove the node */
+ q = q->next;
+ node->next = NULL;
+ if (q) // add node->t to the node after.
+ {
+ q->t += node->t;
+ }
+ coap_delete_node(node);
+ return 1;
+ }
+ return 0;
+}
diff --git a/app/coap/node.h b/app/coap/node.h
new file mode 100644
index 00000000..af06ee10
--- /dev/null
+++ b/app/coap/node.h
@@ -0,0 +1,58 @@
+#ifndef _NODE_H
+#define _NODE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "hash.h"
+#include "pdu.h"
+
+struct coap_queue_t;
+typedef uint32_t coap_tick_t;
+
+/*
+1. queue(first)->t store when to send PDU for the next time, it's a base(absolute) time
+2. queue->next->t store the delta between time and base-time. queue->next->t = timeout + now - basetime
+3. node->next->t store the delta between time and previous->t. node->next->t = timeout + now - node->t - basetime
+4. time to fire: 10, 15, 18, 25
+ node->t: 10, 5, 3, 7
+*/
+
+typedef struct coap_queue_t {
+ struct coap_queue_t *next;
+
+ coap_tick_t t; /**< when to send PDU for the next time */
+ unsigned char retransmit_cnt; /**< retransmission counter, will be removed when zero */
+ unsigned int timeout; /**< the randomized timeout value */
+
+ coap_tid_t id; /**< unique transaction id */
+
+ // coap_packet_t *pkt;
+ coap_pdu_t *pdu; /**< the CoAP PDU to send */
+ struct espconn *pconn;
+} coap_queue_t;
+
+void coap_free_node(coap_queue_t *node);
+
+/** Adds node to given queue, ordered by node->t. */
+int coap_insert_node(coap_queue_t **queue, coap_queue_t *node);
+
+/** Destroys specified node. */
+int coap_delete_node(coap_queue_t *node);
+
+/** Removes all items from given queue and frees the allocated storage. */
+void coap_delete_all(coap_queue_t *queue);
+
+/** Creates a new node suitable for adding to the CoAP sendqueue. */
+coap_queue_t *coap_new_node(void);
+
+coap_queue_t *coap_pop_next( coap_queue_t **queue );
+
+int coap_remove_node( coap_queue_t **queue, const coap_tid_t id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/pdu.c b/app/coap/pdu.c
new file mode 100644
index 00000000..8313146c
--- /dev/null
+++ b/app/coap/pdu.c
@@ -0,0 +1,63 @@
+#include "c_stdlib.h"
+#include "pdu.h"
+
+coap_pdu_t * coap_new_pdu(void) {
+ coap_pdu_t *pdu = NULL;
+ pdu = (coap_pdu_t *)c_zalloc(sizeof(coap_pdu_t));
+ if(!pdu){
+ NODE_DBG("coap_new_pdu malloc error.\n");
+ return NULL;
+ }
+
+ pdu->scratch.p = (uint8_t *)c_zalloc(MAX_REQ_SCRATCH_SIZE);
+ if(!pdu->scratch.p){
+ NODE_DBG("coap_new_pdu malloc error.\n");
+ c_free(pdu);
+ return NULL;
+ }
+ pdu->scratch.len = MAX_REQ_SCRATCH_SIZE;
+
+ pdu->pkt = (coap_packet_t *)c_zalloc(sizeof(coap_packet_t));
+ if(!pdu->pkt){
+ NODE_DBG("coap_new_pdu malloc error.\n");
+ c_free(pdu->scratch.p);
+ c_free(pdu);
+ return NULL;
+ }
+
+ pdu->msg.p = (uint8_t *)c_zalloc(MAX_REQUEST_SIZE+1); // +1 for string '\0'
+ if(!pdu->msg.p){
+ NODE_DBG("coap_new_pdu malloc error.\n");
+ c_free(pdu->pkt);
+ c_free(pdu->scratch.p);
+ c_free(pdu);
+ return NULL;
+ }
+ pdu->msg.len = MAX_REQUEST_SIZE;
+ return pdu;
+}
+
+void coap_delete_pdu(coap_pdu_t *pdu){
+ if(!pdu)
+ return;
+
+ if(pdu->scratch.p){
+ c_free(pdu->scratch.p);
+ pdu->scratch.p = NULL;
+ pdu->scratch.len = 0;
+ }
+
+ if(pdu->pkt){
+ c_free(pdu->pkt);
+ pdu->pkt = NULL;
+ }
+
+ if(pdu->msg.p){
+ c_free(pdu->msg.p);
+ pdu->msg.p = NULL;
+ pdu->msg.len = 0;
+ }
+
+ c_free(pdu);
+ pdu = NULL;
+}
diff --git a/app/coap/pdu.h b/app/coap/pdu.h
new file mode 100644
index 00000000..cd5154da
--- /dev/null
+++ b/app/coap/pdu.h
@@ -0,0 +1,25 @@
+#ifndef _PDU_H
+#define _PDU_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "coap.h"
+
+/** Header structure for CoAP PDUs */
+typedef struct {
+ coap_rw_buffer_t scratch;
+ coap_packet_t *pkt;
+ coap_rw_buffer_t msg; /**< the CoAP msg to send */
+} coap_pdu_t;
+
+coap_pdu_t *coap_new_pdu(void);
+
+void coap_delete_pdu(coap_pdu_t *pdu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/coap/str.c b/app/coap/str.c
new file mode 100644
index 00000000..df2f84be
--- /dev/null
+++ b/app/coap/str.c
@@ -0,0 +1,28 @@
+/* str.c -- strings to be used in the CoAP library
+ *
+ * Copyright (C) 2010,2011 Olaf Bergmann
+ *
+ * This file is part of the CoAP library libcoap. Please see
+ * README for terms of use.
+ */
+
+#include "c_stdlib.h"
+#include "c_types.h"
+
+#include "str.h"
+
+str * coap_new_string(size_t size) {
+ str *s = (str *)c_malloc(sizeof(str) + size + 1);
+ if ( !s ) {
+ return NULL;
+ }
+
+ c_memset(s, 0, sizeof(str));
+ s->s = ((unsigned char *)s) + sizeof(str);
+ return s;
+}
+
+void coap_delete_string(str *s) {
+ c_free(s);
+}
+
diff --git a/app/coap/str.h b/app/coap/str.h
new file mode 100644
index 00000000..c33045b3
--- /dev/null
+++ b/app/coap/str.h
@@ -0,0 +1,30 @@
+/* str.h -- strings to be used in the CoAP library
+ *
+ * Copyright (C) 2010,2011 Olaf Bergmann
+ *
+ * This file is part of the CoAP library libcoap. Please see
+ * README for terms of use.
+ */
+
+#ifndef _COAP_STR_H_
+#define _COAP_STR_H_
+
+#include "c_string.h"
+
+typedef struct {
+ size_t length; /* length of string */
+ unsigned char *s; /* string data */
+} str;
+
+#define COAP_SET_STR(st,l,v) { (st)->length = (l), (st)->s = (v); }
+
+/**
+ * Returns a new string object with at least size bytes storage
+ * allocated. The string must be released using coap_delete_string();
+ */
+str *coap_new_string(size_t size);
+
+/** Deletes the given string and releases any memory allocated. */
+void coap_delete_string(str *);
+
+#endif /* _COAP_STR_H_ */
diff --git a/app/coap/uri.c b/app/coap/uri.c
new file mode 100644
index 00000000..d93cf4dc
--- /dev/null
+++ b/app/coap/uri.c
@@ -0,0 +1,468 @@
+/* uri.c -- helper functions for URI treatment
+ */
+
+#include "c_stdio.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+#include "c_ctype.h"
+
+#include "coap.h"
+#include "uri.h"
+
+#ifndef assert
+// #warning "assertions are disabled"
+# define assert(x) do { \
+ if(!x) NODE_ERR("uri.c assert!\n"); \
+ } while (0)
+#endif
+
+
+/**
+ * A length-safe version of strchr(). This function returns a pointer
+ * to the first occurrence of @p c in @p s, or @c NULL if not found.
+ *
+ * @param s The string to search for @p c.
+ * @param len The length of @p s.
+ * @param c The character to search.
+ *
+ * @return A pointer to the first occurence of @p c, or @c NULL
+ * if not found.
+ */
+static inline unsigned char *
+strnchr(unsigned char *s, size_t len, unsigned char c) {
+ while (len && *s++ != c)
+ --len;
+
+ return len ? s : NULL;
+}
+
+int coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri) {
+ unsigned char *p, *q;
+ int secure = 0, res = 0;
+
+ if (!str_var || !uri)
+ return -1;
+
+ c_memset(uri, 0, sizeof(coap_uri_t));
+ uri->port = COAP_DEFAULT_PORT;
+
+ /* search for scheme */
+ p = str_var;
+ if (*p == '/') {
+ q = p;
+ goto path;
+ }
+
+ q = (unsigned char *)COAP_DEFAULT_SCHEME;
+ while (len && *q && tolower(*p) == *q) {
+ ++p; ++q; --len;
+ }
+
+ /* If q does not point to the string end marker '\0', the schema
+ * identifier is wrong. */
+ if (*q) {
+ res = -1;
+ goto error;
+ }
+
+ /* There might be an additional 's', indicating the secure version: */
+ if (len && (secure = tolower(*p) == 's')) {
+ ++p; --len;
+ }
+
+ q = (unsigned char *)"://";
+ while (len && *q && tolower(*p) == *q) {
+ ++p; ++q; --len;
+ }
+
+ if (*q) {
+ res = -2;
+ goto error;
+ }
+
+ /* p points to beginning of Uri-Host */
+ q = p;
+ if (len && *p == '[') { /* IPv6 address reference */
+ ++p;
+
+ while (len && *q != ']') {
+ ++q; --len;
+ }
+
+ if (!len || *q != ']' || p == q) {
+ res = -3;
+ goto error;
+ }
+
+ COAP_SET_STR(&uri->host, q - p, p);
+ ++q; --len;
+ } else { /* IPv4 address or FQDN */
+ while (len && *q != ':' && *q != '/' && *q != '?') {
+ *q = tolower(*q);
+ ++q;
+ --len;
+ }
+
+ if (p == q) {
+ res = -3;
+ goto error;
+ }
+
+ COAP_SET_STR(&uri->host, q - p, p);
+ }
+
+ /* check for Uri-Port */
+ if (len && *q == ':') {
+ p = ++q;
+ --len;
+
+ while (len && isdigit(*q)) {
+ ++q;
+ --len;
+ }
+
+ if (p < q) { /* explicit port number given */
+ int uri_port = 0;
+
+ while (p < q)
+ uri_port = uri_port * 10 + (*p++ - '0');
+
+ uri->port = uri_port;
+ }
+ }
+
+ path: /* at this point, p must point to an absolute path */
+
+ if (!len)
+ goto end;
+
+ if (*q == '/') {
+ p = ++q;
+ --len;
+
+ while (len && *q != '?') {
+ ++q;
+ --len;
+ }
+
+ if (p < q) {
+ COAP_SET_STR(&uri->path, q - p, p);
+ p = q;
+ }
+ }
+
+ /* Uri_Query */
+ if (len && *p == '?') {
+ ++p;
+ --len;
+ COAP_SET_STR(&uri->query, len, p);
+ len = 0;
+ }
+
+ end:
+ return len ? -1 : 0;
+
+ error:
+ return res;
+}
+
+/**
+ * Calculates decimal value from hexadecimal ASCII character given in
+ * @p c. The caller must ensure that @p c actually represents a valid
+ * heaxdecimal character, e.g. with isxdigit(3).
+ *
+ * @hideinitializer
+ */
+#define hexchar_to_dec(c) ((c) & 0x40 ? ((c) & 0x0F) + 9 : ((c) & 0x0F))
+
+/**
+ * Decodes percent-encoded characters while copying the string @p seg
+ * of size @p length to @p buf. The caller of this function must
+ * ensure that the percent-encodings are correct (i.e. the character
+ * '%' is always followed by two hex digits. and that @p buf provides
+ * sufficient space to hold the result. This function is supposed to
+ * be called by make_decoded_option() only.
+ *
+ * @param seg The segment to decode and copy.
+ * @param length Length of @p seg.
+ * @param buf The result buffer.
+ */
+void decode_segment(const unsigned char *seg, size_t length, unsigned char *buf) {
+
+ while (length--) {
+
+ if (*seg == '%') {
+ *buf = (hexchar_to_dec(seg[1]) << 4) + hexchar_to_dec(seg[2]);
+
+ seg += 2; length -= 2;
+ } else {
+ *buf = *seg;
+ }
+
+ ++buf; ++seg;
+ }
+}
+
+/**
+ * Runs through the given path (or query) segment and checks if
+ * percent-encodings are correct. This function returns @c -1 on error
+ * or the length of @p s when decoded.
+ */
+int check_segment(const unsigned char *s, size_t length) {
+
+ size_t n = 0;
+
+ while (length) {
+ if (*s == '%') {
+ if (length < 2 || !(isxdigit(s[1]) && isxdigit(s[2])))
+ return -1;
+
+ s += 2;
+ length -= 2;
+ }
+
+ ++s; ++n; --length;
+ }
+
+ return n;
+}
+
+/**
+ * Writes a coap option from given string @p s to @p buf. @p s should
+ * point to a (percent-encoded) path or query segment of a coap_uri_t
+ * object. The created option will have type @c 0, and the length
+ * parameter will be set according to the size of the decoded string.
+ * On success, this function returns the option's size, or a value
+ * less than zero on error. This function must be called from
+ * coap_split_path_impl() only.
+ *
+ * @param s The string to decode.
+ * @param length The size of the percent-encoded string @p s.
+ * @param buf The buffer to store the new coap option.
+ * @param buflen The maximum size of @p buf.
+ *
+ * @return The option's size, or @c -1 on error.
+ *
+ * @bug This function does not split segments that are bigger than 270
+ * bytes.
+ */
+int make_decoded_option(const unsigned char *s, size_t length,
+ unsigned char *buf, size_t buflen) {
+ int res;
+ size_t written;
+
+ if (!buflen) {
+ NODE_DBG("make_decoded_option(): buflen is 0!\n");
+ return -1;
+ }
+
+ res = check_segment(s, length);
+ if (res < 0)
+ return -1;
+
+ /* write option header using delta 0 and length res */
+ // written = coap_opt_setheader(buf, buflen, 0, res);
+ written = coap_buildOptionHeader(0, res, buf, buflen);
+
+ assert(written <= buflen);
+
+ if (!written) /* encoding error */
+ return -1;
+
+ buf += written; /* advance past option type/length */
+ buflen -= written;
+
+ if (buflen < (size_t)res) {
+ NODE_DBG("buffer too small for option\n");
+ return -1;
+ }
+
+ decode_segment(s, length, buf);
+
+ return written + res;
+}
+
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+typedef void (*segment_handler_t)(unsigned char *, size_t, void *);
+
+/**
+ * Splits the given string into segments. You should call one of the
+ * macros coap_split_path() or coap_split_query() instead.
+ *
+ * @param parse_iter The iterator used for tokenizing.
+ * @param h A handler that is called with every token.
+ * @param data Opaque data that is passed to @p h when called.
+ *
+ * @return The number of characters that have been parsed from @p s.
+ */
+size_t coap_split_path_impl(coap_parse_iterator_t *parse_iter,
+ segment_handler_t h, void *data) {
+ unsigned char *seg;
+ size_t length;
+
+ assert(parse_iter);
+ assert(h);
+
+ length = parse_iter->n;
+
+ while ( (seg = coap_parse_next(parse_iter)) ) {
+
+ /* any valid path segment is handled here: */
+ h(seg, parse_iter->segment_length, data);
+ }
+
+ return length - (parse_iter->n - parse_iter->segment_length);
+}
+
+struct pkt_scr {
+ coap_packet_t *pkt;
+ coap_rw_buffer_t *scratch;
+ int n;
+};
+
+void write_option(unsigned char *s, size_t len, void *data) {
+ struct pkt_scr *state = (struct pkt_scr *)data;
+ int res;
+ assert(state);
+
+ /* skip empty segments and those that consist of only one or two dots */
+ if (memcmp(s, "..", min(len,2)) == 0)
+ return;
+
+ res = check_segment(s, len);
+ if (res < 0){
+ NODE_DBG("not a valid segment\n");
+ return;
+ }
+
+ if (state->scratch->len < (size_t)res) {
+ NODE_DBG("buffer too small for option\n");
+ return;
+ }
+
+ decode_segment(s, len, state->scratch->p);
+
+ if (res > 0) {
+ state->pkt->opts[state->pkt->numopts].buf.p = state->scratch->p;
+ state->pkt->opts[state->pkt->numopts].buf.len = res;
+ state->scratch->p += res;
+ state->scratch->len -= res;
+ state->pkt->numopts++;
+ state->n++;
+ }
+}
+
+int coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
+ struct pkt_scr tmp = { pkt, scratch, 0 };
+ coap_parse_iterator_t pi;
+
+ coap_parse_iterator_init((unsigned char *)s, length,
+ '/', (unsigned char *)"?#", 2, &pi);
+ coap_split_path_impl(&pi, write_option, &tmp);
+
+ int i;
+ for(i=0;iopts[pkt->numopts - i - 1].num = COAP_OPTION_URI_PATH;
+ }
+
+ return tmp.n;
+}
+
+int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt, const unsigned char *s, size_t length) {
+ struct pkt_scr tmp = { pkt, scratch, 0 };
+ coap_parse_iterator_t pi;
+
+ coap_parse_iterator_init((unsigned char *)s, length,
+ '&', (unsigned char *)"#", 1, &pi);
+
+ coap_split_path_impl(&pi, write_option, &tmp);
+
+ int i;
+ for(i=0;iopts[pkt->numopts - i - 1].num = COAP_OPTION_URI_QUERY;
+ }
+
+ return tmp.n;
+}
+
+#define URI_DATA(uriobj) ((unsigned char *)(uriobj) + sizeof(coap_uri_t))
+
+coap_uri_t * coap_new_uri(const unsigned char *uri, unsigned int length) {
+ unsigned char *result;
+
+ result = (unsigned char *)c_malloc(length + 1 + sizeof(coap_uri_t));
+
+ if (!result)
+ return NULL;
+
+ c_memcpy(URI_DATA(result), uri, length);
+ URI_DATA(result)[length] = '\0'; /* make it zero-terminated */
+
+ if (coap_split_uri(URI_DATA(result), length, (coap_uri_t *)result) < 0) {
+ c_free(result);
+ return NULL;
+ }
+ return (coap_uri_t *)result;
+}
+
+/* iterator functions */
+
+coap_parse_iterator_t * coap_parse_iterator_init(unsigned char *s, size_t n,
+ unsigned char separator,
+ unsigned char *delim, size_t dlen,
+ coap_parse_iterator_t *pi) {
+ assert(pi);
+ assert(separator);
+
+ pi->separator = separator;
+ pi->delim = delim;
+ pi->dlen = dlen;
+ pi->pos = s;
+ pi->n = n;
+ pi->segment_length = 0;
+
+ return pi;
+}
+
+unsigned char * coap_parse_next(coap_parse_iterator_t *pi) {
+ unsigned char *p;
+
+ if (!pi)
+ return NULL;
+
+ /* proceed to the next segment */
+ pi->n -= pi->segment_length;
+ pi->pos += pi->segment_length;
+ pi->segment_length = 0;
+
+ /* last segment? */
+ if (!pi->n || strnchr(pi->delim, pi->dlen, *pi->pos)) {
+ pi->pos = NULL;
+ return NULL;
+ }
+
+ /* skip following separator (the first segment might not have one) */
+ if (*pi->pos == pi->separator) {
+ ++pi->pos;
+ --pi->n;
+ }
+
+ p = pi->pos;
+
+ while (pi->segment_length < pi->n && *p != pi->separator &&
+ !strnchr(pi->delim, pi->dlen, *p)) {
+ ++p;
+ ++pi->segment_length;
+ }
+
+ if (!pi->n) {
+ pi->pos = NULL;
+ pi->segment_length = 0;
+ }
+
+ return pi->pos;
+}
diff --git a/app/coap/uri.h b/app/coap/uri.h
new file mode 100644
index 00000000..da1ef28a
--- /dev/null
+++ b/app/coap/uri.h
@@ -0,0 +1,166 @@
+/* uri.h -- helper functions for URI treatment
+ *
+ * Copyright (C) 2010,2011 Olaf Bergmann
+ *
+ * This file is part of the CoAP library libcoap. Please see
+ * README for terms of use.
+ */
+
+#ifndef _COAP_URI_H_
+#define _COAP_URI_H_
+
+#define COAP_DEFAULT_SCHEME "coap" /* the default scheme for CoAP URIs */
+#define COAP_DEFAULT_PORT 5683
+
+#include "str.h"
+
+/** Representation of parsed URI. Components may be filled from a
+ * string with coap_split_uri() and can be used as input for
+ * option-creation functions. */
+typedef struct {
+ str host; /**< host part of the URI */
+ unsigned short port; /**< The port in host byte order */
+ str path; /**< Beginning of the first path segment.
+ Use coap_split_path() to create Uri-Path options */
+ str query; /**< The query part if present */
+} coap_uri_t;
+
+/**
+ * Creates a new coap_uri_t object from the specified URI. Returns the new
+ * object or NULL on error. The memory allocated by the new coap_uri_t
+ * must be released using coap_free().
+ * @param uri The URI path to copy.
+ * @para length The length of uri.
+ *
+ * @return New URI object or NULL on error.
+ */
+coap_uri_t *coap_new_uri(const unsigned char *uri, unsigned int length);
+
+/**
+ * @defgroup uri_parse URI Parsing Functions
+ *
+ * CoAP PDUs contain normalized URIs with their path and query split into
+ * multiple segments. The functions in this module help splitting strings.
+ * @{
+ */
+
+/**
+ * Iterator to for tokenizing a URI path or query. This structure must
+ * be initialized with coap_parse_iterator_init(). Call
+ * coap_parse_next() to walk through the tokens.
+ *
+ * @code
+ * unsigned char *token;
+ * coap_parse_iterator_t pi;
+ * coap_parse_iterator_init(uri.path.s, uri.path.length, '/', "?#", 2, &pi);
+ *
+ * while ((token = coap_parse_next(&pi))) {
+ * ... do something with token ...
+ * }
+ * @endcode
+ */
+typedef struct {
+ size_t n; /**< number of remaining characters in buffer */
+ unsigned char separator; /**< segment separators */
+ unsigned char *delim; /**< delimiters where to split the string */
+ size_t dlen; /**< length of separator */
+ unsigned char *pos; /**< current position in buffer */
+ size_t segment_length; /**< length of current segment */
+} coap_parse_iterator_t;
+
+/**
+ * Initializes the given iterator @p pi.
+ *
+ * @param s The string to tokenize.
+ * @param n The length of @p s.
+ * @param separator The separator character that delimits tokens.
+ * @param delim A set of characters that delimit @s.
+ * @param dlen The length of @p delim.
+ * @param pi The iterator object to initialize.
+ *
+ * @return The initialized iterator object @p pi.
+ */
+coap_parse_iterator_t *
+coap_parse_iterator_init(unsigned char *s, size_t n,
+ unsigned char separator,
+ unsigned char *delim, size_t dlen,
+ coap_parse_iterator_t *pi);
+
+/**
+ * Updates the iterator @p pi to point to the next token. This
+ * function returns a pointer to that token or @c NULL if no more
+ * tokens exist. The contents of @p pi will be updated. In particular,
+ * @c pi->segment_length specifies the length of the current token, @c
+ * pi->pos points to its beginning.
+ *
+ * @param pi The iterator to update.
+ *
+ * @return The next token or @c NULL if no more tokens exist.
+ */
+unsigned char *coap_parse_next(coap_parse_iterator_t *pi);
+
+/**
+ * Parses a given string into URI components. The identified syntactic
+ * components are stored in the result parameter @p uri. Optional URI
+ * components that are not specified will be set to { 0, 0 }, except
+ * for the port which is set to @c COAP_DEFAULT_PORT. This function
+ * returns @p 0 if parsing succeeded, a value less than zero
+ * otherwise.
+ *
+ * @param str_var The string to split up.
+ * @param len The actual length of @p str_var
+ * @param uri The coap_uri_t object to store the result.
+ * @return @c 0 on success, or < 0 on error.
+ *
+ * @note The host name part will be converted to lower case by this
+ * function.
+ */
+int
+coap_split_uri(unsigned char *str_var, size_t len, coap_uri_t *uri);
+
+/**
+ * Splits the given URI path into segments. Each segment is preceded
+ * by an option pseudo-header with delta-value 0 and the actual length
+ * of the respective segment after percent-decoding.
+ *
+ * @param s The path string to split.
+ * @param length The actual length of @p s.
+ * @param buf Result buffer for parsed segments.
+ * @param buflen Maximum length of @p buf. Will be set to the actual number
+ * of bytes written into buf on success.
+ *
+ * @return The number of segments created or @c -1 on error.
+ */
+#if 0
+int coap_split_path(const unsigned char *s, size_t length,
+ unsigned char *buf, size_t *buflen);
+#else
+int
+coap_split_path(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
+ const unsigned char *s, size_t length);
+#endif
+/**
+ * Splits the given URI query into segments. Each segment is preceded
+ * by an option pseudo-header with delta-value 0 and the actual length
+ * of the respective query term.
+ *
+ * @param s The query string to split.
+ * @param length The actual length of @p s.
+ * @param buf Result buffer for parsed segments.
+ * @param buflen Maximum length of @p buf. Will be set to the actual number
+ * of bytes written into buf on success.
+ *
+ * @return The number of segments created or @c -1 on error.
+ *
+ * @bug This function does not reserve additional space for delta > 12.
+ */
+#if 0
+int coap_split_query(const unsigned char *s, size_t length,
+ unsigned char *buf, size_t *buflen);
+#else
+int coap_split_query(coap_rw_buffer_t *scratch, coap_packet_t *pkt,
+ const unsigned char *s, size_t length);
+#endif
+/** @} */
+
+#endif /* _COAP_URI_H_ */
diff --git a/app/include/user_config.h b/app/include/user_config.h
index f3f36494..c1a9805c 100644
--- a/app/include/user_config.h
+++ b/app/include/user_config.h
@@ -1,14 +1,6 @@
#ifndef __USER_CONFIG_H__
#define __USER_CONFIG_H__
-#define NODE_VERSION_MAJOR 0U
-#define NODE_VERSION_MINOR 9U
-#define NODE_VERSION_REVISION 5U
-#define NODE_VERSION_INTERNAL 0U
-
-#define NODE_VERSION "NodeMCU 0.9.5"
-#define BUILD_DATE "build 20150214"
-
// #define DEVKIT_VERSION_0_9 1 // define this only if you use NodeMCU devkit v0.9
// #define FLASH_512K
@@ -25,6 +17,7 @@
#ifdef DEVELOP_VERSION
#define NODE_DEBUG
+#define COAP_DEBUG
#endif /* DEVELOP_VERSION */
#define NODE_ERROR
@@ -51,33 +44,6 @@
// #define BUILD_WOFS 1
#define BUILD_SPIFFS 1
-#define LUA_USE_MODULES
-
-#ifdef LUA_USE_MODULES
-#define LUA_USE_MODULES_NODE
-#define LUA_USE_MODULES_FILE
-#define LUA_USE_MODULES_GPIO
-#define LUA_USE_MODULES_WIFI
-#define LUA_USE_MODULES_NET
-#define LUA_USE_MODULES_PWM
-#define LUA_USE_MODULES_I2C
-#define LUA_USE_MODULES_SPI
-#define LUA_USE_MODULES_TMR
-#define LUA_USE_MODULES_ADC
-#define LUA_USE_MODULES_UART
-#define LUA_USE_MODULES_OW
-#define LUA_USE_MODULES_BIT
-#define LUA_USE_MODULES_U8G
-#define LUA_USE_MODULES_MQTT
-#define LUA_USE_MODULES_WS2812 // TODO: put this device specific module to device driver section.
-#endif /* LUA_USE_MODULES */
-
-// TODO: put device specific module to device driver section.
-#ifdef LUA_USE_DEVICE_DRIVER
-#define LUA_USE_DEVICE_WS2812
-#endif /* LUA_USE_DEVICE_DRIVER */
-
-
// #define LUA_NUMBER_INTEGRAL
#define LUA_OPTRAM
@@ -99,7 +65,6 @@
#define LED_LOW_COUNT_DEFAULT 0
#endif
-
// Configure U8glib fonts
// add a U8G_FONT_TABLE_ENTRY for each font you want to compile into the image
#define U8G_FONT_TABLE_ENTRY(font)
diff --git a/app/include/user_modules.h b/app/include/user_modules.h
new file mode 100644
index 00000000..a21cee19
--- /dev/null
+++ b/app/include/user_modules.h
@@ -0,0 +1,35 @@
+#ifndef __USER_MODULES_H__
+#define __USER_MODULES_H__
+
+#define LUA_USE_BUILTIN_STRING // for string.xxx()
+#define LUA_USE_BUILTIN_TABLE // for table.xxx()
+#define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx()
+#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
+// #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work
+
+// #define LUA_USE_BUILTIN_OS // for os.xxx(), not work
+// #define LUA_USE_BUILTIN_DEBUG // for debug.xxx(), not work
+
+#define LUA_USE_MODULES
+
+#ifdef LUA_USE_MODULES
+#define LUA_USE_MODULES_NODE
+#define LUA_USE_MODULES_FILE
+#define LUA_USE_MODULES_GPIO
+#define LUA_USE_MODULES_WIFI
+#define LUA_USE_MODULES_NET
+#define LUA_USE_MODULES_PWM
+#define LUA_USE_MODULES_I2C
+#define LUA_USE_MODULES_SPI
+#define LUA_USE_MODULES_TMR
+#define LUA_USE_MODULES_ADC
+#define LUA_USE_MODULES_UART
+#define LUA_USE_MODULES_OW
+#define LUA_USE_MODULES_BIT
+#define LUA_USE_MODULES_MQTT
+// #define LUA_USE_MODULES_COAP // need about 4k more ram for now
+#define LUA_USE_MODULES_U8G
+#define LUA_USE_MODULES_WS2812
+#endif /* LUA_USE_MODULES */
+
+#endif /* __USER_MODULES_H__ */
diff --git a/app/include/user_version.h b/app/include/user_version.h
new file mode 100644
index 00000000..c1144958
--- /dev/null
+++ b/app/include/user_version.h
@@ -0,0 +1,12 @@
+#ifndef __USER_VERSION_H__
+#define __USER_VERSION_H__
+
+#define NODE_VERSION_MAJOR 0U
+#define NODE_VERSION_MINOR 9U
+#define NODE_VERSION_REVISION 5U
+#define NODE_VERSION_INTERNAL 0U
+
+#define NODE_VERSION "NodeMCU 0.9.5"
+#define BUILD_DATE "build 20150306"
+
+#endif /* __USER_VERSION_H__ */
diff --git a/app/lua/lmathlib.c b/app/lua/lmathlib.c
index d9630518..a9f553bf 100644
--- a/app/lua/lmathlib.c
+++ b/app/lua/lmathlib.c
@@ -324,36 +324,36 @@ const LUA_REG_TYPE math_map[] = {
#endif
#else
{LSTRKEY("abs"), LFUNCVAL(math_abs)},
- {LSTRKEY("acos"), LFUNCVAL(math_acos)},
- {LSTRKEY("asin"), LFUNCVAL(math_asin)},
- {LSTRKEY("atan2"), LFUNCVAL(math_atan2)},
- {LSTRKEY("atan"), LFUNCVAL(math_atan)},
+ // {LSTRKEY("acos"), LFUNCVAL(math_acos)},
+ // {LSTRKEY("asin"), LFUNCVAL(math_asin)},
+ // {LSTRKEY("atan2"), LFUNCVAL(math_atan2)},
+ // {LSTRKEY("atan"), LFUNCVAL(math_atan)},
{LSTRKEY("ceil"), LFUNCVAL(math_ceil)},
- {LSTRKEY("cosh"), LFUNCVAL(math_cosh)},
- {LSTRKEY("cos"), LFUNCVAL(math_cos)},
- {LSTRKEY("deg"), LFUNCVAL(math_deg)},
- {LSTRKEY("exp"), LFUNCVAL(math_exp)},
+ // {LSTRKEY("cosh"), LFUNCVAL(math_cosh)},
+ // {LSTRKEY("cos"), LFUNCVAL(math_cos)},
+ // {LSTRKEY("deg"), LFUNCVAL(math_deg)},
+ // {LSTRKEY("exp"), LFUNCVAL(math_exp)},
{LSTRKEY("floor"), LFUNCVAL(math_floor)},
- {LSTRKEY("fmod"), LFUNCVAL(math_fmod)},
+ // {LSTRKEY("fmod"), LFUNCVAL(math_fmod)},
#if LUA_OPTIMIZE_MEMORY > 0 && defined(LUA_COMPAT_MOD)
{LSTRKEY("mod"), LFUNCVAL(math_fmod)},
#endif
- {LSTRKEY("frexp"), LFUNCVAL(math_frexp)},
- {LSTRKEY("ldexp"), LFUNCVAL(math_ldexp)},
- {LSTRKEY("log10"), LFUNCVAL(math_log10)},
- {LSTRKEY("log"), LFUNCVAL(math_log)},
+ // {LSTRKEY("frexp"), LFUNCVAL(math_frexp)},
+ // {LSTRKEY("ldexp"), LFUNCVAL(math_ldexp)},
+ // {LSTRKEY("log10"), LFUNCVAL(math_log10)},
+ // {LSTRKEY("log"), LFUNCVAL(math_log)},
{LSTRKEY("max"), LFUNCVAL(math_max)},
{LSTRKEY("min"), LFUNCVAL(math_min)},
- {LSTRKEY("modf"), LFUNCVAL(math_modf)},
+ // {LSTRKEY("modf"), LFUNCVAL(math_modf)},
{LSTRKEY("pow"), LFUNCVAL(math_pow)},
- {LSTRKEY("rad"), LFUNCVAL(math_rad)},
+ // {LSTRKEY("rad"), LFUNCVAL(math_rad)},
{LSTRKEY("random"), LFUNCVAL(math_random)},
{LSTRKEY("randomseed"), LFUNCVAL(math_randomseed)},
- {LSTRKEY("sinh"), LFUNCVAL(math_sinh)},
- {LSTRKEY("sin"), LFUNCVAL(math_sin)},
+ // {LSTRKEY("sinh"), LFUNCVAL(math_sinh)},
+ // {LSTRKEY("sin"), LFUNCVAL(math_sin)},
{LSTRKEY("sqrt"), LFUNCVAL(math_sqrt)},
- {LSTRKEY("tanh"), LFUNCVAL(math_tanh)},
- {LSTRKEY("tan"), LFUNCVAL(math_tan)},
+ // {LSTRKEY("tanh"), LFUNCVAL(math_tanh)},
+ // {LSTRKEY("tan"), LFUNCVAL(math_tan)},
#if LUA_OPTIMIZE_MEMORY > 0
{LSTRKEY("pi"), LNUMVAL(PI)},
{LSTRKEY("huge"), LNUMVAL(HUGE_VAL)},
diff --git a/app/lua/lua.c b/app/lua/lua.c
index 27bb3b4b..d7d14f40 100644
--- a/app/lua/lua.c
+++ b/app/lua/lua.c
@@ -10,6 +10,7 @@
#include "c_stdlib.h"
#include "c_string.h"
#include "flash_fs.h"
+#include "user_version.h"
#define lua_c
diff --git a/app/lua/lualib.h b/app/lua/lualib.h
index a475d887..9e4e7aea 100644
--- a/app/lua/lualib.h
+++ b/app/lua/lualib.h
@@ -24,17 +24,17 @@ LUALIB_API int (luaopen_table) (lua_State *L);
#define LUA_IOLIBNAME "io"
LUALIB_API int (luaopen_io) (lua_State *L);
-// #define LUA_OSLIBNAME "os"
-// LUALIB_API int (luaopen_os) (lua_State *L);
+#define LUA_OSLIBNAME "os"
+LUALIB_API int (luaopen_os) (lua_State *L);
#define LUA_STRLIBNAME "string"
LUALIB_API int (luaopen_string) (lua_State *L);
-// #define LUA_MATHLIBNAME "math"
-// LUALIB_API int (luaopen_math) (lua_State *L);
+#define LUA_MATHLIBNAME "math"
+LUALIB_API int (luaopen_math) (lua_State *L);
-// #define LUA_DBLIBNAME "debug"
-// LUALIB_API int (luaopen_debug) (lua_State *L);
+#define LUA_DBLIBNAME "debug"
+LUALIB_API int (luaopen_debug) (lua_State *L);
#define LUA_LOADLIBNAME "package"
LUALIB_API int (luaopen_package) (lua_State *L);
diff --git a/app/modules/Makefile b/app/modules/Makefile
index ecb8b880..7d2ae1cc 100644
--- a/app/modules/Makefile
+++ b/app/modules/Makefile
@@ -39,6 +39,7 @@ endif
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../libc
+INCLUDES += -I ../coap
INCLUDES += -I ../mqtt
INCLUDES += -I ../u8glib
INCLUDES += -I ../lua
diff --git a/app/modules/auxmods.h b/app/modules/auxmods.h
index 175a1f2b..2b09448a 100644
--- a/app/modules/auxmods.h
+++ b/app/modules/auxmods.h
@@ -61,6 +61,9 @@ LUALIB_API int ( luaopen_i2c )( lua_State *L );
#define AUXLIB_WIFI "wifi"
LUALIB_API int ( luaopen_wifi )( lua_State *L );
+#define AUXLIB_COAP "coap"
+LUALIB_API int ( luaopen_coap )( lua_State *L );
+
#define AUXLIB_MQTT "mqtt"
LUALIB_API int ( luaopen_mqtt )( lua_State *L );
diff --git a/app/modules/coap.c b/app/modules/coap.c
new file mode 100644
index 00000000..2afb9b54
--- /dev/null
+++ b/app/modules/coap.c
@@ -0,0 +1,646 @@
+// Module for coapwork
+
+//#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+
+#include "c_string.h"
+#include "c_stdlib.h"
+
+#include "c_types.h"
+#include "mem.h"
+#include "espconn.h"
+#include "driver/uart.h"
+
+#include "coap.h"
+#include "uri.h"
+#include "node.h"
+#include "coap_timer.h"
+#include "coap_io.h"
+#include "coap_server.h"
+
+coap_queue_t *gQueue = NULL;
+
+typedef struct lcoap_userdata
+{
+ lua_State *L;
+ struct espconn *pesp_conn;
+ int self_ref;
+}lcoap_userdata;
+
+static void coap_received(void *arg, char *pdata, unsigned short len)
+{
+ NODE_DBG("coap_received is called.\n");
+ struct espconn *pesp_conn = arg;
+ lcoap_userdata *cud = (lcoap_userdata *)pesp_conn->reverse;
+
+ static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
+ c_memset(buf, 0, sizeof(buf)); // wipe prev data
+
+ if (len > MAX_MESSAGE_SIZE) {
+ NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
+ return;
+ } else {
+ c_memcpy(buf, pdata, len);
+ }
+
+ size_t rsplen = coap_server_respond(buf, len, MAX_MESSAGE_SIZE+1);
+ espconn_sent(pesp_conn, (unsigned char *)buf, rsplen);
+
+ c_memset(buf, 0, sizeof(buf));
+}
+
+static void coap_sent(void *arg)
+{
+ NODE_DBG("coap_sent is called.\n");
+}
+
+// Lua: s = coap.create(function(conn))
+static int coap_create( lua_State* L, const char* mt )
+{
+ struct espconn *pesp_conn = NULL;
+ lcoap_userdata *cud;
+ unsigned type;
+ int stack = 1;
+
+ // create a object
+ cud = (lcoap_userdata *)lua_newuserdata(L, sizeof(lcoap_userdata));
+ // pre-initialize it, in case of errors
+ cud->self_ref = LUA_NOREF;
+ cud->pesp_conn = NULL;
+
+ // set its metatable
+ luaL_getmetatable(L, mt);
+ lua_setmetatable(L, -2);
+
+ // create the espconn struct
+ pesp_conn = (struct espconn *)c_zalloc(sizeof(struct espconn));
+ if(!pesp_conn)
+ return luaL_error(L, "not enough memory");
+
+ cud->pesp_conn = pesp_conn;
+
+ pesp_conn->type = ESPCONN_UDP;
+ pesp_conn->proto.tcp = NULL;
+ pesp_conn->proto.udp = NULL;
+
+ pesp_conn->proto.udp = (esp_udp *)c_zalloc(sizeof(esp_udp));
+ if(!pesp_conn->proto.udp){
+ c_free(pesp_conn);
+ cud->pesp_conn = pesp_conn = NULL;
+ return luaL_error(L, "not enough memory");
+ }
+ pesp_conn->state = ESPCONN_NONE;
+ NODE_DBG("UDP server/client is set.\n");
+
+ cud->L = L;
+ pesp_conn->reverse = cud;
+
+ NODE_DBG("coap_create is called.\n");
+ return 1;
+}
+
+// Lua: server:delete()
+static int coap_delete( lua_State* L, const char* mt )
+{
+ struct espconn *pesp_conn = NULL;
+ lcoap_userdata *cud;
+
+ cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, cud, 1, "Server/Client expected");
+ if(cud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ // free (unref) callback ref
+ if(LUA_NOREF!=cud->self_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, cud->self_ref);
+ cud->self_ref = LUA_NOREF;
+ }
+
+ cud->L = NULL;
+ if(cud->pesp_conn)
+ {
+ if(cud->pesp_conn->proto.udp->remote_port || cud->pesp_conn->proto.udp->local_port)
+ espconn_delete(cud->pesp_conn);
+ c_free(cud->pesp_conn->proto.udp);
+ cud->pesp_conn->proto.udp = NULL;
+ c_free(cud->pesp_conn);
+ cud->pesp_conn = NULL;
+ }
+
+ NODE_DBG("coap_delete is called.\n");
+ return 0;
+}
+
+// Lua: server:listen( port, ip )
+static int coap_start( lua_State* L, const char* mt )
+{
+ struct espconn *pesp_conn = NULL;
+ lcoap_userdata *cud;
+ unsigned port;
+ size_t il;
+ ip_addr_t ipaddr;
+
+ cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, cud, 1, "Server/Client expected");
+ if(cud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ pesp_conn = cud->pesp_conn;
+ port = luaL_checkinteger( L, 2 );
+ pesp_conn->proto.udp->local_port = port;
+ NODE_DBG("UDP port is set: %d.\n", port);
+
+ if( lua_isstring(L,3) ) // deal with the ip string
+ {
+ const char *ip = luaL_checklstring( L, 3, &il );
+ if (ip == NULL)
+ {
+ ip = "0.0.0.0";
+ }
+ ipaddr.addr = ipaddr_addr(ip);
+ c_memcpy(pesp_conn->proto.udp->local_ip, &ipaddr.addr, 4);
+ NODE_DBG("UDP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
+ NODE_DBG("\n");
+ }
+
+ if(LUA_NOREF==cud->self_ref){
+ lua_pushvalue(L, 1); // copy to the top of stack
+ cud->self_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ espconn_regist_recvcb(pesp_conn, coap_received);
+ espconn_regist_sentcb(pesp_conn, coap_sent);
+ espconn_create(pesp_conn);
+
+ NODE_DBG("Coap Server started on port: %d\n", port);
+ NODE_DBG("coap_start is called.\n");
+ return 0;
+}
+
+// Lua: server:close()
+static int coap_close( lua_State* L, const char* mt )
+{
+ struct espconn *pesp_conn = NULL;
+ lcoap_userdata *cud;
+
+ cud = (lcoap_userdata *)luaL_checkudata(L, 1, mt);
+ luaL_argcheck(L, cud, 1, "Server/Client expected");
+ if(cud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ if(cud->pesp_conn)
+ {
+ if(cud->pesp_conn->proto.udp->remote_port || cud->pesp_conn->proto.udp->local_port)
+ espconn_delete(cud->pesp_conn);
+ }
+
+ if(LUA_NOREF!=cud->self_ref){
+ luaL_unref(L, LUA_REGISTRYINDEX, cud->self_ref);
+ cud->self_ref = LUA_NOREF;
+ }
+
+ NODE_DBG("coap_close is called.\n");
+ return 0;
+}
+
+// Lua: server/client:on( "method", function(s) )
+static int coap_on( lua_State* L, const char* mt )
+{
+ NODE_DBG("coap_on is called.\n");
+ return 0;
+}
+
+static void coap_response_handler(void *arg, char *pdata, unsigned short len)
+{
+ NODE_DBG("coap_response_handler is called.\n");
+ struct espconn *pesp_conn = arg;
+
+ coap_packet_t pkt;
+ static uint8_t buf[MAX_MESSAGE_SIZE+1] = {0}; // +1 for string '\0'
+ c_memset(buf, 0, sizeof(buf)); // wipe prev data
+ static int n = 0;
+
+ int rc;
+ if ((len == 1460) && (1460 <= MAX_MESSAGE_SIZE)){
+ c_memcpy(buf, pdata, len); // max length is 1460, another part of data is coming in next callback
+ n = len;
+ return;
+ } else {
+ if( len > MAX_MESSAGE_SIZE )
+ {
+ NODE_DBG("Request Entity Too Large.\n"); // NOTE: should response 4.13 to client...
+ c_memset(buf, 0, sizeof(buf)); // wipe prev data
+ n = 0;
+ return;
+ }
+ c_memcpy(buf + n, pdata, len);
+ len += n; // more than 1460
+ }
+
+ if (0 != (rc = coap_parse(&pkt, buf, len))){
+ NODE_DBG("Bad packet rc=%d\n", rc);
+ }
+ else
+ {
+#ifdef COAP_DEBUG
+ coap_dumpPacket(&pkt);
+#endif
+ /* check if this is a response to our original request */
+ if (!check_token(&pkt)) {
+ /* drop if this was just some message, or send RST in case of notification */
+ if (pkt.hdr.t == COAP_TYPE_CON || pkt.hdr.t == COAP_TYPE_NONCON){
+ // coap_send_rst(pkt); // send RST response
+ // or, just ignore it.
+ }
+ goto end;
+ }
+
+ if (pkt.hdr.t == COAP_TYPE_RESET) {
+ NODE_DBG("got RST\n");
+ goto end;
+ }
+
+ uint32_t ip = 0, port = 0;
+ coap_tid_t id = COAP_INVALID_TID;
+
+ c_memcpy(&ip, pesp_conn->proto.udp->remote_ip, sizeof(ip));
+ port = pesp_conn->proto.udp->remote_port;
+
+ coap_transaction_id(ip, port, &pkt, &id);
+
+ /* transaction done, remove the node from queue */
+ // stop timer
+ coap_timer_stop();
+ // remove the node
+ coap_remove_node(&gQueue, id);
+ // calculate time elapsed
+ coap_timer_update(&gQueue);
+ coap_timer_start(&gQueue);
+
+ if (COAP_RESPONSE_CLASS(pkt.hdr.code) == 2)
+ {
+ /* There is no block option set, just read the data and we are done. */
+ NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
+ NODE_DBG((char *)pkt.payload.p);
+ }
+ else if (COAP_RESPONSE_CLASS(pkt.hdr.code) >= 4)
+ {
+ NODE_DBG("%d.%02d\t", (pkt.hdr.code >> 5), pkt.hdr.code & 0x1F);
+ NODE_DBG((char *)pkt.payload.p);
+ }
+ }
+
+end:
+ if(!gQueue){ // if there is no node pending in the queue, disconnect from host.
+ if(pesp_conn->proto.udp->remote_port || pesp_conn->proto.udp->local_port)
+ espconn_delete(pesp_conn);
+ }
+ c_memset(buf, 0, sizeof(buf));
+ n = 0;
+}
+
+// Lua: client:request( [CON], uri, [payload] )
+static int coap_request( lua_State* L, coap_method_t m )
+{
+ struct espconn *pesp_conn = NULL;
+ lcoap_userdata *cud;
+ int stack = 1;
+
+ cud = (lcoap_userdata *)luaL_checkudata(L, stack, "coap_client");
+ luaL_argcheck(L, cud, stack, "Server/Client expected");
+ if(cud==NULL){
+ NODE_DBG("userdata is nil.\n");
+ return 0;
+ }
+
+ stack++;
+ pesp_conn = cud->pesp_conn;
+ ip_addr_t ipaddr;
+ uint8_t host[32];
+
+ unsigned t;
+ if ( lua_isnumber(L, stack) )
+ {
+ t = lua_tointeger(L, stack);
+ stack++;
+ if ( t != COAP_TYPE_CON && t != COAP_TYPE_NONCON )
+ return luaL_error( L, "wrong arg type" );
+ } else {
+ t = COAP_TYPE_CON; // default to CON
+ }
+
+ size_t l;
+ const char *url = luaL_checklstring( L, stack, &l );
+ stack++;
+ if (url == NULL)
+ return luaL_error( L, "wrong arg type" );
+
+ coap_uri_t *uri = coap_new_uri(url, l); // should call free(uri) somewhere
+ if (uri == NULL)
+ return luaL_error( L, "uri wrong format." );
+
+ pesp_conn->proto.udp->remote_port = uri->port;
+ NODE_DBG("UDP port is set: %d.\n", uri->port);
+ pesp_conn->proto.udp->local_port = espconn_port();
+
+ if(uri->host.length){
+ c_memcpy(host, uri->host.s, uri->host.length);
+ host[uri->host.length] = '\0';
+
+ ipaddr.addr = ipaddr_addr(host);
+ NODE_DBG("Host len(%d):", uri->host.length);
+ NODE_DBG(host);
+ NODE_DBG("\n");
+
+ c_memcpy(pesp_conn->proto.udp->remote_ip, &ipaddr.addr, 4);
+ NODE_DBG("UDP ip is set: ");
+ NODE_DBG(IPSTR, IP2STR(&ipaddr.addr));
+ NODE_DBG("\n");
+ }
+
+ coap_pdu_t *pdu = coap_new_pdu(); // should call coap_delete_pdu() somewhere
+ if(!pdu){
+ if(uri)
+ c_free(uri);
+ return;
+ }
+
+ const char *payload = NULL;
+ l = 0;
+ if( lua_isstring(L, stack) ){
+ payload = luaL_checklstring( L, stack, &l );
+ if (payload == NULL)
+ l = 0;
+ }
+
+ coap_make_request(&(pdu->scratch), pdu->pkt, t, m, uri, payload, l);
+
+#ifdef COAP_DEBUG
+ coap_dumpPacket(pdu->pkt);
+#endif
+
+ int rc;
+ if (0 != (rc = coap_build(pdu->msg.p, &(pdu->msg.len), pdu->pkt))){
+ NODE_DBG("coap_build failed rc=%d\n", rc);
+ }
+ else
+ {
+#ifdef COAP_DEBUG
+ NODE_DBG("Sending: ");
+ coap_dump(pdu->msg.p, pdu->msg.len, true);
+ NODE_DBG("\n");
+#endif
+ espconn_regist_recvcb(pesp_conn, coap_response_handler);
+ sint8_t con = espconn_create(pesp_conn);
+ if( ESPCONN_OK != con){
+ NODE_DBG("Connect to host. code:%d\n", con);
+ // coap_delete_pdu(pdu);
+ }
+ // else
+ {
+ coap_tid_t tid = COAP_INVALID_TID;
+ if (pdu->pkt->hdr.t == COAP_TYPE_CON){
+ tid = coap_send_confirmed(pesp_conn, pdu);
+ }
+ else {
+ tid = coap_send(pesp_conn, pdu);
+ }
+ if (pdu->pkt->hdr.t != COAP_TYPE_CON || tid == COAP_INVALID_TID){
+ coap_delete_pdu(pdu);
+ }
+ }
+ }
+
+ if(uri)
+ c_free((void *)uri);
+
+ NODE_DBG("coap_request is called.\n");
+ return 0;
+}
+
+extern coap_luser_entry *variable_entry;
+extern coap_luser_entry *function_entry;
+extern void build_well_known_rsp(void);
+// Lua: coap:var/func( string )
+static int coap_regist( lua_State* L, const char* mt, int isvar )
+{
+ size_t l;
+ const char *name = luaL_checklstring( L, 2, &l );
+ if (name == NULL)
+ return luaL_error( L, "name must be set." );
+
+ coap_luser_entry *h = NULL;
+ // if(lua_isstring(L, 3))
+ if(isvar)
+ h = variable_entry;
+ else
+ h = function_entry;
+
+ while(NULL!=h->next){ // goto the end of the list
+ if(h->name!= NULL && c_strcmp(h->name, name)==0) // key exist, override it
+ break;
+ h = h->next;
+ }
+
+ if(h->name==NULL || c_strcmp(h->name, name)!=0){ // not exists. make a new one.
+ h->next = (coap_luser_entry *)c_zalloc(sizeof(coap_luser_entry));
+ h = h->next;
+ if(h == NULL)
+ return luaL_error(L, "not enough memory");
+ h->next = NULL;
+ h->name = NULL;
+ h->L = NULL;
+ }
+
+ h->L = L;
+ h->name = name;
+
+ build_well_known_rsp(); // rebuild .well-known
+
+ NODE_DBG("coap_regist is called.\n");
+ return 0;
+}
+
+// Lua: s = coap.createServer(function(conn))
+static int coap_createServer( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_create(L, mt);
+}
+
+// Lua: server:delete()
+static int coap_server_delete( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_delete(L, mt);
+}
+
+// Lua: server:listen( port, ip, function(err) )
+static int coap_server_listen( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_start(L, mt);
+}
+
+// Lua: server:close()
+static int coap_server_close( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_close(L, mt);
+}
+
+// Lua: server:on( "method", function(server) )
+static int coap_server_on( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_on(L, mt);
+}
+
+// Lua: server:var( "name" )
+static int coap_server_var( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_regist(L, mt, 1);
+}
+
+// Lua: server:func( "name" )
+static int coap_server_func( lua_State* L )
+{
+ const char *mt = "coap_server";
+ return coap_regist(L, mt, 0);
+}
+
+// Lua: s = coap.createClient(function(conn))
+static int coap_createClient( lua_State* L )
+{
+ const char *mt = "coap_client";
+ return coap_create(L, mt);
+}
+
+// Lua: client:gcdelete()
+static int coap_client_gcdelete( lua_State* L )
+{
+ const char *mt = "coap_client";
+ return coap_delete(L, mt);
+}
+
+// client:get( string, function(sent) )
+static int coap_client_get( lua_State* L )
+{
+ return coap_request(L, COAP_METHOD_GET);
+}
+
+// client:post( string, function(sent) )
+static int coap_client_post( lua_State* L )
+{
+ return coap_request(L, COAP_METHOD_POST);
+}
+
+// client:put( string, function(sent) )
+static int coap_client_put( lua_State* L )
+{
+ return coap_request(L, COAP_METHOD_PUT);
+}
+
+// client:delete( string, function(sent) )
+static int coap_client_delete( lua_State* L )
+{
+ return coap_request(L, COAP_METHOD_DELETE);
+}
+
+// Module function map
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+static const LUA_REG_TYPE coap_server_map[] =
+{
+ { LSTRKEY( "listen" ), LFUNCVAL ( coap_server_listen ) },
+ { LSTRKEY( "close" ), LFUNCVAL ( coap_server_close ) },
+ { LSTRKEY( "var" ), LFUNCVAL ( coap_server_var ) },
+ { LSTRKEY( "func" ), LFUNCVAL ( coap_server_func ) },
+ { LSTRKEY( "__gc" ), LFUNCVAL ( coap_server_delete ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "__index" ), LROVAL ( coap_server_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+static const LUA_REG_TYPE coap_client_map[] =
+{
+ { LSTRKEY( "get" ), LFUNCVAL ( coap_client_get ) },
+ { LSTRKEY( "post" ), LFUNCVAL ( coap_client_post ) },
+ { LSTRKEY( "put" ), LFUNCVAL ( coap_client_put ) },
+ { LSTRKEY( "delete" ), LFUNCVAL ( coap_client_delete ) },
+ { LSTRKEY( "__gc" ), LFUNCVAL ( coap_client_gcdelete ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "__index" ), LROVAL ( coap_client_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+const LUA_REG_TYPE coap_map[] =
+{
+ { LSTRKEY( "Server" ), LFUNCVAL ( coap_createServer ) },
+ { LSTRKEY( "Client" ), LFUNCVAL ( coap_createClient ) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "CON" ), LNUMVAL( COAP_TYPE_CON ) },
+ { LSTRKEY( "NON" ), LNUMVAL( COAP_TYPE_NONCON ) },
+
+ { LSTRKEY( "__metatable" ), LROVAL( coap_map ) },
+#endif
+ { LNILKEY, LNILVAL }
+};
+
+LUALIB_API int luaopen_coap( lua_State *L )
+{
+ endpoint_setup();
+#if LUA_OPTIMIZE_MEMORY > 0
+ luaL_rometatable(L, "coap_server", (void *)coap_server_map); // create metatable for coap_server
+ luaL_rometatable(L, "coap_client", (void *)coap_client_map); // create metatable for coap_client
+ return 0;
+#else // #if LUA_OPTIMIZE_MEMORY > 0
+ int n;
+ luaL_register( L, AUXLIB_COAP, coap_map );
+
+ // Set it as its own metatable
+ lua_pushvalue( L, -1 );
+ lua_setmetatable( L, -2 );
+
+ // Module constants
+ MOD_REG_NUMBER( L, "CON", COAP_TYPE_CON );
+ MOD_REG_NUMBER( L, "NON", COAP_TYPE_NONCON );
+
+ n = lua_gettop(L);
+
+ // create metatable
+ luaL_newmetatable(L, "coap_server");
+ // metatable.__index = metatable
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L,-2);
+ lua_rawset(L,-3);
+ // Setup the methods inside metatable
+ luaL_register( L, NULL, coap_server_map );
+
+ lua_settop(L, n);
+ // create metatable
+ luaL_newmetatable(L, "coap_client");
+ // metatable.__index = metatable
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L,-2);
+ lua_rawset(L,-3);
+ // Setup the methods inside metatable
+ luaL_register( L, NULL, coap_client_map );
+
+ return 1;
+#endif // #if LUA_OPTIMIZE_MEMORY > 0
+}
diff --git a/app/lua/linit.c b/app/modules/linit.c
similarity index 59%
rename from app/lua/linit.c
rename to app/modules/linit.c
index 872321ef..8bdbedfe 100644
--- a/app/lua/linit.c
+++ b/app/modules/linit.c
@@ -15,6 +15,8 @@
#include "lrotable.h"
#include "luaconf.h"
+#include "user_modules.h"
+
#if defined(LUA_USE_MODULES)
#include "modules.h"
#endif
@@ -28,12 +30,26 @@ LUA_MODULES_ROM
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
- // {LUA_IOLIBNAME, luaopen_io},
- {LUA_STRLIBNAME, luaopen_string},
+#if defined(LUA_USE_BUILTIN_IO)
+ {LUA_IOLIBNAME, luaopen_io},
+#endif
+
+#if defined(LUA_USE_BUILTIN_STRING)
+ {LUA_STRLIBNAME, luaopen_string},
+#endif
+
#if LUA_OPTIMIZE_MEMORY == 0
- // {LUA_MATHLIBNAME, luaopen_math},
+ #if defined(LUA_USE_BUILTIN_MATH)
+ {LUA_MATHLIBNAME, luaopen_math},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_TABLE)
{LUA_TABLIBNAME, luaopen_table},
- // {LUA_DBLIBNAME, luaopen_debug},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_DEBUG)
+ {LUA_DBLIBNAME, luaopen_debug},
+ #endif
#endif
#if defined(LUA_MODULES_ROM)
#undef _ROM
@@ -43,12 +59,30 @@ static const luaL_Reg lualibs[] = {
{NULL, NULL}
};
+#if defined(LUA_USE_BUILTIN_STRING)
extern const luaR_entry strlib[];
+#endif
+
+#if defined(LUA_USE_BUILTIN_OS)
extern const luaR_entry syslib[];
+#endif
+
+#if defined(LUA_USE_BUILTIN_TABLE)
extern const luaR_entry tab_funcs[];
-// extern const luaR_entry dblib[];
+#endif
+
+#if defined(LUA_USE_BUILTIN_DEBUG)
+extern const luaR_entry dblib[];
+#endif
+
+#if defined(LUA_USE_BUILTIN_COROUTINE)
extern const luaR_entry co_funcs[];
-// extern const luaR_entry math_map[];
+#endif
+
+#if defined(LUA_USE_BUILTIN_MATH)
+extern const luaR_entry math_map[];
+#endif
+
#if defined(LUA_MODULES_ROM) && LUA_OPTIMIZE_MEMORY == 2
#undef _ROM
#define _ROM( name, openf, table ) extern const luaR_entry table[];
@@ -57,11 +91,30 @@ LUA_MODULES_ROM
const luaR_table lua_rotable[] =
{
#if LUA_OPTIMIZE_MEMORY > 0
+ #if defined(LUA_USE_BUILTIN_STRING)
{LUA_STRLIBNAME, strlib},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_TABLE)
{LUA_TABLIBNAME, tab_funcs},
- // {LUA_DBLIBNAME, dblib},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_DEBUG)
+ {LUA_DBLIBNAME, dblib},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_COROUTINE)
{LUA_COLIBNAME, co_funcs},
- // {LUA_MATHLIBNAME, math_map},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_MATH)
+ {LUA_MATHLIBNAME, math_map},
+ #endif
+
+ #if defined(LUA_USE_BUILTIN_OS)
+ {LUA_OSLIBNAME, syslib},
+ #endif
+
#if defined(LUA_MODULES_ROM) && LUA_OPTIMIZE_MEMORY == 2
#undef _ROM
#define _ROM( name, openf, table ) { name, table },
diff --git a/app/modules/modules.h b/app/modules/modules.h
index 0730fd74..3f077efd 100644
--- a/app/modules/modules.h
+++ b/app/modules/modules.h
@@ -37,6 +37,14 @@
#define ROM_MODULES_NET
#endif
+#if defined(LUA_USE_MODULES_COAP)
+#define MODULES_COAP "coap"
+#define ROM_MODULES_COAP \
+ _ROM(MODULES_COAP, luaopen_coap, coap_map)
+#else
+#define ROM_MODULES_COAP
+#endif
+
#if defined(LUA_USE_MODULES_MQTT)
#define MODULES_MQTT "mqtt"
#define ROM_MODULES_MQTT \
@@ -136,8 +144,9 @@
#define LUA_MODULES_ROM \
ROM_MODULES_GPIO \
- ROM_MODULES_PWM \
- ROM_MODULES_WIFI \
+ ROM_MODULES_PWM \
+ ROM_MODULES_WIFI \
+ ROM_MODULES_COAP \
ROM_MODULES_MQTT \
ROM_MODULES_U8G \
ROM_MODULES_I2C \
diff --git a/app/modules/mqtt.c b/app/modules/mqtt.c
index 13b182bc..018e034e 100644
--- a/app/modules/mqtt.c
+++ b/app/modules/mqtt.c
@@ -1102,7 +1102,7 @@ const LUA_REG_TYPE mqtt_map[] =
{ LNILKEY, LNILVAL }
};
-LUALIB_API int ICACHE_FLASH_ATTR luaopen_mqtt( lua_State *L )
+LUALIB_API int luaopen_mqtt( lua_State *L )
{
#if LUA_OPTIMIZE_MEMORY > 0
luaL_rometatable(L, "mqtt.socket", (void *)mqtt_socket_map); // create metatable for mqtt.socket
diff --git a/app/modules/node.c b/app/modules/node.c
index 90dd0d96..0384d798 100644
--- a/app/modules/node.c
+++ b/app/modules/node.c
@@ -23,6 +23,7 @@
#include "user_interface.h"
#include "flash_api.h"
#include "flash_fs.h"
+#include "user_version.h"
// Lua: restart()
static int node_restart( lua_State* L )
diff --git a/examples/fragment.lua b/examples/fragment.lua
index bceadaf3..c3b980dc 100644
--- a/examples/fragment.lua
+++ b/examples/fragment.lua
@@ -347,3 +347,19 @@ file.close()
node.compile("hello.lua")
dofile("hello.lua")
dofile("hello.lc")
+
+-- use copper addon for firefox
+cs=coap.Server()
+cs:listen(5683)
+
+myvar=1
+cs:var("myvar") -- get coap://192.168.18.103:5683/v1/v/myvar will return the value of myvar: 1
+
+function myfun()
+ print("myfun called")
+end
+cs:func("myfun") -- post coap://192.168.18.103:5683/v1/f/myfun will call myfun
+
+cc = coap.Client()
+cc:get(coap.CON, "coap://192.168.18.100:5683/.well-known/core")
+cc:post(coap.NON, "coap://192.168.18.100:5683/", "Hello")
diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld
index d6e37116..141cab6d 100644
--- a/ld/eagle.app.v6.ld
+++ b/ld/eagle.app.v6.ld
@@ -5,7 +5,7 @@ MEMORY
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
- irom0_0_seg : org = 0x40210000, len = 0x55000
+ irom0_0_seg : org = 0x40210000, len = 0x5A000
}
PHDRS
diff --git a/lib/libm.a b/lib/libm.a
new file mode 100644
index 00000000..bf04ff11
Binary files /dev/null and b/lib/libm.a differ
diff --git a/lua_modules/hdc1000/HDC1000-example.lua b/lua_modules/hdc1000/HDC1000-example.lua
new file mode 100644
index 00000000..df7dbd27
--- /dev/null
+++ b/lua_modules/hdc1000/HDC1000-example.lua
@@ -0,0 +1,12 @@
+HDC1000 = require("HDC1000")
+
+sda = 1
+scl = 2
+
+HDC1000.init(sda, scl)
+HDC1000.config() -- default values are used if called with no arguments. prototype is config(address, resolution, heater)
+
+print(string.format("Temperature: %.2f °C\nHumidity: %.2f %%", HDC1000.getTemp(), HDC1000.getHumi()))
+
+HDC1000 = nil
+package.loaded["HDC1000"]=nil
\ No newline at end of file
diff --git a/lua_modules/hdc1000/HDC1000.lua b/lua_modules/hdc1000/HDC1000.lua
new file mode 100644
index 00000000..80f7e499
--- /dev/null
+++ b/lua_modules/hdc1000/HDC1000.lua
@@ -0,0 +1,99 @@
+-------------------------------------------------------
+
+-- This library was written for the Texas Instruments
+-- HDC1000 temperature and humidity sensor.
+-- It should work for the HDC1008 too.
+-- Written by Francesco Truzzi (francesco@truzzi.me)
+-- Released under GNU GPL v2.0 license.
+
+-------------------------------------------------------
+
+-------------- NON-DEFAULT CONFIG VALUES --------------
+------------- config() optional arguments -------------
+
+-- HDC1000_HEAT_OFF 0x00 (heater)
+-- HDC1000_TEMP_11BIT 0x40 (resolution)
+-- HDC1000_HUMI_11BIT 0x01 (resolution)
+-- HDC1000_HUMI_8BIT 0x20 (resolution)
+
+-------------------------------------------------------
+
+local modname = ...
+local M = {}
+_G[modname] = M
+
+local id = 0
+local i2c = i2c
+local delay = 20000
+
+local HDC1000_ADDR = 0x40
+
+local HDC1000_TEMP = 0x00
+local HDC1000_HUMI = 0x01
+local HDC1000_CONFIG = 0x02
+
+local HDC1000_HEAT_ON = 0x20
+local HDC1000_TEMP_HUMI_14BIT = 0x00
+
+-- reads 16bits from the sensor
+local function read16()
+ i2c.start(id)
+ i2c.address(id, HDC1000_ADDR, i2c.RECEIVER)
+ data_temp = i2c.read(0, 2)
+ i2c.stop(id)
+ data = bit.lshift(string.byte(data_temp, 1, 1), 8) + string.byte(data_temp, 2, 2)
+ return data
+end
+
+-- sets the register to read next
+local function setReadRegister(register)
+ i2c.start(id)
+ i2c.address(id, HDC1000_ADDR, i2c.TRANSMITTER)
+ i2c.write(id, register)
+ i2c.stop(id)
+end
+
+-- writes the 2 configuration bytes
+local function writeConfig(config)
+ i2c.start(id)
+ i2c.address(id, HDC1000_ADDR, i2c.TRANSMITTER)
+ i2c.write(id, HDC1000_CONFIG, config, 0x00)
+ i2c.stop(id)
+end
+
+-- returns true if battery voltage is < 2.7V, false otherwise
+function M.batteryDead()
+ setReadRegister(HDC1000_CONFIG)
+ return(bit.isset(read16(), 11))
+
+end
+
+-- initalize i2c
+function M.init(sda, scl)
+ i2c.setup(id, sda, scl, i2c.SLOW)
+end
+
+function M.config(addr, resolution, heater)
+ -- default values are set if the function is called with no arguments
+ HDC1000_ADDR = addr or HDC1000_ADDR
+ resolution = resolution or HDC1000_TEMP_HUMI_14BIT
+ heater = heater or HDC1000_HEAT_ON
+ writeConfig(bit.bor(resolution, heater))
+end
+
+-- outputs temperature in Celsius degrees
+function M.getHumi()
+ setReadRegister(HDC1000_HUMI)
+ tmr.delay(delay)
+ return(read16()/65535.0*100)
+end
+
+-- outputs humidity in %RH
+function M.getTemp()
+ setReadRegister(HDC1000_TEMP)
+ tmr.delay(delay)
+ return(read16()/65535.0*165-40)
+end
+
+return M
+
diff --git a/lua_modules/hdc1000/README.md b/lua_modules/hdc1000/README.md
new file mode 100644
index 00000000..398cccee
--- /dev/null
+++ b/lua_modules/hdc1000/README.md
@@ -0,0 +1,39 @@
+HDC1000 NodeMCU module
+=======================
+
+Here's my NodeMCU module for the TI HDC1000 temperature and humidity sensor. It should work with the HDC1008 too but I haven't tested it.
+
+### Setup your sensor:
+First, require it:
+
+`HDC1000 = require("HDC1000")`
+
+Then, initialize it:
+
+`HDC1000.init(sda, scl)`
+
+Configure it:
+
+`HDC1000.config()`
+
+Default options set the address to 0x40 and enable both temperature and humidity readings at 14-bit resolution, with the integrated heater on. You can change them by initializing your sensor like this:
+
+`HDC1000.config(address, resolution, heater);`
+
+"resolution" can be set to 14 bits for both temperature and humidity (0x00 - default) 11 bits for temperature (0x40), 11 bits for humidity (0x01), 8 bits for humidity (0x20)
+"heater" can be set to ON (0x20 - default) or OFF (0x00)
+
+### Read some values
+You can read temperature and humidity by using the following commands:
+
+`temperature = HDC1000.getTemp()` in Celsius degrees.
+
+`humidity = HDC1000.getHumi()` in %
+
+### Check your battery
+
+The following code returns true if the battery voltage is <2.8V, false otherwise.
+
+`isDead = HDC1000.batteryDead();`
+
+Happy making! Also, check out my Breakout Board and Arduino library for this chip: http://b.truzzi.me/hdc1000-temperature-and-humidity-sensor-breakout-with-arduino-library/.