Commit c8ac5cfb authored by Arnim Läuger's avatar Arnim Läuger Committed by GitHub
Browse files

Merge pull request #1980 from nodemcu/dev

2.1.0 master drop
parents 22e1adc4 787379f0
......@@ -29,22 +29,12 @@ static uint32_t myfatfs_fsize( const struct vfs_file *fd );
static sint32_t myfatfs_ferrno( const struct vfs_file *fd );
static sint32_t myfatfs_closedir( const struct vfs_dir *dd );
static vfs_item *myfatfs_readdir( const struct vfs_dir *dd );
static void myfatfs_iclose( const struct vfs_item *di );
static uint32_t myfatfs_isize( const struct vfs_item *di );
static sint32_t myfatfs_time( const struct vfs_item *di, struct vfs_time *tm );
static const char *myfatfs_name( const struct vfs_item *di );
static sint32_t myfatfs_is_dir( const struct vfs_item *di );
static sint32_t myfatfs_is_rdonly( const struct vfs_item *di );
static sint32_t myfatfs_is_hidden( const struct vfs_item *di );
static sint32_t myfatfs_is_sys( const struct vfs_item *di );
static sint32_t myfatfs_is_arch( const struct vfs_item *di );
static sint32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf );
static vfs_vol *myfatfs_mount( const char *name, int num );
static vfs_file *myfatfs_open( const char *name, const char *mode );
static vfs_dir *myfatfs_opendir( const char *name );
static vfs_item *myfatfs_stat( const char *name );
static sint32_t myfatfs_stat( const char *name, struct vfs_stat *buf );
static sint32_t myfatfs_remove( const char *name );
static sint32_t myfatfs_rename( const char *oldname, const char *newname );
static sint32_t myfatfs_mkdir( const char *name );
......@@ -89,18 +79,6 @@ static vfs_file_fns myfatfs_file_fns = {
.ferrno = myfatfs_ferrno
};
static vfs_item_fns myfatfs_item_fns = {
.close = myfatfs_iclose,
.size = myfatfs_isize,
.time = myfatfs_time,
.name = myfatfs_name,
.is_dir = myfatfs_is_dir,
.is_rdonly = myfatfs_is_rdonly,
.is_hidden = myfatfs_is_hidden,
.is_sys = myfatfs_is_sys,
.is_arch = myfatfs_is_arch
};
static vfs_dir_fns myfatfs_dir_fns = {
.close = myfatfs_closedir,
.readdir = myfatfs_readdir
......@@ -130,11 +108,6 @@ struct myvfs_dir {
DIR dp;
};
struct myvfs_item {
struct vfs_item vfs_item;
FILINFO fno;
};
// ---------------------------------------------------------------------------
// exported helper functions for FatFS
......@@ -321,105 +294,45 @@ static sint32_t myfatfs_closedir( const struct vfs_dir *dd )
return last_result == FR_OK ? VFS_RES_OK : VFS_RES_ERR;
}
static vfs_item *myfatfs_readdir( const struct vfs_dir *dd )
static void myfatfs_fill_stat( const FILINFO *fno, struct vfs_stat *buf )
{
GET_DIR_DP(dd);
struct myvfs_item *di;
if (di = c_malloc( sizeof( struct myvfs_item ) )) {
FILINFO *fno = &(di->fno);
if (FR_OK == (last_result = f_readdir( dp, fno ))) {
// condition "no further item" is signalled with empty name
if (fno->fname[0] != '\0') {
di->vfs_item.fs_type = VFS_FS_FATFS;
di->vfs_item.fns = &myfatfs_item_fns;
return (vfs_item *)di;
}
}
c_free( di );
}
c_memset( buf, 0, sizeof( struct vfs_stat ) );
return NULL;
}
// ---------------------------------------------------------------------------
// dir info functions
//
#define GET_FILINFO_FNO(descr) \
const struct myvfs_item *mydi = (const struct myvfs_item *)descr; \
FILINFO *fno = (FILINFO *)&(mydi->fno);
static void myfatfs_iclose( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
// free descriptor memory
c_free( (void *)di );
}
static uint32_t myfatfs_isize( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
return fno->fsize;
}
static sint32_t myfatfs_time( const struct vfs_item *di, struct vfs_time *tm )
{
GET_FILINFO_FNO(di);
// fill in supported stat entries
c_strncpy( buf->name, fno->fname, FS_OBJ_NAME_LEN+1 );
buf->name[FS_OBJ_NAME_LEN] = '\0';
buf->size = fno->fsize;
buf->is_dir = fno->fattrib & AM_DIR ? 1 : 0;
buf->is_rdonly = fno->fattrib & AM_RDO ? 1 : 0;
buf->is_hidden = fno->fattrib & AM_HID ? 1 : 0;
buf->is_sys = fno->fattrib & AM_SYS ? 1 : 0;
buf->is_arch = fno->fattrib & AM_ARC ? 1 : 0;
struct vfs_time *tm = &(buf->tm);
tm->year = (fno->fdate >> 9) + 1980;
tm->mon = (fno->fdate >> 5) & 0x0f;
tm->day = fno->fdate & 0x1f;
tm->hour = (fno->ftime >> 11);
tm->min = (fno->ftime >> 5) & 0x3f;
tm->sec = fno->ftime & 0x3f;
return VFS_RES_OK;
buf->tm_valid = 1;
}
static const char *myfatfs_name( const struct vfs_item *di )
static sint32_t myfatfs_readdir( const struct vfs_dir *dd, struct vfs_stat *buf )
{
GET_FILINFO_FNO(di);
return fno->fname;
}
static sint32_t myfatfs_is_dir( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
return fno->fattrib & AM_DIR ? 1 : 0;
}
static sint32_t myfatfs_is_rdonly( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
return fno->fattrib & AM_RDO ? 1 : 0;
}
static sint32_t myfatfs_is_hidden( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
return fno->fattrib & AM_HID ? 1 : 0;
}
static sint32_t myfatfs_is_sys( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
GET_DIR_DP(dd);
FILINFO fno;
return fno->fattrib & AM_SYS ? 1 : 0;
}
if (FR_OK == (last_result = f_readdir( dp, &fno ))) {
// condition "no further item" is signalled with empty name
if (fno.fname[0] != '\0') {
myfatfs_fill_stat( &fno, buf );
static sint32_t myfatfs_is_arch( const struct vfs_item *di )
{
GET_FILINFO_FNO(di);
return VFS_RES_OK;
}
}
return fno->fattrib & AM_ARC ? 1 : 0;
return VFS_RES_ERR;
}
......@@ -521,21 +434,17 @@ static vfs_dir *myfatfs_opendir( const char *name )
return NULL;
}
static vfs_item *myfatfs_stat( const char *name )
static sint32_t myfatfs_stat( const char *name, struct vfs_stat *buf )
{
struct myvfs_item *di;
FILINFO fno;
if (di = c_malloc( sizeof( struct myvfs_item ) )) {
if (FR_OK == (last_result = f_stat( name, &(di->fno) ))) {
di->vfs_item.fs_type = VFS_FS_FATFS;
di->vfs_item.fns = &myfatfs_item_fns;
return (vfs_item *)di;
if (FR_OK == (last_result = f_stat( name, &fno ))) {
myfatfs_fill_stat( &fno, buf );
return VFS_RES_OK;
} else {
c_free( di );
return VFS_RES_ERR;
}
}
return NULL;
}
static sint32_t myfatfs_remove( const char *name )
......
......@@ -74,7 +74,7 @@ void onewire_read_bytes(uint8_t pin, uint8_t *buf, uint16_t count);
// Write a bit. The bus is always left powered at the end, see
// note in write() about that.
static void onewire_write_bit(uint8_t pin, uint8_t v);
static void onewire_write_bit(uint8_t pin, uint8_t v, uint8_t power);
// Read a bit.
static uint8_t onewire_read_bit(uint8_t pin);
......
......@@ -8,8 +8,8 @@
#include "os_type.h"
/*SPI number define*/
#define SPI 0
#define HSPI 1
#define SPI_SPI 0
#define SPI_HSPI 1
#define SPI_ORDER_LSB 0
#define SPI_ORDER_MSB 1
......
......@@ -899,6 +899,15 @@
#define TCP_TTL (IP_DEFAULT_TTL)
#endif
/**
* TCP_MSL: Maximum segment lifetime
* Override for <tcp_impl.h> file
* See https://github.com/nodemcu/nodemcu-firmware/issues/1836 for details
*/
#ifndef TCP_MSL
#define TCP_MSL 5000UL
#endif
/**
* TCP_MAXRTX: Maximum number of retransmissions of data segments.
*/
......@@ -1455,7 +1464,8 @@
* SO_REUSE==1: Enable SO_REUSEADDR option.
*/
#ifndef SO_REUSE
#define SO_REUSE 0
/* See https://github.com/nodemcu/nodemcu-firmware/issues/1836 for details */
#define SO_REUSE 1
#endif
/**
......
#ifndef __DYNARR_H__
#define __DYNARR_H__
#include "user_interface.h"
#include "c_stdio.h"
#include "c_stdlib.h"
//#define DYNARR_DEBUG
//#define DYNARR_ERROR
typedef struct _dynarr{
void* data_ptr;
size_t used;
size_t array_size;
size_t data_size;
} dynarr_t;
bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size);
bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add);
bool dynarr_remove(dynarr_t* array_ptr, void* element_ptr);
bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size);
bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_ptr);
bool dynarr_free(dynarr_t* array_ptr);
#if 0 || defined(DYNARR_DEBUG) || defined(NODE_DEBUG)
#define DYNARR_DBG(fmt, ...) c_printf("\n DYNARR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
#else
#define DYNARR_DBG(...)
#endif
#if 0 || defined(DYNARR_ERROR) || defined(NODE_ERROR)
#define DYNARR_ERR(fmt, ...) c_printf("\n DYNARR: "fmt"\n", ##__VA_ARGS__)
#else
#define DYNARR_ERR(...)
#endif
#endif // __DYNARR_H__
......@@ -46,7 +46,6 @@ extern void mem_init(void * start_addr);
// Interrupt Service Routine functions
typedef void (*ets_isr_fn) (void *arg);
extern int ets_isr_attach (unsigned int interrupt, ets_isr_fn, void *arg);
extern void ets_isr_mask (unsigned intr);
extern void ets_isr_unmask (unsigned intr);
......@@ -119,14 +118,11 @@ void *ets_memset (void *dst, int c, size_t n);
int ets_memcmp (const void *s1, const void *s2, size_t n);
char *ets_strcpy (char *dst, const char *src);
size_t ets_strlen (const char *s);
int ets_strcmp (const char *s1, const char *s2);
int ets_strncmp (const char *s1, const char *s2, size_t n);
char *ets_strncpy(char *dest, const char *src, size_t n);
char *ets_strstr(const char *haystack, const char *needle);
void ets_delay_us (uint32_t us);
int ets_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void ets_str2macaddr (uint8_t *dst, const char *str);
......@@ -140,13 +136,9 @@ void Cache_Read_Disable(void);
void ets_intr_lock(void);
void ets_intr_unlock(void);
void ets_install_putc1(void *routine);
int rand(void);
void srand(unsigned int);
void uart_div_modify(int no, unsigned int freq);
/* Returns 0 on success, 1 on failure */
uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
uint8_t SPIWrite(uint32_t dst_addr, const uint32_t *src, uint32_t size);
......
#ifndef __SW_TIMER_H__
#define __SW_TIMER_H__
#include "user_interface.h"
//#define SWTMR_DEBUG
#define USE_SWTMR_ERROR_STRINGS
#if defined(DEVELOP_VERSION)
#define SWTMR_DEBUG
#endif
#if defined(SWTMR_DEBUG)
#define SWTMR_DBG(fmt, ...) dbg_printf("\tSWTIMER(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__)
#else
#define SWTMR_DBG(...)
#endif
#if defined(NODE_ERROR)
#define SWTMR_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "SWTIMER:", ##__VA_ARGS__)
#else
#define SWTMR_ERR(...)
#endif
enum SWTMR_STATUS{
SWTMR_FAIL = 0,
SWTMR_OK = 1,
SWTMR_MALLOC_FAIL = 10,
SWTMR_TIMER_NOT_ARMED,
// SWTMR_NULL_PTR,
SWTMR_REGISTRY_NO_REGISTERED_TIMERS,
// SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED,
// SWTMR_SUSPEND_ARRAY_ADD_FAILED,
// SWTMR_SUSPEND_ARRAY_REMOVE_FAILED,
SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED,
SWTMR_SUSPEND_TIMER_ALREADY_REARMED,
SWTMR_SUSPEND_NO_SUSPENDED_TIMERS,
SWTMR_SUSPEND_TIMER_NOT_SUSPENDED,
};
/* Global Function Declarations */
void swtmr_register(void* timer_ptr);
void swtmr_unregister(void* timer_ptr);
int swtmr_suspend(os_timer_t* timer_ptr);
int swtmr_resume(os_timer_t* timer_ptr);
void swtmr_print_registry(void);
void swtmr_print_suspended(void);
void swtmr_print_timer_list(void);
const char* swtmr_errorcode2str(int error_value);
bool swtmr_suspended_test(os_timer_t* timer_ptr);
#endif // __SW_TIMER_H__
......@@ -8,7 +8,6 @@
// #define FLASH_8M
// #define FLASH_16M
#define FLASH_AUTOSIZE
#define FLASH_SAFE_API
// This adds the asserts in LUA. It also adds some useful extras to the
// node module. This is all silent in normal operation and so can be enabled
......@@ -116,6 +115,10 @@ extern void luaL_assertfail(const char *file, int line, const char *message);
#define WIFI_SDK_EVENT_MONITOR_ENABLE
#define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE
#define ENABLE_TIMER_SUSPEND
#define PMSLEEP_ENABLE
#define STRBUF_DEFAULT_INCREMENT 32
#endif /* __USER_CONFIG_H__ */
......@@ -18,13 +18,13 @@
// See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions.
// New modules should be disabled by default and added in alphabetical order.
#define LUA_USE_MODULES_ADC
//#define LUA_USE_MODULES_ADS1115
//#define LUA_USE_MODULES_ADXL345
//#define LUA_USE_MODULES_AM2320
//#define LUA_USE_MODULES_APA102
#define LUA_USE_MODULES_BIT
//#define LUA_USE_MODULES_BMP085
//#define LUA_USE_MODULES_BME280
//#define LUA_USE_MODULES_CJSON
//#define LUA_USE_MODULES_COAP
//#define LUA_USE_MODULES_CRON
//#define LUA_USE_MODULES_CRYPTO
......@@ -34,6 +34,7 @@
#define LUA_USE_MODULES_FILE
//#define LUA_USE_MODULES_GDBSTUB
#define LUA_USE_MODULES_GPIO
//#define LUA_USE_MODULES_HDC1080
//#define LUA_USE_MODULES_HMC5883L
//#define LUA_USE_MODULES_HTTP
//#define LUA_USE_MODULES_HX711
......@@ -53,12 +54,15 @@
//#define LUA_USE_MODULES_RTCFIFO
//#define LUA_USE_MODULES_RTCMEM
//#define LUA_USE_MODULES_RTCTIME
//#define LUA_USE_MODULES_SI7021
//#define LUA_USE_MODULES_SIGMA_DELTA
//#define LUA_USE_MODULES_SJSON
//#define LUA_USE_MODULES_SNTP
//#define LUA_USE_MODULES_SOMFY
#define LUA_USE_MODULES_SPI
//#define LUA_USE_MODULES_STRUCT
//#define LUA_USE_MODULES_SWITEC
// #define LUA_USE_MODULES_TCS34725
//#define LUA_USE_MODULES_TM1829
#define LUA_USE_MODULES_TLS
#define LUA_USE_MODULES_TMR
......@@ -71,6 +75,7 @@
//#define LUA_USE_MODULES_WPS
//#define LUA_USE_MODULES_WS2801
//#define LUA_USE_MODULES_WS2812
//#define LUA_USE_MODULES_XPT2046
#endif /* LUA_CROSS_COMPILER */
#endif /* __USER_MODULES_H__ */
......@@ -2,11 +2,11 @@
#define __USER_VERSION_H__
#define NODE_VERSION_MAJOR 2U
#define NODE_VERSION_MINOR 0U
#define NODE_VERSION_MINOR 1U
#define NODE_VERSION_REVISION 0U
#define NODE_VERSION_INTERNAL 0U
#define NODE_VERSION "NodeMCU 2.0.0"
#define NODE_VERSION "NodeMCU 2.1.0"
#ifndef BUILD_DATE
#define BUILD_DATE "unspecified"
#endif
......
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libmisc.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ./include
INCLUDES += -I ../include
INCLUDES += -I ../../include
INCLUDES += -I ../lua
INCLUDES += -I ../platform
INCLUDES += -I ../libc
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
#include "misc/dynarr.h"
#define ARRAY_PTR_CHECK if(array_ptr == NULL || array_ptr->data_ptr == NULL){\
/**/DYNARR_DBG("array not initialized");\
return false; \
}
bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size){
if(array_ptr == NULL || data_size == 0 || array_size == 0){
/**/DYNARR_DBG("Invalid parameter: array_ptr(%p) data_size(%u) array_size(%u)", array_ptr, data_size, array_size);
return false;
}
if(array_ptr->data_ptr != NULL ){
/**/DYNARR_DBG("Array already initialized: array_ptr->data_ptr=%p", array_ptr->data_ptr);
return false;
}
/**/DYNARR_DBG("Array parameters:\n\t\t\tarray_size(%u)\n\t\t\tdata_size(%u)\n\t\t\ttotal size(bytes):%u", array_size, data_size, (array_size * data_size));
void* temp_array = c_zalloc(array_size * data_size);
if(temp_array == NULL){
/**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (array_size * data_size), system_get_free_heap_size());
return false;
}
array_ptr->data_ptr = temp_array;
array_ptr->array_size = array_size;
array_ptr->data_size = data_size;
array_ptr->used = 0;
return true;
}
bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add){
ARRAY_PTR_CHECK;
if(elements_to_add <= 0){
/**/DYNARR_DBG("Invalid qty: elements_to_add=%u", elements_to_add);
return false;
}
size_t new_array_size = array_ptr->array_size + elements_to_add;
/**/DYNARR_DBG("old size=%u\tnew size=%u\tmem used=%u",
array_ptr->array_size, new_array_size, (new_array_size * array_ptr->data_size));
void* temp_array_p = c_realloc(array_ptr->data_ptr, new_array_size * array_ptr->data_size);
if(temp_array_p == NULL){
/**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (new_array_size * array_ptr->data_size), system_get_free_heap_size());
return false;
}
array_ptr->data_ptr = temp_array_p;
size_t prev_size = array_ptr->array_size;
array_ptr->array_size = new_array_size;
//set memory to 0 for newly added array elements
memset((uint8*) array_ptr->data_ptr + (prev_size * array_ptr->data_size), 0, (elements_to_add * array_ptr->data_size));
/**/DYNARR_DBG("Array successfully resized");
return true;
}
bool dynarr_remove(dynarr_t* array_ptr, void* element_to_remove){
ARRAY_PTR_CHECK;
uint8* element_ptr = element_to_remove;
uint8* data_ptr = array_ptr->data_ptr;
if(dynarr_boundaryCheck(array_ptr, element_to_remove) == FALSE){
return false;
}
//overwrite element to be removed by shifting all elements to the left
memmove(element_ptr, element_ptr + array_ptr->data_size, (array_ptr->array_size - 1) * array_ptr->data_size - (element_ptr - data_ptr));
//clear newly freed element
memset(data_ptr + ((array_ptr->array_size-1) * array_ptr->data_size), 0, array_ptr->data_size);
//decrement array used since we removed an element
array_ptr->used--;
/**/DYNARR_DBG("element(%p) removed from array", element_ptr);
return true;
}
bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size){
ARRAY_PTR_CHECK;
if(data_size != array_ptr->data_size){
/**/DYNARR_DBG("Invalid data size: data_size(%u) != arr->data_size(%u)", data_size, array_ptr->data_size);
return false;
}
if(array_ptr->array_size == array_ptr->used){
if(!dynarr_resize(array_ptr, (array_ptr->array_size/2))){
return false;
}
}
memcpy(((uint8*)array_ptr->data_ptr + (array_ptr->used * array_ptr->data_size)), data_ptr, array_ptr->data_size);
array_ptr->used++;
return true;
}
bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_to_check){
ARRAY_PTR_CHECK;
uint8* data_ptr = array_ptr->data_ptr;
uint8* element_ptr = element_to_check;
if(element_ptr < data_ptr || ((element_ptr - data_ptr) / array_ptr->data_size) > array_ptr->array_size - 1){
/**/DYNARR_DBG("element_ptr(%p) out of bounds: first element ptr:%p last element ptr:%p",
element_ptr, data_ptr, data_ptr + ((array_ptr->array_size - 1) * array_ptr->data_size));
return false;
}
return true;
}
bool dynarr_free(dynarr_t* array_ptr){
ARRAY_PTR_CHECK;
c_free(array_ptr->data_ptr);
array_ptr->data_ptr=NULL;
array_ptr->array_size = array_ptr->used = 0;
/**/DYNARR_DBG("array freed");
return true;
}
......@@ -50,11 +50,12 @@ INCLUDES += -I ../pcm
INCLUDES += -I ../platform
INCLUDES += -I ../spiffs
INCLUDES += -I ../smart
INCLUDES += -I ../cjson
INCLUDES += -I ../dhtlib
INCLUDES += -I ../fatfs
INCLUDES += -I ../http
INCLUDES += -I ../sjson
INCLUDES += -I ../websocket
INCLUDES += -I ../pm
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
......@@ -29,12 +29,12 @@ static int adc_init107( lua_State *L )
{
uint8_t byte107 = luaL_checkinteger (L, 1);
uint32 init_sector = flash_safe_get_sec_num () - 4;
uint32 init_sector = flash_rom_get_sec_num () - 4;
// Note 32bit alignment so we can safely cast to uint32 for the flash api
char init_data[SPI_FLASH_SEC_SIZE] __attribute__((aligned(4)));
if (SPI_FLASH_RESULT_OK != flash_safe_read (
if (SPI_FLASH_RESULT_OK != flash_read (
init_sector * SPI_FLASH_SEC_SIZE,
(uint32 *)init_data, sizeof(init_data)))
return luaL_error(L, "flash read error");
......@@ -48,10 +48,10 @@ static int adc_init107( lua_State *L )
// Nope, it differs, we need to rewrite it
init_data[107] = byte107;
if (SPI_FLASH_RESULT_OK != flash_safe_erase_sector (init_sector))
if (SPI_FLASH_RESULT_OK != flash_erase (init_sector))
return luaL_error(L, "flash erase error");
if (SPI_FLASH_RESULT_OK != flash_safe_write (
if (SPI_FLASH_RESULT_OK != flash_write (
init_sector * SPI_FLASH_SEC_SIZE,
(uint32 *)init_data, sizeof(init_data)))
return luaL_error(L, "flash write error");
......
//***************************************************************************
// Si7021 module for ESP8266 with nodeMCU
// fetchbot @github
// MIT license, http://opensource.org/licenses/MIT
//***************************************************************************
#include "module.h"
#include "lauxlib.h"
#include "platform.h"
#include "osapi.h"
//***************************************************************************
// I2C ADDRESS DEFINITON
//***************************************************************************
#define ADS1115_I2C_ADDR_GND (0x48)
#define ADS1115_I2C_ADDR_VDD (0x49)
#define ADS1115_I2C_ADDR_SDA (0x4A)
#define ADS1115_I2C_ADDR_SCL (0x4B)
//***************************************************************************
// POINTER REGISTER
//***************************************************************************
#define ADS1115_POINTER_MASK (0x03)
#define ADS1115_POINTER_CONVERSION (0x00)
#define ADS1115_POINTER_CONFIG (0x01)
#define ADS1115_POINTER_THRESH_LOW (0x02)
#define ADS1115_POINTER_THRESH_HI (0x03)
//***************************************************************************
// CONFIG REGISTER
//***************************************************************************
#define ADS1115_OS_MASK (0x8000)
#define ADS1115_OS_NON (0x0000)
#define ADS1115_OS_SINGLE (0x8000) // Write: Set to start a single-conversion
#define ADS1115_OS_BUSY (0x0000) // Read: Bit = 0 when conversion is in progress
#define ADS1115_OS_NOTBUSY (0x8000) // Read: Bit = 1 when device is not performing a conversion
#define ADS1115_MUX_MASK (0x7000)
#define ADS1115_MUX_DIFF_0_1 (0x0000) // Differential P = AIN0, N = AIN1 (default)
#define ADS1115_MUX_DIFF_0_3 (0x1000) // Differential P = AIN0, N = AIN3
#define ADS1115_MUX_DIFF_1_3 (0x2000) // Differential P = AIN1, N = AIN3
#define ADS1115_MUX_DIFF_2_3 (0x3000) // Differential P = AIN2, N = AIN3
#define ADS1115_MUX_SINGLE_0 (0x4000) // Single-ended AIN0
#define ADS1115_MUX_SINGLE_1 (0x5000) // Single-ended AIN1
#define ADS1115_MUX_SINGLE_2 (0x6000) // Single-ended AIN2
#define ADS1115_MUX_SINGLE_3 (0x7000) // Single-ended AIN3
#define ADS1115_PGA_MASK (0x0E00)
#define ADS1115_PGA_6_144V (0x0000) // +/-6.144V range = Gain 2/3
#define ADS1115_PGA_4_096V (0x0200) // +/-4.096V range = Gain 1
#define ADS1115_PGA_2_048V (0x0400) // +/-2.048V range = Gain 2 (default)
#define ADS1115_PGA_1_024V (0x0600) // +/-1.024V range = Gain 4
#define ADS1115_PGA_0_512V (0x0800) // +/-0.512V range = Gain 8
#define ADS1115_PGA_0_256V (0x0A00) // +/-0.256V range = Gain 16
#define ADS1115_MODE_MASK (0x0100)
#define ADS1115_MODE_CONTIN (0x0000) // Continuous conversion mode
#define ADS1115_MODE_SINGLE (0x0100) // Power-down single-shot mode (default)
#define ADS1115_DR_MASK (0x00E0)
#define ADS1115_DR_8SPS (0x0000) // 8 samples per second
#define ADS1115_DR_16SPS (0x0020) // 16 samples per second
#define ADS1115_DR_32SPS (0x0040) // 32 samples per second
#define ADS1115_DR_64SPS (0x0060) // 64 samples per second
#define ADS1115_DR_128SPS (0x0080) // 128 samples per second (default)
#define ADS1115_DR_250SPS (0x00A0) // 250 samples per second
#define ADS1115_DR_475SPS (0x00C0) // 475 samples per second
#define ADS1115_DR_860SPS (0x00E0) // 860 samples per second
#define ADS1115_CMODE_MASK (0x0010)
#define ADS1115_CMODE_TRAD (0x0000) // Traditional comparator with hysteresis (default)
#define ADS1115_CMODE_WINDOW (0x0010) // Window comparator
#define ADS1115_CPOL_MASK (0x0008)
#define ADS1115_CPOL_ACTVLOW (0x0000) // ALERT/RDY pin is low when active (default)
#define ADS1115_CPOL_ACTVHI (0x0008) // ALERT/RDY pin is high when active
#define ADS1115_CLAT_MASK (0x0004) // Determines if ALERT/RDY pin latches once asserted
#define ADS1115_CLAT_NONLAT (0x0000) // Non-latching comparator (default)
#define ADS1115_CLAT_LATCH (0x0004) // Latching comparator
#define ADS1115_CQUE_MASK (0x0003)
#define ADS1115_CQUE_1CONV (0x0000) // Assert ALERT/RDY after one conversions
#define ADS1115_CQUE_2CONV (0x0001) // Assert ALERT/RDY after two conversions
#define ADS1115_CQUE_4CONV (0x0002) // Assert ALERT/RDY after four conversions
#define ADS1115_CQUE_NONE (0x0003) // Disable the comparator and put ALERT/RDY in high state (default)
//***************************************************************************
static const uint8_t ads1115_i2c_id = 0;
static const uint8_t general_i2c_addr = 0x00;
static const uint8_t ads1115_i2c_reset = 0x06;
static uint8_t ads1115_i2c_addr = ADS1115_I2C_ADDR_GND;
static uint16_t ads1115_os = ADS1115_OS_SINGLE;
static uint16_t ads1115_gain = ADS1115_PGA_6_144V;
static uint16_t ads1115_samples = ADS1115_DR_128SPS;
static uint16_t ads1115_channel = ADS1115_MUX_SINGLE_0;
static uint16_t ads1115_comp = ADS1115_CQUE_NONE;
static uint16_t ads1115_mode = ADS1115_MODE_SINGLE;
static uint16_t ads1115_threshold_low = 0x8000;
static uint16_t ads1115_threshold_hi = 0x7FFF;
static uint16_t ads1115_config = 0x8583;
static uint16_t ads1115_conversion = 0;
static double ads1115_volt = 0;
os_timer_t ads1115_timer; // timer for conversion delay
int ads1115_timer_ref; // callback when readout is ready
static int ads1115_lua_readoutdone(void);
static uint8_t write_reg(uint8_t reg, uint16_t config) {
platform_i2c_send_start(ads1115_i2c_id);
platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(ads1115_i2c_id, reg);
platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config >> 8));
platform_i2c_send_byte(ads1115_i2c_id, (uint8_t)(config & 0xFF));
platform_i2c_send_stop(ads1115_i2c_id);
}
static uint16_t read_reg(uint8_t reg) {
platform_i2c_send_start(ads1115_i2c_id);
platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(ads1115_i2c_id, reg);
platform_i2c_send_stop(ads1115_i2c_id);
platform_i2c_send_start(ads1115_i2c_id);
platform_i2c_send_address(ads1115_i2c_id, ads1115_i2c_addr, PLATFORM_I2C_DIRECTION_RECEIVER);
uint16_t buf = (platform_i2c_recv_byte(ads1115_i2c_id, 1) << 8);
buf += platform_i2c_recv_byte(ads1115_i2c_id, 0);
platform_i2c_send_stop(ads1115_i2c_id);
return buf;
}
// convert ADC value to voltage corresponding to PGA settings
static double get_volt(uint16_t value) {
double volt = 0;
switch (ads1115_gain) {
case (ADS1115_PGA_6_144V):
volt = (int16_t)value * 0.1875;
break;
case (ADS1115_PGA_4_096V):
volt = (int16_t)value * 0.125;
break;
case (ADS1115_PGA_2_048V):
volt = (int16_t)value * 0.0625;
break;
case (ADS1115_PGA_1_024V):
volt = (int16_t)value * 0.03125;
break;
case (ADS1115_PGA_0_512V):
volt = (int16_t)value * 0.015625;
break;
case (ADS1115_PGA_0_256V):
volt = (int16_t)value * 0.0078125;
break;
}
return volt;
}
// convert threshold in volt to ADC value corresponding to PGA settings
static uint8_t get_value(int16_t *volt) {
switch (ads1115_gain) {
case (ADS1115_PGA_6_144V):
if ((*volt >= 6144) || (*volt < -6144) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.1875;
break;
case (ADS1115_PGA_4_096V):
if ((*volt >= 4096) || (*volt < -4096) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.125;
break;
case (ADS1115_PGA_2_048V):
if ((*volt >= 2048) || (*volt < -2048) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.0625;
break;
case (ADS1115_PGA_1_024V):
if ((*volt >= 1024) || (*volt < -1024) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.03125;
break;
case (ADS1115_PGA_0_512V):
if ((*volt >= 512) || (*volt < -512) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.015625;
break;
case (ADS1115_PGA_0_256V):
if ((*volt >= 256) || (*volt < -256) || ((*volt < 0) && (ads1115_channel >> 14))) return 1;
*volt = *volt / 0.0078125;
break;
}
return 0;
}
// Initializes ADC
// Lua: ads11115.setup(ADDRESS)
static int ads1115_lua_setup(lua_State *L) {
// check variables
if (!lua_isnumber(L, 1)) {
return luaL_error(L, "wrong arg range");
}
ads1115_i2c_addr = luaL_checkinteger(L, 1);
if (!((ads1115_i2c_addr == ADS1115_I2C_ADDR_GND) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_VDD) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SDA) || (ads1115_i2c_addr == ADS1115_I2C_ADDR_SCL))) {
return luaL_error(L, "Invalid argument: adddress");
}
platform_i2c_send_start(ads1115_i2c_id);
platform_i2c_send_address(ads1115_i2c_id, general_i2c_addr, PLATFORM_I2C_DIRECTION_TRANSMITTER);
platform_i2c_send_byte(ads1115_i2c_id, ads1115_i2c_reset);
platform_i2c_send_stop(ads1115_i2c_id);
// check for device on i2c bus
if (read_reg(ADS1115_POINTER_CONFIG) != 0x8583) {
return luaL_error(L, "found no device");
}
return 0;
}
// Change ADC settings
// Lua: ads1115.setting(GAIN,SAMPLES,CHANNEL,MODE[,CONVERSION_RDY][,COMPARATOR,THRESHOLD_LOW,THRESHOLD_HI])
static int ads1115_lua_setting(lua_State *L) {
// check variables
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4)) {
return luaL_error(L, "wrong arg range");
}
ads1115_gain = luaL_checkinteger(L, 1);
if (!((ads1115_gain == ADS1115_PGA_6_144V) || (ads1115_gain == ADS1115_PGA_4_096V) || (ads1115_gain == ADS1115_PGA_2_048V) || (ads1115_gain == ADS1115_PGA_1_024V) || (ads1115_gain == ADS1115_PGA_0_512V) || (ads1115_gain == ADS1115_PGA_0_256V))) {
return luaL_error(L, "Invalid argument: gain");
}
ads1115_samples = luaL_checkinteger(L, 2);
if (!((ads1115_samples == ADS1115_DR_8SPS) || (ads1115_samples == ADS1115_DR_16SPS) || (ads1115_samples == ADS1115_DR_32SPS) || (ads1115_samples == ADS1115_DR_64SPS) || (ads1115_samples == ADS1115_DR_128SPS) || (ads1115_samples == ADS1115_DR_250SPS) || (ads1115_samples == ADS1115_DR_475SPS) || (ads1115_samples == ADS1115_DR_860SPS))) {
return luaL_error(L, "Invalid argument: samples");
}
ads1115_channel = luaL_checkinteger(L, 3);
if (!((ads1115_channel == ADS1115_MUX_SINGLE_0) || (ads1115_channel == ADS1115_MUX_SINGLE_1) || (ads1115_channel == ADS1115_MUX_SINGLE_2) || (ads1115_channel == ADS1115_MUX_SINGLE_3) || (ads1115_channel == ADS1115_MUX_DIFF_0_1) || (ads1115_channel == ADS1115_MUX_DIFF_0_3) || (ads1115_channel == ADS1115_MUX_DIFF_1_3) || (ads1115_channel == ADS1115_MUX_DIFF_2_3))) {
return luaL_error(L, "Invalid argument: channel");
}
ads1115_mode = luaL_checkinteger(L, 4);
if (!((ads1115_mode == ADS1115_MODE_SINGLE) || (ads1115_mode == ADS1115_MODE_CONTIN))) {
return luaL_error(L, "Invalid argument: mode");
}
if (ads1115_mode == ADS1115_MODE_SINGLE) {
ads1115_os = ADS1115_OS_SINGLE;
} else {
ads1115_os = ADS1115_OS_NON;
}
ads1115_comp = ADS1115_CQUE_NONE;
// Parse optional parameters
if (lua_isnumber(L, 5) && !(lua_isnumber(L, 6) || lua_isnumber(L, 7))) {
// conversion ready mode
ads1115_comp = luaL_checkinteger(L, 5);
if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) {
return luaL_error(L, "Invalid argument: conversion ready mode");
}
ads1115_threshold_low = 0x7FFF;
ads1115_threshold_hi = 0x8000;
write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low);
write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi);
} else if (lua_isnumber(L, 5) && lua_isnumber(L, 6) && lua_isnumber(L, 7)) {
// comparator mode
ads1115_comp = luaL_checkinteger(L, 5);
if (!((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV))) {
return luaL_error(L, "Invalid argument: comparator mode");
}
ads1115_threshold_low = luaL_checkinteger(L, 5);
ads1115_threshold_hi = luaL_checkinteger(L, 6);
if ((int16_t)ads1115_threshold_low > (int16_t)ads1115_threshold_hi) {
return luaL_error(L, "Invalid argument: threshold_low > threshold_hi");
}
if (get_value(&ads1115_threshold_low)) {
return luaL_error(L, "Invalid argument: threshold_low");
}
if (get_value(&ads1115_threshold_hi)) {
return luaL_error(L, "Invalid argument: threshold_hi");
}
write_reg(ADS1115_POINTER_THRESH_LOW, ads1115_threshold_low);
write_reg(ADS1115_POINTER_THRESH_HI, ads1115_threshold_hi);
}
ads1115_config = (ads1115_os | ads1115_channel | ads1115_gain | ads1115_mode | ads1115_samples | ADS1115_CMODE_TRAD | ADS1115_CPOL_ACTVLOW | ADS1115_CLAT_NONLAT | ads1115_comp);
write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
return 0;
}
// Read the conversion register from the ADC
// Lua: ads1115.startread(function(volt, voltdec, adc) print(volt,voltdec,adc) end)
static int ads1115_lua_startread(lua_State *L) {
if (((ads1115_comp == ADS1115_CQUE_1CONV) || (ads1115_comp == ADS1115_CQUE_2CONV) || (ads1115_comp == ADS1115_CQUE_4CONV)) && (ads1115_threshold_low == 0x7FFF) && (ads1115_threshold_hi == 0x8000)) {
if (ads1115_mode == ADS1115_MODE_SINGLE) {
write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
}
return 0;
} else {
luaL_argcheck(L, (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION), 1, "Must be function");
lua_pushvalue(L, 1);
ads1115_timer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
if (ads1115_mode == ADS1115_MODE_SINGLE) {
write_reg(ADS1115_POINTER_CONFIG, ads1115_config);
}
// Start a timer to wait until ADC conversion is done
os_timer_disarm (&ads1115_timer);
os_timer_setfn (&ads1115_timer, (os_timer_func_t *)ads1115_lua_readoutdone, NULL);
switch (ads1115_samples) {
case (ADS1115_DR_8SPS):
os_timer_arm (&ads1115_timer, 150, 0);
break;
case (ADS1115_DR_16SPS):
os_timer_arm (&ads1115_timer, 75, 0);
break;
case (ADS1115_DR_32SPS):
os_timer_arm (&ads1115_timer, 35, 0);
break;
case (ADS1115_DR_64SPS):
os_timer_arm (&ads1115_timer, 20, 0);
break;
case (ADS1115_DR_128SPS):
os_timer_arm (&ads1115_timer, 10, 0);
break;
case (ADS1115_DR_250SPS):
os_timer_arm (&ads1115_timer, 5, 0);
break;
case (ADS1115_DR_475SPS):
os_timer_arm (&ads1115_timer, 3, 0);
break;
case (ADS1115_DR_860SPS):
os_timer_arm (&ads1115_timer, 2, 0);
break;
}
return 0;
}
}
// adc conversion timer callback
static int ads1115_lua_readoutdone(void) {
ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION);
ads1115_volt = get_volt(ads1115_conversion);
int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec;
lua_State *L = lua_getstate();
os_timer_disarm (&ads1115_timer);
lua_rawgeti (L, LUA_REGISTRYINDEX, ads1115_timer_ref);
luaL_unref (L, LUA_REGISTRYINDEX, ads1115_timer_ref);
ads1115_timer_ref = LUA_NOREF;
lua_pushnumber(L, ads1115_volt);
lua_pushinteger(L, ads1115_voltdec);
lua_pushinteger(L, ads1115_conversion);
lua_call (L, 3, 0);
}
// Read the conversion register from the ADC
// Lua: volt,voltdec,adc = ads1115.read()
static int ads1115_lua_read(lua_State *L) {
ads1115_conversion = read_reg(ADS1115_POINTER_CONVERSION);
ads1115_volt = get_volt(ads1115_conversion);
int ads1115_voltdec = (int)((ads1115_volt - (int)ads1115_volt) * 1000);
ads1115_voltdec = ads1115_voltdec>0?ads1115_voltdec:0-ads1115_voltdec;
lua_pushnumber(L, ads1115_volt);
lua_pushinteger(L, ads1115_voltdec);
lua_pushinteger(L, ads1115_conversion);
return 3;
}
static const LUA_REG_TYPE ads1115_map[] = {
{ LSTRKEY( "setup" ), LFUNCVAL(ads1115_lua_setup) },
{ LSTRKEY( "setting" ), LFUNCVAL(ads1115_lua_setting) },
{ LSTRKEY( "startread" ), LFUNCVAL(ads1115_lua_startread) },
{ LSTRKEY( "read" ), LFUNCVAL(ads1115_lua_read) },
{ LSTRKEY( "ADDR_GND" ), LNUMVAL(ADS1115_I2C_ADDR_GND) },
{ LSTRKEY( "ADDR_VDD" ), LNUMVAL(ADS1115_I2C_ADDR_VDD) },
{ LSTRKEY( "ADDR_SDA" ), LNUMVAL(ADS1115_I2C_ADDR_SDA) },
{ LSTRKEY( "ADDR_SCL" ), LNUMVAL(ADS1115_I2C_ADDR_SCL) },
{ LSTRKEY( "SINGLE_SHOT" ), LNUMVAL(ADS1115_MODE_SINGLE) },
{ LSTRKEY( "CONTINUOUS" ), LNUMVAL(ADS1115_MODE_CONTIN) },
{ LSTRKEY( "DIFF_0_1" ), LNUMVAL(ADS1115_MUX_DIFF_0_1) },
{ LSTRKEY( "DIFF_0_3" ), LNUMVAL(ADS1115_MUX_DIFF_0_3) },
{ LSTRKEY( "DIFF_1_3" ), LNUMVAL(ADS1115_MUX_DIFF_1_3) },
{ LSTRKEY( "DIFF_2_3" ), LNUMVAL(ADS1115_MUX_DIFF_2_3) },
{ LSTRKEY( "SINGLE_0" ), LNUMVAL(ADS1115_MUX_SINGLE_0) },
{ LSTRKEY( "SINGLE_1" ), LNUMVAL(ADS1115_MUX_SINGLE_1) },
{ LSTRKEY( "SINGLE_2" ), LNUMVAL(ADS1115_MUX_SINGLE_2) },
{ LSTRKEY( "SINGLE_3" ), LNUMVAL(ADS1115_MUX_SINGLE_3) },
{ LSTRKEY( "GAIN_6_144V" ), LNUMVAL(ADS1115_PGA_6_144V) },
{ LSTRKEY( "GAIN_4_096V" ), LNUMVAL(ADS1115_PGA_4_096V) },
{ LSTRKEY( "GAIN_2_048V" ), LNUMVAL(ADS1115_PGA_2_048V) },
{ LSTRKEY( "GAIN_1_024V" ), LNUMVAL(ADS1115_PGA_1_024V) },
{ LSTRKEY( "GAIN_0_512V" ), LNUMVAL(ADS1115_PGA_0_512V) },
{ LSTRKEY( "GAIN_0_256V" ), LNUMVAL(ADS1115_PGA_0_256V) },
{ LSTRKEY( "DR_8SPS" ), LNUMVAL(ADS1115_DR_8SPS) },
{ LSTRKEY( "DR_16SPS" ), LNUMVAL(ADS1115_DR_16SPS) },
{ LSTRKEY( "DR_32SPS" ), LNUMVAL(ADS1115_DR_32SPS) },
{ LSTRKEY( "DR_64SPS" ), LNUMVAL(ADS1115_DR_64SPS) },
{ LSTRKEY( "DR_128SPS" ), LNUMVAL(ADS1115_DR_128SPS) },
{ LSTRKEY( "DR_250SPS" ), LNUMVAL(ADS1115_DR_250SPS) },
{ LSTRKEY( "DR_475SPS" ), LNUMVAL(ADS1115_DR_475SPS) },
{ LSTRKEY( "DR_860SPS" ), LNUMVAL(ADS1115_DR_860SPS) },
{ LSTRKEY( "CONV_RDY_1" ), LNUMVAL(ADS1115_CQUE_1CONV) },
{ LSTRKEY( "CONV_RDY_2" ), LNUMVAL(ADS1115_CQUE_2CONV) },
{ LSTRKEY( "CONV_RDY_4" ), LNUMVAL(ADS1115_CQUE_4CONV) },
{ LSTRKEY( "COMP_1CONV" ), LNUMVAL(ADS1115_CQUE_1CONV) },
{ LSTRKEY( "COMP_2CONV" ), LNUMVAL(ADS1115_CQUE_2CONV) },
{ LSTRKEY( "COMP_4CONV" ), LNUMVAL(ADS1115_CQUE_4CONV) },
{ LNILKEY, LNILVAL }
};
NODEMCU_MODULE(ADS1115, "ads1115", ads1115_map, NULL);
\ No newline at end of file
......@@ -249,8 +249,8 @@ static int bme280_lua_init(lua_State* L) {
bme280_ossh = (!lua_isnumber(L, 5))?BME280_OVERSAMP_16X:(luaL_checkinteger(L, 5)&bit3); // 5-th parameter: humidity oversampling
config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 4) // 7-th parameter: inactive duration in normal mode
| ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 1); // 8-th parameter: IIR filter
config = ((!lua_isnumber(L, 7)?BME280_STANDBY_TIME_20_MS:(luaL_checkinteger(L, 7)&bit3))<< 5) // 7-th parameter: inactive duration in normal mode
| ((!lua_isnumber(L, 8)?BME280_FILTER_COEFF_16:(luaL_checkinteger(L, 8)&bit3)) << 2); // 8-th parameter: IIR filter
full_init = !lua_isnumber(L, 9)?1:lua_tointeger(L, 9); // 9-th parameter: init the chip too
NODE_DBG("mode: %x\nhumidity oss: %x\nconfig: %x\n", bme280_mode, bme280_ossh, config);
......
/* Lua CJSON - JSON support for Lua
*
* Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Caveats:
* - JSON "null" values are represented as lightuserdata since Lua
* tables cannot contain "nil". Compare with cjson.null.
* - Invalid UTF-8 characters are not detected and will be passed
* untouched. If required, UTF-8 error checking should be done
* outside this library.
* - Javascript comments are not part of the JSON spec, and are not
* currently supported.
*
* Note: Decoding is slower than encoding. Lua spends significant
* time (30%) managing tables when parsing JSON since it is
* difficult to know object/array sizes ahead of time.
*/
// #include <assert.h>
#include "module.h"
#include "c_string.h"
#include "c_math.h"
#include "c_limits.h"
#include "lauxlib.h"
#include "flash_api.h"
#include "ctype.h"
#include "strbuf.h"
#include "cjson_mem.h"
#define FPCONV_G_FMT_BUFSIZE 32
#define fpconv_strtod c_strtod
#define fpconv_init() ((void)0)
#ifndef CJSON_MODNAME
#define CJSON_MODNAME "cjson"
#endif
#ifndef CJSON_VERSION
#define CJSON_VERSION "2.1devel"
#endif
/* Workaround for Solaris platforms missing isinf() */
#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF))
#define isinf(x) (!isnan(x) && isnan((x) - (x)))
#endif
#define DEFAULT_SPARSE_CONVERT 0
#define DEFAULT_SPARSE_RATIO 2
#define DEFAULT_SPARSE_SAFE 10
#define DEFAULT_ENCODE_MAX_DEPTH 1000
#define DEFAULT_DECODE_MAX_DEPTH 1000
#define DEFAULT_ENCODE_INVALID_NUMBERS 0
#define DEFAULT_DECODE_INVALID_NUMBERS 1
#define DEFAULT_ENCODE_KEEP_BUFFER 0
#define DEFAULT_ENCODE_NUMBER_PRECISION 14
#ifdef DISABLE_INVALID_NUMBERS
#undef DEFAULT_DECODE_INVALID_NUMBERS
#define DEFAULT_DECODE_INVALID_NUMBERS 0
#endif
typedef enum {
T_OBJ_BEGIN,
T_OBJ_END,
T_ARR_BEGIN,
T_ARR_END,
T_STRING,
T_NUMBER,
T_BOOLEAN,
T_NULL,
T_COLON,
T_COMMA,
T_END,
T_WHITESPACE,
T_ERROR,
T_UNKNOWN
} json_token_type_t;
#if 0
static const char *json_token_type_name[] = {
"T_OBJ_BEGIN",
"T_OBJ_END",
"T_ARR_BEGIN",
"T_ARR_END",
"T_STRING",
"T_NUMBER",
"T_BOOLEAN",
"T_NULL",
"T_COLON",
"T_COMMA",
"T_END",
"T_WHITESPACE",
"T_ERROR",
"T_UNKNOWN",
NULL
};
#endif
static const char json_token_type_name[14][16] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = {
{'T','_','O','B','J','_','B','E','G','I','N',0},
{'T','_','O','B','J','_','E','N','D',0},
{'T','_','A','R','R','_','B','E','G','I','N',0},
{'T','_','A','R','R','_','E','N','D',0},
{'T','_','S','T','R','I','N','G',0},
{'T','_','N','U','M','B','E','R',0},
{'T','_','B','O','O','L','E','A','N',0},
{'T','_','N','U','L','L',0},
{'T','_','C','O','L','O','N',0},
{'T','_','C','O','M','M','A',0},
{'T','_','E','N','D',0},
{'T','_','W','H','I','T','E','S','P','A','C','E',0},
{'T','_','E','R','R','O','R',0},
{'T','_','U','N','K','N','O','W','N',0}
};
typedef struct {
// json_token_type_t ch2token[256]; // 256*4 = 1024 byte
// char escape2char[256]; /* Decoding */
/* encode_buf is only allocated and used when
* encode_keep_buffer is set */
strbuf_t encode_buf;
int encode_sparse_convert;
int encode_sparse_ratio;
int encode_sparse_safe;
int encode_max_depth;
int encode_invalid_numbers; /* 2 => Encode as "null" */
int encode_number_precision;
int encode_keep_buffer;
int decode_invalid_numbers;
int decode_max_depth;
} json_config_t;
typedef struct {
const char *data;
const char *ptr;
strbuf_t *tmp; /* Temporary storage for strings */
json_config_t *cfg;
int current_depth;
} json_parse_t;
typedef struct {
json_token_type_t type;
int index;
union {
const char *string;
double number;
int boolean;
} value;
int string_len;
} json_token_t;
#if 0
static const char *char2escape[256] = {
"\\u0000", "\\u0001", "\\u0002", "\\u0003",
"\\u0004", "\\u0005", "\\u0006", "\\u0007",
"\\b", "\\t", "\\n", "\\u000b",
"\\f", "\\r", "\\u000e", "\\u000f",
"\\u0010", "\\u0011", "\\u0012", "\\u0013",
"\\u0014", "\\u0015", "\\u0016", "\\u0017",
"\\u0018", "\\u0019", "\\u001a", "\\u001b",
"\\u001c", "\\u001d", "\\u001e", "\\u001f",
NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};
#endif
/* ===== HELPER FUNCTION ===== */
static const char escape_array[36][8] ICACHE_STORE_ATTR ICACHE_RODATA_ATTR = {
{'\\','u','0','0','0','0','\0','\0'},
{'\\','u','0','0','0','1','\0','\0'},
{'\\','u','0','0','0','2','\0','\0'},
{'\\','u','0','0','0','3','\0','\0'},
{'\\','u','0','0','0','4','\0','\0'},
{'\\','u','0','0','0','5','\0','\0'},
{'\\','u','0','0','0','6','\0','\0'},
{'\\','u','0','0','0','7','\0','\0'},
{'\\','b','\0','\0','\0','\0','\0','\0'},
{'\\','t','\0','\0','\0','\0','\0','\0'},
{'\\','n','\0','\0','\0','\0','\0','\0'},
{'\\','u','0','0','0','b','\0','\0'},
{'\\','f','\0','\0','\0','\0','\0','\0'},
{'\\','r','\0','\0','\0','\0','\0','\0'},
{'\\','u','0','0','0','e','\0','\0'},
{'\\','u','0','0','0','f','\0','\0'},
{'\\','u','0','0','1','0','\0','\0'},
{'\\','u','0','0','1','1','\0','\0'},
{'\\','u','0','0','1','2','\0','\0'},
{'\\','u','0','0','1','3','\0','\0'},
{'\\','u','0','0','1','4','\0','\0'},
{'\\','u','0','0','1','5','\0','\0'},
{'\\','u','0','0','1','6','\0','\0'},
{'\\','u','0','0','1','7','\0','\0'},
{'\\','u','0','0','1','8','\0','\0'},
{'\\','u','0','0','1','9','\0','\0'},
{'\\','u','0','0','1','a','\0','\0'},
{'\\','u','0','0','1','b','\0','\0'},
{'\\','u','0','0','1','c','\0','\0'},
{'\\','u','0','0','1','d','\0','\0'},
{'\\','u','0','0','1','e','\0','\0'},
{'\\','u','0','0','1','f','\0','\0'},
{'\\','\"','\0','\0','\0','\0','\0','\0'},
{'\\','/','\0','\0','\0','\0','\0','\0'},
{'\\','\\','\0','\0','\0','\0','\0','\0'},
{'\\','u','0','0','7','f','\0','\0'}
};
static const char *char2escape(unsigned char c){
if(c<32) return escape_array[c];
switch(c){
case 34: return escape_array[32];
case 47: return escape_array[33];
case 92: return escape_array[34];
case 127: return escape_array[35];
default:
return NULL;
}
}
static json_token_type_t ch2token(unsigned char c){
switch(c){
case '{': return T_OBJ_BEGIN;
case '}': return T_OBJ_END;
case '[': return T_ARR_BEGIN;
case ']': return T_ARR_END;
case ',': return T_COMMA;
case ':': return T_COLON;
case '\0': return T_END;
case ' ': return T_WHITESPACE;
case '\t': return T_WHITESPACE;
case '\n': return T_WHITESPACE;
case '\r': return T_WHITESPACE;
/* Update characters that require further processing */
case 'f': case 'i': case 'I': case 'n': case 'N': case 't': case '"': case '+': case '-':
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
return T_UNKNOWN;
default:
return T_ERROR;
}
}
static char escape2char(unsigned char c){
switch(c){
case '"': return '"';
case '\\': return '\\';
case '/': return '/';
case 'b': return '\b';
case 't': return '\t';
case 'n': return '\n';
case 'f': return '\f';
case 'r': return '\r';
case 'u': return 'u';
default:
return 0;
}
}
/* ===== CONFIGURATION ===== */
#if 0
static json_config_t *json_fetch_config(lua_State *l)
{
json_config_t *cfg;
cfg = lua_touserdata(l, lua_upvalueindex(1));
if (!cfg)
luaL_error(l, "BUG: Unable to fetch CJSON configuration");
return cfg;
}
/* Ensure the correct number of arguments have been provided.
* Pad with nil to allow other functions to simply check arg[i]
* to find whether an argument was provided */
static json_config_t *json_arg_init(lua_State *l, int args)
{
luaL_argcheck(l, lua_gettop(l) <= args, args + 1,
"found too many arguments");
while (lua_gettop(l) < args)
lua_pushnil(l);
return json_fetch_config(l);
}
/* Process integer options for configuration functions */
static int json_integer_option(lua_State *l, int optindex, int *setting,
int min, int max)
{
char errmsg[64];
int value;
if (!lua_isnil(l, optindex)) {
value = luaL_checkinteger(l, optindex);
c_sprintf(errmsg, "expected integer between %d and %d", min, max);
luaL_argcheck(l, min <= value && value <= max, 1, errmsg);
*setting = value;
}
lua_pushinteger(l, *setting);
return 1;
}
/* Process enumerated arguments for a configuration function */
static int json_enum_option(lua_State *l, int optindex, int *setting,
const char **options, int bool_true)
{
static const char *bool_options[] = { "off", "on", NULL };
if (!options) {
options = bool_options;
bool_true = 1;
}
if (!lua_isnil(l, optindex)) {
if (bool_true && lua_isboolean(l, optindex))
*setting = lua_toboolean(l, optindex) * bool_true;
else
*setting = luaL_checkoption(l, optindex, NULL, options);
}
if (bool_true && (*setting == 0 || *setting == bool_true))
lua_pushboolean(l, *setting);
else
lua_pushstring(l, options[*setting]);
return 1;
}
/* Configures handling of extremely sparse arrays:
* convert: Convert extremely sparse arrays into objects? Otherwise error.
* ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio
* safe: Always use an array when the max index <= safe */
static int json_cfg_encode_sparse_array(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 3);
json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1);
json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX);
json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX);
return 3;
}
/* Configures the maximum number of nested arrays/objects allowed when
* encoding */
static int json_cfg_encode_max_depth(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);
return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX);
}
/* Configures the maximum number of nested arrays/objects allowed when
* encoding */
static int json_cfg_decode_max_depth(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);
return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX);
}
/* Configures number precision when converting doubles to text */
static int json_cfg_encode_number_precision(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);
return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 14);
}
/* Configures JSON encoding buffer persistence */
static int json_cfg_encode_keep_buffer(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);
int old_value;
old_value = cfg->encode_keep_buffer;
json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1);
/* Init / free the buffer if the setting has changed */
if (old_value ^ cfg->encode_keep_buffer) {
if (cfg->encode_keep_buffer){
if(-1==strbuf_init(&cfg->encode_buf, 0))
return luaL_error(l, "not enough memory");
}
else
strbuf_free(&cfg->encode_buf);
}
return 1;
}
#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV)
void json_verify_invalid_number_setting(lua_State *l, int *setting)
{
if (*setting == 1) {
*setting = 0;
luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported.");
}
}
#else
#define json_verify_invalid_number_setting(l, s) do { } while(0)
#endif
static int json_cfg_encode_invalid_numbers(lua_State *l)
{
static const char *options[] = { "off", "on", "null", NULL };
json_config_t *cfg = json_arg_init(l, 1);
json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1);
json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);
return 1;
}
static int json_cfg_decode_invalid_numbers(lua_State *l)
{
json_config_t *cfg = json_arg_init(l, 1);
json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1);
json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers);
return 1;
}
static int json_destroy_config(lua_State *l)
{
json_config_t *cfg;
cfg = lua_touserdata(l, 1);
if (cfg)
strbuf_free(&cfg->encode_buf);
cfg = NULL;
return 0;
}
static void json_create_config(lua_State *l)
{
json_config_t *cfg;
int i;
cfg = lua_newuserdata(l, sizeof(*cfg));
/* Create GC method to clean up strbuf */
lua_newtable(l);
lua_pushcfunction(l, json_destroy_config);
lua_setfield(l, -2, "__gc");
lua_setmetatable(l, -2);
cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT;
cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO;
cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE;
cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH;
cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH;
cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS;
cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS;
cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;
cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION;
#if DEFAULT_ENCODE_KEEP_BUFFER > 0
strbuf_init(&cfg->encode_buf, 0);
#endif
/* Decoding init */
/* Tag all characters as an error */
for (i = 0; i < 256; i++)
cfg->ch2token[i] = T_ERROR;
/* Set tokens that require no further processing */
cfg->ch2token['{'] = T_OBJ_BEGIN;
cfg->ch2token['}'] = T_OBJ_END;
cfg->ch2token['['] = T_ARR_BEGIN;
cfg->ch2token[']'] = T_ARR_END;
cfg->ch2token[','] = T_COMMA;
cfg->ch2token[':'] = T_COLON;
cfg->ch2token['\0'] = T_END;
cfg->ch2token[' '] = T_WHITESPACE;
cfg->ch2token['\t'] = T_WHITESPACE;
cfg->ch2token['\n'] = T_WHITESPACE;
cfg->ch2token['\r'] = T_WHITESPACE;
/* Update characters that require further processing */
cfg->ch2token['f'] = T_UNKNOWN; /* false? */
cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */
cfg->ch2token['I'] = T_UNKNOWN;
cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */
cfg->ch2token['N'] = T_UNKNOWN;
cfg->ch2token['t'] = T_UNKNOWN; /* true? */
cfg->ch2token['"'] = T_UNKNOWN; /* string? */
cfg->ch2token['+'] = T_UNKNOWN; /* number? */
cfg->ch2token['-'] = T_UNKNOWN;
for (i = 0; i < 10; i++)
cfg->ch2token['0' + i] = T_UNKNOWN;
/* Lookup table for parsing escape characters */
for (i = 0; i < 256; i++)
cfg->escape2char[i] = 0; /* String error */
cfg->escape2char['"'] = '"';
cfg->escape2char['\\'] = '\\';
cfg->escape2char['/'] = '/';
cfg->escape2char['b'] = '\b';
cfg->escape2char['t'] = '\t';
cfg->escape2char['n'] = '\n';
cfg->escape2char['f'] = '\f';
cfg->escape2char['r'] = '\r';
cfg->escape2char['u'] = 'u'; /* Unicode parsing required */
}
#endif
json_config_t _cfg;
static json_config_t *json_fetch_config(lua_State *l)
{
return &_cfg;
}
static int cfg_init(json_config_t *cfg){
cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT;
cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO;
cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE;
cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH;
cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH;
cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS;
cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS;
cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER;
cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION;
#if DEFAULT_ENCODE_KEEP_BUFFER > 0
if(-1==strbuf_init(&cfg->encode_buf, 0)){
NODE_ERR("not enough memory\n");
return -1;
}
#endif
return 0;
}
/* ===== ENCODING ===== */
static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex,
const char *reason)
{
if (!cfg->encode_keep_buffer)
strbuf_free(json);
luaL_error(l, "Cannot serialise %s: %s",
lua_typename(l, lua_type(l, lindex)), reason);
}
/* json_append_string args:
* - lua_State
* - JSON strbuf
* - String (Lua stack index)
*
* Returns nothing. Doesn't remove string from Lua stack */
static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
{
const char *escstr;
int i;
const char *str;
size_t len;
str = lua_tolstring(l, lindex, &len);
/* Worst case is len * 6 (all unicode escapes).
* This buffer is reused constantly for small strings
* If there are any excess pages, they won't be hit anyway.
* This gains ~5% speedup. */
strbuf_ensure_empty_length(json, len * 6 + 2);
strbuf_append_char_unsafe(json, '\"');
for (i = 0; i < len; i++) {
escstr = char2escape((unsigned char)str[i]);
if (escstr){
int i;
char temp[8]; // for now, 8-bytes is enough.
for (i=0; i < 8; ++i)
{
temp[i] = byte_of_aligned_array(escstr, i);
if(temp[i]==0) break;
}
escstr = temp;
strbuf_append_string(json, escstr);
}
else
strbuf_append_char_unsafe(json, str[i]);
}
strbuf_append_char_unsafe(json, '\"');
}
/* Find the size of the array on the top of the Lua stack
* -1 object (not a pure array)
* >=0 elements in array
*/
static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json)
{
double k;
int max;
int items;
max = 0;
items = 0;
lua_pushnil(l);
/* table, startkey */
while (lua_next(l, -2) != 0) {
/* table, key, value */
if (lua_type(l, -2) == LUA_TNUMBER &&
(k = lua_tonumber(l, -2))) {
/* Integer >= 1 ? */
if (floor(k) == k && k >= 1) {
if (k > max)
max = k;
items++;
lua_pop(l, 1);
continue;
}
}
/* Must not be an array (non integer key) */
lua_pop(l, 2);
return -1;
}
/* Encode excessively sparse arrays as objects (if enabled) */
if (cfg->encode_sparse_ratio > 0 &&
max > items * cfg->encode_sparse_ratio &&
max > cfg->encode_sparse_safe) {
if (!cfg->encode_sparse_convert)
json_encode_exception(l, cfg, json, -1, "excessively sparse array");
return -1;
}
return max;
}
static void json_check_encode_depth(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json)
{
/* Ensure there are enough slots free to traverse a table (key,
* value) and push a string for a potential error message.
*
* Unlike "decode", the key and value are still on the stack when
* lua_checkstack() is called. Hence an extra slot for luaL_error()
* below is required just in case the next check to lua_checkstack()
* fails.
*
* While this won't cause a crash due to the EXTRA_STACK reserve
* slots, it would still be an improper use of the API. */
if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3))
return;
if (!cfg->encode_keep_buffer)
strbuf_free(json);
luaL_error(l, "Cannot serialise, excessive nesting (%d)",
current_depth);
}
static void json_append_data(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json);
/* json_append_array args:
* - lua_State
* - JSON strbuf
* - Size of passwd Lua array (top of stack) */
static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth,
strbuf_t *json, int array_length)
{
int comma, i;
strbuf_append_char(json, '[');
comma = 0;
for (i = 1; i <= array_length; i++) {
if (comma)
strbuf_append_char(json, ',');
else
comma = 1;
lua_rawgeti(l, -1, i);
json_append_data(l, cfg, current_depth, json);
lua_pop(l, 1);
}
strbuf_append_char(json, ']');
}
static void json_append_number(lua_State *l, json_config_t *cfg,
strbuf_t *json, int lindex)
{
double num = lua_tonumber(l, lindex);
int len;
if (cfg->encode_invalid_numbers == 0) {
/* Prevent encoding invalid numbers */
if (isinf(num) || isnan(num))
json_encode_exception(l, cfg, json, lindex,
"must not be NaN or Infinity");
} else if (cfg->encode_invalid_numbers == 1) {
/* Encode NaN/Infinity separately to ensure Javascript compatible
* values are used. */
if (isnan(num)) {
strbuf_append_mem(json, "NaN", 3);
return;
}
if (isinf(num)) {
if (num < 0)
strbuf_append_mem(json, "-Infinity", 9);
else
strbuf_append_mem(json, "Infinity", 8);
return;
}
} else {
/* Encode invalid numbers as "null" */
if (isinf(num) || isnan(num)) {
strbuf_append_mem(json, "null", 4);
return;
}
}
strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE);
// len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision);
c_sprintf(strbuf_empty_ptr(json), LUA_NUMBER_FMT, (LUA_NUMBER)num);
len = c_strlen(strbuf_empty_ptr(json));
strbuf_extend_length(json, len);
}
static void json_append_object(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json)
{
int comma, keytype;
/* Object */
strbuf_append_char(json, '{');
lua_pushnil(l);
/* table, startkey */
comma = 0;
while (lua_next(l, -2) != 0) {
if (comma)
strbuf_append_char(json, ',');
else
comma = 1;
/* table, key, value */
keytype = lua_type(l, -2);
if (keytype == LUA_TNUMBER) {
strbuf_append_char(json, '"');
json_append_number(l, cfg, json, -2);
strbuf_append_mem(json, "\":", 2);
} else if (keytype == LUA_TSTRING) {
json_append_string(l, json, -2);
strbuf_append_char(json, ':');
} else {
json_encode_exception(l, cfg, json, -2,
"table key must be a number or string");
/* never returns */
}
/* table, key, value */
json_append_data(l, cfg, current_depth, json);
lua_pop(l, 1);
/* table, key */
}
strbuf_append_char(json, '}');
}
/* Serialise Lua data into JSON string. */
static void json_append_data(lua_State *l, json_config_t *cfg,
int current_depth, strbuf_t *json)
{
int len;
switch (lua_type(l, -1)) {
case LUA_TSTRING:
json_append_string(l, json, -1);
break;
case LUA_TNUMBER:
json_append_number(l, cfg, json, -1);
break;
case LUA_TBOOLEAN:
if (lua_toboolean(l, -1))
strbuf_append_mem(json, "true", 4);
else
strbuf_append_mem(json, "false", 5);
break;
case LUA_TTABLE:
current_depth++;
json_check_encode_depth(l, cfg, current_depth, json);
len = lua_array_length(l, cfg, json);
if (len > 0)
json_append_array(l, cfg, current_depth, json, len);
else
json_append_object(l, cfg, current_depth, json);
break;
case LUA_TNIL:
strbuf_append_mem(json, "null", 4);
break;
case LUA_TLIGHTUSERDATA:
if (lua_touserdata(l, -1) == NULL) {
strbuf_append_mem(json, "null", 4);
break;
}
default:
/* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD,
* and LUA_TLIGHTUSERDATA) cannot be serialised */
json_encode_exception(l, cfg, json, -1, "type not supported");
/* never returns */
}
}
static int json_encode(lua_State *l)
{
json_config_t *cfg = json_fetch_config(l);
strbuf_t local_encode_buf;
strbuf_t *encode_buf;
char *json;
int len;
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
if (!cfg->encode_keep_buffer) {
/* Use private buffer */
encode_buf = &local_encode_buf;
if(-1==strbuf_init(encode_buf, 0))
return luaL_error(l, "not enough memory");
} else {
/* Reuse existing buffer */
encode_buf = &cfg->encode_buf;
strbuf_reset(encode_buf);
}
json_append_data(l, cfg, 0, encode_buf);
json = strbuf_string(encode_buf, &len);
lua_pushlstring(l, json, len);
if (!cfg->encode_keep_buffer)
strbuf_free(encode_buf);
return 1;
}
/* ===== DECODING ===== */
static void json_process_value(lua_State *l, json_parse_t *json,
json_token_t *token);
static int hexdigit2int(char hex)
{
if ('0' <= hex && hex <= '9')
return hex - '0';
/* Force lowercase */
hex |= 0x20;
if ('a' <= hex && hex <= 'f')
return 10 + hex - 'a';
return -1;
}
static int decode_hex4(const char *hex)
{
int digit[4];
int i;
/* Convert ASCII hex digit to numeric digit
* Note: this returns an error for invalid hex digits, including
* NULL */
for (i = 0; i < 4; i++) {
digit[i] = hexdigit2int(hex[i]);
if (digit[i] < 0) {
return -1;
}
}
return (digit[0] << 12) +
(digit[1] << 8) +
(digit[2] << 4) +
digit[3];
}
/* Converts a Unicode codepoint to UTF-8.
* Returns UTF-8 string length, and up to 4 bytes in *utf8 */
static int codepoint_to_utf8(char *utf8, int codepoint)
{
/* 0xxxxxxx */
if (codepoint <= 0x7F) {
utf8[0] = codepoint;
return 1;
}
/* 110xxxxx 10xxxxxx */
if (codepoint <= 0x7FF) {
utf8[0] = (codepoint >> 6) | 0xC0;
utf8[1] = (codepoint & 0x3F) | 0x80;
return 2;
}
/* 1110xxxx 10xxxxxx 10xxxxxx */
if (codepoint <= 0xFFFF) {
utf8[0] = (codepoint >> 12) | 0xE0;
utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80;
utf8[2] = (codepoint & 0x3F) | 0x80;
return 3;
}
/* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
if (codepoint <= 0x1FFFFF) {
utf8[0] = (codepoint >> 18) | 0xF0;
utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80;
utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80;
utf8[3] = (codepoint & 0x3F) | 0x80;
return 4;
}
return 0;
}
/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX
* \u is guaranteed to exist, but the remaining hex characters may be
* missing.
* Translate to UTF-8 and append to temporary token string.
* Must advance index to the next character to be processed.
* Returns: 0 success
* -1 error
*/
static int json_append_unicode_escape(json_parse_t *json)
{
char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */
int codepoint;
int surrogate_low;
int len;
int escape_len = 6;
/* Fetch UTF-16 code unit */
codepoint = decode_hex4(json->ptr + 2);
if (codepoint < 0)
return -1;
/* UTF-16 surrogate pairs take the following 2 byte form:
* 11011 x yyyyyyyyyy
* When x = 0: y is the high 10 bits of the codepoint
* x = 1: y is the low 10 bits of the codepoint
*
* Check for a surrogate pair (high or low) */
if ((codepoint & 0xF800) == 0xD800) {
/* Error if the 1st surrogate is not high */
if (codepoint & 0x400)
return -1;
/* Ensure the next code is a unicode escape */
if (*(json->ptr + escape_len) != '\\' ||
*(json->ptr + escape_len + 1) != 'u') {
return -1;
}
/* Fetch the next codepoint */
surrogate_low = decode_hex4(json->ptr + 2 + escape_len);
if (surrogate_low < 0)
return -1;
/* Error if the 2nd code is not a low surrogate */
if ((surrogate_low & 0xFC00) != 0xDC00)
return -1;
/* Calculate Unicode codepoint */
codepoint = (codepoint & 0x3FF) << 10;
surrogate_low &= 0x3FF;
codepoint = (codepoint | surrogate_low) + 0x10000;
escape_len = 12;
}
/* Convert codepoint to UTF-8 */
len = codepoint_to_utf8(utf8, codepoint);
if (!len)
return -1;
/* Append bytes and advance parse index */
strbuf_append_mem_unsafe(json->tmp, utf8, len);
json->ptr += escape_len;
return 0;
}
static void json_set_token_error(json_token_t *token, json_parse_t *json,
const char *errtype)
{
token->type = T_ERROR;
token->index = json->ptr - json->data;
token->value.string = errtype;
}
static void json_next_string_token(json_parse_t *json, json_token_t *token)
{
// char *escape2char = json->cfg->escape2char;
char ch;
/* Caller must ensure a string is next */
if(!(*json->ptr == '"')) return;
/* Skip " */
json->ptr++;
/* json->tmp is the temporary strbuf used to accumulate the
* decoded string value.
* json->tmp is sized to handle JSON containing only a string value.
*/
strbuf_reset(json->tmp);
while ((ch = *json->ptr) != '"') {
if (!ch) {
/* Premature end of the string */
json_set_token_error(token, json, "unexpected end of string");
return;
}
/* Handle escapes */
if (ch == '\\') {
/* Fetch escape character */
ch = *(json->ptr + 1);
/* Translate escape code and append to tmp string */
ch = escape2char((unsigned char)ch);
if (ch == 'u') {
if (json_append_unicode_escape(json) == 0)
continue;
json_set_token_error(token, json,
"invalid unicode escape code");
return;
}
if (!ch) {
json_set_token_error(token, json, "invalid escape code");
return;
}
/* Skip '\' */
json->ptr++;
}
/* Append normal character or translated single character
* Unicode escapes are handled above */
strbuf_append_char_unsafe(json->tmp, ch);
json->ptr++;
}
json->ptr++; /* Eat final quote (") */
strbuf_ensure_null(json->tmp);
token->type = T_STRING;
token->value.string = strbuf_string(json->tmp, &token->string_len);
}
/* JSON numbers should take the following form:
* -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)?
*
* json_next_number_token() uses strtod() which allows other forms:
* - numbers starting with '+'
* - NaN, -NaN, infinity, -infinity
* - hexadecimal numbers
* - numbers with leading zeros
*
* json_is_invalid_number() detects "numbers" which may pass strtod()'s
* error checking, but should not be allowed with strict JSON.
*
* json_is_invalid_number() may pass numbers which cause strtod()
* to generate an error.
*/
static int json_is_invalid_number(json_parse_t *json)
{
const char *p = json->ptr;
/* Reject numbers starting with + */
if (*p == '+')
return 1;
/* Skip minus sign if it exists */
if (*p == '-')
p++;
/* Reject numbers starting with 0x, or leading zeros */
if (*p == '0') {
int ch2 = *(p + 1);
if ((ch2 | 0x20) == 'x' || /* Hex */
('0' <= ch2 && ch2 <= '9')) /* Leading zero */
return 1;
return 0;
} else if (*p <= '9') {
return 0; /* Ordinary number */
}
char tmp[4]; // conv to lower. because c_strncasecmp == c_strcmp
int i;
for (i = 0; i < 3; ++i)
{
if(p[i]!=0)
tmp[i] = tolower(p[i]);
else
tmp[i] = 0;
}
tmp[3] = 0;
/* Reject inf/nan */
if (!c_strncasecmp(tmp, "inf", 3))
return 1;
if (!c_strncasecmp(tmp, "nan", 3))
return 1;
/* Pass all other numbers which may still be invalid, but
* strtod() will catch them. */
return 0;
}
static void json_next_number_token(json_parse_t *json, json_token_t *token)
{
char *endptr;
token->type = T_NUMBER;
token->value.number = fpconv_strtod(json->ptr, &endptr);
if (json->ptr == endptr)
json_set_token_error(token, json, "invalid number");
else
json->ptr = endptr; /* Skip the processed number */
return;
}
/* Fills in the token struct.
* T_STRING will return a pointer to the json_parse_t temporary string
* T_ERROR will leave the json->ptr pointer at the error.
*/
static void json_next_token(json_parse_t *json, json_token_t *token)
{
// const json_token_type_t *ch2token = json->cfg->ch2token;
int ch;
/* Eat whitespace. */
while (1) {
ch = (unsigned char)*(json->ptr);
token->type = ch2token(ch);
if (token->type != T_WHITESPACE)
break;
json->ptr++;
}
/* Store location of new token. Required when throwing errors
* for unexpected tokens (syntax errors). */
token->index = json->ptr - json->data;
/* Don't advance the pointer for an error or the end */
if (token->type == T_ERROR) {
json_set_token_error(token, json, "invalid token");
return;
}
if (token->type == T_END) {
return;
}
/* Found a known single character token, advance index and return */
if (token->type != T_UNKNOWN) {
json->ptr++;
return;
}
/* Process characters which triggered T_UNKNOWN
*
* Must use strncmp() to match the front of the JSON string.
* JSON identifier must be lowercase.
* When strict_numbers if disabled, either case is allowed for
* Infinity/NaN (since we are no longer following the spec..) */
if (ch == '"') {
json_next_string_token(json, token);
return;
} else if (ch == '-' || ('0' <= ch && ch <= '9')) {
if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) {
json_set_token_error(token, json, "invalid number");
return;
}
json_next_number_token(json, token);
return;
} else if (!c_strncmp(json->ptr, "true", 4)) {
token->type = T_BOOLEAN;
token->value.boolean = 1;
json->ptr += 4;
return;
} else if (!c_strncmp(json->ptr, "false", 5)) {
token->type = T_BOOLEAN;
token->value.boolean = 0;
json->ptr += 5;
return;
} else if (!c_strncmp(json->ptr, "null", 4)) {
token->type = T_NULL;
json->ptr += 4;
return;
} else if (json->cfg->decode_invalid_numbers &&
json_is_invalid_number(json)) {
/* When decode_invalid_numbers is enabled, only attempt to process
* numbers we know are invalid JSON (Inf, NaN, hex)
* This is required to generate an appropriate token error,
* otherwise all bad tokens will register as "invalid number"
*/
json_next_number_token(json, token);
return;
}
/* Token starts with t/f/n but isn't recognised above. */
json_set_token_error(token, json, "invalid token");
}
/* This function does not return.
* DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED.
* The only supported exception is the temporary parser string
* json->tmp struct.
* json and token should exist on the stack somewhere.
* luaL_error() will long_jmp and release the stack */
static void json_throw_parse_error(lua_State *l, json_parse_t *json,
const char *exp, json_token_t *token)
{
const char *found;
char temp[16]; // for now, 16-bytes is enough.
strbuf_free(json->tmp);
if (token->type == T_ERROR)
found = token->value.string;
else
{
found = json_token_type_name[token->type];
int i;
for (i=0; i < 16; ++i)
{
temp[i] = byte_of_aligned_array(found, i);
if(temp[i]==0) break;
}
found = temp;
}
/* Note: token->index is 0 based, display starting from 1 */
luaL_error(l, "Expected %s but found %s at character %d",
exp, found, token->index + 1);
}
static inline void json_decode_ascend(json_parse_t *json)
{
json->current_depth--;
}
static void json_decode_descend(lua_State *l, json_parse_t *json, int slots)
{
json->current_depth++;
if (json->current_depth <= json->cfg->decode_max_depth &&
lua_checkstack(l, slots)) {
return;
}
strbuf_free(json->tmp);
luaL_error(l, "Found too many nested data structures (%d) at character %d",
json->current_depth, json->ptr - json->data);
}
static void json_parse_object_context(lua_State *l, json_parse_t *json)
{
json_token_t token;
/* 3 slots required:
* .., table, key, value */
json_decode_descend(l, json, 3);
lua_newtable(l);
json_next_token(json, &token);
/* Handle empty objects */
if (token.type == T_OBJ_END) {
json_decode_ascend(json);
return;
}
while (1) {
if (token.type != T_STRING)
json_throw_parse_error(l, json, "object key string", &token);
/* Push key */
lua_pushlstring(l, token.value.string, token.string_len);
json_next_token(json, &token);
if (token.type != T_COLON)
json_throw_parse_error(l, json, "colon", &token);
/* Fetch value */
json_next_token(json, &token);
json_process_value(l, json, &token);
/* Set key = value */
lua_rawset(l, -3);
json_next_token(json, &token);
if (token.type == T_OBJ_END) {
json_decode_ascend(json);
return;
}
if (token.type != T_COMMA)
json_throw_parse_error(l, json, "comma or object end", &token);
json_next_token(json, &token);
}
}
/* Handle the array context */
static void json_parse_array_context(lua_State *l, json_parse_t *json)
{
json_token_t token;
int i;
/* 2 slots required:
* .., table, value */
json_decode_descend(l, json, 2);
lua_newtable(l);
json_next_token(json, &token);
/* Handle empty arrays */
if (token.type == T_ARR_END) {
json_decode_ascend(json);
return;
}
for (i = 1; ; i++) {
json_process_value(l, json, &token);
lua_rawseti(l, -2, i); /* arr[i] = value */
json_next_token(json, &token);
if (token.type == T_ARR_END) {
json_decode_ascend(json);
return;
}
if (token.type != T_COMMA)
json_throw_parse_error(l, json, "comma or array end", &token);
json_next_token(json, &token);
}
}
/* Handle the "value" context */
static void json_process_value(lua_State *l, json_parse_t *json,
json_token_t *token)
{
switch (token->type) {
case T_STRING:
lua_pushlstring(l, token->value.string, token->string_len);
break;;
case T_NUMBER:
lua_pushnumber(l, token->value.number);
break;;
case T_BOOLEAN:
lua_pushboolean(l, token->value.boolean);
break;;
case T_OBJ_BEGIN:
json_parse_object_context(l, json);
break;;
case T_ARR_BEGIN:
json_parse_array_context(l, json);
break;;
case T_NULL:
/* In Lua, setting "t[k] = nil" will delete k from the table.
* Hence a NULL pointer lightuserdata object is used instead */
lua_pushlightuserdata(l, NULL);
break;;
default:
json_throw_parse_error(l, json, "value", token);
}
}
static int json_decode(lua_State *l)
{
json_parse_t json;
json_token_t token;
size_t json_len;
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
json.cfg = json_fetch_config(l);
json.data = luaL_checklstring(l, 1, &json_len);
json.current_depth = 0;
json.ptr = json.data;
/* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3)
*
* CJSON can support any simple data type, hence only the first
* character is guaranteed to be ASCII (at worst: '"'). This is
* still enough to detect whether the wrong encoding is in use. */
if (json_len >= 2 && (!json.data[0] || !json.data[1]))
luaL_error(l, "JSON parser does not support UTF-16 or UTF-32");
/* Ensure the temporary buffer can hold the entire string.
* This means we no longer need to do length checks since the decoded
* string must be smaller than the entire json string */
json.tmp = strbuf_new(json_len);
if(json.tmp == NULL){
return luaL_error(l, "not enough memory");
}
json_next_token(&json, &token);
json_process_value(l, &json, &token);
/* Ensure there is no more input left */
json_next_token(&json, &token);
if (token.type != T_END)
json_throw_parse_error(l, &json, "the end", &token);
strbuf_free(json.tmp);
return 1;
}
/* ===== INITIALISATION ===== */
#if 0
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502
/* Compatibility for Lua 5.1.
*
* luaL_setfuncs() is used to create a module table where the functions have
* json_config_t as their first upvalue. Code borrowed from Lua 5.2 source. */
static void luaL_setfuncs (lua_State *l, const luaL_Reg *reg, int nup)
{
int i;
luaL_checkstack(l, nup, "too many upvalues");
for (; reg->name != NULL; reg++) { /* fill the table with given functions */
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(l, -nup);
lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */
lua_setfield(l, -(nup + 2), reg->name);
}
lua_pop(l, nup); /* remove upvalues */
}
#endif
/* Call target function in protected mode with all supplied args.
* Assumes target function only returns a single non-nil value.
* Convert and return thrown errors as: nil, "error message" */
static int json_protect_conversion(lua_State *l)
{
int err;
/* Deliberately throw an error for invalid arguments */
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
/* pcall() the function stored as upvalue(1) */
lua_pushvalue(l, lua_upvalueindex(1));
lua_insert(l, 1);
err = lua_pcall(l, 1, 1, 0);
if (!err)
return 1;
if (err == LUA_ERRRUN) {
lua_pushnil(l);
lua_insert(l, -2);
return 2;
}
/* Since we are not using a custom error handler, the only remaining
* errors are memory related */
return luaL_error(l, "Memory allocation error in CJSON protected call");
}
/* Return cjson module table */
static int lua_cjson_new(lua_State *l)
{
/* Initialise number conversions */
fpconv_init();
/* cjson module table */
lua_newtable(l);
/* Register functions with config data as upvalue */
json_create_config(l);
luaL_setfuncs(l, reg, 1);
/* Set cjson.null */
lua_pushlightuserdata(l, NULL);
lua_setfield(l, -2, "null");
/* Set module name / version fields */
lua_pushliteral(l, CJSON_MODNAME);
lua_setfield(l, -2, "_NAME");
lua_pushliteral(l, CJSON_VERSION);
lua_setfield(l, -2, "_VERSION");
return 1;
}
/* Return cjson.safe module table */
static int lua_cjson_safe_new(lua_State *l)
{
const char *func[] = { "decode", "encode", NULL };
int i;
lua_cjson_new(l);
/* Fix new() method */
lua_pushcfunction(l, lua_cjson_safe_new);
lua_setfield(l, -2, "new");
for (i = 0; func[i]; i++) {
lua_getfield(l, -1, func[i]);
lua_pushcclosure(l, json_protect_conversion, 1);
lua_setfield(l, -2, func[i]);
}
return 1;
}
int luaopen_cjson(lua_State *l)
{
lua_cjson_new(l);
#ifdef ENABLE_CJSON_GLOBAL
/* Register a global "cjson" table. */
lua_pushvalue(l, -1);
lua_setglobal(l, CJSON_MODNAME);
#endif
/* Return cjson table */
return 1;
}
int luaopen_cjson_safe(lua_State *l)
{
lua_cjson_safe_new(l);
/* Return cjson.safe table */
return 1;
}
#endif
// Module function map
static const LUA_REG_TYPE cjson_map[] = {
{ LSTRKEY( "encode" ), LFUNCVAL( json_encode ) },
{ LSTRKEY( "decode" ), LFUNCVAL( json_decode ) },
//{ LSTRKEY( "encode_sparse_array" ), LFUNCVAL( json_cfg_encode_sparse_array ) },
//{ LSTRKEY( "encode_max_depth" ), LFUNCVAL( json_cfg_encode_max_depth ) },
//{ LSTRKEY( "decode_max_depth" ), LFUNCVAL( json_cfg_decode_max_depth ) },
//{ LSTRKEY( "encode_number_precision" ), LFUNCVAL( json_cfg_encode_number_precision ) },
//{ LSTRKEY( "encode_keep_buffer" ), LFUNCVAL( json_cfg_encode_keep_buffer ) },
//{ LSTRKEY( "encode_invalid_numbers" ), LFUNCVAL( json_cfg_encode_invalid_numbers ) },
//{ LSTRKEY( "decode_invalid_numbers" ), LFUNCVAL( json_cfg_decode_invalid_numbers ) },
//{ LSTRKEY( "new" ), LFUNCVAL( lua_cjson_new ) },
{ LNILKEY, LNILVAL }
};
int luaopen_cjson( lua_State *L )
{
/* Initialise number conversions */
// fpconv_init(); // not needed for a specific cpu.
if(-1==cfg_init(&_cfg)){
return luaL_error(L, "BUG: Unable to init config for cjson");;
}
return 0;
}
NODEMCU_MODULE(CJSON, "cjson", cjson_map, luaopen_cjson);
/* vi:ai et sw=4 ts=4:
*/
......@@ -53,7 +53,7 @@ static uint64_t lcron_parsepart(lua_State *L, char *str, char **end, uint8_t min
if (val < min || val > max) {
return luaL_error(L, "invalid spec (val %d out of range %d..%d)", val, min, max);
}
res |= (1 << (val - min));
res |= (uint64_t)1 << (val - min);
if (**end != ',') break;
str = *end + 1;
}
......@@ -67,11 +67,11 @@ static int lcron_parsedesc(lua_State *L, char *str, struct cronent_desc *desc) {
if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str);
desc->hour = lcron_parsepart(L, s + 1, &s, 0, 23);
if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str);
desc->dow = lcron_parsepart(L, s + 1, &s, 0, 6);
if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str);
desc->dom = lcron_parsepart(L, s + 1, &s, 1, 31);
if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str);
desc->mon = lcron_parsepart(L, s + 1, &s, 1, 12);
if (*s != ' ') return luaL_error(L, "invalid spec (separator @%d)", s - str);
desc->dow = lcron_parsepart(L, s + 1, &s, 0, 6);
if (*s != 0) return luaL_error(L, "invalid spec (trailing @%d)", s - str);
return 0;
}
......
......@@ -144,6 +144,7 @@ static void enduser_setup_ap_stop(void);
static void enduser_setup_check_station(void *p);
static void enduser_setup_debug(int line, const char *str);
static char ipaddr[16];
#if ENDUSER_SETUP_DEBUG_ENABLE
#define ENDUSER_SETUP_DEBUG(str) enduser_setup_debug(__LINE__, str)
......@@ -285,6 +286,8 @@ static void enduser_setup_check_station(void *p)
return;
}
c_sprintf (ipaddr, "%d.%d.%d.%d", IP2STR(&ip.ip.addr));
state->success = 1;
state->lastStationStatus = 5; /* We have an IP Address, so the status is 5 (as of SDK 1.5.1) */
state->connecting = 0;
......@@ -874,7 +877,23 @@ static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client)
uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status ();
char json_payload[64];
struct ip_info ip_info;
if (curr_status == 5)
{
wifi_get_ip_info(STATION_IF , &ip_info);
/* If IP address not yet available, get now */
if (strlen(ipaddr) == 0)
{
c_sprintf(ipaddr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr));
}
c_sprintf(json_payload, "{\"deviceid\":\"%s\", \"status\":%d}", ipaddr, curr_status);
}
else
{
c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status);
}
const char fmt[] =
"HTTP/1.1 200 OK\r\n"
......@@ -1684,6 +1703,8 @@ static int enduser_setup_start(lua_State *L)
/* Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation. */
ENDUSER_SETUP_DEBUG("enduser_setup_start");
ipaddr[0] = '\0';
if (!do_station_cfg_handle)
{
do_station_cfg_handle = task_get_id(do_station_cfg);
......
......@@ -214,14 +214,13 @@ static int file_open( lua_State* L )
static int file_list( lua_State* L )
{
vfs_dir *dir;
vfs_item *item;
if (dir = vfs_opendir("")) {
lua_newtable( L );
while (item = vfs_readdir(dir)) {
lua_pushinteger(L, vfs_item_size(item));
lua_setfield(L, -2, vfs_item_name(item));
vfs_closeitem(item);
struct vfs_stat stat;
while (vfs_readdir(dir, &stat) == VFS_RES_OK) {
lua_pushinteger(L, stat.size);
lua_setfield(L, -2, stat.name);
}
vfs_closedir(dir);
return 1;
......@@ -270,11 +269,8 @@ static int file_exists( lua_State* L )
const char *basename = vfs_basename( fname );
luaL_argcheck(L, c_strlen(basename) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid");
vfs_item *stat = vfs_stat((char *)fname);
lua_pushboolean(L, stat ? 1 : 0);
if (stat) vfs_closeitem(stat);
struct vfs_stat stat;
lua_pushboolean(L, vfs_stat((char *)fname, &stat) == VFS_RES_OK ? 1 : 0);
return 1;
}
......@@ -332,58 +328,54 @@ static int file_stat( lua_State* L )
const char *fname = luaL_checklstring( L, 1, &len );
luaL_argcheck( L, c_strlen(fname) <= FS_OBJ_NAME_LEN && c_strlen(fname) == len, 1, "filename invalid" );
vfs_item *stat = vfs_stat( (char *)fname );
if (!stat) {
struct vfs_stat stat;
if (vfs_stat( (char *)fname, &stat ) != VFS_RES_OK) {
lua_pushnil( L );
return 1;
}
lua_createtable( L, 0, 7 );
lua_pushinteger( L, vfs_item_size( stat ) );
lua_pushinteger( L, stat.size );
lua_setfield( L, -2, "size" );
lua_pushstring( L, vfs_item_name( stat ) );
lua_pushstring( L, stat.name );
lua_setfield( L, -2, "name" );
lua_pushboolean( L, vfs_item_is_dir( stat ) );
lua_pushboolean( L, stat.is_dir );
lua_setfield( L, -2, "is_dir" );
lua_pushboolean( L, vfs_item_is_rdonly( stat ) );
lua_pushboolean( L, stat.is_rdonly );
lua_setfield( L, -2, "is_rdonly" );
lua_pushboolean( L, vfs_item_is_hidden( stat ) );
lua_pushboolean( L, stat.is_hidden );
lua_setfield( L, -2, "is_hidden" );
lua_pushboolean( L, vfs_item_is_sys( stat ) );
lua_pushboolean( L, stat.is_sys );
lua_setfield( L, -2, "is_sys" );
lua_pushboolean( L, vfs_item_is_arch( stat ) );
lua_pushboolean( L, stat.is_arch );
lua_setfield( L, -2, "is_arch" );
// time stamp as sub-table
vfs_time tm;
int got_time = VFS_RES_OK == vfs_item_time( stat, &tm ) ? TRUE : FALSE;
lua_createtable( L, 0, 6 );
lua_pushinteger( L, got_time ? tm.year : FILE_TIMEDEF_YEAR );
lua_pushinteger( L, stat.tm_valid ? stat.tm.year : FILE_TIMEDEF_YEAR );
lua_setfield( L, -2, "year" );
lua_pushinteger( L, got_time ? tm.mon : FILE_TIMEDEF_MON );
lua_pushinteger( L, stat.tm_valid ? stat.tm.mon : FILE_TIMEDEF_MON );
lua_setfield( L, -2, "mon" );
lua_pushinteger( L, got_time ? tm.day : FILE_TIMEDEF_DAY );
lua_pushinteger( L, stat.tm_valid ? stat.tm.day : FILE_TIMEDEF_DAY );
lua_setfield( L, -2, "day" );
lua_pushinteger( L, got_time ? tm.hour : FILE_TIMEDEF_HOUR );
lua_pushinteger( L, stat.tm_valid ? stat.tm.hour : FILE_TIMEDEF_HOUR );
lua_setfield( L, -2, "hour" );
lua_pushinteger( L, got_time ? tm.min : FILE_TIMEDEF_MIN );
lua_pushinteger( L, stat.tm_valid ? stat.tm.min : FILE_TIMEDEF_MIN );
lua_setfield( L, -2, "min" );
lua_pushinteger( L, got_time ? tm.sec : FILE_TIMEDEF_SEC );
lua_pushinteger( L, stat.tm_valid ? stat.tm.sec : FILE_TIMEDEF_SEC );
lua_setfield( L, -2, "sec" );
lua_setfield( L, -2, "time" );
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment