commit
dadd2ae96e
|
@ -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
|
41
README.md
41
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)<br />
|
||||
Lua core based on [eLua project](http://www.eluaproject.net/)<br />
|
||||
|
@ -7,24 +10,26 @@ File system based on [spiffs](https://github.com/pellepl/spiffs)<br />
|
|||
Open source development kit for NodeMCU [nodemcu-devkit](https://github.com/nodemcu/nodemcu-devkit)<br />
|
||||
Flash tool for NodeMCU [nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)<br />
|
||||
|
||||
wiki: [nodemcu wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)<br />
|
||||
api: [nodemcu api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en)<br />
|
||||
wiki: [NodeMCU wiki](https://github.com/nodemcu/nodemcu-firmware/wiki)<br />
|
||||
api: [NodeMCU api](https://github.com/nodemcu/nodemcu-firmware/wiki/nodemcu_api_en)<br />
|
||||
home: [nodemcu.com](http://www.nodemcu.com)<br />
|
||||
bbs: [Chinese bbs](http://bbs.nodemcu.com)<br />
|
||||
docs: [NodeMCU docs](http://www.nodemcu.com/docs/)<br />
|
||||
Tencent QQ group: 309957875<br />
|
||||
|
||||
# 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
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
*.output*
|
||||
mapfile
|
||||
!.gitignore
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2013 Toby Jaffey <toby@1248.io>
|
||||
2015 Zeroday Hong <zeroday@nodemcu.com> 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.
|
|
@ -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
|
||||
|
|
@ -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;i<numopt;i++)
|
||||
{
|
||||
c_printf(" 0x%02X [ ", opts[i].num);
|
||||
coap_dump(opts[i].buf.p, opts[i].buf.len, true);
|
||||
c_printf(" ]\n");
|
||||
}
|
||||
}
|
||||
|
||||
void coap_dumpPacket(coap_packet_t *pkt)
|
||||
{
|
||||
coap_dumpHeader(&pkt->hdr);
|
||||
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;i<pkt->numopts;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;i<pkt->numopts;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;i<ep->path->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;
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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:<MAX_PAYLOAD_SIZE" );
|
||||
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -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]);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,28 @@
|
|||
/* str.c -- strings to be used in the CoAP library
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/* str.h -- strings to be used in the CoAP library
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* 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_ */
|
|
@ -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;i<tmp.n;i++){
|
||||
pkt->opts[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;i<tmp.n;i++){
|
||||
pkt->opts[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;
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/* uri.h -- helper functions for URI treatment
|
||||
*
|
||||
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
||||
*
|
||||
* 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_ */
|
|
@ -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)
|
||||
|
|
|
@ -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__ */
|
|
@ -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__ */
|
|
@ -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)},
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "c_stdlib.h"
|
||||
#include "c_string.h"
|
||||
#include "flash_fs.h"
|
||||
#include "user_version.h"
|
||||
|
||||
#define lua_c
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 },
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,13 @@
|
|||
HDC1000 = require("HDC1000")
|
||||
|
||||
sda = 1
|
||||
scl = 2
|
||||
drdyn = false
|
||||
|
||||
HDC1000.init(sda, scl, drdyn)
|
||||
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
|
|
@ -0,0 +1,109 @@
|
|||
-------------------------------------------------------
|
||||
|
||||
-- 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 _drdyn_pin
|
||||
|
||||
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, drdyn_pin)
|
||||
_drdyn_pin = drdyn_pin
|
||||
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)
|
||||
if(_drdyn_pin ~= false) then
|
||||
gpio.mode(_drdyn_pin, gpio.INPUT)
|
||||
while(gpio.read(_drdyn_pin)==1) do
|
||||
end
|
||||
else tmr.delay(delay) end
|
||||
return(read16()/65535.0*100)
|
||||
end
|
||||
|
||||
-- outputs humidity in %RH
|
||||
function M.getTemp()
|
||||
setReadRegister(HDC1000_TEMP)
|
||||
if(_drdyn_pin ~= false) then
|
||||
gpio.mode(_drdyn_pin, gpio.INPUT)
|
||||
while(gpio.read(_drdyn_pin)==1) do
|
||||
end
|
||||
else tmr.delay(delay) end
|
||||
return(read16()/65535.0*165-40)
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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, drdyn)`
|
||||
|
||||
If you don't want to use the DRDYn pin, set it to false: a 20ms delay will be automatically set after each read request.
|
||||
|
||||
`HDC1000.init(sda, scl, false)`
|
||||
|
||||
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/.
|
Loading…
Reference in New Issue